summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-26 06:35:07 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-26 06:35:07 +0000
commitcfcc93eec947df1e540a41375912a5fa84ce75c3 (patch)
tree8575a324f257e2c8533986c80d3418e337a3ff68 /sbin
parentf185bf7f09d364269a9c6dcee37a105241cf240f (diff)
downloadFreeBSD-src-cfcc93eec947df1e540a41375912a5fa84ce75c3.zip
FreeBSD-src-cfcc93eec947df1e540a41375912a5fa84ce75c3.tar.gz
BSD 4.4 Lite sbin Sources
Note: XNSrouted and routed NOT imported here, they shall be imported with usr.sbin.
Diffstat (limited to 'sbin')
-rw-r--r--sbin/bsdlabel/Makefile14
-rw-r--r--sbin/bsdlabel/bsdlabel.8343
-rw-r--r--sbin/bsdlabel/bsdlabel.c1314
-rw-r--r--sbin/bsdlabel/disklabel.5.5384
-rw-r--r--sbin/bsdlabel/dkcksum.c53
-rw-r--r--sbin/bsdlabel/pathnames.h40
-rw-r--r--sbin/fsck_ffs/Makefile9
-rw-r--r--sbin/fsck_ffs/SMM.doc/0.t150
-rw-r--r--sbin/fsck_ffs/SMM.doc/1.t83
-rw-r--r--sbin/fsck_ffs/SMM.doc/2.t265
-rw-r--r--sbin/fsck_ffs/SMM.doc/3.t439
-rw-r--r--sbin/fsck_ffs/SMM.doc/4.t1424
-rw-r--r--sbin/fsck_ffs/SMM.doc/Makefile7
-rw-r--r--sbin/fsck_ffs/dir.c681
-rw-r--r--sbin/fsck_ffs/fsck.h215
-rw-r--r--sbin/fsck_ffs/fsck_ffs.8291
-rw-r--r--sbin/fsck_ffs/inode.c543
-rw-r--r--sbin/fsck_ffs/main.c318
-rw-r--r--sbin/fsck_ffs/pass1.c314
-rw-r--r--sbin/fsck_ffs/pass1b.c99
-rw-r--r--sbin/fsck_ffs/pass2.c430
-rw-r--r--sbin/fsck_ffs/pass3.c71
-rw-r--r--sbin/fsck_ffs/pass4.c133
-rw-r--r--sbin/fsck_ffs/pass5.c319
-rw-r--r--sbin/fsck_ffs/preen.c354
-rw-r--r--sbin/fsck_ffs/setup.c466
-rw-r--r--sbin/fsck_ffs/utilities.c566
-rw-r--r--sbin/fsck_ifs/Makefile9
-rw-r--r--sbin/fsck_ifs/dir.c681
-rw-r--r--sbin/fsck_ifs/fsck.h215
-rw-r--r--sbin/fsck_ifs/fsck_ifs.8291
-rw-r--r--sbin/fsck_ifs/inode.c543
-rw-r--r--sbin/fsck_ifs/main.c318
-rw-r--r--sbin/fsck_ifs/pass1.c314
-rw-r--r--sbin/fsck_ifs/pass1b.c99
-rw-r--r--sbin/fsck_ifs/pass2.c430
-rw-r--r--sbin/fsck_ifs/pass3.c71
-rw-r--r--sbin/fsck_ifs/pass4.c133
-rw-r--r--sbin/fsck_ifs/pass5.c319
-rw-r--r--sbin/fsck_ifs/preen.c354
-rw-r--r--sbin/fsck_ifs/setup.c466
-rw-r--r--sbin/fsck_ifs/utilities.c566
-rw-r--r--sbin/mount_ifs/Makefile8
-rw-r--r--sbin/mount_ifs/getmntopts.3163
-rw-r--r--sbin/mount_ifs/getmntopts.c87
-rw-r--r--sbin/mount_ifs/mntopts.h72
-rw-r--r--sbin/mount_ifs/mount.8264
-rw-r--r--sbin/mount_ifs/mount.c512
-rw-r--r--sbin/mount_ifs/mount_ufs.c131
-rw-r--r--sbin/mount_ifs/pathnames.h38
-rw-r--r--sbin/mount_nullfs/Makefile11
-rw-r--r--sbin/mount_nullfs/mount_nullfs.8220
-rw-r--r--sbin/mount_nullfs/mount_nullfs.c129
-rw-r--r--sbin/mount_portalfs/Makefile15
-rw-r--r--sbin/mount_portalfs/activate.c215
-rw-r--r--sbin/mount_portalfs/conf.c329
-rw-r--r--sbin/mount_portalfs/mount_portalfs.8136
-rw-r--r--sbin/mount_portalfs/mount_portalfs.c261
-rw-r--r--sbin/mount_portalfs/pathnames.h44
-rw-r--r--sbin/mount_portalfs/portal.conf7
-rw-r--r--sbin/mount_portalfs/portald.h82
-rw-r--r--sbin/mount_portalfs/pt_conf.c51
-rw-r--r--sbin/mount_portalfs/pt_exec.c61
-rw-r--r--sbin/mount_portalfs/pt_file.c106
-rw-r--r--sbin/mount_portalfs/pt_tcp.c158
-rw-r--r--sbin/mount_umapfs/Makefile11
-rw-r--r--sbin/mount_umapfs/mount_umapfs.8131
-rw-r--r--sbin/mount_umapfs/mount_umapfs.c232
-rw-r--r--sbin/mount_umapfs/umap_manual175
-rw-r--r--sbin/mount_unionfs/Makefile14
-rw-r--r--sbin/mount_unionfs/mount_unionfs.8204
-rw-r--r--sbin/mount_unionfs/mount_unionfs.c140
-rw-r--r--sbin/quotacheck/preen.c354
73 files changed, 18495 insertions, 0 deletions
diff --git a/sbin/bsdlabel/Makefile b/sbin/bsdlabel/Makefile
new file mode 100644
index 0000000..3f4749c
--- /dev/null
+++ b/sbin/bsdlabel/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.2 (Berkeley) 3/17/94
+
+PROG= disklabel
+SRCS= disklabel.c dkcksum.c
+MAN8= disklabel.0
+CLEANFILES=disklabel.5.0
+
+all: ${PROG} disklabel.5.0 ${MAN8}
+
+beforeinstall:
+ install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} disklabel.5.0 \
+ ${DESTDIR}${MANDIR}5/disklabel.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/bsdlabel/bsdlabel.8 b/sbin/bsdlabel/bsdlabel.8
new file mode 100644
index 0000000..bf14e0f
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.8
@@ -0,0 +1,343 @@
+.\" Copyright (c) 1987, 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Symmetric Computer Systems.
+.\"
+.\" 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.
+.\"
+.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd "April 19, 1994"
+.Dt DISKLABEL 8
+.Os BSD 4.2
+.Sh NAME
+.Nm disklabel
+.Nd read and write disk pack label
+.Sh SYNOPSIS
+.Nm disklabel
+.Op Fl r
+.Ar disk
+.Nm disklabel
+.Fl w
+.Op Fl r
+.Ar disk Ar disktype
+.Oo Ar packid Oc
+.Nm disklabel
+.Fl e
+.Op Fl r
+.Ar disk
+.Nm disklabel
+.Fl R
+.Op Fl r
+.Ar disk Ar protofile
+.Nm disklabel
+.Op Fl NW
+.Ar disk
+.sp
+.Nm disklabel
+.Fl B
+.Oo
+.Fl b Ar boot1
+.Op Fl s Ar boot2
+.Oc
+.Ar disk
+.Oo Ar disktype Oc
+.Nm disklabel
+.Fl w
+.Fl B
+.Oo
+.Fl b Ar boot1
+.Op Fl s Ar boot2
+.Oc
+.Ar disk Ar disktype
+.Oo Ar packid Oc
+.Nm disklabel
+.Fl R
+.Fl B
+.Oo
+.Fl b Ar boot1
+.Op Fl s Ar boot2
+.Oc
+.Ar disk Ar protofile
+.Oo Ar disktype Oc
+.Sh DESCRIPTION
+.Nm Disklabel
+can be used to install, examine or modify the label on a disk drive or pack.
+When writing the label, it can be used
+to change the drive identification,
+the disk partitions on the drive,
+or to replace a damaged label.
+On some systems,
+.Nm disklabel
+can be used to install bootstrap code as well.
+There are several forms of the command that read (display), install or edit
+the label on a disk.
+Each form has an additional option,
+.Fl r ,
+which causes the label to be read from or written to the disk directly,
+rather than going through the system's in-core copy of the label.
+This option may allow a label to be installed on a disk
+without kernel support for a label, such as when labels are first installed
+on a system; it must be used when first installing a label on a disk.
+The specific effect of
+.Fl r
+is described under each command.
+The read and install forms also support the
+.Fl B
+option to install bootstrap code.
+These variants are described later.
+.Pp
+The first form of the command (read) is used to examine the label on the named
+disk drive (e.g. sd0 or /dev/rsd0c).
+It will display all of the parameters associated with the drive
+and its partition layout.
+Unless the
+.Fl r
+flag is given,
+the kernel's in-core copy of the label is displayed;
+if the disk has no label, or the partition types on the disk are incorrect,
+the kernel may have constructed or modified the label.
+If the
+.Fl r
+flag is given, the label from the raw disk will be displayed rather
+than the in-core label.
+.Pp
+The second form of the command, with the
+.Fl w
+flag, is used to write a standard label on the designated drive.
+The required arguments to
+.Nm disklabel
+are the drive to be labelled (e.g. sd0), and
+the drive type as described in the
+.Xr disktab 5
+file.
+The drive parameters and partitions are taken from that file.
+If different disks of the same physical type are to have different
+partitions, it will be necessary to have separate disktab entries
+describing each, or to edit the label after installation as described below.
+The optional argument is a pack identification string,
+up to 16 characters long.
+The pack id must be quoted if it contains blanks.
+If the
+.Fl r
+flag is given, the disk sectors containing the label and bootstrap
+will be written directly.
+A side-effect of this is that any existing bootstrap code will be overwritten
+and the disk rendered unbootable.
+If
+.Fl r
+is not specified,
+the existing label will be updated via the in-core copy and any bootstrap
+code will be unaffected.
+If the disk does not already have a label, the
+.Fl r
+flag must be used.
+In either case, the kernel's in-core label is replaced.
+.Pp
+An existing disk label may be edited by using the
+.Fl e
+flag.
+The label is read from the in-core kernel copy,
+or directly from the disk if the
+.Fl r
+flag is also given.
+The label is formatted and then supplied to an editor for changes.
+If no editor is specified in an
+.Ev EDITOR
+environment variable,
+.Xr vi 1
+is used.
+When the editor terminates, the formatted label is reread
+and used to rewrite the disk label.
+Existing bootstrap code is unchanged regardless of whether
+.Fl r
+was specified.
+.Pp
+With the
+.Fl R
+flag,
+.Nm disklabel
+is capable of restoring a disk label that was formatted
+in a prior operation and saved in an ascii file.
+The prototype file used to create the label should be in the same format
+as that produced when reading or editing a label.
+Comments are delimited by
+.Ar \&#
+and newline.
+As with
+.Fl w ,
+any existing bootstrap code will be clobbered if
+.Fl r
+is specified and will be unaffected otherwise.
+.Pp
+The
+.Fl NW
+flags for
+.Nm disklabel
+explicitly disallow and
+allow, respectively, writing of the pack label area on the selected disk.
+.Pp
+The final three forms of
+.Nm disklabel
+are used to install boostrap code on machines where the bootstrap is part
+of the label.
+The bootstrap code is comprised of one or two boot programs depending on
+the machine.
+The
+.Fl B
+option is used to denote that bootstrap code is to be installed.
+The
+.Fl r
+flag is implied by
+.Fl B
+and never needs to be specified.
+The name of the boot program(s) to be installed can be selected in a
+variety of ways.
+First, the names can be specified explicitly via the
+.Fl b
+and
+.Fl s
+flags.
+On machines with only a single level of boot program,
+.Fl b
+is the name of that program.
+For machines with a two-level bootstrap,
+.Fl b
+indicates the primary boot program and
+.Fl s
+the secondary boot program.
+If the names are not explicitly given, standard boot programs will be used.
+The boot programs are located in
+.Pa /usr/mdec .
+The names of the programs are taken from the ``b0'' and ``b1'' parameters
+of the
+.Xr disktab 5
+entry for the disk if
+.Ar disktype
+was given and its disktab entry exists and includes those parameters.
+Otherwise, boot program names are derived from the name of the disk.
+These names are of the form
+.Pa basename Ns boot
+for the primary (or only) bootstrap, and
+.Pf boot Pa basename
+for the secondary bootstrap;
+for example,
+.Pa /usr/mdec/sdboot
+and
+.Pa /usr/mdec/bootsd
+if the disk device is
+.Em sd0 .
+.Pp
+The first of the three boot-installation forms is used to install
+bootstrap code without changing the existing label.
+It is essentially a read command with respect to the disk label
+itself and all options are related to the specification of the boot
+program as described previously.
+The final two forms are analogous to the basic write and restore versions
+except that they will install bootstrap code in addition to a new label.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/disktab
+.It Pa /usr/mdec/ Ns Em xx Ns boot
+.It Pa /usr/mdec/boot Ns Em xx
+.El
+.Sh EXAMPLES
+.Dl disklabel sd0
+.Pp
+Display the in-core label for sd0 as obtained via
+.Pa /dev/rsd0c .
+.Pp
+.Dl disklabel -w -r /dev/rsd0c sd2212 foo
+.Pp
+Create a label for sd0 based on information for ``sd2212'' found in
+.Pa /etc/disktab .
+Any existing bootstrap code will be clobbered.
+.Pp
+.Dl disklabel -e -r sd0
+.Pp
+Read the on-disk label for sd0, edit it and reinstall in-core as well
+as on-disk.
+Existing bootstrap code is unaffected.
+.Pp
+.Dl disklabel -R sd0 mylabel
+.Pp
+Restore the on-disk and in-core label for sd0 from information in
+.Pa mylabel .
+Existing bootstrap code is unaffected.
+.Pp
+.Dl disklabel -B sd0
+.Pp
+Install a new bootstrap on sd0.
+The boot code comes from
+.Pa /usr/mdec/sdboot
+and possibly
+.Pa /usr/mdec/bootsd .
+On-disk and in-core labels are unchanged.
+.Pp
+.Dl disklabel -w -B /dev/rsd0c -b newboot sd2212
+.Pp
+Install a new label and bootstrap.
+The label is derived from disktab information for ``sd2212'' and
+installed both in-core and on-disk.
+The bootstrap code comes from the file
+.Pa /usr/mdec/newboot .
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 5
+.Sh DIAGNOSTICS
+The kernel device drivers will not allow the size of a disk partition
+to be decreased or the offset of a partition to be changed while it is open.
+Some device drivers create a label containing only a single large partition
+if a disk is unlabeled; thus, the label must be written to the ``a''
+partition of the disk while it is open.
+This sometimes requires the desired label to be set in two steps,
+the first one creating at least one other partition,
+and the second setting the label on the new partition
+while shrinking the ``a'' partition.
+.Pp
+On some machines the bootstrap code may not fit entirely in the area
+allocated for it by some filesystems.
+As a result, it may not be possible to have filesystems on some partitions
+of a ``bootable'' disk.
+When installing bootstrap code,
+.Nm disklabel
+checks for these cases.
+If the installed boot code would overlap a partition of type FS_UNUSED
+it is marked as type FS_BOOT.
+The
+.Xr newfs 8
+utility will disallow creation of filesystems on FS_BOOT partitions.
+Conversely, if a partition has a type other than FS_UNUSED or FS_BOOT,
+.Nm disklabel
+will not install bootstrap code that overlaps it.
+.Sh BUGS
+When a disk name is given without a full pathname,
+the constructed device name uses the ``a'' partition on the tahoe,
+the ``c'' partition on all others.
diff --git a/sbin/bsdlabel/bsdlabel.c b/sbin/bsdlabel/bsdlabel.c
new file mode 100644
index 0000000..4a29340
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.c
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Symmetric Computer Systems.
+ *
+ * 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) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94";
+/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#define DKTYPENAMES
+#include <sys/disklabel.h>
+#include <ufs/ffs/fs.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "pathnames.h"
+
+/*
+ * Disklabel: read and write disklabels.
+ * The label is usually placed on one of the first sectors of the disk.
+ * Many machines also place a bootstrap in the same area,
+ * in which case the label is embedded in the bootstrap.
+ * The bootstrap source must leave space at the proper offset
+ * for the label on such machines.
+ */
+
+#ifdef tahoe
+#define RAWPARTITION 'a'
+#else
+#define RAWPARTITION 'c'
+#endif
+
+#ifndef BBSIZE
+#define BBSIZE 8192 /* size of boot area, with label */
+#endif
+
+#ifdef tahoe
+#define NUMBOOT 0
+#else
+#if defined(hp300) || defined(hp800)
+#define NUMBOOT 1
+#else
+#define NUMBOOT 2
+#endif
+#endif
+
+#define DEFEDITOR _PATH_VI
+#define streq(a,b) (strcmp(a,b) == 0)
+
+char *dkname;
+char *specname;
+char tmpfil[] = _PATH_TMP;
+
+extern int errno;
+char namebuf[BBSIZE], *np = namebuf;
+struct disklabel lab;
+struct disklabel *readlabel(), *makebootarea();
+char bootarea[BBSIZE];
+
+#if NUMBOOT > 0
+int installboot; /* non-zero if we should install a boot program */
+char *bootbuf; /* pointer to buffer with remainder of boot prog */
+int bootsize; /* size of remaining boot program */
+char *xxboot; /* primary boot */
+char *bootxx; /* secondary boot */
+char boot0[MAXPATHLEN];
+char boot1[MAXPATHLEN];
+#endif
+
+enum {
+ UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT
+} op = UNSPEC;
+
+int rflag;
+
+#ifdef DEBUG
+int debug;
+#define OPTIONS "BNRWb:ders:w"
+#else
+#define OPTIONS "BNRWb:ers:w"
+#endif
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ register struct disklabel *lp;
+ FILE *t;
+ int ch, f, flag, error = 0;
+ char *name = 0;
+
+ while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
+ switch (ch) {
+#if NUMBOOT > 0
+ case 'B':
+ ++installboot;
+ break;
+ case 'b':
+ xxboot = optarg;
+ break;
+#if NUMBOOT > 1
+ case 's':
+ bootxx = optarg;
+ break;
+#endif
+#endif
+ case 'N':
+ if (op != UNSPEC)
+ usage();
+ op = NOWRITE;
+ break;
+ case 'R':
+ if (op != UNSPEC)
+ usage();
+ op = RESTORE;
+ break;
+ case 'W':
+ if (op != UNSPEC)
+ usage();
+ op = WRITEABLE;
+ break;
+ case 'e':
+ if (op != UNSPEC)
+ usage();
+ op = EDIT;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'w':
+ if (op != UNSPEC)
+ usage();
+ op = WRITE;
+ break;
+#ifdef DEBUG
+ case 'd':
+ debug++;
+ break;
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+#if NUMBOOT > 0
+ if (installboot) {
+ rflag++;
+ if (op == UNSPEC)
+ op = WRITEBOOT;
+ } else {
+ if (op == UNSPEC)
+ op = READ;
+ xxboot = bootxx = 0;
+ }
+#else
+ if (op == UNSPEC)
+ op = READ;
+#endif
+ if (argc < 1)
+ usage();
+
+ dkname = argv[0];
+ if (dkname[0] != '/') {
+ (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION);
+ specname = np;
+ np += strlen(specname) + 1;
+ } else
+ specname = dkname;
+ f = open(specname, op == READ ? O_RDONLY : O_RDWR);
+ if (f < 0 && errno == ENOENT && dkname[0] != '/') {
+ (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname);
+ np = namebuf + strlen(specname) + 1;
+ f = open(specname, op == READ ? O_RDONLY : O_RDWR);
+ }
+ if (f < 0)
+ Perror(specname);
+
+ switch(op) {
+
+ case EDIT:
+ if (argc != 1)
+ usage();
+ lp = readlabel(f);
+ error = edit(lp, f);
+ break;
+
+ case NOWRITE:
+ flag = 0;
+ if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
+ Perror("ioctl DIOCWLABEL");
+ break;
+
+ case READ:
+ if (argc != 1)
+ usage();
+ lp = readlabel(f);
+ display(stdout, lp);
+ error = checklabel(lp);
+ break;
+
+ case RESTORE:
+#if NUMBOOT > 0
+ if (installboot && argc == 3) {
+ makelabel(argv[2], 0, &lab);
+ argc--;
+ }
+#endif
+ if (argc != 2)
+ usage();
+ lp = makebootarea(bootarea, &lab, f);
+ if (!(t = fopen(argv[1], "r")))
+ Perror(argv[1]);
+ if (getasciilabel(t, lp))
+ error = writelabel(f, bootarea, lp);
+ break;
+
+ case WRITE:
+ if (argc == 3) {
+ name = argv[2];
+ argc--;
+ }
+ if (argc != 2)
+ usage();
+ makelabel(argv[1], name, &lab);
+ lp = makebootarea(bootarea, &lab, f);
+ *lp = lab;
+ if (checklabel(lp) == 0)
+ error = writelabel(f, bootarea, lp);
+ break;
+
+ case WRITEABLE:
+ flag = 1;
+ if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
+ Perror("ioctl DIOCWLABEL");
+ break;
+
+#if NUMBOOT > 0
+ case WRITEBOOT:
+ {
+ struct disklabel tlab;
+
+ lp = readlabel(f);
+ tlab = *lp;
+ if (argc == 2)
+ makelabel(argv[1], 0, &lab);
+ lp = makebootarea(bootarea, &lab, f);
+ *lp = tlab;
+ if (checklabel(lp) == 0)
+ error = writelabel(f, bootarea, lp);
+ break;
+ }
+#endif
+ }
+ exit(error);
+}
+
+/*
+ * Construct a prototype disklabel from /etc/disktab. As a side
+ * effect, set the names of the primary and secondary boot files
+ * if specified.
+ */
+makelabel(type, name, lp)
+ char *type, *name;
+ register struct disklabel *lp;
+{
+ register struct disklabel *dp;
+ char *strcpy();
+
+ dp = getdiskbyname(type);
+ if (dp == NULL) {
+ fprintf(stderr, "%s: unknown disk type\n", type);
+ exit(1);
+ }
+ *lp = *dp;
+#if NUMBOOT > 0
+ /*
+ * Set bootstrap name(s).
+ * 1. If set from command line, use those,
+ * 2. otherwise, check if disktab specifies them (b0 or b1),
+ * 3. otherwise, makebootarea() will choose ones based on the name
+ * of the disk special file. E.g. /dev/ra0 -> raboot, bootra
+ */
+ if (!xxboot && lp->d_boot0) {
+ if (*lp->d_boot0 != '/')
+ (void)sprintf(boot0, "%s/%s",
+ _PATH_BOOTDIR, lp->d_boot0);
+ else
+ (void)strcpy(boot0, lp->d_boot0);
+ xxboot = boot0;
+ }
+#if NUMBOOT > 1
+ if (!bootxx && lp->d_boot1) {
+ if (*lp->d_boot1 != '/')
+ (void)sprintf(boot1, "%s/%s",
+ _PATH_BOOTDIR, lp->d_boot1);
+ else
+ (void)strcpy(boot1, lp->d_boot1);
+ bootxx = boot1;
+ }
+#endif
+#endif
+ /* d_packname is union d_boot[01], so zero */
+ bzero(lp->d_packname, sizeof(lp->d_packname));
+ if (name)
+ (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname));
+}
+
+writelabel(f, boot, lp)
+ int f;
+ char *boot;
+ register struct disklabel *lp;
+{
+ register int i;
+ int flag;
+
+ setbootflag(lp);
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (rflag) {
+ /*
+ * First set the kernel disk label,
+ * then write a label to the raw disk.
+ * If the SDINFO ioctl fails because it is unimplemented,
+ * keep going; otherwise, the kernel consistency checks
+ * may prevent us from changing the current (in-core)
+ * label.
+ */
+ if (ioctl(f, DIOCSDINFO, lp) < 0 &&
+ errno != ENODEV && errno != ENOTTY) {
+ l_perror("ioctl DIOCSDINFO");
+ return (1);
+ }
+ (void)lseek(f, (off_t)0, SEEK_SET);
+ /*
+ * write enable label sector before write (if necessary),
+ * disable after writing.
+ */
+ flag = 1;
+ if (ioctl(f, DIOCWLABEL, &flag) < 0)
+ perror("ioctl DIOCWLABEL");
+ if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
+ perror("write");
+ return (1);
+ }
+#if NUMBOOT > 0
+ /*
+ * Output the remainder of the disklabel
+ */
+ if (bootbuf && write(f, bootbuf, bootsize) != bootsize) {
+ perror("write");
+ return(1);
+ }
+#endif
+ flag = 0;
+ (void) ioctl(f, DIOCWLABEL, &flag);
+ } else if (ioctl(f, DIOCWDINFO, lp) < 0) {
+ l_perror("ioctl DIOCWDINFO");
+ return (1);
+ }
+#ifdef vax
+ if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
+ daddr_t alt;
+
+ alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
+ for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
+ (void)lseek(f, (off_t)((alt + i) * lp->d_secsize),
+ SEEK_SET);
+ if (write(f, boot, lp->d_secsize) < lp->d_secsize) {
+ int oerrno = errno;
+ fprintf(stderr, "alternate label %d ", i/2);
+ errno = oerrno;
+ perror("write");
+ }
+ }
+ }
+#endif
+ return (0);
+}
+
+l_perror(s)
+ char *s;
+{
+ int saverrno = errno;
+
+ fprintf(stderr, "disklabel: %s: ", s);
+
+ switch (saverrno) {
+
+ case ESRCH:
+ fprintf(stderr, "No disk label on disk;\n");
+ fprintf(stderr,
+ "use \"disklabel -r\" to install initial label\n");
+ break;
+
+ case EINVAL:
+ fprintf(stderr, "Label magic number or checksum is wrong!\n");
+ fprintf(stderr, "(disklabel or kernel is out of date?)\n");
+ break;
+
+ case EBUSY:
+ fprintf(stderr, "Open partition would move or shrink\n");
+ break;
+
+ case EXDEV:
+ fprintf(stderr,
+ "Labeled partition or 'a' partition must start at beginning of disk\n");
+ break;
+
+ default:
+ errno = saverrno;
+ perror((char *)NULL);
+ break;
+ }
+}
+
+/*
+ * Fetch disklabel for disk.
+ * Use ioctl to get label unless -r flag is given.
+ */
+struct disklabel *
+readlabel(f)
+ int f;
+{
+ register struct disklabel *lp;
+
+ if (rflag) {
+ if (read(f, bootarea, BBSIZE) < BBSIZE)
+ Perror(specname);
+ for (lp = (struct disklabel *)bootarea;
+ lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp));
+ lp = (struct disklabel *)((char *)lp + 16))
+ if (lp->d_magic == DISKMAGIC &&
+ lp->d_magic2 == DISKMAGIC)
+ break;
+ if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) ||
+ lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC ||
+ dkcksum(lp) != 0) {
+ fprintf(stderr,
+ "Bad pack magic number (label is damaged, or pack is unlabeled)\n");
+ /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */
+ exit (1);
+ }
+ } else {
+ lp = &lab;
+ if (ioctl(f, DIOCGDINFO, lp) < 0)
+ Perror("ioctl DIOCGDINFO");
+ }
+ return (lp);
+}
+
+/*
+ * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot''
+ * Returns a pointer to the disklabel portion of the bootarea.
+ */
+struct disklabel *
+makebootarea(boot, dp, f)
+ char *boot;
+ register struct disklabel *dp;
+ int f;
+{
+ struct disklabel *lp;
+ register char *p;
+ int b;
+#if NUMBOOT > 0
+ char *dkbasename;
+ struct stat sb;
+#endif
+
+ /* XXX */
+ if (dp->d_secsize == 0) {
+ dp->d_secsize = DEV_BSIZE;
+ dp->d_bbsize = BBSIZE;
+ }
+ lp = (struct disklabel *)
+ (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
+ bzero((char *)lp, sizeof *lp);
+#if NUMBOOT > 0
+ /*
+ * If we are not installing a boot program but we are installing a
+ * label on disk then we must read the current bootarea so we don't
+ * clobber the existing boot.
+ */
+ if (!installboot) {
+ if (rflag) {
+ if (read(f, boot, BBSIZE) < BBSIZE)
+ Perror(specname);
+ bzero((char *)lp, sizeof *lp);
+ }
+ return (lp);
+ }
+ /*
+ * We are installing a boot program. Determine the name(s) and
+ * read them into the appropriate places in the boot area.
+ */
+ if (!xxboot || !bootxx) {
+ dkbasename = np;
+ if ((p = rindex(dkname, '/')) == NULL)
+ p = dkname;
+ else
+ p++;
+ while (*p && !isdigit(*p))
+ *np++ = *p++;
+ *np++ = '\0';
+
+ if (!xxboot) {
+ (void)sprintf(np, "%s/%sboot",
+ _PATH_BOOTDIR, dkbasename);
+ if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
+ dkbasename++;
+ xxboot = np;
+ (void)sprintf(xxboot, "%s/%sboot",
+ _PATH_BOOTDIR, dkbasename);
+ np += strlen(xxboot) + 1;
+ }
+#if NUMBOOT > 1
+ if (!bootxx) {
+ (void)sprintf(np, "%s/boot%s",
+ _PATH_BOOTDIR, dkbasename);
+ if (access(np, F_OK) < 0 && dkbasename[0] == 'r')
+ dkbasename++;
+ bootxx = np;
+ (void)sprintf(bootxx, "%s/boot%s",
+ _PATH_BOOTDIR, dkbasename);
+ np += strlen(bootxx) + 1;
+ }
+#endif
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n",
+ xxboot, bootxx ? bootxx : "NONE");
+#endif
+
+ /*
+ * Strange rules:
+ * 1. One-piece bootstrap (hp300/hp800)
+ * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest
+ * is remembered and written later following the bootarea.
+ * 2. Two-piece bootstraps (vax/i386?/mips?)
+ * up to d_secsize bytes of ``xxboot'' go in first d_secsize
+ * bytes of bootarea, remaining d_bbsize-d_secsize filled
+ * from ``bootxx''.
+ */
+ b = open(xxboot, O_RDONLY);
+ if (b < 0)
+ Perror(xxboot);
+#if NUMBOOT > 1
+ if (read(b, boot, (int)dp->d_secsize) < 0)
+ Perror(xxboot);
+ (void)close(b);
+ b = open(bootxx, O_RDONLY);
+ if (b < 0)
+ Perror(bootxx);
+ if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0)
+ Perror(bootxx);
+#else
+ if (read(b, boot, (int)dp->d_bbsize) < 0)
+ Perror(xxboot);
+ (void)fstat(b, &sb);
+ bootsize = (int)sb.st_size - dp->d_bbsize;
+ if (bootsize > 0) {
+ /* XXX assume d_secsize is a power of two */
+ bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
+ bootbuf = (char *)malloc((size_t)bootsize);
+ if (bootbuf == 0)
+ Perror(xxboot);
+ if (read(b, bootbuf, bootsize) < 0) {
+ free(bootbuf);
+ Perror(xxboot);
+ }
+ }
+#endif
+ (void)close(b);
+#endif
+ /*
+ * Make sure no part of the bootstrap is written in the area
+ * reserved for the label.
+ */
+ for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
+ if (*p) {
+ fprintf(stderr,
+ "Bootstrap doesn't leave room for disk label\n");
+ exit(2);
+ }
+ return (lp);
+}
+
+display(f, lp)
+ FILE *f;
+ register struct disklabel *lp;
+{
+ register int i, j;
+ register struct partition *pp;
+
+ fprintf(f, "# %s:\n", specname);
+ if ((unsigned) lp->d_type < DKMAXTYPES)
+ fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
+ else
+ fprintf(f, "type: %d\n", lp->d_type);
+ fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename);
+ fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname);
+ fprintf(f, "flags:");
+ if (lp->d_flags & D_REMOVABLE)
+ fprintf(f, " removeable");
+ if (lp->d_flags & D_ECC)
+ fprintf(f, " ecc");
+ if (lp->d_flags & D_BADSECT)
+ fprintf(f, " badsect");
+ fprintf(f, "\n");
+ fprintf(f, "bytes/sector: %d\n", lp->d_secsize);
+ fprintf(f, "sectors/track: %d\n", lp->d_nsectors);
+ fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks);
+ fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl);
+ fprintf(f, "cylinders: %d\n", lp->d_ncylinders);
+ fprintf(f, "rpm: %d\n", lp->d_rpm);
+ fprintf(f, "interleave: %d\n", lp->d_interleave);
+ fprintf(f, "trackskew: %d\n", lp->d_trackskew);
+ fprintf(f, "cylinderskew: %d\n", lp->d_cylskew);
+ fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch);
+ fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek);
+ fprintf(f, "drivedata: ");
+ for (i = NDDATA - 1; i >= 0; i--)
+ if (lp->d_drivedata[i])
+ break;
+ if (i < 0)
+ i = 0;
+ for (j = 0; j <= i; j++)
+ fprintf(f, "%d ", lp->d_drivedata[j]);
+ fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
+ fprintf(f,
+ "# size offset fstype [fsize bsize cpg]\n");
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ fprintf(f, " %c: %8d %8d ", 'a' + i,
+ pp->p_size, pp->p_offset);
+ if ((unsigned) pp->p_fstype < FSMAXTYPES)
+ fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
+ else
+ fprintf(f, "%8d", pp->p_fstype);
+ switch (pp->p_fstype) {
+
+ case FS_UNUSED: /* XXX */
+ fprintf(f, " %5d %5d %5.5s ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag, "");
+ break;
+
+ case FS_BSDFFS:
+ fprintf(f, " %5d %5d %5d ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ default:
+ fprintf(f, "%20.20s", "");
+ break;
+ }
+ fprintf(f, "\t# (Cyl. %4d",
+ pp->p_offset / lp->d_secpercyl);
+ if (pp->p_offset % lp->d_secpercyl)
+ putc('*', f);
+ else
+ putc(' ', f);
+ fprintf(f, "- %d",
+ (pp->p_offset +
+ pp->p_size + lp->d_secpercyl - 1) /
+ lp->d_secpercyl - 1);
+ if (pp->p_size % lp->d_secpercyl)
+ putc('*', f);
+ fprintf(f, ")\n");
+ }
+ }
+ fflush(f);
+}
+
+edit(lp, f)
+ struct disklabel *lp;
+ int f;
+{
+ register int c;
+ struct disklabel label;
+ FILE *fd;
+ char *mktemp();
+
+ (void) mktemp(tmpfil);
+ fd = fopen(tmpfil, "w");
+ if (fd == NULL) {
+ fprintf(stderr, "%s: Can't create\n", tmpfil);
+ return (1);
+ }
+ (void)fchmod(fileno(fd), 0600);
+ display(fd, lp);
+ fclose(fd);
+ for (;;) {
+ if (!editit())
+ break;
+ fd = fopen(tmpfil, "r");
+ if (fd == NULL) {
+ fprintf(stderr, "%s: Can't reopen for reading\n",
+ tmpfil);
+ break;
+ }
+ bzero((char *)&label, sizeof(label));
+ if (getasciilabel(fd, &label)) {
+ *lp = label;
+ if (writelabel(f, bootarea, lp) == 0) {
+ (void) unlink(tmpfil);
+ return (0);
+ }
+ }
+ printf("re-edit the label? [y]: "); fflush(stdout);
+ c = getchar();
+ if (c != EOF && c != (int)'\n')
+ while (getchar() != (int)'\n')
+ ;
+ if (c == (int)'n')
+ break;
+ }
+ (void) unlink(tmpfil);
+ return (1);
+}
+
+editit()
+{
+ register int pid, xpid;
+ int stat, omask;
+ extern char *getenv();
+
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = fork()) < 0) {
+ extern int errno;
+
+ if (errno == EPROCLIM) {
+ fprintf(stderr, "You have too many processes\n");
+ return(0);
+ }
+ if (errno != EAGAIN) {
+ perror("fork");
+ return(0);
+ }
+ sleep(1);
+ }
+ if (pid == 0) {
+ register char *ed;
+
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = DEFEDITOR;
+ execlp(ed, ed, tmpfil, 0);
+ perror(ed);
+ exit(1);
+ }
+ while ((xpid = wait(&stat)) >= 0)
+ if (xpid == pid)
+ break;
+ sigsetmask(omask);
+ return(!stat);
+}
+
+char *
+skip(cp)
+ register char *cp;
+{
+
+ while (*cp != '\0' && isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return ((char *)NULL);
+ return (cp);
+}
+
+char *
+word(cp)
+ register char *cp;
+{
+ register char c;
+
+ while (*cp != '\0' && !isspace(*cp) && *cp != '#')
+ cp++;
+ if ((c = *cp) != '\0') {
+ *cp++ = '\0';
+ if (c != '#')
+ return (skip(cp));
+ }
+ return ((char *)NULL);
+}
+
+/*
+ * Read an ascii label in from fd f,
+ * in the same format as that put out by display(),
+ * and fill in lp.
+ */
+getasciilabel(f, lp)
+ FILE *f;
+ register struct disklabel *lp;
+{
+ register char **cpp, *cp;
+ register struct partition *pp;
+ char *tp, *s, line[BUFSIZ];
+ int v, lineno = 0, errors = 0;
+
+ lp->d_bbsize = BBSIZE; /* XXX */
+ lp->d_sbsize = SBSIZE; /* XXX */
+ while (fgets(line, sizeof(line) - 1, f)) {
+ lineno++;
+ if (cp = index(line,'\n'))
+ *cp = '\0';
+ cp = skip(line);
+ if (cp == NULL)
+ continue;
+ tp = index(cp, ':');
+ if (tp == NULL) {
+ fprintf(stderr, "line %d: syntax error\n", lineno);
+ errors++;
+ continue;
+ }
+ *tp++ = '\0', tp = skip(tp);
+ if (streq(cp, "type")) {
+ if (tp == NULL)
+ tp = "unknown";
+ cpp = dktypenames;
+ for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
+ if ((s = *cpp) && streq(s, tp)) {
+ lp->d_type = cpp - dktypenames;
+ goto next;
+ }
+ v = atoi(tp);
+ if ((unsigned)v >= DKMAXTYPES)
+ fprintf(stderr, "line %d:%s %d\n", lineno,
+ "Warning, unknown disk type", v);
+ lp->d_type = v;
+ continue;
+ }
+ if (streq(cp, "flags")) {
+ for (v = 0; (cp = tp) && *cp != '\0';) {
+ tp = word(cp);
+ if (streq(cp, "removeable"))
+ v |= D_REMOVABLE;
+ else if (streq(cp, "ecc"))
+ v |= D_ECC;
+ else if (streq(cp, "badsect"))
+ v |= D_BADSECT;
+ else {
+ fprintf(stderr,
+ "line %d: %s: bad flag\n",
+ lineno, cp);
+ errors++;
+ }
+ }
+ lp->d_flags = v;
+ continue;
+ }
+ if (streq(cp, "drivedata")) {
+ register int i;
+
+ for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
+ lp->d_drivedata[i++] = atoi(cp);
+ tp = word(cp);
+ }
+ continue;
+ }
+ if (sscanf(cp, "%d partitions", &v) == 1) {
+ if (v == 0 || (unsigned)v > MAXPARTITIONS) {
+ fprintf(stderr,
+ "line %d: bad # of partitions\n", lineno);
+ lp->d_npartitions = MAXPARTITIONS;
+ errors++;
+ } else
+ lp->d_npartitions = v;
+ continue;
+ }
+ if (tp == NULL)
+ tp = "";
+ if (streq(cp, "disk")) {
+ strncpy(lp->d_typename, tp, sizeof (lp->d_typename));
+ continue;
+ }
+ if (streq(cp, "label")) {
+ strncpy(lp->d_packname, tp, sizeof (lp->d_packname));
+ continue;
+ }
+ if (streq(cp, "bytes/sector")) {
+ v = atoi(tp);
+ if (v <= 0 || (v % 512) != 0) {
+ fprintf(stderr,
+ "line %d: %s: bad sector size\n",
+ lineno, tp);
+ errors++;
+ } else
+ lp->d_secsize = v;
+ continue;
+ }
+ if (streq(cp, "sectors/track")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_nsectors = v;
+ continue;
+ }
+ if (streq(cp, "sectors/cylinder")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_secpercyl = v;
+ continue;
+ }
+ if (streq(cp, "tracks/cylinder")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_ntracks = v;
+ continue;
+ }
+ if (streq(cp, "cylinders")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_ncylinders = v;
+ continue;
+ }
+ if (streq(cp, "rpm")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_rpm = v;
+ continue;
+ }
+ if (streq(cp, "interleave")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_interleave = v;
+ continue;
+ }
+ if (streq(cp, "trackskew")) {
+ v = atoi(tp);
+ if (v < 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_trackskew = v;
+ continue;
+ }
+ if (streq(cp, "cylinderskew")) {
+ v = atoi(tp);
+ if (v < 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_cylskew = v;
+ continue;
+ }
+ if (streq(cp, "headswitch")) {
+ v = atoi(tp);
+ if (v < 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_headswitch = v;
+ continue;
+ }
+ if (streq(cp, "track-to-track seek")) {
+ v = atoi(tp);
+ if (v < 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_trkseek = v;
+ continue;
+ }
+ if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
+ unsigned part = *cp - 'a';
+
+ if (part > lp->d_npartitions) {
+ fprintf(stderr,
+ "line %d: bad partition name\n", lineno);
+ errors++;
+ continue;
+ }
+ pp = &lp->d_partitions[part];
+#define NXTNUM(n) { \
+ cp = tp, tp = word(cp); \
+ if (tp == NULL) \
+ tp = cp; \
+ (n) = atoi(cp); \
+ }
+
+ NXTNUM(v);
+ if (v < 0) {
+ fprintf(stderr,
+ "line %d: %s: bad partition size\n",
+ lineno, cp);
+ errors++;
+ } else
+ pp->p_size = v;
+ NXTNUM(v);
+ if (v < 0) {
+ fprintf(stderr,
+ "line %d: %s: bad partition offset\n",
+ lineno, cp);
+ errors++;
+ } else
+ pp->p_offset = v;
+ cp = tp, tp = word(cp);
+ cpp = fstypenames;
+ for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
+ if ((s = *cpp) && streq(s, cp)) {
+ pp->p_fstype = cpp - fstypenames;
+ goto gottype;
+ }
+ if (isdigit(*cp))
+ v = atoi(cp);
+ else
+ v = FSMAXTYPES;
+ if ((unsigned)v >= FSMAXTYPES) {
+ fprintf(stderr, "line %d: %s %s\n", lineno,
+ "Warning, unknown filesystem type", cp);
+ v = FS_UNUSED;
+ }
+ pp->p_fstype = v;
+ gottype:
+
+ switch (pp->p_fstype) {
+
+ case FS_UNUSED: /* XXX */
+ NXTNUM(pp->p_fsize);
+ if (pp->p_fsize == 0)
+ break;
+ NXTNUM(v);
+ pp->p_frag = v / pp->p_fsize;
+ break;
+
+ case FS_BSDFFS:
+ NXTNUM(pp->p_fsize);
+ if (pp->p_fsize == 0)
+ break;
+ NXTNUM(v);
+ pp->p_frag = v / pp->p_fsize;
+ NXTNUM(pp->p_cpg);
+ break;
+
+ default:
+ break;
+ }
+ continue;
+ }
+ fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
+ lineno, cp);
+ errors++;
+ next:
+ ;
+ }
+ errors += checklabel(lp);
+ return (errors == 0);
+}
+
+/*
+ * Check disklabel for errors and fill in
+ * derived fields according to supplied values.
+ */
+checklabel(lp)
+ register struct disklabel *lp;
+{
+ register struct partition *pp;
+ int i, errors = 0;
+ char part;
+
+ if (lp->d_secsize == 0) {
+ fprintf(stderr, "sector size %d\n", lp->d_secsize);
+ return (1);
+ }
+ if (lp->d_nsectors == 0) {
+ fprintf(stderr, "sectors/track %d\n", lp->d_nsectors);
+ return (1);
+ }
+ if (lp->d_ntracks == 0) {
+ fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks);
+ return (1);
+ }
+ if (lp->d_ncylinders == 0) {
+ fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders);
+ errors++;
+ }
+ if (lp->d_rpm == 0)
+ Warning("revolutions/minute %d", lp->d_rpm);
+ if (lp->d_secpercyl == 0)
+ lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
+ if (lp->d_secperunit == 0)
+ lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+ if (lp->d_bbsize == 0) {
+ fprintf(stderr, "boot block size %d\n", lp->d_bbsize);
+ errors++;
+ } else if (lp->d_bbsize % lp->d_secsize)
+ Warning("boot block size %% sector-size != 0");
+ if (lp->d_sbsize == 0) {
+ fprintf(stderr, "super block size %d\n", lp->d_sbsize);
+ errors++;
+ } else if (lp->d_sbsize % lp->d_secsize)
+ Warning("super block size %% sector-size != 0");
+ if (lp->d_npartitions > MAXPARTITIONS)
+ Warning("number of partitions (%d) > MAXPARTITIONS (%d)",
+ lp->d_npartitions, MAXPARTITIONS);
+ for (i = 0; i < lp->d_npartitions; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (pp->p_size == 0 && pp->p_offset != 0)
+ Warning("partition %c: size 0, but offset %d",
+ part, pp->p_offset);
+#ifdef notdef
+ if (pp->p_size % lp->d_secpercyl)
+ Warning("partition %c: size %% cylinder-size != 0",
+ part);
+ if (pp->p_offset % lp->d_secpercyl)
+ Warning("partition %c: offset %% cylinder-size != 0",
+ part);
+#endif
+ if (pp->p_offset > lp->d_secperunit) {
+ fprintf(stderr,
+ "partition %c: offset past end of unit\n", part);
+ errors++;
+ }
+ if (pp->p_offset + pp->p_size > lp->d_secperunit) {
+ fprintf(stderr,
+ "partition %c: partition extends past end of unit\n",
+ part);
+ errors++;
+ }
+ }
+ for (; i < MAXPARTITIONS; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (pp->p_size || pp->p_offset)
+ Warning("unused partition %c: size %d offset %d",
+ 'a' + i, pp->p_size, pp->p_offset);
+ }
+ return (errors);
+}
+
+/*
+ * If we are installing a boot program that doesn't fit in d_bbsize
+ * we need to mark those partitions that the boot overflows into.
+ * This allows newfs to prevent creation of a filesystem where it might
+ * clobber bootstrap code.
+ */
+setbootflag(lp)
+ register struct disklabel *lp;
+{
+ register struct partition *pp;
+ int i, errors = 0;
+ char part;
+ u_long boffset;
+
+ if (bootbuf == 0)
+ return;
+ boffset = bootsize / lp->d_secsize;
+ for (i = 0; i < lp->d_npartitions; i++) {
+ part = 'a' + i;
+ pp = &lp->d_partitions[i];
+ if (pp->p_size == 0)
+ continue;
+ if (boffset <= pp->p_offset) {
+ if (pp->p_fstype == FS_BOOT)
+ pp->p_fstype = FS_UNUSED;
+ } else if (pp->p_fstype != FS_BOOT) {
+ if (pp->p_fstype != FS_UNUSED) {
+ fprintf(stderr,
+ "boot overlaps used partition %c\n",
+ part);
+ errors++;
+ } else {
+ pp->p_fstype = FS_BOOT;
+ Warning("boot overlaps partition %c, %s",
+ part, "marked as FS_BOOT");
+ }
+ }
+ }
+ if (errors) {
+ fprintf(stderr, "Cannot install boot program\n");
+ exit(4);
+ }
+}
+
+/*VARARGS1*/
+Warning(fmt, a1, a2, a3, a4, a5)
+ char *fmt;
+{
+
+ fprintf(stderr, "Warning, ");
+ fprintf(stderr, fmt, a1, a2, a3, a4, a5);
+ fprintf(stderr, "\n");
+}
+
+Perror(str)
+ char *str;
+{
+ fputs("disklabel: ", stderr); perror(str);
+ exit(4);
+}
+
+usage()
+{
+#if NUMBOOT > 0
+ fprintf(stderr,
+"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n",
+"usage: disklabel [-r] disk",
+ "(to read label)",
+"or disklabel -w [-r] disk type [ packid ]",
+ "(to write label with existing boot program)",
+"or disklabel -e [-r] disk",
+ "(to edit label)",
+"or disklabel -R [-r] disk protofile",
+ "(to restore label with existing boot program)",
+#if NUMBOOT > 1
+"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]",
+ "(to install boot program with existing label)",
+"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
+ "(to write label and boot program)",
+"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
+ "(to restore label and boot program)",
+#else
+"or disklabel -B [ -b bootprog ] disk [ type ]",
+ "(to install boot program with existing on-disk label)",
+"or disklabel -w -B [ -b bootprog ] disk type [ packid ]",
+ "(to write label and install boot program)",
+"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]",
+ "(to restore label and install boot program)",
+#endif
+"or disklabel [-NW] disk",
+ "(to write disable/enable label)");
+#else
+ fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n",
+"usage: disklabel [-r] disk", "(to read label)",
+"or disklabel -w [-r] disk type [ packid ]", "(to write label)",
+"or disklabel -e [-r] disk", "(to edit label)",
+"or disklabel -R [-r] disk protofile", "(to restore label)",
+"or disklabel [-NW] disk", "(to write disable/enable label)");
+#endif
+ exit(1);
+}
diff --git a/sbin/bsdlabel/disklabel.5.5 b/sbin/bsdlabel/disklabel.5.5
new file mode 100644
index 0000000..fb6f6cd
--- /dev/null
+++ b/sbin/bsdlabel/disklabel.5.5
@@ -0,0 +1,384 @@
+.\" Copyright (c) 1987, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Symmetric Computer Systems.
+.\"
+.\" 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.
+.\"
+.\" @(#)disklabel.5.5 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt DISKLABEL 5
+.Os
+.Sh NAME
+.Nm disklabel
+.Nd disk pack label
+.Sh SYNOPSIS
+.Fd #include <sys/disklabel.h>
+.Sh DESCRIPTION
+Each disk or disk pack on a system may contain a disk label
+which provides detailed information
+about the geometry of the disk and the partitions into which the disk
+is divided.
+It should be initialized when the disk is formatted,
+and may be changed later with the
+.Xr disklabel 8
+program.
+This information is used by the system disk driver and by the bootstrap
+program to determine how to program the drive
+and where to find the filesystems on the disk partitions.
+Additional information is used by the filesystem in order
+to use the disk most efficiently and to locate important filesystem information.
+The description of each partition contains an identifier for the partition
+type (standard filesystem, swap area, etc.).
+The filesystem updates the in-core copy of the label if it contains
+incomplete information about the filesystem.
+.Pp
+The label is located in sector number
+.Dv LABELSECTOR
+of the drive, usually sector 0 where it may be found
+without any information about the disk geometry.
+It is at an offset
+.Dv LABELOFFSET
+from the beginning of the sector, to allow room for the initial bootstrap.
+The disk sector containing the label is normally made read-only
+so that it is not accidentally overwritten by pack-to-pack copies
+or swap operations;
+the
+.Dv DIOCWLABEL
+.Xr ioctl 2 ,
+which is done as needed by the
+.Xr disklabel
+program.
+.Pp
+A copy of the in-core label for a disk can be obtained with the
+.Dv DIOCGDINFO
+.Xr ioctl ;
+this works with a file descriptor for a block or character (``raw'') device
+for any partition of the disk.
+The in-core copy of the label is set by the
+.Dv DIOCSDINFO
+.Xr ioctl .
+The offset of a partition cannot generally be changed while it is open,
+nor can it be made smaller while it is open.
+One exception is that any change is allowed if no label was found
+on the disk, and the driver was able to construct only a skeletal label
+without partition information.
+Finally, the
+.Dv DIOCWDINFO
+.Xr ioctl
+operation sets the in-core label and then updates the on-disk label;
+there must be an existing label on the disk for this operation to succeed.
+Thus, the initial label for a disk or disk pack must be installed
+by writing to the raw disk.
+All of these operations are normally done using
+.Xr disklabel .
+.Pp
+The format of the disk label, as specified in
+.Aw Pa sys/disklabel.h ,
+is
+.Bd -literal
+/*
+* Disk description table, see disktab(5)
+*/
+#define DISKTAB "/etc/disktab"
+
+/*
+* Each disk has a label which includes information about the hardware
+* disk geometry, filesystem partitions, and drive specific information.
+* The label is in block 0 or 1, possibly offset from the beginning
+* to leave room for a bootstrap, etc.
+*/
+
+#ifndef LABELSECTOR
+#define LABELSECTOR 0 /* sector containing label */
+#endif
+
+#ifndef LABELOFFSET
+#define LABELOFFSET 64 /* offset of label in sector */
+#endif
+
+#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS 8
+#endif
+
+#ifndef LOCORE
+struct disklabel {
+ u_long d_magic; /* the magic number */
+ short d_type; /* drive type */
+ short d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ /*
+ * d_packname contains the pack identifier and is returned when
+ * the disklabel is read off the disk or in-core copy.
+ * d_boot0 and d_boot1 are the (optional) names of the
+ * primary (block 0) and secondary (block 1-15) bootstraps
+ * as found in /usr/mdec. These are returned when using
+ * getdiskbyname(3)
+ to retrieve the values from /etc/disktab.
+ */
+#if defined(KERNEL) || defined(STANDALONE)
+ char d_packname[16]; /* pack identifier */
+#else
+ union {
+ char un_d_packname[16]; /* pack identifier */
+ struct {
+ char *un_d_boot0; /* primary bootstrap name */
+ char *un_d_boot1; /* secondary bootstrap name */
+ } un_b;
+ } d_un;
+
+#define d_packname d_un.un_d_packname
+#define d_boot0 d_un.un_b.un_d_boot0
+#define d_boot1 d_un.un_b.un_d_boot1
+#endif /* ! KERNEL or STANDALONE */
+
+ /* disk geometry: */
+ u_long d_secsize; /* # of bytes per sector */
+ u_long d_nsectors; /* # of data sectors per track */
+ u_long d_ntracks; /* # of tracks per cylinder */
+ u_long d_ncylinders; /* # of data cylinders per unit */
+ u_long d_secpercyl; /* # of data sectors per cylinder */
+ u_long d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ u_short d_sparespertrack; /* # of spare sectors per track */
+ u_short d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ u_long d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ u_short d_rpm; /* rotational speed */
+ u_short d_interleave; /* hardware sector interleave */
+ u_short d_trackskew; /* sector 0 skew, per track */
+ u_short d_cylskew; /* sector 0 skew, per cylinder */
+ u_long d_headswitch; /* head switch time, usec */
+ u_long d_trkseek; /* track-to-track seek, usec */
+ u_long d_flags; /* generic flags */
+#define NDDATA 5
+ u_long d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ u_long d_spare[NSPARE]; /* reserved for future use */
+ u_long d_magic2; /* the magic number (again) */
+ u_short d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ u_short d_npartitions; /* number of partitions in following */
+ u_long d_bbsize; /* size of boot area at sn0, bytes */
+ u_long d_sbsize; /* max size of fs superblock, bytes */
+ struct partition { /* the partition table */
+ u_long p_size; /* number of sectors in partition */
+ u_long p_offset; /* starting sector */
+ u_long p_fsize; /* filesystem basic fragment size */
+ u_char p_fstype; /* filesystem type, see below */
+ u_char p_frag; /* filesystem fragments per block */
+ union {
+ u_short cpg; /* UFS: FS cylinders per group */
+ u_short sgs; /* LFS: FS segment shift */
+ } __partition_u1;
+#define p_cpg __partition_u1.cpg
+#define p_sgs __partition_u1.sgs
+ u_short p_cpg; /* filesystem cylinders per group */
+ } d_partitions[MAXPARTITIONS]; /* actually may be more */
+};
+
+/* d_type values: */
+#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define DTYPE_MSCP 2 /* MSCP */
+#define DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define DTYPE_SCSI 4 /* SCSI */
+#define DTYPE_ESDI 5 /* ESDI interface */
+#define DTYPE_ST506 6 /* ST506 etc. */
+#define DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define DTYPE_HPFL 8 /* HP Fiber-link */
+#define DTYPE_FLOPPY 10 /* floppy */
+
+#ifdef DKTYPENAMES
+static char *dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1)
+#endif
+
+/*
+* Filesystem type and version.
+* Used to interpret other filesystem-specific
+* per-partition information.
+*/
+#define FS_UNUSED 0 /* unused */
+#define FS_SWAP 1 /* swap */
+#define FS_V6 2 /* Sixth Edition */
+#define FS_V7 3 /* Seventh Edition */
+#define FS_SYSV 4 /* System V */
+#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define FS_V8 6 /* Eighth Edition, 4K blocks */
+#define FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define FS_MSDOS 8 /* MSDOS file system */
+#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define FS_OTHER 10 /* in use, but unknown/unsupported */
+#define FS_HPFS 11 /* OS/2 high-performance file system */
+#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */
+#define FS_BOOT 13 /* partition contains bootstrap */
+
+#ifdef DKTYPENAMES
+static char *fstypenames[] = {
+ "unused",
+ "swap",
+ "Version 6",
+ "Version 7",
+ "System V",
+ "4.1BSD",
+ "Eighth Edition",
+ "4.2BSD",
+ "MSDOS",
+ "4.4LFS",
+ "unknown",
+ "HPFS",
+ "ISO9660",
+ "boot",
+ 0
+};
+#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1)
+#endif
+
+/*
+* flags shared by various drives:
+*/
+#define D_REMOVABLE 0x01 /* removable media */
+#define D_ECC 0x02 /* supports ECC */
+#define D_BADSECT 0x04 /* supports bad sector forw. */
+#define D_RAMDISK 0x08 /* disk emulator */
+#define D_CHAIN 0x10 /* can do back-back transfers */
+
+/*
+* Drive data for SMD.
+*/
+
+#define d_smdflags d_drivedata[0]
+#define D_SSE 0x1 /* supports skip sectoring */
+#define d_mindist d_drivedata[1]
+#define d_maxdist d_drivedata[2]
+#define d_sdist d_drivedata[3]
+
+/*
+* Drive data for ST506.
+*/
+#define d_precompcyl d_drivedata[0]
+#define d_gap3 d_drivedata[1] /* used only when formatting */
+
+/*
+ * Drive data for SCSI.
+ */
+#define d_blind d_drivedata[0]
+
+#ifndef LOCORE
+/*
+* Structure used to perform a format
+* or other raw operation, returning data
+* and/or register values.
+* Register identification and format
+* are device- and driver-dependent.
+*/
+struct format_op {
+ char *df_buf;
+ int df_count; /* value-result */
+ daddr_t df_startblk;
+ int df_reg[8]; /* result */
+};
+
+/*
+* Structure used internally to retrieve
+* information about a partition on a disk.
+*/
+struct partinfo {
+ struct disklabel *disklab;
+ struct partition *part;
+};
+
+/*
+* Disk-specific ioctls.
+*/
+ /* get and set disklabel; DIOCGPART used internally */
+#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */
+#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */
+#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */
+#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */
+
+/* do format operation, read or write */
+#define DIOCRFORMAT _IOWR('d', 105, struct format_op)
+#define DIOCWFORMAT _IOWR('d', 106, struct format_op)
+
+#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */
+#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */
+#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */
+
+#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */
+
+#endif LOCORE
+.Ed
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8
+.Sh HISTORY
diff --git a/sbin/bsdlabel/dkcksum.c b/sbin/bsdlabel/dkcksum.c
new file mode 100644
index 0000000..f7a1e49
--- /dev/null
+++ b/sbin/bsdlabel/dkcksum.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 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[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+
+u_short
+dkcksum(lp)
+ register struct disklabel *lp;
+{
+ register u_short *start, *end;
+ register u_short sum = 0;
+
+ start = (u_short *)lp;
+ end = (u_short *)&lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return (sum);
+}
diff --git a/sbin/bsdlabel/pathnames.h b/sbin/bsdlabel/pathnames.h
new file mode 100644
index 0000000..def3297
--- /dev/null
+++ b/sbin/bsdlabel/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * 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_BOOTDIR "/usr/mdec"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/EdDk.aXXXXXX"
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile
new file mode 100644
index 0000000..224f6b2
--- /dev/null
+++ b/sbin/fsck_ffs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck
+MAN8= fsck.0
+SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ffs/SMM.doc/0.t b/sbin/fsck_ffs/SMM.doc/0.t
new file mode 100644
index 0000000..9de391e
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/0.t
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1986, 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.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 6/8/93
+.\"
+.if n .ND
+.TL
+Fsck \- The UNIX\(dg File System Check Program
+.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program'
+.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%'
+.AU
+Marshall Kirk McKusick
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AU
+T. J. Kowalski
+.AI
+Bell Laboratories
+Murray Hill, New Jersey 07974
+.AB
+.FS
+\(dgUNIX is a trademark of Bell Laboratories.
+.FE
+.FS
+This work was done under grants from
+the National Science Foundation under grant MCS80-05144,
+and the Defense Advance Research Projects Agency (DoD) under
+Arpa Order No. 4031 monitored by Naval Electronic System Command under
+Contract No. N00039-82-C-0235.
+.FE
+This document reflects the use of
+.I fsck
+with the 4.2BSD and 4.3BSD file system organization. This
+is a revision of the
+original paper written by
+T. J. Kowalski.
+.PP
+File System Check Program (\fIfsck\fR)
+is an interactive file system check and repair program.
+.I Fsck
+uses the redundant structural information in the
+UNIX file system to perform several consistency checks.
+If an inconsistency is detected, it is reported
+to the operator, who may elect to fix or ignore
+each inconsistency.
+These inconsistencies result from the permanent interruption
+of the file system updates, which are performed every
+time a file is modified.
+Unless there has been a hardware failure,
+.I fsck
+is able to repair corrupted file systems
+using procedures based upon the order in which UNIX honors
+these file system update requests.
+.PP
+The purpose of this document is to describe the normal updating
+of the file system,
+to discuss the possible causes of file system corruption,
+and to present the corrective actions implemented
+by
+.I fsck.
+Both the program and the interaction between the
+program and the operator are described.
+.sp 2
+.LP
+Revised July 16, 1985
+.AE
+.LP
+.bp
+.ce
+.B "TABLE OF CONTENTS"
+.LP
+.sp 1
+.nf
+.B "1. Introduction"
+.LP
+.sp .5v
+.nf
+.B "2. Overview of the file system
+2.1. Superblock
+2.2. Summary Information
+2.3. Cylinder groups
+2.4. Fragments
+2.5. Updates to the file system
+.LP
+.sp .5v
+.nf
+.B "3. Fixing corrupted file systems
+3.1. Detecting and correcting corruption
+3.2. Super block checking
+3.3. Free block checking
+3.4. Checking the inode state
+3.5. Inode links
+3.6. Inode data size
+3.7. Checking the data associated with an inode
+3.8. File system connectivity
+.LP
+.sp .5v
+.nf
+.B Acknowledgements
+.LP
+.sp .5v
+.nf
+.B References
+.LP
+.sp .5v
+.nf
+.B "4. Appendix A
+4.1. Conventions
+4.2. Initialization
+4.3. Phase 1 - Check Blocks and Sizes
+4.4. Phase 1b - Rescan for more Dups
+4.5. Phase 2 - Check Pathnames
+4.6. Phase 3 - Check Connectivity
+4.7. Phase 4 - Check Reference Counts
+4.8. Phase 5 - Check Cyl groups
+4.9. Cleanup
+.ds RH Introduction
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/1.t b/sbin/fsck_ffs/SMM.doc/1.t
new file mode 100644
index 0000000..4d2f535
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/1.t
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1982, 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.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Introduction
+.NH
+Introduction
+.PP
+This document reflects the use of
+.I fsck
+with the 4.2BSD and 4.3BSD file system organization. This
+is a revision of the
+original paper written by
+T. J. Kowalski.
+.PP
+When a UNIX
+operating system is brought up, a consistency
+check of the file systems should always be performed.
+This precautionary measure helps to insure
+a reliable environment for file storage on disk.
+If an inconsistency is discovered,
+corrective action must be taken.
+.I Fsck
+runs in two modes.
+Normally it is run non-interactively by the system after
+a normal boot.
+When running in this mode,
+it will only make changes to the file system that are known
+to always be correct.
+If an unexpected inconsistency is found
+.I fsck
+will exit with a non-zero exit status,
+leaving the system running single-user.
+Typically the operator then runs
+.I fsck
+interactively.
+When running in this mode,
+each problem is listed followed by a suggested corrective action.
+The operator must decide whether or not the suggested correction
+should be made.
+.PP
+The purpose of this memo is to dispel the
+mystique surrounding
+file system inconsistencies.
+It first describes the updating of the file system
+(the calm before the storm) and
+then describes file system corruption (the storm).
+Finally,
+the set of deterministic corrective actions
+used by
+.I fsck
+(the Coast Guard
+to the rescue) is presented.
+.ds RH Overview of the File System
diff --git a/sbin/fsck_ffs/SMM.doc/2.t b/sbin/fsck_ffs/SMM.doc/2.t
new file mode 100644
index 0000000..7d00cea
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/2.t
@@ -0,0 +1,265 @@
+.\" Copyright (c) 1982, 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.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Overview of the file system
+.NH
+Overview of the file system
+.PP
+The file system is discussed in detail in [Mckusick84];
+this section gives a brief overview.
+.NH 2
+Superblock
+.PP
+A file system is described by its
+.I "super-block" .
+The super-block is built when the file system is created (\c
+.I newfs (8))
+and never changes.
+The super-block
+contains the basic parameters of the file system,
+such as the number of data blocks it contains
+and a count of the maximum number of files.
+Because the super-block contains critical data,
+.I newfs
+replicates it to protect against catastrophic loss.
+The
+.I "default super block"
+always resides at a fixed offset from the beginning
+of the file system's disk partition.
+The
+.I "redundant super blocks"
+are not referenced unless a head crash
+or other hard disk error causes the default super-block
+to be unusable.
+The redundant blocks are sprinkled throughout the disk partition.
+.PP
+Within the file system are files.
+Certain files are distinguished as directories and contain collections
+of pointers to files that may themselves be directories.
+Every file has a descriptor associated with it called an
+.I "inode".
+The inode contains information describing ownership of the file,
+time stamps indicating modification and access times for the file,
+and an array of indices pointing to the data blocks for the file.
+In this section,
+we assume that the first 12 blocks
+of the file are directly referenced by values stored
+in the inode structure itself\(dg.
+.FS
+\(dgThe actual number may vary from system to system, but is usually in
+the range 5-13.
+.FE
+The inode structure may also contain references to indirect blocks
+containing further data block indices.
+In a file system with a 4096 byte block size, a singly indirect
+block contains 1024 further block addresses,
+a doubly indirect block contains 1024 addresses of further single indirect
+blocks,
+and a triply indirect block contains 1024 addresses of further doubly indirect
+blocks (the triple indirect block is never needed in practice).
+.PP
+In order to create files with up to
+2\(ua32 bytes,
+using only two levels of indirection,
+the minimum size of a file system block is 4096 bytes.
+The size of file system blocks can be any power of two
+greater than or equal to 4096.
+The block size of the file system is maintained in the super-block,
+so it is possible for file systems of different block sizes
+to be accessible simultaneously on the same system.
+The block size must be decided when
+.I newfs
+creates the file system;
+the block size cannot be subsequently
+changed without rebuilding the file system.
+.NH 2
+Summary information
+.PP
+Associated with the super block is non replicated
+.I "summary information" .
+The summary information changes
+as the file system is modified.
+The summary information contains
+the number of blocks, fragments, inodes and directories in the file system.
+.NH 2
+Cylinder groups
+.PP
+The file system partitions the disk into one or more areas called
+.I "cylinder groups".
+A cylinder group is comprised of one or more consecutive
+cylinders on a disk.
+Each cylinder group includes inode slots for files, a
+.I "block map"
+describing available blocks in the cylinder group,
+and summary information describing the usage of data blocks
+within the cylinder group.
+A fixed number of inodes is allocated for each cylinder group
+when the file system is created.
+The current policy is to allocate one inode for each 2048
+bytes of disk space;
+this is expected to be far more inodes than will ever be needed.
+.PP
+All the cylinder group bookkeeping information could be
+placed at the beginning of each cylinder group.
+However if this approach were used,
+all the redundant information would be on the top platter.
+A single hardware failure that destroyed the top platter
+could cause the loss of all copies of the redundant super-blocks.
+Thus the cylinder group bookkeeping information
+begins at a floating offset from the beginning of the cylinder group.
+The offset for
+the
+.I "i+1" st
+cylinder group is about one track further
+from the beginning of the cylinder group
+than it was for the
+.I "i" th
+cylinder group.
+In this way,
+the redundant
+information spirals down into the pack;
+any single track, cylinder,
+or platter can be lost without losing all copies of the super-blocks.
+Except for the first cylinder group,
+the space between the beginning of the cylinder group
+and the beginning of the cylinder group information stores data.
+.NH 2
+Fragments
+.PP
+To avoid waste in storing small files,
+the file system space allocator divides a single
+file system block into one or more
+.I "fragments".
+The fragmentation of the file system is specified
+when the file system is created;
+each file system block can be optionally broken into
+2, 4, or 8 addressable fragments.
+The lower bound on the size of these fragments is constrained
+by the disk sector size;
+typically 512 bytes is the lower bound on fragment size.
+The block map associated with each cylinder group
+records the space availability at the fragment level.
+Aligned fragments are examined
+to determine block availability.
+.PP
+On a file system with a block size of 4096 bytes
+and a fragment size of 1024 bytes,
+a file is represented by zero or more 4096 byte blocks of data,
+and possibly a single fragmented block.
+If a file system block must be fragmented to obtain
+space for a small amount of data,
+the remainder of the block is made available for allocation
+to other files.
+For example,
+consider an 11000 byte file stored on
+a 4096/1024 byte file system.
+This file uses two full size blocks and a 3072 byte fragment.
+If no fragments with at least 3072 bytes
+are available when the file is created,
+a full size block is split yielding the necessary 3072 byte
+fragment and an unused 1024 byte fragment.
+This remaining fragment can be allocated to another file, as needed.
+.NH 2
+Updates to the file system
+.PP
+Every working day hundreds of files
+are created, modified, and removed.
+Every time a file is modified,
+the operating system performs a
+series of file system updates.
+These updates, when written on disk, yield a consistent file system.
+The file system stages
+all modifications of critical information;
+modification can
+either be completed or cleanly backed out after a crash.
+Knowing the information that is first written to the file system,
+deterministic procedures can be developed to
+repair a corrupted file system.
+To understand this process,
+the order that the update
+requests were being honored must first be understood.
+.PP
+When a user program does an operation to change the file system,
+such as a
+.I write ,
+the data to be written is copied into an internal
+.I "in-core"
+buffer in the kernel.
+Normally, the disk update is handled asynchronously;
+the user process is allowed to proceed even though
+the data has not yet been written to the disk.
+The data,
+along with the inode information reflecting the change,
+is eventually written out to disk.
+The real disk write may not happen until long after the
+.I write
+system call has returned.
+Thus at any given time, the file system,
+as it resides on the disk,
+lags the state of the file system represented by the in-core information.
+.PP
+The disk information is updated to reflect the in-core information
+when the buffer is required for another use,
+when a
+.I sync (2)
+is done (at 30 second intervals) by
+.I "/etc/update" "(8),"
+or by manual operator intervention with the
+.I sync (8)
+command.
+If the system is halted without writing out the in-core information,
+the file system on the disk will be in an inconsistent state.
+.PP
+If all updates are done asynchronously, several serious
+inconsistencies can arise.
+One inconsistency is that a block may be claimed by two inodes.
+Such an inconsistency can occur when the system is halted before
+the pointer to the block in the old inode has been cleared
+in the copy of the old inode on the disk,
+and after the pointer to the block in the new inode has been written out
+to the copy of the new inode on the disk.
+Here,
+there is no deterministic method for deciding
+which inode should really claim the block.
+A similar problem can arise with a multiply claimed inode.
+.PP
+The problem with asynchronous inode updates
+can be avoided by doing all inode deallocations synchronously.
+Consequently,
+inodes and indirect blocks are written to the disk synchronously
+(\fIi.e.\fP the process blocks until the information is
+really written to disk)
+when they are being deallocated.
+Similarly inodes are kept consistent by synchronously
+deleting, adding, or changing directory entries.
+.ds RH Fixing corrupted file systems
diff --git a/sbin/fsck_ffs/SMM.doc/3.t b/sbin/fsck_ffs/SMM.doc/3.t
new file mode 100644
index 0000000..07b5431
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/3.t
@@ -0,0 +1,439 @@
+.\" Copyright (c) 1982, 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.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Fixing corrupted file systems
+.NH
+Fixing corrupted file systems
+.PP
+A file system
+can become corrupted in several ways.
+The most common of these ways are
+improper shutdown procedures
+and hardware failures.
+.PP
+File systems may become corrupted during an
+.I "unclean halt" .
+This happens when proper shutdown
+procedures are not observed,
+physically write-protecting a mounted file system,
+or a mounted file system is taken off-line.
+The most common operator procedural failure is forgetting to
+.I sync
+the system before halting the CPU.
+.PP
+File systems may become further corrupted if proper startup
+procedures are not observed, e.g.,
+not checking a file system for inconsistencies,
+and not repairing inconsistencies.
+Allowing a corrupted file system to be used (and, thus, to be modified
+further) can be disastrous.
+.PP
+Any piece of hardware can fail at any time.
+Failures
+can be as subtle as a bad block
+on a disk pack, or as blatant as a non-functional disk-controller.
+.NH 2
+Detecting and correcting corruption
+.PP
+Normally
+.I fsck
+is run non-interactively.
+In this mode it will only fix
+corruptions that are expected to occur from an unclean halt.
+These actions are a proper subset of the actions that
+.I fsck
+will take when it is running interactively.
+Throughout this paper we assume that
+.I fsck
+is being run interactively,
+and all possible errors can be encountered.
+When an inconsistency is discovered in this mode,
+.I fsck
+reports the inconsistency for the operator to
+chose a corrective action.
+.PP
+A quiescent\(dd
+.FS
+\(dd I.e., unmounted and not being written on.
+.FE
+file system may be checked for structural integrity
+by performing consistency checks on the
+redundant data intrinsic to a file system.
+The redundant data is either read from
+the file system,
+or computed from other known values.
+The file system
+.B must
+be in a quiescent state when
+.I fsck
+is run,
+since
+.I fsck
+is a multi-pass program.
+.PP
+In the following sections,
+we discuss methods to discover inconsistencies
+and possible corrective actions
+for the cylinder group blocks, the inodes, the indirect blocks, and
+the data blocks containing directory entries.
+.NH 2
+Super-block checking
+.PP
+The most commonly corrupted item in a file system
+is the summary information
+associated with the super-block.
+The summary information is prone to corruption
+because it is modified with every change to the file
+system's blocks or inodes,
+and is usually corrupted
+after an unclean halt.
+.PP
+The super-block is checked for inconsistencies
+involving file-system size, number of inodes,
+free-block count, and the free-inode count.
+The file-system size must be larger than the
+number of blocks used by the super-block
+and the number of blocks used by the list of inodes.
+The file-system size and layout information
+are the most critical pieces of information for
+.I fsck .
+While there is no way to actually check these sizes,
+since they are statically determined by
+.I newfs ,
+.I fsck
+can check that these sizes are within reasonable bounds.
+All other file system checks require that these sizes be correct.
+If
+.I fsck
+detects corruption in the static parameters of the default super-block,
+.I fsck
+requests the operator to specify the location of an alternate super-block.
+.NH 2
+Free block checking
+.PP
+.I Fsck
+checks that all the blocks
+marked as free in the cylinder group block maps
+are not claimed by any files.
+When all the blocks have been initially accounted for,
+.I fsck
+checks that
+the number of free blocks
+plus the number of blocks claimed by the inodes
+equals the total number of blocks in the file system.
+.PP
+If anything is wrong with the block allocation maps,
+.I fsck
+will rebuild them,
+based on the list it has computed of allocated blocks.
+.PP
+The summary information associated with the super-block
+counts the total number of free blocks within the file system.
+.I Fsck
+compares this count to the
+number of free blocks it found within the file system.
+If the two counts do not agree, then
+.I fsck
+replaces the incorrect count in the summary information
+by the actual free-block count.
+.PP
+The summary information
+counts the total number of free inodes within the file system.
+.I Fsck
+compares this count to the number
+of free inodes it found within the file system.
+If the two counts do not agree, then
+.I fsck
+replaces the incorrect count in the
+summary information by the actual free-inode count.
+.NH 2
+Checking the inode state
+.PP
+An individual inode is not as likely to be corrupted as
+the allocation information.
+However, because of the great number of active inodes,
+a few of the inodes are usually corrupted.
+.PP
+The list of inodes in the file system
+is checked sequentially starting with inode 2
+(inode 0 marks unused inodes;
+inode 1 is saved for future generations)
+and progressing through the last inode in the file system.
+The state of each inode is checked for
+inconsistencies involving format and type,
+link count,
+duplicate blocks,
+bad blocks,
+and inode size.
+.PP
+Each inode contains a mode word.
+This mode word describes the type and state of the inode.
+Inodes must be one of six types:
+regular inode, directory inode, symbolic link inode,
+special block inode, special character inode, or socket inode.
+Inodes may be found in one of three allocation states:
+unallocated, allocated, and neither unallocated nor allocated.
+This last state suggests an incorrectly formated inode.
+An inode can get in this state if
+bad data is written into the inode list.
+The only possible corrective action is for
+.I fsck
+is to clear the inode.
+.NH 2
+Inode links
+.PP
+Each inode counts the
+total number of directory entries
+linked to the inode.
+.I Fsck
+verifies the link count of each inode
+by starting at the root of the file system,
+and descending through the directory structure.
+The actual link count for each inode
+is calculated during the descent.
+.PP
+If the stored link count is non-zero and the actual
+link count is zero,
+then no directory entry appears for the inode.
+If this happens,
+.I fsck
+will place the disconnected file in the
+.I lost+found
+directory.
+If the stored and actual link counts are non-zero and unequal,
+a directory entry may have been added or removed without the inode being
+updated.
+If this happens,
+.I fsck
+replaces the incorrect stored link count by the actual link count.
+.PP
+Each inode contains a list,
+or pointers to
+lists (indirect blocks),
+of all the blocks claimed by the inode.
+Since indirect blocks are owned by an inode,
+inconsistencies in indirect blocks directly
+affect the inode that owns it.
+.PP
+.I Fsck
+compares each block number claimed by an inode
+against a list of already allocated blocks.
+If another inode already claims a block number,
+then the block number is added to a list of
+.I "duplicate blocks" .
+Otherwise, the list of allocated blocks
+is updated to include the block number.
+.PP
+If there are any duplicate blocks,
+.I fsck
+will perform a partial second
+pass over the inode list
+to find the inode of the duplicated block.
+The second pass is needed,
+since without examining the files associated with
+these inodes for correct content,
+not enough information is available
+to determine which inode is corrupted and should be cleared.
+If this condition does arise
+(only hardware failure will cause it),
+then the inode with the earliest
+modify time is usually incorrect,
+and should be cleared.
+If this happens,
+.I fsck
+prompts the operator to clear both inodes.
+The operator must decide which one should be kept
+and which one should be cleared.
+.PP
+.I Fsck
+checks the range of each block number claimed by an inode.
+If the block number is
+lower than the first data block in the file system,
+or greater than the last data block,
+then the block number is a
+.I "bad block number" .
+Many bad blocks in an inode are usually caused by
+an indirect block that was not written to the file system,
+a condition which can only occur if there has been a hardware failure.
+If an inode contains bad block numbers,
+.I fsck
+prompts the operator to clear it.
+.NH 2
+Inode data size
+.PP
+Each inode contains a count of the number of data blocks
+that it contains.
+The number of actual data blocks
+is the sum of the allocated data blocks
+and the indirect blocks.
+.I Fsck
+computes the actual number of data blocks
+and compares that block count against
+the actual number of blocks the inode claims.
+If an inode contains an incorrect count
+.I fsck
+prompts the operator to fix it.
+.PP
+Each inode contains a thirty-two bit size field.
+The size is the number of data bytes
+in the file associated with the inode.
+The consistency of the byte size field is roughly checked
+by computing from the size field the maximum number of blocks
+that should be associated with the inode,
+and comparing that expected block count against
+the actual number of blocks the inode claims.
+.NH 2
+Checking the data associated with an inode
+.PP
+An inode can directly or indirectly
+reference three kinds of data blocks.
+All referenced blocks must be the same kind.
+The three types of data blocks are:
+plain data blocks, symbolic link data blocks, and directory data blocks.
+Plain data blocks
+contain the information stored in a file;
+symbolic link data blocks
+contain the path name stored in a link.
+Directory data blocks contain directory entries.
+.I Fsck
+can only check the validity of directory data blocks.
+.PP
+Each directory data block is checked for
+several types of inconsistencies.
+These inconsistencies include
+directory inode numbers pointing to unallocated inodes,
+directory inode numbers that are greater than
+the number of inodes in the file system,
+incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'',
+and directories that are not attached to the file system.
+If the inode number in a directory data block
+references an unallocated inode,
+then
+.I fsck
+will remove that directory entry.
+Again,
+this condition can only arise when there has been a hardware failure.
+.PP
+If a directory entry inode number references
+outside the inode list, then
+.I fsck
+will remove that directory entry.
+This condition occurs if bad data is written into a directory data block.
+.PP
+The directory inode number entry for ``\fB.\fP''
+must be the first entry in the directory data block.
+The inode number for ``\fB.\fP''
+must reference itself;
+e.g., it must equal the inode number
+for the directory data block.
+The directory inode number entry
+for ``\fB..\fP'' must be
+the second entry in the directory data block.
+Its value must equal the inode number for the
+parent of the directory entry
+(or the inode number of the directory
+data block if the directory is the
+root directory).
+If the directory inode numbers are
+incorrect,
+.I fsck
+will replace them with the correct values.
+If there are multiple hard links to a directory,
+the first one encountered is considered the real parent
+to which ``\fB..\fP'' should point;
+\fIfsck\fP recommends deletion for the subsequently discovered names.
+.NH 2
+File system connectivity
+.PP
+.I Fsck
+checks the general connectivity of the file system.
+If directories are not linked into the file system, then
+.I fsck
+links the directory back into the file system in the
+.I lost+found
+directory.
+This condition only occurs when there has been a hardware failure.
+.ds RH "References"
+.SH
+\s+2Acknowledgements\s0
+.PP
+I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie
+for their suggestions and help in implementing the new file system.
+Thanks also to Robert Henry for his editorial input to
+get this document together.
+Finally we thank our sponsors,
+the National Science Foundation under grant MCS80-05144,
+and the Defense Advance Research Projects Agency (DoD) under
+Arpa Order No. 4031 monitored by Naval Electronic System Command under
+Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983)
+.PP
+I would like to thank Larry A. Wehr for advice that lead
+to the first version of
+.I fsck
+and Rick B. Brandt for adapting
+.I fsck
+to
+UNIX/TS. (T. Kowalski, July 1979)
+.sp 2
+.SH
+\s+2References\s0
+.LP
+.IP [Dolotta78] 20
+Dolotta, T. A., and Olsson, S. B. eds.,
+.I "UNIX User's Manual, Edition 1.1\^" ,
+January 1978.
+.IP [Joy83] 20
+Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D.
+4.2BSD System Manual,
+.I "University of California at Berkeley" ,
+.I "Computer Systems Research Group Technical Report"
+#4, 1982.
+.IP [McKusick84] 20
+McKusick, M., Joy, W., Leffler, S., and Fabry, R.
+A Fast File System for UNIX,
+\fIACM Transactions on Computer Systems 2\fP, 3.
+pp. 181-197, August 1984.
+.IP [Ritchie78] 20
+Ritchie, D. M., and Thompson, K.,
+The UNIX Time-Sharing System,
+.I "The Bell System Technical Journal"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1905-29.
+.IP [Thompson78] 20
+Thompson, K.,
+UNIX Implementation,
+.I "The Bell System Technical Journal\^"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1931-46.
+.ds RH Appendix A \- Fsck Error Conditions
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/4.t b/sbin/fsck_ffs/SMM.doc/4.t
new file mode 100644
index 0000000..5ea8179
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/4.t
@@ -0,0 +1,1424 @@
+.\" Copyright (c) 1982, 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.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Appendix A \- Fsck Error Conditions
+.NH
+Appendix A \- Fsck Error Conditions
+.NH 2
+Conventions
+.PP
+.I Fsck
+is
+a multi-pass file system check program.
+Each file system pass invokes a different Phase of the
+.I fsck
+program.
+After the initial setup,
+.I fsck
+performs successive Phases over each file system,
+checking blocks and sizes,
+path-names,
+connectivity,
+reference counts,
+and the map of free blocks,
+(possibly rebuilding it),
+and performs some cleanup.
+.LP
+Normally
+.I fsck
+is run non-interactively to
+.I preen
+the file systems after an unclean halt.
+While preen'ing a file system,
+it will only fix corruptions that are expected
+to occur from an unclean halt.
+These actions are a proper subset of the actions that
+.I fsck
+will take when it is running interactively.
+Throughout this appendix many errors have several options
+that the operator can take.
+When an inconsistency is detected,
+.I fsck
+reports the error condition to the operator.
+If a response is required,
+.I fsck
+prints a prompt message and
+waits for a response.
+When preen'ing most errors are fatal.
+For those that are expected,
+the response taken is noted.
+This appendix explains the meaning of each error condition,
+the possible responses, and the related error conditions.
+.LP
+The error conditions are organized by the
+.I Phase
+of the
+.I fsck
+program in which they can occur.
+The error conditions that may occur
+in more than one Phase
+will be discussed in initialization.
+.NH 2
+Initialization
+.PP
+Before a file system check can be performed, certain
+tables have to be set up and certain files opened.
+This section concerns itself with the opening of files and
+the initialization of tables.
+This section lists error conditions resulting from
+command line options,
+memory requests,
+opening of files,
+status of files,
+file system size checks,
+and creation of the scratch file.
+All the initialization errors are fatal
+when the file system is being preen'ed.
+.sp
+.LP
+.B "\fIC\fP option?"
+.br
+\fIC\fP is not a legal option to
+.I fsck ;
+legal options are \-b, \-c, \-y, \-n, and \-p.
+.I Fsck
+terminates on this error condition.
+See the
+.I fsck (8)
+manual entry for further detail.
+.sp
+.LP
+.B "cannot alloc NNN bytes for blockmap"
+.br
+.B "cannot alloc NNN bytes for freemap"
+.br
+.B "cannot alloc NNN bytes for statemap"
+.br
+.B "cannot alloc NNN bytes for lncntp"
+.br
+.I Fsck 's
+request for memory for its virtual
+memory tables failed.
+This should never happen.
+.I Fsck
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't open checklist file: \fIF\fP"
+.br
+The file system checklist file
+\fIF\fP (usually
+.I /etc/fstab )
+can not be opened for reading.
+.I Fsck
+terminates on this error condition.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't stat root"
+.br
+.I Fsck 's
+request for statistics about the root directory ``/'' failed.
+This should never happen.
+.I Fsck
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't stat \fIF\fP"
+.br
+.B "Can't make sense out of name \fIF\fP"
+.br
+.I Fsck 's
+request for statistics about the file system \fIF\fP failed.
+When running manually,
+it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't open \fIF\fP"
+.br
+.I Fsck 's
+request attempt to open the file system \fIF\fP failed.
+When running manually, it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "\fIF\fP: (NO WRITE)"
+.br
+Either the \-n flag was specified or
+.I fsck 's
+attempt to open the file system \fIF\fP for writing failed.
+When running manually,
+all the diagnostics are printed out,
+but no modifications are attempted to fix them.
+.sp
+.LP
+.B "file is not a block or character device; OK"
+.br
+You have given
+.I fsck
+a regular file name by mistake.
+Check the type of the file specified.
+.LP
+Possible responses to the OK prompt are:
+.IP YES
+ignore this error condition.
+.IP NO
+ignore this file system and continues checking
+the next file system given.
+.sp
+.LP
+.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock optimization parameter is neither OPT_TIME
+nor OPT_SPACE.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The superblock is set to request optimization to minimize
+running time of the system.
+(If optimization to minimize disk space utilization is
+desired, it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock minimum space percentage is greater than 99%
+or less then 0%.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The minfree parameter is set to 10%.
+(If some other percentage is desired,
+it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The file system interleave is less than or equal to zero.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The interleave parameter is set to 1.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The number of physical sectors per track is less than the number
+of usable sectors per track.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The npsect parameter is set to the number of usable sectors per track.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+One of the following messages will appear:
+.br
+.B "MAGIC NUMBER WRONG"
+.br
+.B "NCG OUT OF RANGE"
+.br
+.B "CPG OUT OF RANGE"
+.br
+.B "NCYL DOES NOT JIVE WITH NCG*CPG"
+.br
+.B "SIZE PREPOSTEROUSLY LARGE"
+.br
+.B "TRASHED VALUES IN SUPER BLOCK"
+.br
+and will be followed by the message:
+.br
+.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP"
+.br
+.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE"
+.br
+.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)."
+.br
+The super block has been corrupted.
+An alternative super block must be selected from among those
+listed by
+.I newfs
+(8) when the file system was created.
+For file systems with a blocksize less than 32K,
+specifying \-b 32 is a good first choice.
+.sp
+.LP
+.B "INTERNAL INCONSISTENCY: \fIM\fP"
+.br
+.I Fsck 's
+has had an internal panic, whose message is specified as \fIM\fP.
+This should never happen.
+See a guru.
+.sp
+.LP
+.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for moving to a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+Often,
+however the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for reading a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+It will retry the read and print out the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be read.
+If
+.I fsck
+ever tries to write back one of the blocks on which the read failed
+it will print the message:
+.br
+.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK"
+.br
+where \fIN\fP indicates the sector that was written with zero's.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for writing a specified block number \fIB\fP
+in the file system failed.
+The disk is write-protected;
+check the write protect lock on the drive.
+If that is not the problem, see a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+The write operation will be retried with the failed blocks
+indicated by the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be written.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "bad inode number DDD to ginode"
+.br
+An internal error has attempted to read non-existent inode \fIDDD\fP.
+This error causes
+.I fsck
+to exit.
+See a guru.
+.NH 2
+Phase 1 \- Check Blocks and Sizes
+.PP
+This phase concerns itself with
+the inode list.
+This section lists error conditions resulting from
+checking inode types,
+setting up the zero-link-count table,
+examining inode block numbers for bad or duplicate blocks,
+checking inode size,
+and checking inode format.
+All errors in this phase except
+.B "INCORRECT BLOCK COUNT"
+and
+.B "PARTIALLY TRUNCATED INODE"
+are fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)"
+.br
+The mode word of the inode \fII\fP indicates that the inode is not a
+special block inode, special character inode, socket inode, regular inode,
+symbolic link, or directory inode.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+This will always invoke the UNALLOCATED error condition in Phase 2
+for each directory entry pointing to this inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)"
+.br
+.I Fsck
+has found inode \fII\fP whose size is shorter than the number of
+blocks allocated to it.
+This condition should only occur if the system crashes while in the
+midst of truncating a file.
+When preen'ing the file system,
+.I fsck
+completes the truncation to the specified size.
+.LP
+Possible responses to SALVAGE are:
+.IP YES
+complete the truncation to the size specified in the inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "LINK COUNT TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table for
+.I fsck
+containing allocated inodes with a link count of
+zero cannot allocate more memory.
+Increase the virtual memory for
+.I fsck .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If another allocated inode with a zero link count is found,
+this error condition is repeated.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "\fIB\fP BAD I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP with a number
+lower than the number of the first data block in the file system or
+greater than the number of the last block
+in the file system.
+This error condition may invoke the
+.B "EXCESSIVE BAD BLKS"
+error condition in Phase 1 (see next paragraph) if
+inode \fII\fP has too many block numbers outside the file system range.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks with a number
+lower than the number of the first data block in the file system or greater than
+the number of last block in the file system associated with inode \fII\fP.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "BAD STATE DDD TO BLKERR"
+.br
+An internal error has scrambled
+.I fsck 's
+state map to have the impossible value \fIDDD\fP.
+.I Fsck
+exits immediately.
+See a guru.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that is already claimed by
+another inode.
+This error condition may invoke the
+.B "EXCESSIVE DUP BLKS"
+error condition in Phase 1 if
+inode \fII\fP has too many block numbers claimed by other inodes.
+This error condition will always invoke Phase 1b and the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks claimed by other
+inodes.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUP TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table in
+.I fsck
+containing duplicate block numbers cannot allocate any more space.
+Increase the amount of virtual memory available to
+.I fsck .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If another duplicate block is found, this error condition will repeat.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)"
+.br
+Inode \fII\fP is neither allocated nor unallocated.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)"
+.br
+The block count for inode \fII\fP is \fIX\fP blocks,
+but should be \fIY\fP blocks.
+When preen'ing the count is corrected.
+.LP
+Possible responses to the CORRECT prompt are:
+.IP YES
+replace the block count of inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 1B: Rescan for More Dups
+.PP
+When a duplicate block is found in the file system, the file system is
+rescanned to find the inode that previously claimed that block.
+This section lists the error condition when the duplicate block is found.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that
+is already claimed by another inode.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2.
+You can determine which inodes have overlapping blocks by examining
+this error condition and the DUP error condition in Phase 1.
+.NH 2
+Phase 2 \- Check Pathnames
+.PP
+This phase concerns itself with removing directory entries
+pointing to
+error conditioned inodes
+from Phase 1 and Phase 1b.
+This section lists error conditions resulting from
+root inode mode and status,
+directory inode pointers in range,
+and directory entries pointing to bad inodes,
+and directory integrity checks.
+All errors in this phase are fatal if the file system is being preen'ed,
+except for directories not being a multiple of the blocks size
+and extraneous hard links.
+.sp
+.LP
+.B "ROOT INODE UNALLOCATED (ALLOCATE)"
+.br
+The root inode (usually inode number 2) has no allocate mode bits.
+This should never happen.
+.LP
+Possible responses to the ALLOCATE prompt are:
+.IP YES
+allocate inode 2 as the root inode.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will exit.
+.sp
+.LP
+.B "ROOT INODE NOT DIRECTORY (REALLOCATE)"
+.br
+The root inode (usually inode number 2)
+is not directory inode type.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will then prompt with
+.B "FIX"
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+replace the root inode's type to be a directory.
+If the root inode's data blocks are not directory blocks,
+many error conditions will be produced.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUPS/BAD IN ROOT INODE (REALLOCATE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks in the root inode (usually inode number 2) for the file system.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will then prompt with
+.B "CONTINUE" .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the
+.B "DUPS/BAD"
+error condition in the root inode and
+attempt to continue to run the file system check.
+If the root inode is not correct,
+then this may result in many other error conditions.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "NAME TOO LONG \fIF\fP"
+.br
+An excessively long path name has been found.
+This usually indicates loops in the file system name space.
+This can occur if the super user has made circular links to directories.
+The offending links must be removed (by a guru).
+.sp
+.LP
+.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has an inode number \fII\fP that is greater than
+the end of the inode list.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+A directory or file entry \fIF\fP points to an unallocated inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks or bad blocks
+associated with directory or file entry \fIF\fP, inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has a size \fIS\fP that is zero.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed;
+this will always invoke the BAD/DUP error condition in Phase 4.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fIF\fP has been found whose size \fIS\fP
+is less than the minimum size directory.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+increase the size of the directory to the minimum directory size.
+.IP NO
+ignore this directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP.
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)"
+.br
+A directory with an inconsistent internal state has been found.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+throw away all entries up to the next directory boundary (usually 512-byte)
+boundary.
+This drastic action can throw away up to 42 entries,
+and should be taken only after other recovery efforts have failed.
+.IP NO
+skip up to the next directory boundary and resume reading,
+but do not modify the directory.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `.' does
+does not equal \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `.' to be equal to \fII\fP.
+.IP NO
+leave the inode number for `.' unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose first entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `.' with inode number equal to \fII\fP.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose first entry is \fIF\fP.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'"
+.br
+A directory \fII\fP has been found whose first entry is not `.'.
+.I Fsck
+cannot resolve this problem as it should never happen.
+See a guru.
+.sp
+.LP
+.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `.'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `.'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `..' does
+does not equal the parent of \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `..' to be equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the inode number for `..' unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose second entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `..' with inode number equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose second entry is \fIF\fP.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'"
+.br
+A directory \fII\fP has been found whose second entry is not `..'.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the second entry in the directory
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `..'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `..'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE)
+.br
+.I Fsck
+has found a hard link, \fIN\fP, to a directory, \fID\fP.
+When preen'ing the extraneous links are ignored.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+delete the extraneous entry, \fIN\fP.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD RETURN STATE \fIS\fP FROM DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be returned
+from the routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD STATE \fIS\fP FOR ROOT INODE"
+.br
+An internal error has caused an impossible state \fIS\fP to be assigned
+to the root inode.
+.I Fsck
+exits.
+See a guru.
+.NH 2
+Phase 3 \- Check Connectivity
+.PP
+This phase concerns itself with the directory connectivity seen in
+Phase 2.
+This section lists error conditions resulting from
+unreferenced directories,
+and missing or full
+.I lost+found
+directories.
+.sp
+.LP
+.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+The directory inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of directory inode \fII\fP are printed.
+When preen'ing, the directory is reconnected if its size is non-zero,
+otherwise it is cleared.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect directory inode \fII\fP to the file system in the
+directory for lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 3
+if there are problems connecting directory inode \fII\fP to \fIlost+found\fP.
+This may also invoke the CONNECTED error condition in Phase 3 if the link
+was successful.
+.IP NO
+ignore this error condition.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP"
+.br
+This is an advisory message indicating a directory inode \fII1\fP was
+successfully connected to the
+.I lost+found
+directory.
+The parent inode \fII2\fP of the directory inode \fII1\fP is
+replaced by the inode number of the
+.I lost+found
+directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP
+(this can reoccur in Phase 3 if it is not adjusted in Phase 2).
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.NH 2
+Phase 4 \- Check Reference Counts
+.PP
+This phase concerns itself with the link count information
+seen in Phase 2 and Phase 3.
+This section lists error conditions resulting from
+unreferenced files,
+missing or full
+.I lost+found
+directory,
+incorrect link counts for files, directories, symbolic links, or special files,
+unreferenced files, symbolic links, and directories,
+and bad or duplicate blocks in files, symbolic links, and directories.
+All errors in this phase are correctable if the file system is being preen'ed
+except running out of space in the \fIlost+found\fP directory.
+.sp
+.LP
+.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+Inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of inode \fII\fP are printed.
+When preen'ing the file is cleared if either its size or its
+link count is zero,
+otherwise it is reconnected.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect inode \fII\fP to the file system in the directory for
+lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 4
+if there are problems connecting inode \fII\fP to
+.I lost+found .
+.IP NO
+ignore this error condition.
+This will always invoke the CLEAR error condition in Phase 4.
+.sp
+.LP
+.B "(CLEAR)"
+.br
+The inode mentioned in the immediately previous error condition can not be
+reconnected.
+This cannot occur if the file system is being preen'ed,
+since lack of space to reconnect files is a fatal error.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)"
+.br
+The link count for inode \fII\fP,
+is \fIX\fP but should be \fIY\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP
+are printed.
+When preen'ing the link count is adjusted unless the number of references
+is increasing, a condition that should never occur unless precipitated
+by a hardware failure.
+When the number of references is increasing under preen mode,
+.I fsck
+exits with the message:
+.br
+.B "LINK COUNT INCREASING"
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+replace the link count of file inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Inode \fII\fP, was not connected to a directory entry when the
+file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+When preen'ing,
+this is a file that was not connected because its size or link count was zero,
+hence it is cleared.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks associated with
+inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+This error cannot arise when the file system is being preen'ed,
+as it would have caused a fatal error earlier.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 5 - Check Cyl groups
+.PP
+This phase concerns itself with the free-block and used-inode maps.
+This section lists error conditions resulting from
+allocated blocks in the free-block maps,
+free blocks missing from free-block maps,
+and the total free-block count incorrect.
+It also lists error conditions resulting from
+free inodes in the used-inode maps,
+allocated inodes missing from used-inode maps,
+and the total used-inode count incorrect.
+.sp
+.LP
+.B "CG \fIC\fP: BAD MAGIC NUMBER"
+.br
+The magic number of cylinder group \fIC\fP is wrong.
+This usually indicates that the cylinder group maps have been destroyed.
+When running manually the cylinder group is marked as needing
+to be reconstructed.
+This error is fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)"
+.br
+A cylinder group block map is missing some free blocks.
+During preen'ing the maps are reconstructed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the free block map.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "SUMMARY INFORMATION BAD (SALVAGE)"
+.br
+The summary information was found to be incorrect.
+When preen'ing,
+the summary information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the summary information.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)"
+.br
+The superblock free block information was found to be incorrect.
+When preen'ing,
+the superblock free block information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the superblock free block information.
+.IP NO
+ignore this error condition.
+.NH 2
+Cleanup
+.PP
+Once a file system has been checked, a few cleanup functions are performed.
+This section lists advisory messages about
+the file system
+and modify status of the file system.
+.sp
+.LP
+.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)"
+.br
+This is an advisory message indicating that
+the file system checked contained
+\fIV\fP files using
+\fIW\fP fragment sized blocks leaving
+\fIX\fP fragment sized blocks free in the file system.
+The numbers in parenthesis breaks the free count down into
+\fIY\fP free fragments and
+\fIZ\fP free full sized blocks.
+.sp
+.LP
+.B "***** REBOOT UNIX *****"
+.br
+This is an advisory message indicating that
+the root file system has been modified by
+.I fsck.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck
+may be undone by the in-core copies of tables
+UNIX keeps.
+When preen'ing,
+.I fsck
+will exit with a code of 4.
+The standard auto-reboot script distributed with 4.3BSD
+interprets an exit code of 4 by issuing a reboot system call.
+.sp
+.LP
+.B "***** FILE SYSTEM WAS MODIFIED *****"
+.br
+This is an advisory message indicating that
+the current file system was modified by
+.I fsck.
+If this file system is mounted or is the current root file system,
+.I fsck
+should be halted and UNIX rebooted.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck
+may be undone by the in-core copies of tables
+UNIX keeps.
diff --git a/sbin/fsck_ffs/SMM.doc/Makefile b/sbin/fsck_ffs/SMM.doc/Makefile
new file mode 100644
index 0000000..26823bc
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/03.fsck
+SRCS= 0.t 1.t 2.t 3.t 4.t
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
new file mode 100644
index 0000000..ee5d095
--- /dev/null
+++ b/sbin/fsck_ffs/dir.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+char *lfname = "lost+found";
+int lfmode = 01777;
+struct dirtemplate emptydir = { 0, DIRBLKSIZ };
+struct dirtemplate dirhead = {
+ 0, 12, DT_DIR, 1, ".",
+ 0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
+};
+struct odirtemplate odirhead = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+struct direct *fsck_readdir();
+struct bufarea *getdirblk();
+
+/*
+ * Propagate connected state through the tree.
+ */
+propagate()
+{
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ long change;
+
+ inpend = &inpsort[inplast];
+ do {
+ change = 0;
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE) {
+ statemap[inp->i_number] = DFOUND;
+ change++;
+ }
+ }
+ } while (change > 0);
+}
+
+/*
+ * Scan each entry in a directory block.
+ */
+dirscan(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp;
+ register struct bufarea *bp;
+ int dsize, n;
+ long blksiz;
+ char dbuf[DIRBLKSIZ];
+
+ if (idesc->id_type != DATA)
+ errexit("wrong type to dirscan %d\n", idesc->id_type);
+ if (idesc->id_entryno == 0 &&
+ (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
+ idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
+ idesc->id_filesize -= blksiz;
+ return (SKIP);
+ }
+ idesc->id_loc = 0;
+ for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
+ dsize = dp->d_reclen;
+ bcopy((char *)dp, dbuf, (size_t)dsize);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ struct direct *tdp = (struct direct *)dbuf;
+ u_char tmp;
+
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ idesc->id_dirp = (struct direct *)dbuf;
+ if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt && !doinglevel2) {
+ struct direct *tdp;
+ u_char tmp;
+
+ tdp = (struct direct *)dbuf;
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+fsck_readdir(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp, *ndp;
+ register struct bufarea *bp;
+ long size, blksiz, fix, dploc;
+
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
+ idesc->id_loc < blksiz) {
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (dircheck(idesc, dp))
+ goto dpok;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ dp->d_reclen = DIRBLKSIZ;
+ dp->d_ino = 0;
+ dp->d_type = 0;
+ dp->d_namlen = 0;
+ dp->d_name[0] = '\0';
+ if (fix)
+ dirty(bp);
+ idesc->id_loc += DIRBLKSIZ;
+ idesc->id_filesize -= DIRBLKSIZ;
+ return (dp);
+ }
+dpok:
+ if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
+ return NULL;
+ dploc = idesc->id_loc;
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ idesc->id_loc += dp->d_reclen;
+ idesc->id_filesize -= dp->d_reclen;
+ if ((idesc->id_loc % DIRBLKSIZ) == 0)
+ return (dp);
+ ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
+ dircheck(idesc, ndp) == 0) {
+ size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ idesc->id_loc += size;
+ idesc->id_filesize -= size;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ dp->d_reclen += size;
+ if (fix)
+ dirty(bp);
+ }
+ return (dp);
+}
+
+/*
+ * Verify that a directory entry is valid.
+ * This is a superset of the checks made in the kernel.
+ */
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+
+ size = DIRSIZ(!newinofmt, dp);
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ type = dp->d_namlen;
+ namlen = dp->d_type;
+ } else {
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+ }
+# else
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+# endif
+ if (dp->d_ino < maxino &&
+ dp->d_reclen != 0 &&
+ dp->d_reclen <= spaceleft &&
+ (dp->d_reclen & 0x3) == 0 &&
+ dp->d_reclen >= size &&
+ idesc->id_filesize >= size &&
+ namlen <= MAXNAMLEN &&
+ type <= 15) {
+ if (dp->d_ino == 0)
+ return (1);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == 0 || (*cp++ == '/'))
+ return (0);
+ if (*cp == 0)
+ return (1);
+ }
+ return (0);
+}
+
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+fileerror(cwd, ino, errmesg)
+ ino_t cwd, ino;
+ char *errmesg;
+{
+ register struct dinode *dp;
+ char pathbuf[MAXPATHLEN + 1];
+
+ pwarn("%s ", errmesg);
+ pinode(ino);
+ printf("\n");
+ getpathname(pathbuf, cwd, ino);
+ if (ino < ROOTINO || ino > maxino) {
+ pfatal("NAME=%s\n", pathbuf);
+ return;
+ }
+ dp = ginode(ino);
+ if (ftypeok(dp))
+ pfatal("%s=%s\n",
+ (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
+ else
+ pfatal("NAME=%s\n", pathbuf);
+}
+
+adjust(idesc, lcnt)
+ register struct inodesc *idesc;
+ short lcnt;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (dp->di_nlink == lcnt) {
+ if (linkup(idesc->id_number, (ino_t)0) == 0)
+ clri(idesc, "UNREF", 0);
+ } else {
+ pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
+ ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
+ pinode(idesc->id_number);
+ printf(" COUNT %d SHOULD BE %d",
+ dp->di_nlink, dp->di_nlink - lcnt);
+ if (preen) {
+ if (lcnt < 0) {
+ printf("\n");
+ pfatal("LINK COUNT INCREASING");
+ }
+ printf(" (ADJUSTED)\n");
+ }
+ if (preen || reply("ADJUST") == 1) {
+ dp->di_nlink -= lcnt;
+ inodirty();
+ }
+ }
+}
+
+mkentry(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ struct direct newent;
+ int newlen, oldlen;
+
+ newent.d_namlen = strlen(idesc->id_name);
+ newlen = DIRSIZ(0, &newent);
+ if (dirp->d_ino != 0)
+ oldlen = DIRSIZ(0, dirp);
+ else
+ oldlen = 0;
+ if (dirp->d_reclen - oldlen < newlen)
+ return (KEEPON);
+ newent.d_reclen = dirp->d_reclen - oldlen;
+ dirp->d_reclen = oldlen;
+ dirp = (struct direct *)(((char *)dirp) + oldlen);
+ dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ dirp->d_reclen = newent.d_reclen;
+ dirp->d_namlen = newent.d_namlen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
+ return (KEEPON);
+ dirp->d_ino = idesc->id_parent;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ return (ALTERED|STOP);
+}
+
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+ extern int pass4check();
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ dp = ginode(orphan);
+ lostdir = (dp->di_mode & IFMT) == IFDIR;
+ pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+ pinode(orphan);
+ if (preen && dp->di_size == 0)
+ return (0);
+ if (preen)
+ printf(" (RECONNECTED)\n");
+ else
+ if (reply("RECONNECT") == 0)
+ return (0);
+ if (lfdir == 0) {
+ dp = ginode(ROOTINO);
+ idesc.id_name = lfname;
+ idesc.id_type = DATA;
+ idesc.id_func = findino;
+ idesc.id_number = ROOTINO;
+ if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ lfdir = idesc.id_parent;
+ } else {
+ pwarn("NO lost+found DIRECTORY");
+ if (preen || reply("CREATE")) {
+ lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
+ if (lfdir != 0) {
+ if (makeentry(ROOTINO, lfdir, lfname) != 0) {
+ if (preen)
+ printf(" (CREATED)\n");
+ } else {
+ freedir(lfdir, ROOTINO);
+ lfdir = 0;
+ if (preen)
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (lfdir == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ }
+ dp = ginode(lfdir);
+ if ((dp->di_mode & IFMT) != IFDIR) {
+ pfatal("lost+found IS NOT A DIRECTORY");
+ if (reply("REALLOCATE") == 0)
+ return (0);
+ oldlfdir = lfdir;
+ if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ inodirty();
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = oldlfdir;
+ adjust(&idesc, lncntp[oldlfdir] + 1);
+ lncntp[oldlfdir] = 0;
+ dp = ginode(lfdir);
+ }
+ if (statemap[lfdir] != DFOUND) {
+ pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ (void)lftempname(tempname, orphan);
+ if (makeentry(lfdir, orphan, tempname) == 0) {
+ pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ lncntp[orphan]--;
+ if (lostdir) {
+ if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ parentdir != (ino_t)-1)
+ (void)makeentry(orphan, lfdir, "..");
+ dp = ginode(lfdir);
+ dp->di_nlink++;
+ inodirty();
+ lncntp[lfdir]++;
+ pwarn("DIR I=%lu CONNECTED. ", orphan);
+ if (parentdir != (ino_t)-1)
+ printf("PARENT WAS I=%lu\n", parentdir);
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+changeino(dir, name, newnum)
+ ino_t dir;
+ char *name;
+ ino_t newnum;
+{
+ struct inodesc idesc;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = chgino;
+ idesc.id_number = dir;
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ idesc.id_parent = newnum; /* new value for name */
+ return (ckinode(ginode(dir), &idesc));
+}
+
+/*
+ * make an entry in a directory
+ */
+makeentry(parent, ino, name)
+ ino_t parent, ino;
+ char *name;
+{
+ struct dinode *dp;
+ struct inodesc idesc;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (parent < ROOTINO || parent >= maxino ||
+ ino < ROOTINO || ino >= maxino)
+ return (0);
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = mkentry;
+ idesc.id_number = parent;
+ idesc.id_parent = ino; /* this is the inode to enter */
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ dp = ginode(parent);
+ if (dp->di_size % DIRBLKSIZ) {
+ dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
+ inodirty();
+ }
+ if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ return (1);
+ getpathname(pathbuf, parent, parent);
+ dp = ginode(parent);
+ if (expanddir(dp, pathbuf) == 0)
+ return (0);
+ return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ daddr_t lastbn, newblk;
+ register struct bufarea *bp;
+ char *cp, firstblk[DIRBLKSIZ];
+
+ lastbn = lblkno(&sblock, dp->di_size);
+ if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
+ return (0);
+ if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ return (0);
+ dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+ dp->di_db[lastbn] = newblk;
+ dp->di_size += sblock.fs_bsize;
+ dp->di_blocks += btodb(sblock.fs_bsize);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir);
+ pwarn("NO SPACE LEFT IN %s", name);
+ if (preen)
+ printf(" (EXPANDED)\n");
+ else if (reply("EXPAND") == 0)
+ goto bad;
+ dirty(bp);
+ inodirty();
+ return (1);
+bad:
+ dp->di_db[lastbn] = dp->di_db[lastbn + 1];
+ dp->di_db[lastbn + 1] = 0;
+ dp->di_size -= sblock.fs_bsize;
+ dp->di_blocks -= btodb(sblock.fs_bsize);
+ freeblk(newblk, sblock.fs_frag);
+ return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+allocdir(parent, request, mode)
+ ino_t parent, request;
+ int mode;
+{
+ ino_t ino;
+ char *cp;
+ struct dinode *dp;
+ register struct bufarea *bp;
+ struct dirtemplate *dirp;
+
+ ino = allocino(request, IFDIR|mode);
+ if (newinofmt)
+ dirp = &dirhead;
+ else
+ dirp = (struct dirtemplate *)&odirhead;
+ dirp->dot_ino = ino;
+ dirp->dotdot_ino = parent;
+ dp = ginode(ino);
+ bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
+ if (bp->b_errs) {
+ freeino(ino);
+ return (0);
+ }
+ bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ dp->di_nlink = 2;
+ inodirty();
+ if (ino == ROOTINO) {
+ lncntp[ino] = dp->di_nlink;
+ cacheino(dp, ino);
+ return(ino);
+ }
+ if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
+ freeino(ino);
+ return (0);
+ }
+ cacheino(dp, ino);
+ statemap[ino] = statemap[parent];
+ if (statemap[ino] == DSTATE) {
+ lncntp[ino] = dp->di_nlink;
+ lncntp[parent]++;
+ }
+ dp = ginode(parent);
+ dp->di_nlink++;
+ inodirty();
+ return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+freedir(ino, parent)
+ ino_t ino, parent;
+{
+ struct dinode *dp;
+
+ if (ino != parent) {
+ dp = ginode(parent);
+ dp->di_nlink--;
+ inodirty();
+ }
+ freeino(ino);
+}
+
+/*
+ * generate a temporary name for the lost+found directory.
+ */
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
new file mode 100644
index 0000000..7fa831f
--- /dev/null
+++ b/sbin/fsck_ffs/fsck.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ *
+ * @(#)fsck.h 8.1 (Berkeley) 6/5/93
+ */
+
+#define MAXDUP 10 /* limit on dup blks (per inode) */
+#define MAXBAD 10 /* limit on bad blks (per inode) */
+#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+#define USTATE 01 /* inode not allocated */
+#define FSTATE 02 /* inode is file */
+#define DSTATE 03 /* inode is directory */
+#define DFOUND 04 /* directory found during descent */
+#define DCLEAR 05 /* directory is to be cleared */
+#define FCLEAR 06 /* file is to be cleared */
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+ struct bufarea *b_next; /* free list queue */
+ struct bufarea *b_prev; /* free list queue */
+ daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ daddr_t *b_indir; /* indirect block */
+ struct fs *b_fs; /* super block */
+ struct cg *b_cg; /* cylinder group */
+ struct dinode *b_dinode; /* inode block */
+ } b_un;
+ char b_dirty;
+};
+
+#define B_INUSE 1
+
+#define MINBUFS 5 /* minimum number of buffers required */
+struct bufarea bufhead; /* head of list of other blks in filesys */
+struct bufarea sblk; /* file system superblock */
+struct bufarea cgblk; /* cylinder group blocks */
+struct bufarea *pdirbp; /* current directory contents */
+struct bufarea *pbp; /* current inode block */
+struct bufarea *getdatablk();
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (daddr_t)-1; \
+ (bp)->b_flags = 0;
+
+#define sbdirty() sblk.b_dirty = 1
+#define cgdirty() cgblk.b_dirty = 1
+#define sblock (*sblk.b_un.b_fs)
+#define cgrp (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+
+struct inodesc {
+ enum fixstate id_fix; /* policy on fixing errors */
+ int (*id_func)(); /* function to be applied to blocks of inode */
+ ino_t id_number; /* inode number described */
+ ino_t id_parent; /* for DATA nodes, their parent */
+ daddr_t id_blkno; /* current block number being examined */
+ int id_numfrags; /* number of frags contained in block */
+ quad_t id_filesize; /* for DATA nodes, the size of the directory */
+ int id_loc; /* for DATA nodes, current location in dir */
+ int id_entryno; /* for DATA nodes, current entry number */
+ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
+ char *id_name; /* for DATA nodes, name to find or enter */
+ char id_type; /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define DATA 1
+#define ADDR 2
+
+/*
+ * Linked list of duplicate blocks.
+ *
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ * w -> y -> x -> z -> y -> w -> y
+ * ^ ^
+ * | |
+ * duplist muldup
+ */
+struct dups {
+ struct dups *next;
+ daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+/*
+ * Linked list of inodes with zero link counts.
+ */
+struct zlncnt {
+ struct zlncnt *next;
+ ino_t zlncnt;
+};
+struct zlncnt *zlnhead; /* head of zero link count list */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode number of this entry */
+ ino_t i_parent; /* inode number of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ size_t i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ daddr_t i_blks[1]; /* actually longer */
+} **inphead, **inpsort;
+long numdirs, listmax, inplast;
+
+char *cdevname; /* name of device being checked */
+long dev_bsize; /* computed value of DEV_BSIZE */
+long secsize; /* actual disk sector size */
+char nflag; /* assume a no response */
+char yflag; /* assume a yes response */
+int bflag; /* location of alternate super block */
+int debug; /* output debugging info */
+int cvtlevel; /* convert to newer file system format */
+int doinglevel1; /* converting to new cylinder group format */
+int doinglevel2; /* converting to new inode format */
+int newinofmt; /* filesystem has new inode format */
+char preen; /* just fix normal inconsistencies */
+char hotroot; /* checking root device */
+char havesb; /* superblock has been read */
+int fsmodified; /* 1 => write done to file system */
+int fsreadfd; /* file descriptor for reading file system */
+int fswritefd; /* file descriptor for writing file system */
+
+daddr_t maxfsblock; /* number of blocks in the file system */
+char *blockmap; /* ptr to primary blk allocation map */
+ino_t maxino; /* number of inodes in file system */
+ino_t lastino; /* last inode in use */
+char *statemap; /* ptr to inode state table */
+char *typemap; /* ptr to inode type table */
+short *lncntp; /* ptr to link count table */
+
+ino_t lfdir; /* lost & found directory inode number */
+char *lfname; /* lost & found directory name */
+int lfmode; /* lost & found directory creation mode */
+
+daddr_t n_blks; /* number of blocks in use */
+daddr_t n_files; /* number of files in use */
+
+#define clearinode(dp) (*(dp) = zino)
+struct dinode zino;
+
+#define setbmap(blkno) setbit(blockmap, blkno)
+#define testbmap(blkno) isset(blockmap, blkno)
+#define clrbmap(blkno) clrbit(blockmap, blkno)
+
+#define STOP 0x01
+#define SKIP 0x02
+#define KEEPON 0x04
+#define ALTERED 0x08
+#define FOUND 0x10
+
+time_t time();
+struct dinode *ginode();
+struct inoinfo *getinoinfo();
+void getblk();
+ino_t allocino();
+int findino();
diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8
new file mode 100644
index 0000000..cf8c499
--- /dev/null
+++ b/sbin/fsck_ffs/fsck_ffs.8
@@ -0,0 +1,291 @@
+.\" Copyright (c) 1980, 1989, 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.
+.\"
+.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt FSCK 8
+.Os BSD 4
+.Sh NAME
+.Nm fsck
+.Nd filesystem consistency check and interactive repair
+.Sh SYNOPSIS
+.Nm fsck
+.Fl p
+.Op Fl m Ar mode
+.Nm fsck
+.Op Fl b Ar block#
+.Op Fl c Ar level
+.Op Fl l Ar maxparallel
+.Op Fl y
+.Op Fl n
+.Op Fl m Ar mode
+.Op Ar filesystem
+.Ar ...
+.Sh DESCRIPTION
+The first form of
+.Nm fsck
+preens a standard set of filesystems or the specified filesystems.
+It is normally used in the script
+.Pa /etc/rc
+during automatic reboot.
+Here
+.Nm fsck
+reads the table
+.Pa /etc/fstab
+to determine which filesystems to check.
+Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro''
+and that have non-zero pass number are checked.
+Filesystems with pass number 1 (normally just the root filesystem)
+are checked one at a time.
+When pass 1 completes, all remaining filesystems are checked,
+running one process per disk drive.
+The disk drive containing each filesystem is inferred from the longest prefix
+of the device name that ends in a digit; the remaining characters are assumed
+to be the partition designator.
+.Pp
+The kernel takes care that only a restricted class of innocuous filesystem
+inconsistencies can happen unless hardware or software failures intervene.
+These are limited to the following:
+.Bl -item -compact
+.It
+Unreferenced inodes
+.It
+Link counts in inodes too large
+.It
+Missing blocks in the free map
+.It
+Blocks in the free map also in files
+.It
+Counts in the super-block wrong
+.El
+.Pp
+These are the only inconsistencies that
+.Nm fsck
+with the
+.Fl p
+option will correct; if it encounters other inconsistencies, it exits
+with an abnormal return status and an automatic reboot will then fail.
+For each corrected inconsistency one or more lines will be printed
+identifying the filesystem on which the correction will take place,
+and the nature of the correction. After successfully correcting a filesystem,
+.Nm fsck
+will print the number of files on that filesystem,
+the number of used and free blocks,
+and the percentage of fragmentation.
+.Pp
+If sent a
+.Dv QUIT
+signal,
+.Nm fsck
+will finish the filesystem checks, then exit with an abnormal
+return status that causes an automatic reboot to fail.
+This is useful when you want to finish the filesystem checks during an
+automatic reboot,
+but do not want the machine to come up multiuser after the checks complete.
+.Pp
+Without the
+.Fl p
+option,
+.Nm fsck
+audits and interactively repairs inconsistent conditions for filesystems.
+If the filesystem is inconsistent the operator is prompted for concurrence
+before each correction is attempted.
+It should be noted that some of the corrective actions which are not
+correctable under the
+.Fl p
+option will result in some loss of data.
+The amount and severity of data lost may be determined from the diagnostic
+output.
+The default action for each consistency correction
+is to wait for the operator to respond
+.Li yes
+or
+.Li no .
+If the operator does not have write permission on the filesystem
+.Nm fsck
+will default to a
+.Fl n
+action.
+.Pp
+.Nm Fsck
+has more consistency checks than
+its predecessors
+.Em check , dcheck , fcheck ,
+and
+.Em icheck
+combined.
+.Pp
+The following flags are interpreted by
+.Nm fsck .
+.Bl -tag -width indent
+.It Fl b
+Use the block specified immediately after the flag as
+the super block for the filesystem. Block 32 is usually
+an alternate super block.
+.It Fl l
+Limit the number of parallel checks to the number specified in the following
+argument.
+By default, the limit is the number of disks, running one process per disk.
+If a smaller limit is given, the disks are checked round-robin, one filesystem
+at a time.
+.It Fl m
+Use the mode specified in octal immediately after the flag as the
+permission bits to use when creating the
+.Pa lost+found
+directory rather than the default 1777.
+In particular, systems that do not wish to have lost files accessible
+by all users on the system should use a more restrictive
+set of permissions such as 700.
+.It Fl y
+Assume a yes response to all questions asked by
+.Nm fsck ;
+this should be used with great caution as this is a free license
+to continue after essentially unlimited trouble has been encountered.
+.It Fl n
+Assume a no response to all questions asked by
+.Nm fsck
+except for
+.Ql CONTINUE? ,
+which is assumed to be affirmative;
+do not open the filesystem for writing.
+.It Fl c
+Convert the filesystem to the specified level.
+Note that the level of a filesystem can only be raised.
+.Bl -tag -width indent
+There are currently three levels defined:
+.It 0
+The filesystem is in the old (static table) format.
+.It 1
+The filesystem is in the new (dynamic table) format.
+.It 2
+The filesystem supports 32-bit uid's and gid's,
+short symbolic links are stored in the inode,
+and directories have an added field showing the file type.
+.El
+.Pp
+In interactive mode,
+.Nm fsck
+will list the conversion to be made
+and ask whether the conversion should be done.
+If a negative answer is given,
+no further operations are done on the filesystem.
+In preen mode,
+the conversion is listed and done if
+possible without user interaction.
+Conversion in preen mode is best used when all the filesystems
+are being converted at once.
+The format of a filesystem can be determined from the
+first line of output from
+.Xr dumpfs 8 .
+.El
+.Pp
+If no filesystems are given to
+.Nm fsck
+then a default list of filesystems is read from
+the file
+.Pa /etc/fstab .
+.Pp
+.Bl -enum -indent indent -compact
+Inconsistencies checked are as follows:
+.It
+Blocks claimed by more than one inode or the free map.
+.It
+Blocks claimed by an inode outside the range of the filesystem.
+.It
+Incorrect link counts.
+.It
+Size checks:
+.Bl -item -indent indent -compact
+.It
+Directory size not a multiple of DIRBLKSIZ.
+.It
+Partially truncated file.
+.El
+.It
+Bad inode format.
+.It
+Blocks not accounted for anywhere.
+.It
+Directory checks:
+.Bl -item -indent indent -compact
+.It
+File pointing to unallocated inode.
+.It
+Inode number out of range.
+.It
+Dot or dot-dot not the first two entries of a directory
+or having the wrong inode number.
+.El
+.It
+Super Block checks:
+.Bl -item -indent indent -compact
+.It
+More blocks for inodes than there are in the filesystem.
+.It
+Bad free block map format.
+.It
+Total free block and/or free inode count incorrect.
+.El
+.El
+.Pp
+Orphaned files and directories (allocated but unreferenced) are,
+with the operator's concurrence, reconnected by
+placing them in the
+.Pa lost+found
+directory.
+The name assigned is the inode number.
+If the
+.Pa lost+found
+directory does not exist, it is created.
+If there is insufficient space its size is increased.
+.Pp
+Because of inconsistencies between the block device and the buffer cache,
+the raw device should always be used.
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+contains default list of filesystems to check.
+.El
+.Sh DIAGNOSTICS
+The diagnostics produced by
+.Nm fsck
+are fully enumerated and explained in Appendix A of
+.Rs
+.%T "Fsck \- The UNIX File System Check Program"
+.Re
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr fs 5 ,
+.Xr fsdb 8 ,
+.Xr newfs 8 ,
+.Xr mkfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
new file mode 100644
index 0000000..f1c1758
--- /dev/null
+++ b/sbin/fsck_ffs/inode.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ long ret, n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+
+ if (idesc->id_fix != IGNORE)
+ idesc->id_fix = DONTKNOW;
+ idesc->id_entryno = 0;
+ idesc->id_filesize = dp->di_size;
+ mode = dp->di_mode & IFMT;
+ if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
+ dp->di_size < sblock.fs_maxsymlinklen))
+ return (KEEPON);
+ dino = *dp;
+ ndb = howmany(dino.di_size, sblock.fs_bsize);
+ for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
+ if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
+ idesc->id_numfrags =
+ numfrags(&sblock, fragroundup(&sblock, offset));
+ else
+ idesc->id_numfrags = sblock.fs_frag;
+ if (*ap == 0)
+ continue;
+ idesc->id_blkno = *ap;
+ if (idesc->id_type == ADDR)
+ ret = (*idesc->id_func)(idesc);
+ else
+ ret = dirscan(idesc);
+ if (ret & STOP)
+ return (ret);
+ }
+ idesc->id_numfrags = sblock.fs_frag;
+ remsize = dino.di_size - sblock.fs_bsize * NDADDR;
+ sizepb = sblock.fs_bsize;
+ for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ ret = iblock(idesc, n, remsize);
+ if (ret & STOP)
+ return (ret);
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+iblock(idesc, ilevel, isize)
+ struct inodesc *idesc;
+ long ilevel;
+ quad_t isize;
+{
+ register daddr_t *ap;
+ register daddr_t *aplim;
+ register struct bufarea *bp;
+ int i, n, (*func)(), nif;
+ quad_t sizepb;
+ char buf[BUFSIZ];
+ extern int dirscan(), pass1check();
+
+ if (idesc->id_type == ADDR) {
+ func = idesc->id_func;
+ if (((n = (*func)(idesc)) & KEEPON) == 0)
+ return (n);
+ } else
+ func = dirscan;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+ return (SKIP);
+ bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
+ ilevel--;
+ for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
+ sizepb *= NINDIR(&sblock);
+ nif = howmany(isize , sizepb);
+ if (nif > NINDIR(&sblock))
+ nif = NINDIR(&sblock);
+ if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
+ aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
+ for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
+ if (*ap == 0)
+ continue;
+ (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
+ idesc->id_number);
+ if (dofix(idesc, buf)) {
+ *ap = 0;
+ dirty(bp);
+ }
+ }
+ flush(fswritefd, bp);
+ }
+ aplim = &bp->b_un.b_indir[nif];
+ for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ if (ilevel == 0)
+ n = (*func)(idesc);
+ else
+ n = iblock(idesc, ilevel, isize);
+ if (n & STOP) {
+ bp->b_flags &= ~B_INUSE;
+ return (n);
+ }
+ }
+ isize -= sizepb;
+ }
+ bp->b_flags &= ~B_INUSE;
+ return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+chkrange(blk, cnt)
+ daddr_t blk;
+ int cnt;
+{
+ register int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return (1);
+ c = dtog(&sblock, blk);
+ if (blk < cgdmin(&sblock, c)) {
+ if ((blk + cnt) > cgsblock(&sblock, c)) {
+ if (debug) {
+ printf("blk %ld < cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > cgsbase %ld\n",
+ blk + cnt, cgsblock(&sblock, c));
+ }
+ return (1);
+ }
+ } else {
+ if ((blk + cnt) > cgbase(&sblock, c+1)) {
+ if (debug) {
+ printf("blk %ld >= cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
+ blk+cnt, sblock.fs_fpg);
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+struct dinode *
+ginode(inumber)
+ ino_t inumber;
+{
+ daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errexit("bad inode number %d to ginode\n", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ iblk = ino_to_fsba(&sblock, inumber);
+ if (pbp != 0)
+ pbp->b_flags &= ~B_INUSE;
+ pbp = getdatablk(iblk, sblock.fs_bsize);
+ startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+ }
+ return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+
+struct dinode *
+getnextinode(inumber)
+ ino_t inumber;
+{
+ long size;
+ daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errexit("bad inode number %d to nextinode\n", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
+ dp = inodebuf;
+ }
+ return (dp++);
+}
+
+resetinodebuf()
+{
+
+ startinum = 0;
+ nextino = 0;
+ lastinum = 0;
+ readcnt = 0;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / sizeof(struct dinode);
+ readpercg = sblock.fs_ipg / fullcnt;
+ partialcnt = sblock.fs_ipg % fullcnt;
+ partialsize = partialcnt * sizeof(struct dinode);
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if (inodebuf == NULL &&
+ (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
+ errexit("Cannot allocate space for inode buffer\n");
+ while (nextino < ROOTINO)
+ (void)getnextinode(nextino);
+}
+
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free((char *)inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+cacheino(dp, inumber)
+ register struct dinode *dp;
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+ struct inoinfo **inpp;
+ unsigned int blks;
+
+ blks = howmany(dp->di_size, sblock.fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ inp = (struct inoinfo *)
+ malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
+ if (inp == NULL)
+ return;
+ inpp = &inphead[inumber % numdirs];
+ inp->i_nexthash = *inpp;
+ *inpp = inp;
+ inp->i_parent = (ino_t)0;
+ inp->i_dotdot = (ino_t)0;
+ inp->i_number = inumber;
+ inp->i_isize = dp->di_size;
+ inp->i_numblks = blks * sizeof(daddr_t);
+ bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
+ (size_t)inp->i_numblks);
+ if (inplast == listmax) {
+ listmax += 100;
+ inpsort = (struct inoinfo **)realloc((char *)inpsort,
+ (unsigned)listmax * sizeof(struct inoinfo *));
+ if (inpsort == NULL)
+ errexit("cannot increase directory list");
+ }
+ inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(inumber)
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+
+ for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
+ if (inp->i_number != inumber)
+ continue;
+ return (inp);
+ }
+ errexit("cannot find inode %d\n", inumber);
+ return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+inocleanup()
+{
+ register struct inoinfo **inpp;
+
+ if (inphead == NULL)
+ return;
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+ free((char *)(*inpp));
+ free((char *)inphead);
+ free((char *)inpsort);
+ inphead = inpsort = NULL;
+}
+
+inodirty()
+{
+
+ dirty(pbp);
+}
+
+clri(idesc, type, flag)
+ register struct inodesc *idesc;
+ char *type;
+ int flag;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (flag == 1) {
+ pwarn("%s %s", type,
+ (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
+ pinode(idesc->id_number);
+ }
+ if (preen || reply("CLEAR") == 1) {
+ if (preen)
+ printf(" (CLEARED)\n");
+ n_files--;
+ (void)ckinode(dp, idesc);
+ clearinode(dp);
+ statemap[idesc->id_number] = USTATE;
+ inodirty();
+ }
+}
+
+findname(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino != idesc->id_parent)
+ return (KEEPON);
+ bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
+ return (STOP|FOUND);
+}
+
+findino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino == 0)
+ return (KEEPON);
+ if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
+ dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
+ idesc->id_parent = dirp->d_ino;
+ return (STOP|FOUND);
+ }
+ return (KEEPON);
+}
+
+pinode(ino)
+ ino_t ino;
+{
+ register struct dinode *dp;
+ register char *p;
+ struct passwd *pw;
+ char *ctime();
+
+ printf(" I=%lu ", ino);
+ if (ino < ROOTINO || ino > maxino)
+ return;
+ dp = ginode(ino);
+ printf(" OWNER=");
+ if ((pw = getpwuid((int)dp->di_uid)) != 0)
+ printf("%s ", pw->pw_name);
+ else
+ printf("%u ", (unsigned)dp->di_uid);
+ printf("MODE=%o\n", dp->di_mode);
+ if (preen)
+ printf("%s: ", cdevname);
+ printf("SIZE=%qu ", dp->di_size);
+ p = ctime(&dp->di_mtime.ts_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ daddr_t blk;
+{
+
+ pfatal("%ld %s I=%lu", blk, type, ino);
+ printf("\n");
+ switch (statemap[ino]) {
+
+ case FSTATE:
+ statemap[ino] = FCLEAR;
+ return;
+
+ case DSTATE:
+ statemap[ino] = DCLEAR;
+ return;
+
+ case FCLEAR:
+ case DCLEAR:
+ return;
+
+ default:
+ errexit("BAD STATE %d TO BLKERR", statemap[ino]);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(request, type)
+ ino_t request;
+ int type;
+{
+ register ino_t ino;
+ register struct dinode *dp;
+
+ if (request == 0)
+ request = ROOTINO;
+ else if (statemap[request] != USTATE)
+ return (0);
+ for (ino = request; ino < maxino; ino++)
+ if (statemap[ino] == USTATE)
+ break;
+ if (ino == maxino)
+ return (0);
+ switch (type & IFMT) {
+ case IFDIR:
+ statemap[ino] = DSTATE;
+ break;
+ case IFREG:
+ case IFLNK:
+ statemap[ino] = FSTATE;
+ break;
+ default:
+ return (0);
+ }
+ dp = ginode(ino);
+ dp->di_db[0] = allocblk((long)1);
+ if (dp->di_db[0] == 0) {
+ statemap[ino] = USTATE;
+ return (0);
+ }
+ dp->di_mode = type;
+ (void)time(&dp->di_atime.ts_sec);
+ dp->di_mtime = dp->di_ctime = dp->di_atime;
+ dp->di_size = sblock.fs_fsize;
+ dp->di_blocks = btodb(sblock.fs_fsize);
+ n_files++;
+ inodirty();
+ if (newinofmt)
+ typemap[ino] = IFTODT(type);
+ return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ extern int pass4check();
+ struct dinode *dp;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
new file mode 100644
index 0000000..2e69715
--- /dev/null
+++ b/sbin/fsck_ffs/main.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1980, 1986, 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) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "fsck.h"
+
+void catch(), catchquit(), voidquit();
+int returntosingle;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern int docheck(), checkfilesys();
+ extern char *optarg, *blockcheck();
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) {
+ switch (ch) {
+ case 'p':
+ preen++;
+ break;
+
+ case 'b':
+ bflag = argtoi('b', "number", optarg, 10);
+ printf("Alternate super block location: %d\n", bflag);
+ break;
+
+ case 'c':
+ cvtlevel = argtoi('c', "conversion level", optarg, 10);
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'l':
+ maxrun = argtoi('l', "number", optarg, 10);
+ break;
+
+ case 'm':
+ lfmode = argtoi('m', "mode", optarg, 8);
+ if (lfmode &~ 07777)
+ errexit("bad mode to -m: %o\n", lfmode);
+ printf("** lost+found creation mode %o\n", lfmode);
+ break;
+
+ case 'n':
+ case 'N':
+ nflag++;
+ yflag = 0;
+ break;
+
+ case 'y':
+ case 'Y':
+ yflag++;
+ nflag = 0;
+ break;
+
+ default:
+ errexit("%c option?\n", ch);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, catch);
+ if (preen)
+ (void)signal(SIGQUIT, catchquit);
+ if (argc) {
+ while (argc-- > 0)
+ (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0);
+ exit(0);
+ }
+ ret = checkfstab(preen, maxrun, docheck, checkfilesys);
+ if (returntosingle)
+ exit(2);
+ exit(ret);
+}
+
+argtoi(flag, req, str, base)
+ int flag;
+ char *req, *str;
+ int base;
+{
+ char *cp;
+ int ret;
+
+ ret = (int)strtol(str, &cp, base);
+ if (cp == str || *cp)
+ errexit("-%c flag requires a %s\n", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+docheck(fsp)
+ register struct fstab *fsp;
+{
+
+ if (strcmp(fsp->fs_vfstype, "ufs") ||
+ (strcmp(fsp->fs_type, FSTAB_RW) &&
+ strcmp(fsp->fs_type, FSTAB_RO)) ||
+ fsp->fs_passno == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Check the specified filesystem.
+ */
+/* ARGSUSED */
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+{
+ daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ if (setup(filesys) == 0) {
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ return (0);
+ }
+ /*
+ * 1: scan inodes tallying blocks used
+ */
+ if (preen == 0) {
+ printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
+ if (hotroot)
+ printf("** Root file system\n");
+ printf("** Phase 1 - Check Blocks and Sizes\n");
+ }
+ pass1();
+
+ /*
+ * 1b: locate first references to duplicates, if any
+ */
+ if (duplist) {
+ if (preen)
+ pfatal("INTERNAL ERROR: dups with -p");
+ printf("** Phase 1b - Rescan For More DUPS\n");
+ pass1b();
+ }
+
+ /*
+ * 2: traverse directories from root to mark all connected directories
+ */
+ if (preen == 0)
+ printf("** Phase 2 - Check Pathnames\n");
+ pass2();
+
+ /*
+ * 3: scan inodes looking for disconnected directories
+ */
+ if (preen == 0)
+ printf("** Phase 3 - Check Connectivity\n");
+ pass3();
+
+ /*
+ * 4: scan inodes looking for disconnected files; check reference counts
+ */
+ if (preen == 0)
+ printf("** Phase 4 - Check Reference Counts\n");
+ pass4();
+
+ /*
+ * 5: check and repair resource counts in cylinder groups
+ */
+ if (preen == 0)
+ printf("** Phase 5 - Check Cyl groups\n");
+ pass5();
+
+ /*
+ * print out summary statistics
+ */
+ n_ffree = sblock.fs_cstotal.cs_nffree;
+ n_bfree = sblock.fs_cstotal.cs_nbfree;
+ pwarn("%ld files, %ld used, %ld free ",
+ n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
+ printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n",
+ n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize,
+ ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10);
+ if (debug &&
+ (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
+ printf("%ld files missing\n", n_files);
+ if (debug) {
+ n_blks += sblock.fs_ncg *
+ (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
+ n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
+ n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
+ printf("%ld blocks missing\n", n_blks);
+ if (duplist != NULL) {
+ printf("The following duplicate blocks remain:");
+ for (dp = duplist; dp; dp = dp->next)
+ printf(" %ld,", dp->dup);
+ printf("\n");
+ }
+ if (zlnhead != NULL) {
+ printf("The following zero link count inodes remain:");
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ printf(" %lu,", zlnp->zlncnt);
+ printf("\n");
+ }
+ }
+ zlnhead = (struct zlncnt *)0;
+ duplist = (struct dups *)0;
+ muldup = (struct dups *)0;
+ inocleanup();
+ if (fsmodified) {
+ (void)time(&sblock.fs_time);
+ sbdirty();
+ }
+ if (cvtlevel && sblk.b_dirty) {
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ bwrite(fswritefd, (char *)&sblock,
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE);
+ }
+ ckfini();
+ free(blockmap);
+ free(statemap);
+ free((char *)lncntp);
+ if (!fsmodified)
+ return (0);
+ if (!preen)
+ printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+ if (hotroot) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_UFS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
new file mode 100644
index 0000000..ca255fe
--- /dev/null
+++ b/sbin/fsck_ffs/pass1.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+int pass1check();
+struct dinode *getnextinode();
+
+pass1()
+{
+ ino_t inumber;
+ int c, i, cgd;
+ struct inodesc idesc;
+
+ /*
+ * Set file system reserved blocks in used block map.
+ */
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ cgd = cgdmin(&sblock, c);
+ if (c == 0) {
+ i = cgbase(&sblock, c);
+ cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ } else
+ i = cgsblock(&sblock, c);
+ for (; i < cgd; i++)
+ setbmap(i);
+ }
+ /*
+ * Find all allocated blocks.
+ */
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1check;
+ inumber = 0;
+ n_files = n_blks = 0;
+ resetinodebuf();
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ checkinode(inumber, &idesc);
+ }
+ }
+ freeinodebuf();
+}
+
+checkinode(inumber, idesc)
+ ino_t inumber;
+ register struct inodesc *idesc;
+{
+ register struct dinode *dp;
+ struct zlncnt *zlnp;
+ int ndb, j;
+ mode_t mode;
+ char symbuf[MAXSYMLINKLEN];
+
+ dp = getnextinode(inumber);
+ mode = dp->di_mode & IFMT;
+ if (mode == 0) {
+ if (bcmp((char *)dp->di_db, (char *)zino.di_db,
+ NDADDR * sizeof(daddr_t)) ||
+ bcmp((char *)dp->di_ib, (char *)zino.di_ib,
+ NIADDR * sizeof(daddr_t)) ||
+ dp->di_mode || dp->di_size) {
+ pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber);
+ if (reply("CLEAR") == 1) {
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+ }
+ statemap[inumber] = USTATE;
+ return;
+ }
+ lastino = inumber;
+ if (/* dp->di_size < 0 || */
+ dp->di_size + sblock.fs_bsize - 1 < dp->di_size) {
+ if (debug)
+ printf("bad size %qu:", dp->di_size);
+ goto unknown;
+ }
+ if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
+ dp = ginode(inumber);
+ dp->di_size = sblock.fs_fsize;
+ dp->di_mode = IFREG|0600;
+ inodirty();
+ }
+ ndb = howmany(dp->di_size, sblock.fs_bsize);
+ if (ndb < 0) {
+ if (debug)
+ printf("bad size %qu ndb %d:",
+ dp->di_size, ndb);
+ goto unknown;
+ }
+ if (mode == IFBLK || mode == IFCHR)
+ ndb++;
+ if (mode == IFLNK) {
+ if (doinglevel2 &&
+ dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
+ dp->di_blocks != 0) {
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)dp->di_size) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %d(%s) of size %d\n",
+ inumber, symbuf, (long)dp->di_size);
+ }
+ dp = ginode(inumber);
+ bcopy(symbuf, (caddr_t)dp->di_shortlink,
+ (long)dp->di_size);
+ dp->di_blocks = 0;
+ inodirty();
+ }
+ /*
+ * Fake ndb value so direct/indirect block checks below
+ * will detect any garbage after symlink string.
+ */
+ if (dp->di_size < sblock.fs_maxsymlinklen) {
+ ndb = howmany(dp->di_size, sizeof(daddr_t));
+ if (ndb > NDADDR) {
+ j = ndb - NDADDR;
+ for (ndb = 1; j > 1; j--)
+ ndb *= NINDIR(&sblock);
+ ndb += NDADDR;
+ }
+ }
+ }
+ for (j = ndb; j < NDADDR; j++)
+ if (dp->di_db[j] != 0) {
+ if (debug)
+ printf("bad direct addr: %ld\n", dp->di_db[j]);
+ goto unknown;
+ }
+ for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+ ndb /= NINDIR(&sblock);
+ for (; j < NIADDR; j++)
+ if (dp->di_ib[j] != 0) {
+ if (debug)
+ printf("bad indirect addr: %ld\n",
+ dp->di_ib[j]);
+ goto unknown;
+ }
+ if (ftypeok(dp) == 0)
+ goto unknown;
+ n_files++;
+ lncntp[inumber] = dp->di_nlink;
+ if (dp->di_nlink <= 0) {
+ zlnp = (struct zlncnt *)malloc(sizeof *zlnp);
+ if (zlnp == NULL) {
+ pfatal("LINK COUNT TABLE OVERFLOW");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ } else {
+ zlnp->zlncnt = inumber;
+ zlnp->next = zlnhead;
+ zlnhead = zlnp;
+ }
+ }
+ if (mode == IFDIR) {
+ if (dp->di_size == 0)
+ statemap[inumber] = DCLEAR;
+ else
+ statemap[inumber] = DSTATE;
+ cacheino(dp, inumber);
+ } else
+ statemap[inumber] = FSTATE;
+ typemap[inumber] = IFTODT(mode);
+ if (doinglevel2 &&
+ (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) {
+ dp = ginode(inumber);
+ dp->di_uid = dp->di_ouid;
+ dp->di_ouid = -1;
+ dp->di_gid = dp->di_ogid;
+ dp->di_ogid = -1;
+ inodirty();
+ }
+ badblk = dupblk = 0;
+ idesc->id_number = inumber;
+ (void)ckinode(dp, idesc);
+ idesc->id_entryno *= btodb(sblock.fs_fsize);
+ if (dp->di_blocks != idesc->id_entryno) {
+ pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)",
+ inumber, dp->di_blocks, idesc->id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ dp = ginode(inumber);
+ dp->di_blocks = idesc->id_entryno;
+ inodirty();
+ }
+ return;
+unknown:
+ pfatal("UNKNOWN FILE TYPE I=%lu", inumber);
+ statemap[inumber] = FCLEAR;
+ if (reply("CLEAR") == 1) {
+ statemap[inumber] = USTATE;
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+}
+
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c
new file mode 100644
index 0000000..27b2dfd
--- /dev/null
+++ b/sbin/fsck_ffs/pass1b.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+int pass1bcheck();
+static struct dups *duphead;
+
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1bcheck;
+ duphead = duplist;
+ inumber = 0;
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ dp = ginode(inumber);
+ if (dp == NULL)
+ continue;
+ idesc.id_number = inumber;
+ if (statemap[inumber] != USTATE &&
+ (ckinode(dp, &idesc) & STOP))
+ return;
+ }
+ }
+}
+
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
new file mode 100644
index 0000000..6314751
--- /dev/null
+++ b/sbin/fsck_ffs/pass2.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+pass2()
+{
+ register struct dinode *dp;
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ struct inodesc curino;
+ struct dinode dino;
+ char pathbuf[MAXPATHLEN + 1];
+
+ switch (statemap[ROOTINO]) {
+
+ case USTATE:
+ pfatal("ROOT INODE UNALLOCATED");
+ if (reply("ALLOCATE") == 0)
+ errexit("");
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("FIX") == 0)
+ errexit("");
+ dp = ginode(ROOTINO);
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ bzero((char *)&curino, sizeof(struct inodesc));
+ curino.id_type = DATA;
+ curino.id_func = pass2check;
+ dp = &dino;
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_isize == 0)
+ continue;
+ if (inp->i_isize < MINDIRSIZE) {
+ direrror(inp->i_number, "DIRECTORY TOO SHORT");
+ inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+ if (reply("FIX") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = inp->i_isize;
+ inodirty();
+ dp = &dino;
+ }
+ } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+ getpathname(pathbuf, inp->i_number, inp->i_number);
+ pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+ pathbuf, inp->i_isize, DIRBLKSIZ);
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+ if (preen || reply("ADJUST") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
+ inodirty();
+ dp = &dino;
+ }
+ }
+ bzero((char *)&dino, sizeof(struct dinode));
+ dino.di_mode = IFDIR;
+ dp->di_size = inp->i_isize;
+ bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
+ (size_t)inp->i_numblks);
+ curino.id_number = inp->i_number;
+ curino.id_parent = inp->i_parent;
+ (void)ckinode(dp, &curino);
+ }
+ /*
+ * Now that the parents of all directories have been found,
+ * make another pass to verify the value of `..'
+ */
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 || inp->i_isize == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE)
+ statemap[inp->i_number] = DFOUND;
+ if (inp->i_dotdot == inp->i_parent ||
+ inp->i_dotdot == (ino_t)-1)
+ continue;
+ if (inp->i_dotdot == 0) {
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+ if (reply("FIX") == 0)
+ continue;
+ (void)makeentry(inp->i_number, inp->i_parent, "..");
+ lncntp[inp->i_parent]--;
+ continue;
+ }
+ fileerror(inp->i_parent, inp->i_number,
+ "BAD INODE NUMBER FOR '..'");
+ if (reply("FIX") == 0)
+ continue;
+ lncntp[inp->i_dotdot]++;
+ lncntp[inp->i_parent]--;
+ inp->i_dotdot = inp->i_parent;
+ (void)changeino(inp->i_number, "..", inp->i_parent);
+ }
+ /*
+ * Mark all the directories that can be found from the root.
+ */
+ propagate();
+}
+
+pass2check(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ register struct inoinfo *inp;
+ int n, entrysize, ret = 0;
+ struct dinode *dp;
+ char *errmsg;
+ struct direct proto;
+ char namebuf[MAXPATHLEN + 1];
+ char pathbuf[MAXPATHLEN + 1];
+
+ /*
+ * If converting, set directory entry type.
+ */
+ if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
+ dirp->d_type = typemap[dirp->d_ino];
+ ret |= ALTERED;
+ }
+ /*
+ * check for "."
+ */
+ if (idesc->id_entryno != 0)
+ goto chk1;
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
+ if (dirp->d_ino != idesc->id_number) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+ dirp->d_ino = idesc->id_number;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk1;
+ }
+ direrror(idesc->id_number, "MISSING '.'");
+ proto.d_ino = idesc->id_number;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 1;
+ (void)strcpy(proto.d_name, ".");
+ entrysize = DIRSIZ(0, &proto);
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
+ pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ } else if (dirp->d_reclen < entrysize) {
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+ } else if (dirp->d_reclen < 2 * entrysize) {
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ bzero((char *)dirp, (size_t)n);
+ dirp->d_reclen = n;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+chk1:
+ if (idesc->id_entryno > 1)
+ goto chk2;
+ inp = getinoinfo(idesc->id_number);
+ proto.d_ino = inp->i_parent;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 2;
+ (void)strcpy(proto.d_name, "..");
+ entrysize = DIRSIZ(0, &proto);
+ if (idesc->id_entryno == 0) {
+ n = DIRSIZ(0, dirp);
+ if (dirp->d_reclen < n + entrysize)
+ goto chk2;
+ proto.d_reclen = dirp->d_reclen - n;
+ dirp->d_reclen = n;
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + n);
+ bzero((char *)dirp, (size_t)proto.d_reclen);
+ dirp->d_reclen = proto.d_reclen;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+ inp->i_dotdot = dirp->d_ino;
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk2;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ inp->i_dotdot = (ino_t)-1;
+ } else if (dirp->d_reclen < entrysize) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+ inp->i_dotdot = (ino_t)-1;
+ } else if (inp->i_parent != 0) {
+ /*
+ * We know the parent, so fix now.
+ */
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ idesc->id_entryno++;
+ if (dirp->d_ino != 0)
+ lncntp[dirp->d_ino]--;
+ return (ret|KEEPON);
+chk2:
+ if (dirp->d_ino == 0)
+ return (ret|KEEPON);
+ if (dirp->d_namlen <= 2 &&
+ dirp->d_name[0] == '.' &&
+ idesc->id_entryno >= 2) {
+ if (dirp->d_namlen == 1) {
+ direrror(idesc->id_number, "EXTRA '.' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ if (dirp->d_name[1] == '.') {
+ direrror(idesc->id_number, "EXTRA '..' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ }
+ idesc->id_entryno++;
+ n = 0;
+ if (dirp->d_ino > maxino) {
+ fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
+ n = reply("REMOVE");
+ } else {
+again:
+ switch (statemap[dirp->d_ino]) {
+ case USTATE:
+ if (idesc->id_entryno <= 2)
+ break;
+ fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
+ n = reply("REMOVE");
+ break;
+
+ case DCLEAR:
+ case FCLEAR:
+ if (idesc->id_entryno <= 2)
+ break;
+ if (statemap[dirp->d_ino] == FCLEAR)
+ errmsg = "DUP/BAD";
+ else if (!preen)
+ errmsg = "ZERO LENGTH DIRECTORY";
+ else {
+ n = 1;
+ break;
+ }
+ fileerror(idesc->id_number, dirp->d_ino, errmsg);
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ dp = ginode(dirp->d_ino);
+ statemap[dirp->d_ino] =
+ (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
+ lncntp[dirp->d_ino] = dp->di_nlink;
+ goto again;
+
+ case DSTATE:
+ if (statemap[idesc->id_number] == DFOUND)
+ statemap[dirp->d_ino] = DFOUND;
+ /* fall through */
+
+ case DFOUND:
+ inp = getinoinfo(dirp->d_ino);
+ if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ getpathname(namebuf, dirp->d_ino, dirp->d_ino);
+ pwarn("%s %s %s\n", pathbuf,
+ "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+ namebuf);
+ if (preen)
+ printf(" (IGNORED)\n");
+ else if ((n = reply("REMOVE")) == 1)
+ break;
+ }
+ if (idesc->id_entryno > 2)
+ inp->i_parent = idesc->id_number;
+ /* fall through */
+
+ case FSTATE:
+ if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
+ fileerror(idesc->id_number, dirp->d_ino,
+ "BAD TYPE VALUE");
+ dirp->d_type = typemap[dirp->d_ino];
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ lncntp[dirp->d_ino]--;
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[dirp->d_ino], dirp->d_ino);
+ }
+ }
+ if (n == 0)
+ return (ret|KEEPON);
+ dirp->d_ino = 0;
+ return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+blksort(inpp1, inpp2)
+ struct inoinfo **inpp1, **inpp2;
+{
+
+ return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
new file mode 100644
index 0000000..5c6f09d
--- /dev/null
+++ b/sbin/fsck_ffs/pass3.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include "fsck.h"
+
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
new file mode 100644
index 0000000..7450530
--- /dev/null
+++ b/sbin/fsck_ffs/pass4.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+int pass4check();
+
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ for (inumber = ROOTINO; inumber <= lastino; inumber++) {
+ idesc.id_number = inumber;
+ switch (statemap[inumber]) {
+
+ case FSTATE:
+ case DFOUND:
+ n = lncntp[inumber];
+ if (n)
+ adjust(&idesc, (short)n);
+ else {
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ if (zlnp->zlncnt == inumber) {
+ zlnp->zlncnt = zlnhead->zlncnt;
+ zlnp = zlnhead;
+ zlnhead = zlnhead->next;
+ free((char *)zlnp);
+ clri(&idesc, "UNREF", 1);
+ break;
+ }
+ }
+ break;
+
+ case DSTATE:
+ clri(&idesc, "UNREF", 1);
+ break;
+
+ case DCLEAR:
+ dp = ginode(inumber);
+ if (dp->di_size == 0) {
+ clri(&idesc, "ZERO LENGTH", 1);
+ break;
+ }
+ /* fall through */
+ case FCLEAR:
+ clri(&idesc, "BAD/DUP", 1);
+ break;
+
+ case USTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[inumber], inumber);
+ }
+ }
+}
+
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c
new file mode 100644
index 0000000..2e98f96
--- /dev/null
+++ b/sbin/fsck_ffs/pass5.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ register struct fs *fs = &sblock;
+ register struct cg *cg = &cgrp;
+ daddr_t dbase, dmax;
+ register daddr_t d;
+ register long i, j;
+ struct csum *cs;
+ struct csum cstotal;
+ struct inodesc idesc[3];
+ char buf[MAXBSIZE];
+ register struct cg *newcg = (struct cg *)buf;
+ struct ocg *ocg = (struct ocg *)buf;
+
+ bzero((char *)newcg, (size_t)fs->fs_cgsize);
+ newcg->cg_niblk = fs->fs_ipg;
+ if (cvtlevel > 3) {
+ if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) {
+ if (preen)
+ pwarn("DELETING CLUSTERING MAPS\n");
+ if (preen || reply("DELETE CLUSTERING MAPS")) {
+ fs->fs_contigsumsize = 0;
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ if (fs->fs_maxcontig > 1) {
+ char *doit = 0;
+
+ if (fs->fs_contigsumsize < 1) {
+ doit = "CREAT";
+ } else if (fs->fs_contigsumsize < fs->fs_maxcontig &&
+ fs->fs_contigsumsize < FS_MAXCONTIG) {
+ doit = "EXPAND";
+ }
+ if (doit) {
+ i = fs->fs_contigsumsize;
+ fs->fs_contigsumsize =
+ MIN(fs->fs_maxcontig, FS_MAXCONTIG);
+ if (CGSIZE(fs) > fs->fs_bsize) {
+ pwarn("CANNOT %s CLUSTER MAPS\n", doit);
+ fs->fs_contigsumsize = i;
+ } else if (preen ||
+ reply("CREATE CLUSTER MAPS")) {
+ if (preen)
+ pwarn("%sING CLUSTER MAPS\n",
+ doit);
+ fs->fs_cgsize =
+ fragroundup(fs, CGSIZE(fs));
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ }
+ }
+ switch ((int)fs->fs_postblformat) {
+
+ case FS_42POSTBLFMT:
+ basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link);
+ sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]);
+ mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] -
+ (u_char *)&ocg->cg_iused[0];
+ ocg->cg_magic = CG_MAGIC;
+ savednrpos = fs->fs_nrpos;
+ fs->fs_nrpos = 8;
+ break;
+
+ case FS_DYNAMICPOSTBLFMT:
+ newcg->cg_btotoff =
+ &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ newcg->cg_boff =
+ newcg->cg_btotoff + fs->fs_cpg * sizeof(long);
+ newcg->cg_iusedoff = newcg->cg_boff +
+ fs->fs_cpg * fs->fs_nrpos * sizeof(short);
+ newcg->cg_freeoff =
+ newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY);
+ if (fs->fs_contigsumsize <= 0) {
+ newcg->cg_nextfreeoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY);
+ } else {
+ newcg->cg_clustersumoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) -
+ sizeof(long);
+ newcg->cg_clustersumoff =
+ roundup(newcg->cg_clustersumoff, sizeof(long));
+ newcg->cg_clusteroff = newcg->cg_clustersumoff +
+ (fs->fs_contigsumsize + 1) * sizeof(long);
+ newcg->cg_nextfreeoff = newcg->cg_clusteroff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY);
+ }
+ newcg->cg_magic = CG_MAGIC;
+ basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n",
+ fs->fs_postblformat);
+ }
+ bzero((char *)&idesc[0], sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ bzero((char *)&cstotal, sizeof(struct csum));
+ j = blknum(fs, fs->fs_size + fs->fs_frag - 1);
+ for (i = fs->fs_size; i < j; i++)
+ setbmap(i);
+ for (c = 0; c < fs->fs_ncg; c++) {
+ getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
+ if (!cg_chkmagic(cg))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+ dbase = cgbase(fs, c);
+ dmax = dbase + fs->fs_fpg;
+ if (dmax > fs->fs_size)
+ dmax = fs->fs_size;
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_cgx = c;
+ if (c == fs->fs_ncg - 1)
+ newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg;
+ else
+ newcg->cg_ncyl = fs->fs_cpg;
+ newcg->cg_ndblk = dmax - dbase;
+ if (fs->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = fs->fs_ipg;
+ if (cg->cg_rotor < newcg->cg_ndblk)
+ newcg->cg_rotor = cg->cg_rotor;
+ else
+ newcg->cg_rotor = 0;
+ if (cg->cg_frotor < newcg->cg_ndblk)
+ newcg->cg_frotor = cg->cg_frotor;
+ else
+ newcg->cg_frotor = 0;
+ if (cg->cg_irotor < newcg->cg_niblk)
+ newcg->cg_irotor = cg->cg_irotor;
+ else
+ newcg->cg_irotor = 0;
+ bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+ bzero((char *)&cg_blktot(newcg)[0],
+ (size_t)(sumsize + mapsize));
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ ocg->cg_magic = CG_MAGIC;
+ j = fs->fs_ipg * c;
+ for (i = 0; i < fs->fs_ipg; j++, i++) {
+ switch (statemap[j]) {
+
+ case USTATE:
+ break;
+
+ case DSTATE:
+ case DCLEAR:
+ case DFOUND:
+ newcg->cg_cs.cs_ndir++;
+ /* fall through */
+
+ case FSTATE:
+ case FCLEAR:
+ newcg->cg_cs.cs_nifree--;
+ setbit(cg_inosused(newcg), i);
+ break;
+
+ default:
+ if (j < ROOTINO)
+ break;
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[j], j);
+ }
+ }
+ if (c == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += fs->fs_frag, i += fs->fs_frag) {
+ frags = 0;
+ for (j = 0; j < fs->fs_frag; j++) {
+ if (testbmap(d + j))
+ continue;
+ setbit(cg_blksfree(newcg), i + j);
+ frags++;
+ }
+ if (frags == fs->fs_frag) {
+ newcg->cg_cs.cs_nbfree++;
+ j = cbtocylno(fs, i);
+ cg_blktot(newcg)[j]++;
+ cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++;
+ if (fs->fs_contigsumsize > 0)
+ setbit(cg_clustersfree(newcg),
+ i / fs->fs_frag);
+ } else if (frags > 0) {
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap(fs, cg_blksfree(newcg), i);
+ ffs_fragacct(fs, blk, newcg->cg_frsum, 1);
+ }
+ }
+ if (fs->fs_contigsumsize > 0) {
+ long *sump = cg_clustersum(newcg);
+ u_char *mapp = cg_clustersfree(newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+ cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+ cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+ cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+ cs = &fs->fs_cs(fs, c);
+ if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (bcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ bcopy(cg_inosused(newcg), cg_inosused(cg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 ||
+ bcmp((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ bcopy((char *)newcg, (char *)cg, (size_t)basesize);
+ bcopy((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs);
+ fs->fs_ronly = 0;
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+}
diff --git a/sbin/fsck_ffs/preen.c b/sbin/fsck_ffs/preen.c
new file mode 100644
index 0000000..005a65d
--- /dev/null
+++ b/sbin/fsck_ffs/preen.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1990, 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[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char *rawname(), *unrawname(), *blockcheck();
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted filesystem name */
+ long auxdata; /* auxillary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+char hotroot;
+
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen, maxrun;
+ int (*docheck)(), (*chkit)();
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+ if (name = blockcheck(fsp->fs_spec)) {
+ if (sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0))
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while (ret = startdisk(dk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while (ret = startdisk(nextdisk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (p = name + strlen(name) - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+ if (p < name)
+ len = strlen(name);
+
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+addpart(name, fsname, auxdata)
+ char *name, *fsname;
+ long auxdata;
+{
+ struct disk *dk = finddisk(name);
+ register struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)();
+{
+ register struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
+char *
+blockcheck(name)
+ char *name;
+{
+ struct stat stslash, stblock, stchar;
+ char *raw;
+ int retried = 0;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (0);
+ }
+retry:
+ if (stat(name, &stblock) < 0) {
+ perror(name);
+ printf("Can't stat %s\n", name);
+ return (0);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(name);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (name);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (name);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ name = unrawname(name);
+ retried++;
+ goto retry;
+ }
+ printf("Can't make sense out of name %s\n", name);
+ return (0);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
new file mode 100644
index 0000000..a32b23c
--- /dev/null
+++ b/sbin/fsck_ffs/setup.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+struct disklabel *getdisklabel();
+
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ if (stat(dev, &statb) < 0) {
+ printf("Can't stat %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if ((statb.st_mode & S_IFMT) != S_IFCHR) {
+ pfatal("%s is not a character device", dev);
+ if (reply("CONTINUE") == 0)
+ return (0);
+ }
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ printf("Can't open %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (preen == 0)
+ printf("** %s", dev);
+ if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ fsmodified = 0;
+ lfdir = 0;
+ initbarea(&sblk);
+ initbarea(&asblk);
+ sblk.b_un.b_buf = malloc(SBSIZE);
+ asblk.b_un.b_buf = malloc(SBSIZE);
+ if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+ errexit("cannot allocate space for superblock\n");
+ if (lp = getdisklabel((char *)NULL, fsreadfd))
+ dev_bsize = secsize = lp->d_secsize;
+ else
+ dev_bsize = secsize = DEV_BSIZE;
+ /*
+ * Read in the superblock, looking for alternates if necessary
+ */
+ if (readsb(1) == 0) {
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+ return(0);
+ if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+ return (0);
+ for (cg = 0; cg < proto.fs_ncg; cg++) {
+ bflag = fsbtodb(&proto, cgsblock(&proto, cg));
+ if (readsb(0) != 0)
+ break;
+ }
+ if (cg >= proto.fs_ncg) {
+ printf("%s %s\n%s %s\n%s %s\n",
+ "SEARCH FOR ALTERNATE SUPER-BLOCK",
+ "FAILED. YOU MUST USE THE",
+ "-b OPTION TO FSCK TO SPECIFY THE",
+ "LOCATION OF AN ALTERNATE",
+ "SUPER-BLOCK TO SUPPLY NEEDED",
+ "INFORMATION; SEE fsck(8).");
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ }
+ maxfsblock = sblock.fs_size;
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ /*
+ * Check and potentially fix certain fields in the super block.
+ */
+ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
+ pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_optim = FS_OPTTIME;
+ sbdirty();
+ }
+ }
+ if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
+ pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
+ sblock.fs_minfree);
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_minfree = 10;
+ sbdirty();
+ }
+ }
+ if (sblock.fs_interleave < 1 ||
+ sblock.fs_interleave > sblock.fs_nsect) {
+ pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK",
+ sblock.fs_interleave);
+ sblock.fs_interleave = 1;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_npsect < sblock.fs_nsect ||
+ sblock.fs_npsect > sblock.fs_nsect*2) {
+ pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
+ sblock.fs_npsect);
+ sblock.fs_npsect = sblock.fs_nsect;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_inodefmt >= FS_44INODEFMT) {
+ newinofmt = 1;
+ } else {
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ newinofmt = 0;
+ }
+ /*
+ * Convert to new inode format.
+ */
+ if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW INODE FORMAT\n");
+ else if (!reply("CONVERT TO NEW INODE FORMAT"))
+ return(0);
+ doinglevel2++;
+ sblock.fs_inodefmt = FS_44INODEFMT;
+ sizepb = sblock.fs_bsize;
+ sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+ for (i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+ sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ sbdirty();
+ dirty(&asblk);
+ }
+ /*
+ * Convert to new cylinder group format.
+ */
+ if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n");
+ else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT"))
+ return(0);
+ doinglevel1++;
+ sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+ sblock.fs_nrpos = 8;
+ sblock.fs_postbloff =
+ (char *)(&sblock.fs_opostbl[0][0]) -
+ (char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty) {
+ bcopy((char *)&sblock, (char *)&altsblock,
+ (size_t)sblock.fs_sbsize);
+ flush(fswritefd, &asblk);
+ }
+ /*
+ * read in the summary info.
+ */
+ asked = 0;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ size = sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize;
+ sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size);
+ if (bread(fsreadfd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ size) != 0 && !asked) {
+ pfatal("BAD SUMMARY INFORMATION");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ asked++;
+ }
+ }
+ /*
+ * allocate and initialize the necessary maps
+ */
+ bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short));
+ blockmap = calloc((unsigned)bmapsize, sizeof (char));
+ if (blockmap == NULL) {
+ printf("cannot alloc %u bytes for blockmap\n",
+ (unsigned)bmapsize);
+ goto badsb;
+ }
+ statemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (statemap == NULL) {
+ printf("cannot alloc %u bytes for statemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ typemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (typemap == NULL) {
+ printf("cannot alloc %u bytes for typemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short));
+ if (lncntp == NULL) {
+ printf("cannot alloc %u bytes for lncntp\n",
+ (unsigned)(maxino + 1) * sizeof(short));
+ goto badsb;
+ }
+ numdirs = sblock.fs_cstotal.cs_ndir;
+ inplast = 0;
+ listmax = numdirs + 10;
+ inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+ sizeof(struct inoinfo *));
+ inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+ sizeof(struct inoinfo *));
+ if (inpsort == NULL || inphead == NULL) {
+ printf("cannot alloc %u bytes for inphead\n",
+ (unsigned)numdirs * sizeof(struct inoinfo *));
+ goto badsb;
+ }
+ bufinit();
+ return (1);
+
+badsb:
+ ckfini();
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+readsb(listerr)
+ int listerr;
+{
+ daddr_t super = bflag ? bflag : SBOFF / dev_bsize;
+
+ if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0)
+ return (0);
+ sblk.b_bno = super;
+ sblk.b_size = SBSIZE;
+ /*
+ * run a few consistency checks of the super block
+ */
+ if (sblock.fs_magic != FS_MAGIC)
+ { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); }
+ if (sblock.fs_ncg < 1)
+ { badsb(listerr, "NCG OUT OF RANGE"); return (0); }
+ if (sblock.fs_cpg < 1)
+ { badsb(listerr, "CPG OUT OF RANGE"); return (0); }
+ if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
+ (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
+ { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); }
+ if (sblock.fs_sbsize > SBSIZE)
+ { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); }
+ /*
+ * Compute block size that the filesystem is based on,
+ * according to fsbtodb, and adjust superblock block number
+ * so we can tell if this is an alternate later.
+ */
+ super *= dev_bsize;
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ sblk.b_bno = super / dev_bsize;
+ if (bflag) {
+ havesb = 1;
+ return (1);
+ }
+ /*
+ * Set all possible fields that could differ, then do check
+ * of whole super block against an alternate super block.
+ * When an alternate super-block is specified this check is skipped.
+ */
+ getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
+ if (asblk.b_errs)
+ return (0);
+ altsblock.fs_link = sblock.fs_link;
+ altsblock.fs_rlink = sblock.fs_rlink;
+ altsblock.fs_time = sblock.fs_time;
+ altsblock.fs_cstotal = sblock.fs_cstotal;
+ altsblock.fs_cgrotor = sblock.fs_cgrotor;
+ altsblock.fs_fmod = sblock.fs_fmod;
+ altsblock.fs_clean = sblock.fs_clean;
+ altsblock.fs_ronly = sblock.fs_ronly;
+ altsblock.fs_flags = sblock.fs_flags;
+ altsblock.fs_maxcontig = sblock.fs_maxcontig;
+ altsblock.fs_minfree = sblock.fs_minfree;
+ altsblock.fs_optim = sblock.fs_optim;
+ altsblock.fs_rotdelay = sblock.fs_rotdelay;
+ altsblock.fs_maxbpg = sblock.fs_maxbpg;
+ bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp,
+ sizeof sblock.fs_csp);
+ bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt,
+ sizeof sblock.fs_fsmnt);
+ bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon,
+ sizeof sblock.fs_sparecon);
+ /*
+ * The following should not have to be copied.
+ */
+ altsblock.fs_fsbtodb = sblock.fs_fsbtodb;
+ altsblock.fs_interleave = sblock.fs_interleave;
+ altsblock.fs_npsect = sblock.fs_npsect;
+ altsblock.fs_nrpos = sblock.fs_nrpos;
+ altsblock.fs_qbmask = sblock.fs_qbmask;
+ altsblock.fs_qfmask = sblock.fs_qfmask;
+ altsblock.fs_state = sblock.fs_state;
+ altsblock.fs_maxfilesize = sblock.fs_maxfilesize;
+ if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) {
+ badsb(listerr,
+ "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+ return (0);
+ }
+ havesb = 1;
+ return (1);
+}
+
+badsb(listerr, s)
+ int listerr;
+ char *s;
+{
+
+ if (!listerr)
+ return;
+ if (preen)
+ printf("%s: ", cdevname);
+ pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = index(dev, '\0') - 1;
+ if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ bzero((char *)fs, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_cpg = pp->p_cpg;
+ fs->fs_size = pp->p_size;
+ fs->fs_ntrak = lp->d_ntracks;
+ fs->fs_nsect = lp->d_nsectors;
+ fs->fs_spc = lp->d_secpercyl;
+ fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ fs->fs_cgmask = 0xffffffff;
+ for (i = fs->fs_ntrak; i > 1; i >>= 1)
+ fs->fs_cgmask <<= 1;
+ if (!POWEROF2(fs->fs_ntrak))
+ fs->fs_cgmask <<= 1;
+ fs->fs_cgoffset = roundup(
+ howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+ fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+ fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+ for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c
new file mode 100644
index 0000000..64a4cac
--- /dev/null
+++ b/sbin/fsck_ffs/utilities.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1980, 1986, 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.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+ftypeok(dp)
+ struct dinode *dp;
+{
+ switch (dp->di_mode & IFMT) {
+
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ return (1);
+
+ default:
+ if (debug)
+ printf("bad file type 0%o\n", dp->di_mode);
+ return (0);
+ }
+}
+
+reply(question)
+ char *question;
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ pfatal("INTERNAL ERROR: GOT TO reply()");
+ persevere = !strcmp(question, "CONTINUE");
+ printf("\n");
+ if (!persevere && (nflag || fswritefd < 0)) {
+ printf("%s? no\n\n", question);
+ return (0);
+ }
+ if (yflag || (persevere && nflag)) {
+ printf("%s? yes\n\n", question);
+ return (1);
+ }
+ do {
+ printf("%s? [yn] ", question);
+ (void) fflush(stdout);
+ c = getc(stdin);
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ printf("\n");
+ if (c == 'y' || c == 'Y')
+ return (1);
+ return (0);
+}
+
+/*
+ * Malloc buffers and set up cache.
+ */
+bufinit()
+{
+ register struct bufarea *bp;
+ long bufcnt, i;
+ char *bufp;
+
+ pbp = pdirbp = (struct bufarea *)0;
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bufp == 0)
+ errexit("cannot allocate buffer pool\n");
+ cgblk.b_un.b_buf = bufp;
+ initbarea(&cgblk);
+ bufhead.b_next = bufhead.b_prev = &bufhead;
+ bufcnt = MAXBUFSPACE / sblock.fs_bsize;
+ if (bufcnt < MINBUFS)
+ bufcnt = MINBUFS;
+ for (i = 0; i < bufcnt; i++) {
+ bp = (struct bufarea *)malloc(sizeof(struct bufarea));
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ if (i >= MINBUFS)
+ break;
+ errexit("cannot allocate buffer pool\n");
+ }
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ initbarea(bp);
+ }
+ bufhead.b_size = i; /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+ register struct bufarea *bp;
+
+ for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+ if (bp->b_bno == fsbtodb(&sblock, blkno))
+ goto foundit;
+ for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+ if ((bp->b_flags & B_INUSE) == 0)
+ break;
+ if (bp == &bufhead)
+ errexit("deadlocked buffer pool\n");
+ getblk(bp, blkno, size);
+ /* fall through */
+foundit:
+ totalreads++;
+ bp->b_prev->b_next = bp->b_next;
+ bp->b_next->b_prev = bp->b_prev;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ bp->b_flags |= B_INUSE;
+ return (bp);
+}
+
+void
+getblk(bp, blk, size)
+ register struct bufarea *bp;
+ daddr_t blk;
+ long size;
+{
+ daddr_t dblk;
+
+ dblk = fsbtodb(&sblock, blk);
+ if (bp->b_bno != dblk) {
+ flush(fswritefd, bp);
+ diskreads++;
+ bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ bp->b_bno = dblk;
+ bp->b_size = size;
+ }
+}
+
+flush(fd, bp)
+ int fd;
+ register struct bufarea *bp;
+{
+ register int i, j;
+
+ if (!bp->b_dirty)
+ return;
+ if (bp->b_errs != 0)
+ pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+ (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+ bp->b_bno);
+ bp->b_dirty = 0;
+ bp->b_errs = 0;
+ bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ if (bp != &sblk)
+ return;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ bwrite(fswritefd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize);
+ }
+}
+
+rwerror(mesg, blk)
+ char *mesg;
+ daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ errexit("Program terminated\n");
+}
+
+ckfini()
+{
+ register struct bufarea *bp, *nbp;
+ int cnt = 0;
+
+ if (fswritefd < 0) {
+ (void)close(fsreadfd);
+ return;
+ }
+ flush(fswritefd, &sblk);
+ if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+ !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+ sblk.b_bno = SBOFF / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ flush(fswritefd, &cgblk);
+ free(cgblk.b_un.b_buf);
+ for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+ cnt++;
+ flush(fswritefd, bp);
+ nbp = bp->b_prev;
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ if (bufhead.b_size != cnt)
+ errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (debug)
+ printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+ totalreads, (int)(diskreads * 100 / totalreads));
+ (void)close(fsreadfd);
+ (void)close(fswritefd);
+}
+
+bread(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ char *cp;
+ int i, errs;
+ off_t offset;
+
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (read(fd, buf, (int)size) == size)
+ return (0);
+ rwerror("READ", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ errs = 0;
+ bzero(buf, (size_t)size);
+ printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+ for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+ if (read(fd, cp, (int)secsize) != secsize) {
+ (void)lseek(fd, offset + i + secsize, 0);
+ if (secsize != dev_bsize && dev_bsize != 1)
+ printf(" %ld (%ld),",
+ (blk * dev_bsize + i) / secsize,
+ blk + i / dev_bsize);
+ else
+ printf(" %ld,", blk + i / dev_bsize);
+ errs++;
+ }
+ }
+ printf("\n");
+ return (errs);
+}
+
+bwrite(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ int i;
+ char *cp;
+ off_t offset;
+
+ if (fd < 0)
+ return;
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (write(fd, buf, (int)size) == size) {
+ fsmodified = 1;
+ return;
+ }
+ rwerror("WRITE", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+ for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+ if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ (void)lseek(fd, offset + i + dev_bsize, 0);
+ printf(" %ld,", blk + i / dev_bsize);
+ }
+ printf("\n");
+ return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+allocblk(frags)
+ long frags;
+{
+ register int i, j, k;
+
+ if (frags <= 0 || frags > sblock.fs_frag)
+ return (0);
+ for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (testbmap(i + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (testbmap(i + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ for (k = 0; k < frags; k++)
+ setbmap(i + j + k);
+ n_blks += frags;
+ return (i + j);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Free a previously allocated block
+ */
+freeblk(blkno, frags)
+ daddr_t blkno;
+ long frags;
+{
+ struct inodesc idesc;
+
+ idesc.id_blkno = blkno;
+ idesc.id_numfrags = frags;
+ (void)pass4check(&idesc);
+}
+
+/*
+ * Find a pathname
+ */
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+ extern int findname();
+
+ if (curdir == ino && ino == ROOTINO) {
+ (void)strcpy(namebuf, "/");
+ return;
+ }
+ if (busy ||
+ (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
+ (void)strcpy(namebuf, "?");
+ return;
+ }
+ busy = 1;
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ cp = &namebuf[MAXPATHLEN - 1];
+ *cp = '\0';
+ if (curdir != ino) {
+ idesc.id_parent = curdir;
+ goto namelookup;
+ }
+ while (ino != ROOTINO) {
+ idesc.id_number = ino;
+ idesc.id_func = findino;
+ idesc.id_name = "..";
+ if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ break;
+ namelookup:
+ idesc.id_number = idesc.id_parent;
+ idesc.id_parent = ino;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ break;
+ len = strlen(namebuf);
+ cp -= len;
+ bcopy(namebuf, cp, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch()
+{
+ if (!doinglevel2)
+ ckfini();
+ exit(12);
+}
+
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit()
+{
+ extern returntosingle;
+
+ printf("returning to single-user after filesystem check\n");
+ returntosingle = 1;
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+void
+voidquit()
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+dofix(idesc, msg)
+ register struct inodesc *idesc;
+ char *msg;
+{
+
+ switch (idesc->id_fix) {
+
+ case DONTKNOW:
+ if (idesc->id_type == DATA)
+ direrror(idesc->id_number, msg);
+ else
+ pwarn(msg);
+ if (preen) {
+ printf(" (SALVAGED)\n");
+ idesc->id_fix = FIX;
+ return (ALTERED);
+ }
+ if (reply("SALVAGE") == 0) {
+ idesc->id_fix = NOFIX;
+ return (0);
+ }
+ idesc->id_fix = FIX;
+ return (ALTERED);
+
+ case FIX:
+ return (ALTERED);
+
+ case NOFIX:
+ case IGNORE:
+ return (0);
+
+ default:
+ errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+errexit(s1, s2, s3, s4)
+ char *s1;
+{
+ printf(s1, s2, s3, s4);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+pfatal(s, a1, a2, a3)
+ char *s;
+{
+
+ if (preen) {
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ printf(s, a1, a2, a3);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+pwarn(s, a1, a2, a3, a4, a5, a6)
+ char *s;
+{
+
+ if (preen)
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3, a4, a5, a6);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+panic(s)
+ char *s;
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(s);
+}
+#endif
diff --git a/sbin/fsck_ifs/Makefile b/sbin/fsck_ifs/Makefile
new file mode 100644
index 0000000..224f6b2
--- /dev/null
+++ b/sbin/fsck_ifs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck
+MAN8= fsck.0
+SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ifs/dir.c b/sbin/fsck_ifs/dir.c
new file mode 100644
index 0000000..ee5d095
--- /dev/null
+++ b/sbin/fsck_ifs/dir.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+char *lfname = "lost+found";
+int lfmode = 01777;
+struct dirtemplate emptydir = { 0, DIRBLKSIZ };
+struct dirtemplate dirhead = {
+ 0, 12, DT_DIR, 1, ".",
+ 0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
+};
+struct odirtemplate odirhead = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+struct direct *fsck_readdir();
+struct bufarea *getdirblk();
+
+/*
+ * Propagate connected state through the tree.
+ */
+propagate()
+{
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ long change;
+
+ inpend = &inpsort[inplast];
+ do {
+ change = 0;
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE) {
+ statemap[inp->i_number] = DFOUND;
+ change++;
+ }
+ }
+ } while (change > 0);
+}
+
+/*
+ * Scan each entry in a directory block.
+ */
+dirscan(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp;
+ register struct bufarea *bp;
+ int dsize, n;
+ long blksiz;
+ char dbuf[DIRBLKSIZ];
+
+ if (idesc->id_type != DATA)
+ errexit("wrong type to dirscan %d\n", idesc->id_type);
+ if (idesc->id_entryno == 0 &&
+ (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
+ idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
+ idesc->id_filesize -= blksiz;
+ return (SKIP);
+ }
+ idesc->id_loc = 0;
+ for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
+ dsize = dp->d_reclen;
+ bcopy((char *)dp, dbuf, (size_t)dsize);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ struct direct *tdp = (struct direct *)dbuf;
+ u_char tmp;
+
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ idesc->id_dirp = (struct direct *)dbuf;
+ if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt && !doinglevel2) {
+ struct direct *tdp;
+ u_char tmp;
+
+ tdp = (struct direct *)dbuf;
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+fsck_readdir(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp, *ndp;
+ register struct bufarea *bp;
+ long size, blksiz, fix, dploc;
+
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
+ idesc->id_loc < blksiz) {
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (dircheck(idesc, dp))
+ goto dpok;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ dp->d_reclen = DIRBLKSIZ;
+ dp->d_ino = 0;
+ dp->d_type = 0;
+ dp->d_namlen = 0;
+ dp->d_name[0] = '\0';
+ if (fix)
+ dirty(bp);
+ idesc->id_loc += DIRBLKSIZ;
+ idesc->id_filesize -= DIRBLKSIZ;
+ return (dp);
+ }
+dpok:
+ if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
+ return NULL;
+ dploc = idesc->id_loc;
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ idesc->id_loc += dp->d_reclen;
+ idesc->id_filesize -= dp->d_reclen;
+ if ((idesc->id_loc % DIRBLKSIZ) == 0)
+ return (dp);
+ ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
+ dircheck(idesc, ndp) == 0) {
+ size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ idesc->id_loc += size;
+ idesc->id_filesize -= size;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ dp->d_reclen += size;
+ if (fix)
+ dirty(bp);
+ }
+ return (dp);
+}
+
+/*
+ * Verify that a directory entry is valid.
+ * This is a superset of the checks made in the kernel.
+ */
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+
+ size = DIRSIZ(!newinofmt, dp);
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ type = dp->d_namlen;
+ namlen = dp->d_type;
+ } else {
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+ }
+# else
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+# endif
+ if (dp->d_ino < maxino &&
+ dp->d_reclen != 0 &&
+ dp->d_reclen <= spaceleft &&
+ (dp->d_reclen & 0x3) == 0 &&
+ dp->d_reclen >= size &&
+ idesc->id_filesize >= size &&
+ namlen <= MAXNAMLEN &&
+ type <= 15) {
+ if (dp->d_ino == 0)
+ return (1);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == 0 || (*cp++ == '/'))
+ return (0);
+ if (*cp == 0)
+ return (1);
+ }
+ return (0);
+}
+
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+fileerror(cwd, ino, errmesg)
+ ino_t cwd, ino;
+ char *errmesg;
+{
+ register struct dinode *dp;
+ char pathbuf[MAXPATHLEN + 1];
+
+ pwarn("%s ", errmesg);
+ pinode(ino);
+ printf("\n");
+ getpathname(pathbuf, cwd, ino);
+ if (ino < ROOTINO || ino > maxino) {
+ pfatal("NAME=%s\n", pathbuf);
+ return;
+ }
+ dp = ginode(ino);
+ if (ftypeok(dp))
+ pfatal("%s=%s\n",
+ (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
+ else
+ pfatal("NAME=%s\n", pathbuf);
+}
+
+adjust(idesc, lcnt)
+ register struct inodesc *idesc;
+ short lcnt;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (dp->di_nlink == lcnt) {
+ if (linkup(idesc->id_number, (ino_t)0) == 0)
+ clri(idesc, "UNREF", 0);
+ } else {
+ pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
+ ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"));
+ pinode(idesc->id_number);
+ printf(" COUNT %d SHOULD BE %d",
+ dp->di_nlink, dp->di_nlink - lcnt);
+ if (preen) {
+ if (lcnt < 0) {
+ printf("\n");
+ pfatal("LINK COUNT INCREASING");
+ }
+ printf(" (ADJUSTED)\n");
+ }
+ if (preen || reply("ADJUST") == 1) {
+ dp->di_nlink -= lcnt;
+ inodirty();
+ }
+ }
+}
+
+mkentry(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ struct direct newent;
+ int newlen, oldlen;
+
+ newent.d_namlen = strlen(idesc->id_name);
+ newlen = DIRSIZ(0, &newent);
+ if (dirp->d_ino != 0)
+ oldlen = DIRSIZ(0, dirp);
+ else
+ oldlen = 0;
+ if (dirp->d_reclen - oldlen < newlen)
+ return (KEEPON);
+ newent.d_reclen = dirp->d_reclen - oldlen;
+ dirp->d_reclen = oldlen;
+ dirp = (struct direct *)(((char *)dirp) + oldlen);
+ dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ dirp->d_reclen = newent.d_reclen;
+ dirp->d_namlen = newent.d_namlen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
+ return (KEEPON);
+ dirp->d_ino = idesc->id_parent;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ return (ALTERED|STOP);
+}
+
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+ extern int pass4check();
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ dp = ginode(orphan);
+ lostdir = (dp->di_mode & IFMT) == IFDIR;
+ pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+ pinode(orphan);
+ if (preen && dp->di_size == 0)
+ return (0);
+ if (preen)
+ printf(" (RECONNECTED)\n");
+ else
+ if (reply("RECONNECT") == 0)
+ return (0);
+ if (lfdir == 0) {
+ dp = ginode(ROOTINO);
+ idesc.id_name = lfname;
+ idesc.id_type = DATA;
+ idesc.id_func = findino;
+ idesc.id_number = ROOTINO;
+ if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ lfdir = idesc.id_parent;
+ } else {
+ pwarn("NO lost+found DIRECTORY");
+ if (preen || reply("CREATE")) {
+ lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
+ if (lfdir != 0) {
+ if (makeentry(ROOTINO, lfdir, lfname) != 0) {
+ if (preen)
+ printf(" (CREATED)\n");
+ } else {
+ freedir(lfdir, ROOTINO);
+ lfdir = 0;
+ if (preen)
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (lfdir == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ }
+ dp = ginode(lfdir);
+ if ((dp->di_mode & IFMT) != IFDIR) {
+ pfatal("lost+found IS NOT A DIRECTORY");
+ if (reply("REALLOCATE") == 0)
+ return (0);
+ oldlfdir = lfdir;
+ if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ inodirty();
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = oldlfdir;
+ adjust(&idesc, lncntp[oldlfdir] + 1);
+ lncntp[oldlfdir] = 0;
+ dp = ginode(lfdir);
+ }
+ if (statemap[lfdir] != DFOUND) {
+ pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ (void)lftempname(tempname, orphan);
+ if (makeentry(lfdir, orphan, tempname) == 0) {
+ pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ lncntp[orphan]--;
+ if (lostdir) {
+ if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ parentdir != (ino_t)-1)
+ (void)makeentry(orphan, lfdir, "..");
+ dp = ginode(lfdir);
+ dp->di_nlink++;
+ inodirty();
+ lncntp[lfdir]++;
+ pwarn("DIR I=%lu CONNECTED. ", orphan);
+ if (parentdir != (ino_t)-1)
+ printf("PARENT WAS I=%lu\n", parentdir);
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+changeino(dir, name, newnum)
+ ino_t dir;
+ char *name;
+ ino_t newnum;
+{
+ struct inodesc idesc;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = chgino;
+ idesc.id_number = dir;
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ idesc.id_parent = newnum; /* new value for name */
+ return (ckinode(ginode(dir), &idesc));
+}
+
+/*
+ * make an entry in a directory
+ */
+makeentry(parent, ino, name)
+ ino_t parent, ino;
+ char *name;
+{
+ struct dinode *dp;
+ struct inodesc idesc;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (parent < ROOTINO || parent >= maxino ||
+ ino < ROOTINO || ino >= maxino)
+ return (0);
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = mkentry;
+ idesc.id_number = parent;
+ idesc.id_parent = ino; /* this is the inode to enter */
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ dp = ginode(parent);
+ if (dp->di_size % DIRBLKSIZ) {
+ dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
+ inodirty();
+ }
+ if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ return (1);
+ getpathname(pathbuf, parent, parent);
+ dp = ginode(parent);
+ if (expanddir(dp, pathbuf) == 0)
+ return (0);
+ return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ daddr_t lastbn, newblk;
+ register struct bufarea *bp;
+ char *cp, firstblk[DIRBLKSIZ];
+
+ lastbn = lblkno(&sblock, dp->di_size);
+ if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
+ return (0);
+ if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ return (0);
+ dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+ dp->di_db[lastbn] = newblk;
+ dp->di_size += sblock.fs_bsize;
+ dp->di_blocks += btodb(sblock.fs_bsize);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir);
+ pwarn("NO SPACE LEFT IN %s", name);
+ if (preen)
+ printf(" (EXPANDED)\n");
+ else if (reply("EXPAND") == 0)
+ goto bad;
+ dirty(bp);
+ inodirty();
+ return (1);
+bad:
+ dp->di_db[lastbn] = dp->di_db[lastbn + 1];
+ dp->di_db[lastbn + 1] = 0;
+ dp->di_size -= sblock.fs_bsize;
+ dp->di_blocks -= btodb(sblock.fs_bsize);
+ freeblk(newblk, sblock.fs_frag);
+ return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+allocdir(parent, request, mode)
+ ino_t parent, request;
+ int mode;
+{
+ ino_t ino;
+ char *cp;
+ struct dinode *dp;
+ register struct bufarea *bp;
+ struct dirtemplate *dirp;
+
+ ino = allocino(request, IFDIR|mode);
+ if (newinofmt)
+ dirp = &dirhead;
+ else
+ dirp = (struct dirtemplate *)&odirhead;
+ dirp->dot_ino = ino;
+ dirp->dotdot_ino = parent;
+ dp = ginode(ino);
+ bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
+ if (bp->b_errs) {
+ freeino(ino);
+ return (0);
+ }
+ bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ dp->di_nlink = 2;
+ inodirty();
+ if (ino == ROOTINO) {
+ lncntp[ino] = dp->di_nlink;
+ cacheino(dp, ino);
+ return(ino);
+ }
+ if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
+ freeino(ino);
+ return (0);
+ }
+ cacheino(dp, ino);
+ statemap[ino] = statemap[parent];
+ if (statemap[ino] == DSTATE) {
+ lncntp[ino] = dp->di_nlink;
+ lncntp[parent]++;
+ }
+ dp = ginode(parent);
+ dp->di_nlink++;
+ inodirty();
+ return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+freedir(ino, parent)
+ ino_t ino, parent;
+{
+ struct dinode *dp;
+
+ if (ino != parent) {
+ dp = ginode(parent);
+ dp->di_nlink--;
+ inodirty();
+ }
+ freeino(ino);
+}
+
+/*
+ * generate a temporary name for the lost+found directory.
+ */
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ifs/fsck.h b/sbin/fsck_ifs/fsck.h
new file mode 100644
index 0000000..7fa831f
--- /dev/null
+++ b/sbin/fsck_ifs/fsck.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ *
+ * @(#)fsck.h 8.1 (Berkeley) 6/5/93
+ */
+
+#define MAXDUP 10 /* limit on dup blks (per inode) */
+#define MAXBAD 10 /* limit on bad blks (per inode) */
+#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+#define USTATE 01 /* inode not allocated */
+#define FSTATE 02 /* inode is file */
+#define DSTATE 03 /* inode is directory */
+#define DFOUND 04 /* directory found during descent */
+#define DCLEAR 05 /* directory is to be cleared */
+#define FCLEAR 06 /* file is to be cleared */
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+ struct bufarea *b_next; /* free list queue */
+ struct bufarea *b_prev; /* free list queue */
+ daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ daddr_t *b_indir; /* indirect block */
+ struct fs *b_fs; /* super block */
+ struct cg *b_cg; /* cylinder group */
+ struct dinode *b_dinode; /* inode block */
+ } b_un;
+ char b_dirty;
+};
+
+#define B_INUSE 1
+
+#define MINBUFS 5 /* minimum number of buffers required */
+struct bufarea bufhead; /* head of list of other blks in filesys */
+struct bufarea sblk; /* file system superblock */
+struct bufarea cgblk; /* cylinder group blocks */
+struct bufarea *pdirbp; /* current directory contents */
+struct bufarea *pbp; /* current inode block */
+struct bufarea *getdatablk();
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (daddr_t)-1; \
+ (bp)->b_flags = 0;
+
+#define sbdirty() sblk.b_dirty = 1
+#define cgdirty() cgblk.b_dirty = 1
+#define sblock (*sblk.b_un.b_fs)
+#define cgrp (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+
+struct inodesc {
+ enum fixstate id_fix; /* policy on fixing errors */
+ int (*id_func)(); /* function to be applied to blocks of inode */
+ ino_t id_number; /* inode number described */
+ ino_t id_parent; /* for DATA nodes, their parent */
+ daddr_t id_blkno; /* current block number being examined */
+ int id_numfrags; /* number of frags contained in block */
+ quad_t id_filesize; /* for DATA nodes, the size of the directory */
+ int id_loc; /* for DATA nodes, current location in dir */
+ int id_entryno; /* for DATA nodes, current entry number */
+ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
+ char *id_name; /* for DATA nodes, name to find or enter */
+ char id_type; /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define DATA 1
+#define ADDR 2
+
+/*
+ * Linked list of duplicate blocks.
+ *
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ * w -> y -> x -> z -> y -> w -> y
+ * ^ ^
+ * | |
+ * duplist muldup
+ */
+struct dups {
+ struct dups *next;
+ daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+/*
+ * Linked list of inodes with zero link counts.
+ */
+struct zlncnt {
+ struct zlncnt *next;
+ ino_t zlncnt;
+};
+struct zlncnt *zlnhead; /* head of zero link count list */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode number of this entry */
+ ino_t i_parent; /* inode number of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ size_t i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ daddr_t i_blks[1]; /* actually longer */
+} **inphead, **inpsort;
+long numdirs, listmax, inplast;
+
+char *cdevname; /* name of device being checked */
+long dev_bsize; /* computed value of DEV_BSIZE */
+long secsize; /* actual disk sector size */
+char nflag; /* assume a no response */
+char yflag; /* assume a yes response */
+int bflag; /* location of alternate super block */
+int debug; /* output debugging info */
+int cvtlevel; /* convert to newer file system format */
+int doinglevel1; /* converting to new cylinder group format */
+int doinglevel2; /* converting to new inode format */
+int newinofmt; /* filesystem has new inode format */
+char preen; /* just fix normal inconsistencies */
+char hotroot; /* checking root device */
+char havesb; /* superblock has been read */
+int fsmodified; /* 1 => write done to file system */
+int fsreadfd; /* file descriptor for reading file system */
+int fswritefd; /* file descriptor for writing file system */
+
+daddr_t maxfsblock; /* number of blocks in the file system */
+char *blockmap; /* ptr to primary blk allocation map */
+ino_t maxino; /* number of inodes in file system */
+ino_t lastino; /* last inode in use */
+char *statemap; /* ptr to inode state table */
+char *typemap; /* ptr to inode type table */
+short *lncntp; /* ptr to link count table */
+
+ino_t lfdir; /* lost & found directory inode number */
+char *lfname; /* lost & found directory name */
+int lfmode; /* lost & found directory creation mode */
+
+daddr_t n_blks; /* number of blocks in use */
+daddr_t n_files; /* number of files in use */
+
+#define clearinode(dp) (*(dp) = zino)
+struct dinode zino;
+
+#define setbmap(blkno) setbit(blockmap, blkno)
+#define testbmap(blkno) isset(blockmap, blkno)
+#define clrbmap(blkno) clrbit(blockmap, blkno)
+
+#define STOP 0x01
+#define SKIP 0x02
+#define KEEPON 0x04
+#define ALTERED 0x08
+#define FOUND 0x10
+
+time_t time();
+struct dinode *ginode();
+struct inoinfo *getinoinfo();
+void getblk();
+ino_t allocino();
+int findino();
diff --git a/sbin/fsck_ifs/fsck_ifs.8 b/sbin/fsck_ifs/fsck_ifs.8
new file mode 100644
index 0000000..cf8c499
--- /dev/null
+++ b/sbin/fsck_ifs/fsck_ifs.8
@@ -0,0 +1,291 @@
+.\" Copyright (c) 1980, 1989, 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.
+.\"
+.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt FSCK 8
+.Os BSD 4
+.Sh NAME
+.Nm fsck
+.Nd filesystem consistency check and interactive repair
+.Sh SYNOPSIS
+.Nm fsck
+.Fl p
+.Op Fl m Ar mode
+.Nm fsck
+.Op Fl b Ar block#
+.Op Fl c Ar level
+.Op Fl l Ar maxparallel
+.Op Fl y
+.Op Fl n
+.Op Fl m Ar mode
+.Op Ar filesystem
+.Ar ...
+.Sh DESCRIPTION
+The first form of
+.Nm fsck
+preens a standard set of filesystems or the specified filesystems.
+It is normally used in the script
+.Pa /etc/rc
+during automatic reboot.
+Here
+.Nm fsck
+reads the table
+.Pa /etc/fstab
+to determine which filesystems to check.
+Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro''
+and that have non-zero pass number are checked.
+Filesystems with pass number 1 (normally just the root filesystem)
+are checked one at a time.
+When pass 1 completes, all remaining filesystems are checked,
+running one process per disk drive.
+The disk drive containing each filesystem is inferred from the longest prefix
+of the device name that ends in a digit; the remaining characters are assumed
+to be the partition designator.
+.Pp
+The kernel takes care that only a restricted class of innocuous filesystem
+inconsistencies can happen unless hardware or software failures intervene.
+These are limited to the following:
+.Bl -item -compact
+.It
+Unreferenced inodes
+.It
+Link counts in inodes too large
+.It
+Missing blocks in the free map
+.It
+Blocks in the free map also in files
+.It
+Counts in the super-block wrong
+.El
+.Pp
+These are the only inconsistencies that
+.Nm fsck
+with the
+.Fl p
+option will correct; if it encounters other inconsistencies, it exits
+with an abnormal return status and an automatic reboot will then fail.
+For each corrected inconsistency one or more lines will be printed
+identifying the filesystem on which the correction will take place,
+and the nature of the correction. After successfully correcting a filesystem,
+.Nm fsck
+will print the number of files on that filesystem,
+the number of used and free blocks,
+and the percentage of fragmentation.
+.Pp
+If sent a
+.Dv QUIT
+signal,
+.Nm fsck
+will finish the filesystem checks, then exit with an abnormal
+return status that causes an automatic reboot to fail.
+This is useful when you want to finish the filesystem checks during an
+automatic reboot,
+but do not want the machine to come up multiuser after the checks complete.
+.Pp
+Without the
+.Fl p
+option,
+.Nm fsck
+audits and interactively repairs inconsistent conditions for filesystems.
+If the filesystem is inconsistent the operator is prompted for concurrence
+before each correction is attempted.
+It should be noted that some of the corrective actions which are not
+correctable under the
+.Fl p
+option will result in some loss of data.
+The amount and severity of data lost may be determined from the diagnostic
+output.
+The default action for each consistency correction
+is to wait for the operator to respond
+.Li yes
+or
+.Li no .
+If the operator does not have write permission on the filesystem
+.Nm fsck
+will default to a
+.Fl n
+action.
+.Pp
+.Nm Fsck
+has more consistency checks than
+its predecessors
+.Em check , dcheck , fcheck ,
+and
+.Em icheck
+combined.
+.Pp
+The following flags are interpreted by
+.Nm fsck .
+.Bl -tag -width indent
+.It Fl b
+Use the block specified immediately after the flag as
+the super block for the filesystem. Block 32 is usually
+an alternate super block.
+.It Fl l
+Limit the number of parallel checks to the number specified in the following
+argument.
+By default, the limit is the number of disks, running one process per disk.
+If a smaller limit is given, the disks are checked round-robin, one filesystem
+at a time.
+.It Fl m
+Use the mode specified in octal immediately after the flag as the
+permission bits to use when creating the
+.Pa lost+found
+directory rather than the default 1777.
+In particular, systems that do not wish to have lost files accessible
+by all users on the system should use a more restrictive
+set of permissions such as 700.
+.It Fl y
+Assume a yes response to all questions asked by
+.Nm fsck ;
+this should be used with great caution as this is a free license
+to continue after essentially unlimited trouble has been encountered.
+.It Fl n
+Assume a no response to all questions asked by
+.Nm fsck
+except for
+.Ql CONTINUE? ,
+which is assumed to be affirmative;
+do not open the filesystem for writing.
+.It Fl c
+Convert the filesystem to the specified level.
+Note that the level of a filesystem can only be raised.
+.Bl -tag -width indent
+There are currently three levels defined:
+.It 0
+The filesystem is in the old (static table) format.
+.It 1
+The filesystem is in the new (dynamic table) format.
+.It 2
+The filesystem supports 32-bit uid's and gid's,
+short symbolic links are stored in the inode,
+and directories have an added field showing the file type.
+.El
+.Pp
+In interactive mode,
+.Nm fsck
+will list the conversion to be made
+and ask whether the conversion should be done.
+If a negative answer is given,
+no further operations are done on the filesystem.
+In preen mode,
+the conversion is listed and done if
+possible without user interaction.
+Conversion in preen mode is best used when all the filesystems
+are being converted at once.
+The format of a filesystem can be determined from the
+first line of output from
+.Xr dumpfs 8 .
+.El
+.Pp
+If no filesystems are given to
+.Nm fsck
+then a default list of filesystems is read from
+the file
+.Pa /etc/fstab .
+.Pp
+.Bl -enum -indent indent -compact
+Inconsistencies checked are as follows:
+.It
+Blocks claimed by more than one inode or the free map.
+.It
+Blocks claimed by an inode outside the range of the filesystem.
+.It
+Incorrect link counts.
+.It
+Size checks:
+.Bl -item -indent indent -compact
+.It
+Directory size not a multiple of DIRBLKSIZ.
+.It
+Partially truncated file.
+.El
+.It
+Bad inode format.
+.It
+Blocks not accounted for anywhere.
+.It
+Directory checks:
+.Bl -item -indent indent -compact
+.It
+File pointing to unallocated inode.
+.It
+Inode number out of range.
+.It
+Dot or dot-dot not the first two entries of a directory
+or having the wrong inode number.
+.El
+.It
+Super Block checks:
+.Bl -item -indent indent -compact
+.It
+More blocks for inodes than there are in the filesystem.
+.It
+Bad free block map format.
+.It
+Total free block and/or free inode count incorrect.
+.El
+.El
+.Pp
+Orphaned files and directories (allocated but unreferenced) are,
+with the operator's concurrence, reconnected by
+placing them in the
+.Pa lost+found
+directory.
+The name assigned is the inode number.
+If the
+.Pa lost+found
+directory does not exist, it is created.
+If there is insufficient space its size is increased.
+.Pp
+Because of inconsistencies between the block device and the buffer cache,
+the raw device should always be used.
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+contains default list of filesystems to check.
+.El
+.Sh DIAGNOSTICS
+The diagnostics produced by
+.Nm fsck
+are fully enumerated and explained in Appendix A of
+.Rs
+.%T "Fsck \- The UNIX File System Check Program"
+.Re
+.Sh SEE ALSO
+.Xr fstab 5 ,
+.Xr fs 5 ,
+.Xr fsdb 8 ,
+.Xr newfs 8 ,
+.Xr mkfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck_ifs/inode.c b/sbin/fsck_ifs/inode.c
new file mode 100644
index 0000000..f1c1758
--- /dev/null
+++ b/sbin/fsck_ifs/inode.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ long ret, n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+
+ if (idesc->id_fix != IGNORE)
+ idesc->id_fix = DONTKNOW;
+ idesc->id_entryno = 0;
+ idesc->id_filesize = dp->di_size;
+ mode = dp->di_mode & IFMT;
+ if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
+ dp->di_size < sblock.fs_maxsymlinklen))
+ return (KEEPON);
+ dino = *dp;
+ ndb = howmany(dino.di_size, sblock.fs_bsize);
+ for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
+ if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
+ idesc->id_numfrags =
+ numfrags(&sblock, fragroundup(&sblock, offset));
+ else
+ idesc->id_numfrags = sblock.fs_frag;
+ if (*ap == 0)
+ continue;
+ idesc->id_blkno = *ap;
+ if (idesc->id_type == ADDR)
+ ret = (*idesc->id_func)(idesc);
+ else
+ ret = dirscan(idesc);
+ if (ret & STOP)
+ return (ret);
+ }
+ idesc->id_numfrags = sblock.fs_frag;
+ remsize = dino.di_size - sblock.fs_bsize * NDADDR;
+ sizepb = sblock.fs_bsize;
+ for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ ret = iblock(idesc, n, remsize);
+ if (ret & STOP)
+ return (ret);
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+iblock(idesc, ilevel, isize)
+ struct inodesc *idesc;
+ long ilevel;
+ quad_t isize;
+{
+ register daddr_t *ap;
+ register daddr_t *aplim;
+ register struct bufarea *bp;
+ int i, n, (*func)(), nif;
+ quad_t sizepb;
+ char buf[BUFSIZ];
+ extern int dirscan(), pass1check();
+
+ if (idesc->id_type == ADDR) {
+ func = idesc->id_func;
+ if (((n = (*func)(idesc)) & KEEPON) == 0)
+ return (n);
+ } else
+ func = dirscan;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+ return (SKIP);
+ bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
+ ilevel--;
+ for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
+ sizepb *= NINDIR(&sblock);
+ nif = howmany(isize , sizepb);
+ if (nif > NINDIR(&sblock))
+ nif = NINDIR(&sblock);
+ if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
+ aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
+ for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
+ if (*ap == 0)
+ continue;
+ (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
+ idesc->id_number);
+ if (dofix(idesc, buf)) {
+ *ap = 0;
+ dirty(bp);
+ }
+ }
+ flush(fswritefd, bp);
+ }
+ aplim = &bp->b_un.b_indir[nif];
+ for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ if (ilevel == 0)
+ n = (*func)(idesc);
+ else
+ n = iblock(idesc, ilevel, isize);
+ if (n & STOP) {
+ bp->b_flags &= ~B_INUSE;
+ return (n);
+ }
+ }
+ isize -= sizepb;
+ }
+ bp->b_flags &= ~B_INUSE;
+ return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+chkrange(blk, cnt)
+ daddr_t blk;
+ int cnt;
+{
+ register int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return (1);
+ c = dtog(&sblock, blk);
+ if (blk < cgdmin(&sblock, c)) {
+ if ((blk + cnt) > cgsblock(&sblock, c)) {
+ if (debug) {
+ printf("blk %ld < cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > cgsbase %ld\n",
+ blk + cnt, cgsblock(&sblock, c));
+ }
+ return (1);
+ }
+ } else {
+ if ((blk + cnt) > cgbase(&sblock, c+1)) {
+ if (debug) {
+ printf("blk %ld >= cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
+ blk+cnt, sblock.fs_fpg);
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+struct dinode *
+ginode(inumber)
+ ino_t inumber;
+{
+ daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errexit("bad inode number %d to ginode\n", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ iblk = ino_to_fsba(&sblock, inumber);
+ if (pbp != 0)
+ pbp->b_flags &= ~B_INUSE;
+ pbp = getdatablk(iblk, sblock.fs_bsize);
+ startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+ }
+ return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+
+struct dinode *
+getnextinode(inumber)
+ ino_t inumber;
+{
+ long size;
+ daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errexit("bad inode number %d to nextinode\n", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
+ dp = inodebuf;
+ }
+ return (dp++);
+}
+
+resetinodebuf()
+{
+
+ startinum = 0;
+ nextino = 0;
+ lastinum = 0;
+ readcnt = 0;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / sizeof(struct dinode);
+ readpercg = sblock.fs_ipg / fullcnt;
+ partialcnt = sblock.fs_ipg % fullcnt;
+ partialsize = partialcnt * sizeof(struct dinode);
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if (inodebuf == NULL &&
+ (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
+ errexit("Cannot allocate space for inode buffer\n");
+ while (nextino < ROOTINO)
+ (void)getnextinode(nextino);
+}
+
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free((char *)inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+cacheino(dp, inumber)
+ register struct dinode *dp;
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+ struct inoinfo **inpp;
+ unsigned int blks;
+
+ blks = howmany(dp->di_size, sblock.fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ inp = (struct inoinfo *)
+ malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
+ if (inp == NULL)
+ return;
+ inpp = &inphead[inumber % numdirs];
+ inp->i_nexthash = *inpp;
+ *inpp = inp;
+ inp->i_parent = (ino_t)0;
+ inp->i_dotdot = (ino_t)0;
+ inp->i_number = inumber;
+ inp->i_isize = dp->di_size;
+ inp->i_numblks = blks * sizeof(daddr_t);
+ bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
+ (size_t)inp->i_numblks);
+ if (inplast == listmax) {
+ listmax += 100;
+ inpsort = (struct inoinfo **)realloc((char *)inpsort,
+ (unsigned)listmax * sizeof(struct inoinfo *));
+ if (inpsort == NULL)
+ errexit("cannot increase directory list");
+ }
+ inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(inumber)
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+
+ for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
+ if (inp->i_number != inumber)
+ continue;
+ return (inp);
+ }
+ errexit("cannot find inode %d\n", inumber);
+ return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+inocleanup()
+{
+ register struct inoinfo **inpp;
+
+ if (inphead == NULL)
+ return;
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+ free((char *)(*inpp));
+ free((char *)inphead);
+ free((char *)inpsort);
+ inphead = inpsort = NULL;
+}
+
+inodirty()
+{
+
+ dirty(pbp);
+}
+
+clri(idesc, type, flag)
+ register struct inodesc *idesc;
+ char *type;
+ int flag;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (flag == 1) {
+ pwarn("%s %s", type,
+ (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE");
+ pinode(idesc->id_number);
+ }
+ if (preen || reply("CLEAR") == 1) {
+ if (preen)
+ printf(" (CLEARED)\n");
+ n_files--;
+ (void)ckinode(dp, idesc);
+ clearinode(dp);
+ statemap[idesc->id_number] = USTATE;
+ inodirty();
+ }
+}
+
+findname(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino != idesc->id_parent)
+ return (KEEPON);
+ bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
+ return (STOP|FOUND);
+}
+
+findino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino == 0)
+ return (KEEPON);
+ if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
+ dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
+ idesc->id_parent = dirp->d_ino;
+ return (STOP|FOUND);
+ }
+ return (KEEPON);
+}
+
+pinode(ino)
+ ino_t ino;
+{
+ register struct dinode *dp;
+ register char *p;
+ struct passwd *pw;
+ char *ctime();
+
+ printf(" I=%lu ", ino);
+ if (ino < ROOTINO || ino > maxino)
+ return;
+ dp = ginode(ino);
+ printf(" OWNER=");
+ if ((pw = getpwuid((int)dp->di_uid)) != 0)
+ printf("%s ", pw->pw_name);
+ else
+ printf("%u ", (unsigned)dp->di_uid);
+ printf("MODE=%o\n", dp->di_mode);
+ if (preen)
+ printf("%s: ", cdevname);
+ printf("SIZE=%qu ", dp->di_size);
+ p = ctime(&dp->di_mtime.ts_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ daddr_t blk;
+{
+
+ pfatal("%ld %s I=%lu", blk, type, ino);
+ printf("\n");
+ switch (statemap[ino]) {
+
+ case FSTATE:
+ statemap[ino] = FCLEAR;
+ return;
+
+ case DSTATE:
+ statemap[ino] = DCLEAR;
+ return;
+
+ case FCLEAR:
+ case DCLEAR:
+ return;
+
+ default:
+ errexit("BAD STATE %d TO BLKERR", statemap[ino]);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(request, type)
+ ino_t request;
+ int type;
+{
+ register ino_t ino;
+ register struct dinode *dp;
+
+ if (request == 0)
+ request = ROOTINO;
+ else if (statemap[request] != USTATE)
+ return (0);
+ for (ino = request; ino < maxino; ino++)
+ if (statemap[ino] == USTATE)
+ break;
+ if (ino == maxino)
+ return (0);
+ switch (type & IFMT) {
+ case IFDIR:
+ statemap[ino] = DSTATE;
+ break;
+ case IFREG:
+ case IFLNK:
+ statemap[ino] = FSTATE;
+ break;
+ default:
+ return (0);
+ }
+ dp = ginode(ino);
+ dp->di_db[0] = allocblk((long)1);
+ if (dp->di_db[0] == 0) {
+ statemap[ino] = USTATE;
+ return (0);
+ }
+ dp->di_mode = type;
+ (void)time(&dp->di_atime.ts_sec);
+ dp->di_mtime = dp->di_ctime = dp->di_atime;
+ dp->di_size = sblock.fs_fsize;
+ dp->di_blocks = btodb(sblock.fs_fsize);
+ n_files++;
+ inodirty();
+ if (newinofmt)
+ typemap[ino] = IFTODT(type);
+ return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ extern int pass4check();
+ struct dinode *dp;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ifs/main.c b/sbin/fsck_ifs/main.c
new file mode 100644
index 0000000..2e69715
--- /dev/null
+++ b/sbin/fsck_ifs/main.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1980, 1986, 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) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "fsck.h"
+
+void catch(), catchquit(), voidquit();
+int returntosingle;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern int docheck(), checkfilesys();
+ extern char *optarg, *blockcheck();
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) {
+ switch (ch) {
+ case 'p':
+ preen++;
+ break;
+
+ case 'b':
+ bflag = argtoi('b', "number", optarg, 10);
+ printf("Alternate super block location: %d\n", bflag);
+ break;
+
+ case 'c':
+ cvtlevel = argtoi('c', "conversion level", optarg, 10);
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'l':
+ maxrun = argtoi('l', "number", optarg, 10);
+ break;
+
+ case 'm':
+ lfmode = argtoi('m', "mode", optarg, 8);
+ if (lfmode &~ 07777)
+ errexit("bad mode to -m: %o\n", lfmode);
+ printf("** lost+found creation mode %o\n", lfmode);
+ break;
+
+ case 'n':
+ case 'N':
+ nflag++;
+ yflag = 0;
+ break;
+
+ case 'y':
+ case 'Y':
+ yflag++;
+ nflag = 0;
+ break;
+
+ default:
+ errexit("%c option?\n", ch);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, catch);
+ if (preen)
+ (void)signal(SIGQUIT, catchquit);
+ if (argc) {
+ while (argc-- > 0)
+ (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0);
+ exit(0);
+ }
+ ret = checkfstab(preen, maxrun, docheck, checkfilesys);
+ if (returntosingle)
+ exit(2);
+ exit(ret);
+}
+
+argtoi(flag, req, str, base)
+ int flag;
+ char *req, *str;
+ int base;
+{
+ char *cp;
+ int ret;
+
+ ret = (int)strtol(str, &cp, base);
+ if (cp == str || *cp)
+ errexit("-%c flag requires a %s\n", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+docheck(fsp)
+ register struct fstab *fsp;
+{
+
+ if (strcmp(fsp->fs_vfstype, "ufs") ||
+ (strcmp(fsp->fs_type, FSTAB_RW) &&
+ strcmp(fsp->fs_type, FSTAB_RO)) ||
+ fsp->fs_passno == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Check the specified filesystem.
+ */
+/* ARGSUSED */
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+{
+ daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ if (setup(filesys) == 0) {
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ return (0);
+ }
+ /*
+ * 1: scan inodes tallying blocks used
+ */
+ if (preen == 0) {
+ printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
+ if (hotroot)
+ printf("** Root file system\n");
+ printf("** Phase 1 - Check Blocks and Sizes\n");
+ }
+ pass1();
+
+ /*
+ * 1b: locate first references to duplicates, if any
+ */
+ if (duplist) {
+ if (preen)
+ pfatal("INTERNAL ERROR: dups with -p");
+ printf("** Phase 1b - Rescan For More DUPS\n");
+ pass1b();
+ }
+
+ /*
+ * 2: traverse directories from root to mark all connected directories
+ */
+ if (preen == 0)
+ printf("** Phase 2 - Check Pathnames\n");
+ pass2();
+
+ /*
+ * 3: scan inodes looking for disconnected directories
+ */
+ if (preen == 0)
+ printf("** Phase 3 - Check Connectivity\n");
+ pass3();
+
+ /*
+ * 4: scan inodes looking for disconnected files; check reference counts
+ */
+ if (preen == 0)
+ printf("** Phase 4 - Check Reference Counts\n");
+ pass4();
+
+ /*
+ * 5: check and repair resource counts in cylinder groups
+ */
+ if (preen == 0)
+ printf("** Phase 5 - Check Cyl groups\n");
+ pass5();
+
+ /*
+ * print out summary statistics
+ */
+ n_ffree = sblock.fs_cstotal.cs_nffree;
+ n_bfree = sblock.fs_cstotal.cs_nbfree;
+ pwarn("%ld files, %ld used, %ld free ",
+ n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
+ printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n",
+ n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize,
+ ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10);
+ if (debug &&
+ (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
+ printf("%ld files missing\n", n_files);
+ if (debug) {
+ n_blks += sblock.fs_ncg *
+ (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
+ n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
+ n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
+ printf("%ld blocks missing\n", n_blks);
+ if (duplist != NULL) {
+ printf("The following duplicate blocks remain:");
+ for (dp = duplist; dp; dp = dp->next)
+ printf(" %ld,", dp->dup);
+ printf("\n");
+ }
+ if (zlnhead != NULL) {
+ printf("The following zero link count inodes remain:");
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ printf(" %lu,", zlnp->zlncnt);
+ printf("\n");
+ }
+ }
+ zlnhead = (struct zlncnt *)0;
+ duplist = (struct dups *)0;
+ muldup = (struct dups *)0;
+ inocleanup();
+ if (fsmodified) {
+ (void)time(&sblock.fs_time);
+ sbdirty();
+ }
+ if (cvtlevel && sblk.b_dirty) {
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ bwrite(fswritefd, (char *)&sblock,
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE);
+ }
+ ckfini();
+ free(blockmap);
+ free(statemap);
+ free((char *)lncntp);
+ if (!fsmodified)
+ return (0);
+ if (!preen)
+ printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+ if (hotroot) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_UFS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
diff --git a/sbin/fsck_ifs/pass1.c b/sbin/fsck_ifs/pass1.c
new file mode 100644
index 0000000..ca255fe
--- /dev/null
+++ b/sbin/fsck_ifs/pass1.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+int pass1check();
+struct dinode *getnextinode();
+
+pass1()
+{
+ ino_t inumber;
+ int c, i, cgd;
+ struct inodesc idesc;
+
+ /*
+ * Set file system reserved blocks in used block map.
+ */
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ cgd = cgdmin(&sblock, c);
+ if (c == 0) {
+ i = cgbase(&sblock, c);
+ cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ } else
+ i = cgsblock(&sblock, c);
+ for (; i < cgd; i++)
+ setbmap(i);
+ }
+ /*
+ * Find all allocated blocks.
+ */
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1check;
+ inumber = 0;
+ n_files = n_blks = 0;
+ resetinodebuf();
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ checkinode(inumber, &idesc);
+ }
+ }
+ freeinodebuf();
+}
+
+checkinode(inumber, idesc)
+ ino_t inumber;
+ register struct inodesc *idesc;
+{
+ register struct dinode *dp;
+ struct zlncnt *zlnp;
+ int ndb, j;
+ mode_t mode;
+ char symbuf[MAXSYMLINKLEN];
+
+ dp = getnextinode(inumber);
+ mode = dp->di_mode & IFMT;
+ if (mode == 0) {
+ if (bcmp((char *)dp->di_db, (char *)zino.di_db,
+ NDADDR * sizeof(daddr_t)) ||
+ bcmp((char *)dp->di_ib, (char *)zino.di_ib,
+ NIADDR * sizeof(daddr_t)) ||
+ dp->di_mode || dp->di_size) {
+ pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber);
+ if (reply("CLEAR") == 1) {
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+ }
+ statemap[inumber] = USTATE;
+ return;
+ }
+ lastino = inumber;
+ if (/* dp->di_size < 0 || */
+ dp->di_size + sblock.fs_bsize - 1 < dp->di_size) {
+ if (debug)
+ printf("bad size %qu:", dp->di_size);
+ goto unknown;
+ }
+ if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
+ dp = ginode(inumber);
+ dp->di_size = sblock.fs_fsize;
+ dp->di_mode = IFREG|0600;
+ inodirty();
+ }
+ ndb = howmany(dp->di_size, sblock.fs_bsize);
+ if (ndb < 0) {
+ if (debug)
+ printf("bad size %qu ndb %d:",
+ dp->di_size, ndb);
+ goto unknown;
+ }
+ if (mode == IFBLK || mode == IFCHR)
+ ndb++;
+ if (mode == IFLNK) {
+ if (doinglevel2 &&
+ dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
+ dp->di_blocks != 0) {
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)dp->di_size) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %d(%s) of size %d\n",
+ inumber, symbuf, (long)dp->di_size);
+ }
+ dp = ginode(inumber);
+ bcopy(symbuf, (caddr_t)dp->di_shortlink,
+ (long)dp->di_size);
+ dp->di_blocks = 0;
+ inodirty();
+ }
+ /*
+ * Fake ndb value so direct/indirect block checks below
+ * will detect any garbage after symlink string.
+ */
+ if (dp->di_size < sblock.fs_maxsymlinklen) {
+ ndb = howmany(dp->di_size, sizeof(daddr_t));
+ if (ndb > NDADDR) {
+ j = ndb - NDADDR;
+ for (ndb = 1; j > 1; j--)
+ ndb *= NINDIR(&sblock);
+ ndb += NDADDR;
+ }
+ }
+ }
+ for (j = ndb; j < NDADDR; j++)
+ if (dp->di_db[j] != 0) {
+ if (debug)
+ printf("bad direct addr: %ld\n", dp->di_db[j]);
+ goto unknown;
+ }
+ for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+ ndb /= NINDIR(&sblock);
+ for (; j < NIADDR; j++)
+ if (dp->di_ib[j] != 0) {
+ if (debug)
+ printf("bad indirect addr: %ld\n",
+ dp->di_ib[j]);
+ goto unknown;
+ }
+ if (ftypeok(dp) == 0)
+ goto unknown;
+ n_files++;
+ lncntp[inumber] = dp->di_nlink;
+ if (dp->di_nlink <= 0) {
+ zlnp = (struct zlncnt *)malloc(sizeof *zlnp);
+ if (zlnp == NULL) {
+ pfatal("LINK COUNT TABLE OVERFLOW");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ } else {
+ zlnp->zlncnt = inumber;
+ zlnp->next = zlnhead;
+ zlnhead = zlnp;
+ }
+ }
+ if (mode == IFDIR) {
+ if (dp->di_size == 0)
+ statemap[inumber] = DCLEAR;
+ else
+ statemap[inumber] = DSTATE;
+ cacheino(dp, inumber);
+ } else
+ statemap[inumber] = FSTATE;
+ typemap[inumber] = IFTODT(mode);
+ if (doinglevel2 &&
+ (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) {
+ dp = ginode(inumber);
+ dp->di_uid = dp->di_ouid;
+ dp->di_ouid = -1;
+ dp->di_gid = dp->di_ogid;
+ dp->di_ogid = -1;
+ inodirty();
+ }
+ badblk = dupblk = 0;
+ idesc->id_number = inumber;
+ (void)ckinode(dp, idesc);
+ idesc->id_entryno *= btodb(sblock.fs_fsize);
+ if (dp->di_blocks != idesc->id_entryno) {
+ pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)",
+ inumber, dp->di_blocks, idesc->id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ dp = ginode(inumber);
+ dp->di_blocks = idesc->id_entryno;
+ inodirty();
+ }
+ return;
+unknown:
+ pfatal("UNKNOWN FILE TYPE I=%lu", inumber);
+ statemap[inumber] = FCLEAR;
+ if (reply("CLEAR") == 1) {
+ statemap[inumber] = USTATE;
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+}
+
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass1b.c b/sbin/fsck_ifs/pass1b.c
new file mode 100644
index 0000000..27b2dfd
--- /dev/null
+++ b/sbin/fsck_ifs/pass1b.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+int pass1bcheck();
+static struct dups *duphead;
+
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1bcheck;
+ duphead = duplist;
+ inumber = 0;
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ dp = ginode(inumber);
+ if (dp == NULL)
+ continue;
+ idesc.id_number = inumber;
+ if (statemap[inumber] != USTATE &&
+ (ckinode(dp, &idesc) & STOP))
+ return;
+ }
+ }
+}
+
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass2.c b/sbin/fsck_ifs/pass2.c
new file mode 100644
index 0000000..6314751
--- /dev/null
+++ b/sbin/fsck_ifs/pass2.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+pass2()
+{
+ register struct dinode *dp;
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ struct inodesc curino;
+ struct dinode dino;
+ char pathbuf[MAXPATHLEN + 1];
+
+ switch (statemap[ROOTINO]) {
+
+ case USTATE:
+ pfatal("ROOT INODE UNALLOCATED");
+ if (reply("ALLOCATE") == 0)
+ errexit("");
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("FIX") == 0)
+ errexit("");
+ dp = ginode(ROOTINO);
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ bzero((char *)&curino, sizeof(struct inodesc));
+ curino.id_type = DATA;
+ curino.id_func = pass2check;
+ dp = &dino;
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_isize == 0)
+ continue;
+ if (inp->i_isize < MINDIRSIZE) {
+ direrror(inp->i_number, "DIRECTORY TOO SHORT");
+ inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+ if (reply("FIX") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = inp->i_isize;
+ inodirty();
+ dp = &dino;
+ }
+ } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+ getpathname(pathbuf, inp->i_number, inp->i_number);
+ pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+ pathbuf, inp->i_isize, DIRBLKSIZ);
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+ if (preen || reply("ADJUST") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
+ inodirty();
+ dp = &dino;
+ }
+ }
+ bzero((char *)&dino, sizeof(struct dinode));
+ dino.di_mode = IFDIR;
+ dp->di_size = inp->i_isize;
+ bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
+ (size_t)inp->i_numblks);
+ curino.id_number = inp->i_number;
+ curino.id_parent = inp->i_parent;
+ (void)ckinode(dp, &curino);
+ }
+ /*
+ * Now that the parents of all directories have been found,
+ * make another pass to verify the value of `..'
+ */
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 || inp->i_isize == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE)
+ statemap[inp->i_number] = DFOUND;
+ if (inp->i_dotdot == inp->i_parent ||
+ inp->i_dotdot == (ino_t)-1)
+ continue;
+ if (inp->i_dotdot == 0) {
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+ if (reply("FIX") == 0)
+ continue;
+ (void)makeentry(inp->i_number, inp->i_parent, "..");
+ lncntp[inp->i_parent]--;
+ continue;
+ }
+ fileerror(inp->i_parent, inp->i_number,
+ "BAD INODE NUMBER FOR '..'");
+ if (reply("FIX") == 0)
+ continue;
+ lncntp[inp->i_dotdot]++;
+ lncntp[inp->i_parent]--;
+ inp->i_dotdot = inp->i_parent;
+ (void)changeino(inp->i_number, "..", inp->i_parent);
+ }
+ /*
+ * Mark all the directories that can be found from the root.
+ */
+ propagate();
+}
+
+pass2check(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ register struct inoinfo *inp;
+ int n, entrysize, ret = 0;
+ struct dinode *dp;
+ char *errmsg;
+ struct direct proto;
+ char namebuf[MAXPATHLEN + 1];
+ char pathbuf[MAXPATHLEN + 1];
+
+ /*
+ * If converting, set directory entry type.
+ */
+ if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
+ dirp->d_type = typemap[dirp->d_ino];
+ ret |= ALTERED;
+ }
+ /*
+ * check for "."
+ */
+ if (idesc->id_entryno != 0)
+ goto chk1;
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
+ if (dirp->d_ino != idesc->id_number) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+ dirp->d_ino = idesc->id_number;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk1;
+ }
+ direrror(idesc->id_number, "MISSING '.'");
+ proto.d_ino = idesc->id_number;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 1;
+ (void)strcpy(proto.d_name, ".");
+ entrysize = DIRSIZ(0, &proto);
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
+ pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ } else if (dirp->d_reclen < entrysize) {
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+ } else if (dirp->d_reclen < 2 * entrysize) {
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ bzero((char *)dirp, (size_t)n);
+ dirp->d_reclen = n;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+chk1:
+ if (idesc->id_entryno > 1)
+ goto chk2;
+ inp = getinoinfo(idesc->id_number);
+ proto.d_ino = inp->i_parent;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 2;
+ (void)strcpy(proto.d_name, "..");
+ entrysize = DIRSIZ(0, &proto);
+ if (idesc->id_entryno == 0) {
+ n = DIRSIZ(0, dirp);
+ if (dirp->d_reclen < n + entrysize)
+ goto chk2;
+ proto.d_reclen = dirp->d_reclen - n;
+ dirp->d_reclen = n;
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + n);
+ bzero((char *)dirp, (size_t)proto.d_reclen);
+ dirp->d_reclen = proto.d_reclen;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+ inp->i_dotdot = dirp->d_ino;
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk2;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ inp->i_dotdot = (ino_t)-1;
+ } else if (dirp->d_reclen < entrysize) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+ inp->i_dotdot = (ino_t)-1;
+ } else if (inp->i_parent != 0) {
+ /*
+ * We know the parent, so fix now.
+ */
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ idesc->id_entryno++;
+ if (dirp->d_ino != 0)
+ lncntp[dirp->d_ino]--;
+ return (ret|KEEPON);
+chk2:
+ if (dirp->d_ino == 0)
+ return (ret|KEEPON);
+ if (dirp->d_namlen <= 2 &&
+ dirp->d_name[0] == '.' &&
+ idesc->id_entryno >= 2) {
+ if (dirp->d_namlen == 1) {
+ direrror(idesc->id_number, "EXTRA '.' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ if (dirp->d_name[1] == '.') {
+ direrror(idesc->id_number, "EXTRA '..' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ }
+ idesc->id_entryno++;
+ n = 0;
+ if (dirp->d_ino > maxino) {
+ fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
+ n = reply("REMOVE");
+ } else {
+again:
+ switch (statemap[dirp->d_ino]) {
+ case USTATE:
+ if (idesc->id_entryno <= 2)
+ break;
+ fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
+ n = reply("REMOVE");
+ break;
+
+ case DCLEAR:
+ case FCLEAR:
+ if (idesc->id_entryno <= 2)
+ break;
+ if (statemap[dirp->d_ino] == FCLEAR)
+ errmsg = "DUP/BAD";
+ else if (!preen)
+ errmsg = "ZERO LENGTH DIRECTORY";
+ else {
+ n = 1;
+ break;
+ }
+ fileerror(idesc->id_number, dirp->d_ino, errmsg);
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ dp = ginode(dirp->d_ino);
+ statemap[dirp->d_ino] =
+ (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE;
+ lncntp[dirp->d_ino] = dp->di_nlink;
+ goto again;
+
+ case DSTATE:
+ if (statemap[idesc->id_number] == DFOUND)
+ statemap[dirp->d_ino] = DFOUND;
+ /* fall through */
+
+ case DFOUND:
+ inp = getinoinfo(dirp->d_ino);
+ if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ getpathname(namebuf, dirp->d_ino, dirp->d_ino);
+ pwarn("%s %s %s\n", pathbuf,
+ "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+ namebuf);
+ if (preen)
+ printf(" (IGNORED)\n");
+ else if ((n = reply("REMOVE")) == 1)
+ break;
+ }
+ if (idesc->id_entryno > 2)
+ inp->i_parent = idesc->id_number;
+ /* fall through */
+
+ case FSTATE:
+ if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
+ fileerror(idesc->id_number, dirp->d_ino,
+ "BAD TYPE VALUE");
+ dirp->d_type = typemap[dirp->d_ino];
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ lncntp[dirp->d_ino]--;
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[dirp->d_ino], dirp->d_ino);
+ }
+ }
+ if (n == 0)
+ return (ret|KEEPON);
+ dirp->d_ino = 0;
+ return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+blksort(inpp1, inpp2)
+ struct inoinfo **inpp1, **inpp2;
+{
+
+ return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ifs/pass3.c b/sbin/fsck_ifs/pass3.c
new file mode 100644
index 0000000..5c6f09d
--- /dev/null
+++ b/sbin/fsck_ifs/pass3.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include "fsck.h"
+
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ifs/pass4.c b/sbin/fsck_ifs/pass4.c
new file mode 100644
index 0000000..7450530
--- /dev/null
+++ b/sbin/fsck_ifs/pass4.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+int pass4check();
+
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ for (inumber = ROOTINO; inumber <= lastino; inumber++) {
+ idesc.id_number = inumber;
+ switch (statemap[inumber]) {
+
+ case FSTATE:
+ case DFOUND:
+ n = lncntp[inumber];
+ if (n)
+ adjust(&idesc, (short)n);
+ else {
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ if (zlnp->zlncnt == inumber) {
+ zlnp->zlncnt = zlnhead->zlncnt;
+ zlnp = zlnhead;
+ zlnhead = zlnhead->next;
+ free((char *)zlnp);
+ clri(&idesc, "UNREF", 1);
+ break;
+ }
+ }
+ break;
+
+ case DSTATE:
+ clri(&idesc, "UNREF", 1);
+ break;
+
+ case DCLEAR:
+ dp = ginode(inumber);
+ if (dp->di_size == 0) {
+ clri(&idesc, "ZERO LENGTH", 1);
+ break;
+ }
+ /* fall through */
+ case FCLEAR:
+ clri(&idesc, "BAD/DUP", 1);
+ break;
+
+ case USTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[inumber], inumber);
+ }
+ }
+}
+
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass5.c b/sbin/fsck_ifs/pass5.c
new file mode 100644
index 0000000..2e98f96
--- /dev/null
+++ b/sbin/fsck_ifs/pass5.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <string.h>
+#include "fsck.h"
+
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ register struct fs *fs = &sblock;
+ register struct cg *cg = &cgrp;
+ daddr_t dbase, dmax;
+ register daddr_t d;
+ register long i, j;
+ struct csum *cs;
+ struct csum cstotal;
+ struct inodesc idesc[3];
+ char buf[MAXBSIZE];
+ register struct cg *newcg = (struct cg *)buf;
+ struct ocg *ocg = (struct ocg *)buf;
+
+ bzero((char *)newcg, (size_t)fs->fs_cgsize);
+ newcg->cg_niblk = fs->fs_ipg;
+ if (cvtlevel > 3) {
+ if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) {
+ if (preen)
+ pwarn("DELETING CLUSTERING MAPS\n");
+ if (preen || reply("DELETE CLUSTERING MAPS")) {
+ fs->fs_contigsumsize = 0;
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ if (fs->fs_maxcontig > 1) {
+ char *doit = 0;
+
+ if (fs->fs_contigsumsize < 1) {
+ doit = "CREAT";
+ } else if (fs->fs_contigsumsize < fs->fs_maxcontig &&
+ fs->fs_contigsumsize < FS_MAXCONTIG) {
+ doit = "EXPAND";
+ }
+ if (doit) {
+ i = fs->fs_contigsumsize;
+ fs->fs_contigsumsize =
+ MIN(fs->fs_maxcontig, FS_MAXCONTIG);
+ if (CGSIZE(fs) > fs->fs_bsize) {
+ pwarn("CANNOT %s CLUSTER MAPS\n", doit);
+ fs->fs_contigsumsize = i;
+ } else if (preen ||
+ reply("CREATE CLUSTER MAPS")) {
+ if (preen)
+ pwarn("%sING CLUSTER MAPS\n",
+ doit);
+ fs->fs_cgsize =
+ fragroundup(fs, CGSIZE(fs));
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ }
+ }
+ switch ((int)fs->fs_postblformat) {
+
+ case FS_42POSTBLFMT:
+ basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link);
+ sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]);
+ mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] -
+ (u_char *)&ocg->cg_iused[0];
+ ocg->cg_magic = CG_MAGIC;
+ savednrpos = fs->fs_nrpos;
+ fs->fs_nrpos = 8;
+ break;
+
+ case FS_DYNAMICPOSTBLFMT:
+ newcg->cg_btotoff =
+ &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ newcg->cg_boff =
+ newcg->cg_btotoff + fs->fs_cpg * sizeof(long);
+ newcg->cg_iusedoff = newcg->cg_boff +
+ fs->fs_cpg * fs->fs_nrpos * sizeof(short);
+ newcg->cg_freeoff =
+ newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY);
+ if (fs->fs_contigsumsize <= 0) {
+ newcg->cg_nextfreeoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY);
+ } else {
+ newcg->cg_clustersumoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) -
+ sizeof(long);
+ newcg->cg_clustersumoff =
+ roundup(newcg->cg_clustersumoff, sizeof(long));
+ newcg->cg_clusteroff = newcg->cg_clustersumoff +
+ (fs->fs_contigsumsize + 1) * sizeof(long);
+ newcg->cg_nextfreeoff = newcg->cg_clusteroff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY);
+ }
+ newcg->cg_magic = CG_MAGIC;
+ basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n",
+ fs->fs_postblformat);
+ }
+ bzero((char *)&idesc[0], sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ bzero((char *)&cstotal, sizeof(struct csum));
+ j = blknum(fs, fs->fs_size + fs->fs_frag - 1);
+ for (i = fs->fs_size; i < j; i++)
+ setbmap(i);
+ for (c = 0; c < fs->fs_ncg; c++) {
+ getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
+ if (!cg_chkmagic(cg))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+ dbase = cgbase(fs, c);
+ dmax = dbase + fs->fs_fpg;
+ if (dmax > fs->fs_size)
+ dmax = fs->fs_size;
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_cgx = c;
+ if (c == fs->fs_ncg - 1)
+ newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg;
+ else
+ newcg->cg_ncyl = fs->fs_cpg;
+ newcg->cg_ndblk = dmax - dbase;
+ if (fs->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = fs->fs_ipg;
+ if (cg->cg_rotor < newcg->cg_ndblk)
+ newcg->cg_rotor = cg->cg_rotor;
+ else
+ newcg->cg_rotor = 0;
+ if (cg->cg_frotor < newcg->cg_ndblk)
+ newcg->cg_frotor = cg->cg_frotor;
+ else
+ newcg->cg_frotor = 0;
+ if (cg->cg_irotor < newcg->cg_niblk)
+ newcg->cg_irotor = cg->cg_irotor;
+ else
+ newcg->cg_irotor = 0;
+ bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+ bzero((char *)&cg_blktot(newcg)[0],
+ (size_t)(sumsize + mapsize));
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ ocg->cg_magic = CG_MAGIC;
+ j = fs->fs_ipg * c;
+ for (i = 0; i < fs->fs_ipg; j++, i++) {
+ switch (statemap[j]) {
+
+ case USTATE:
+ break;
+
+ case DSTATE:
+ case DCLEAR:
+ case DFOUND:
+ newcg->cg_cs.cs_ndir++;
+ /* fall through */
+
+ case FSTATE:
+ case FCLEAR:
+ newcg->cg_cs.cs_nifree--;
+ setbit(cg_inosused(newcg), i);
+ break;
+
+ default:
+ if (j < ROOTINO)
+ break;
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[j], j);
+ }
+ }
+ if (c == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += fs->fs_frag, i += fs->fs_frag) {
+ frags = 0;
+ for (j = 0; j < fs->fs_frag; j++) {
+ if (testbmap(d + j))
+ continue;
+ setbit(cg_blksfree(newcg), i + j);
+ frags++;
+ }
+ if (frags == fs->fs_frag) {
+ newcg->cg_cs.cs_nbfree++;
+ j = cbtocylno(fs, i);
+ cg_blktot(newcg)[j]++;
+ cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++;
+ if (fs->fs_contigsumsize > 0)
+ setbit(cg_clustersfree(newcg),
+ i / fs->fs_frag);
+ } else if (frags > 0) {
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap(fs, cg_blksfree(newcg), i);
+ ffs_fragacct(fs, blk, newcg->cg_frsum, 1);
+ }
+ }
+ if (fs->fs_contigsumsize > 0) {
+ long *sump = cg_clustersum(newcg);
+ u_char *mapp = cg_clustersfree(newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+ cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+ cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+ cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+ cs = &fs->fs_cs(fs, c);
+ if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (bcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ bcopy(cg_inosused(newcg), cg_inosused(cg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 ||
+ bcmp((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ bcopy((char *)newcg, (char *)cg, (size_t)basesize);
+ bcopy((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs);
+ fs->fs_ronly = 0;
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+}
diff --git a/sbin/fsck_ifs/preen.c b/sbin/fsck_ifs/preen.c
new file mode 100644
index 0000000..005a65d
--- /dev/null
+++ b/sbin/fsck_ifs/preen.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1990, 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[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char *rawname(), *unrawname(), *blockcheck();
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted filesystem name */
+ long auxdata; /* auxillary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+char hotroot;
+
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen, maxrun;
+ int (*docheck)(), (*chkit)();
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+ if (name = blockcheck(fsp->fs_spec)) {
+ if (sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0))
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while (ret = startdisk(dk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while (ret = startdisk(nextdisk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (p = name + strlen(name) - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+ if (p < name)
+ len = strlen(name);
+
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+addpart(name, fsname, auxdata)
+ char *name, *fsname;
+ long auxdata;
+{
+ struct disk *dk = finddisk(name);
+ register struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)();
+{
+ register struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
+char *
+blockcheck(name)
+ char *name;
+{
+ struct stat stslash, stblock, stchar;
+ char *raw;
+ int retried = 0;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (0);
+ }
+retry:
+ if (stat(name, &stblock) < 0) {
+ perror(name);
+ printf("Can't stat %s\n", name);
+ return (0);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(name);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (name);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (name);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ name = unrawname(name);
+ retried++;
+ goto retry;
+ }
+ printf("Can't make sense out of name %s\n", name);
+ return (0);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/sbin/fsck_ifs/setup.c b/sbin/fsck_ifs/setup.c
new file mode 100644
index 0000000..a32b23c
--- /dev/null
+++ b/sbin/fsck_ifs/setup.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 1980, 1986, 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[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+struct disklabel *getdisklabel();
+
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ if (stat(dev, &statb) < 0) {
+ printf("Can't stat %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if ((statb.st_mode & S_IFMT) != S_IFCHR) {
+ pfatal("%s is not a character device", dev);
+ if (reply("CONTINUE") == 0)
+ return (0);
+ }
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ printf("Can't open %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (preen == 0)
+ printf("** %s", dev);
+ if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ fsmodified = 0;
+ lfdir = 0;
+ initbarea(&sblk);
+ initbarea(&asblk);
+ sblk.b_un.b_buf = malloc(SBSIZE);
+ asblk.b_un.b_buf = malloc(SBSIZE);
+ if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+ errexit("cannot allocate space for superblock\n");
+ if (lp = getdisklabel((char *)NULL, fsreadfd))
+ dev_bsize = secsize = lp->d_secsize;
+ else
+ dev_bsize = secsize = DEV_BSIZE;
+ /*
+ * Read in the superblock, looking for alternates if necessary
+ */
+ if (readsb(1) == 0) {
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+ return(0);
+ if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+ return (0);
+ for (cg = 0; cg < proto.fs_ncg; cg++) {
+ bflag = fsbtodb(&proto, cgsblock(&proto, cg));
+ if (readsb(0) != 0)
+ break;
+ }
+ if (cg >= proto.fs_ncg) {
+ printf("%s %s\n%s %s\n%s %s\n",
+ "SEARCH FOR ALTERNATE SUPER-BLOCK",
+ "FAILED. YOU MUST USE THE",
+ "-b OPTION TO FSCK TO SPECIFY THE",
+ "LOCATION OF AN ALTERNATE",
+ "SUPER-BLOCK TO SUPPLY NEEDED",
+ "INFORMATION; SEE fsck(8).");
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ }
+ maxfsblock = sblock.fs_size;
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ /*
+ * Check and potentially fix certain fields in the super block.
+ */
+ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
+ pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_optim = FS_OPTTIME;
+ sbdirty();
+ }
+ }
+ if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
+ pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
+ sblock.fs_minfree);
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_minfree = 10;
+ sbdirty();
+ }
+ }
+ if (sblock.fs_interleave < 1 ||
+ sblock.fs_interleave > sblock.fs_nsect) {
+ pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK",
+ sblock.fs_interleave);
+ sblock.fs_interleave = 1;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_npsect < sblock.fs_nsect ||
+ sblock.fs_npsect > sblock.fs_nsect*2) {
+ pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
+ sblock.fs_npsect);
+ sblock.fs_npsect = sblock.fs_nsect;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_inodefmt >= FS_44INODEFMT) {
+ newinofmt = 1;
+ } else {
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ newinofmt = 0;
+ }
+ /*
+ * Convert to new inode format.
+ */
+ if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW INODE FORMAT\n");
+ else if (!reply("CONVERT TO NEW INODE FORMAT"))
+ return(0);
+ doinglevel2++;
+ sblock.fs_inodefmt = FS_44INODEFMT;
+ sizepb = sblock.fs_bsize;
+ sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+ for (i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+ sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ sbdirty();
+ dirty(&asblk);
+ }
+ /*
+ * Convert to new cylinder group format.
+ */
+ if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n");
+ else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT"))
+ return(0);
+ doinglevel1++;
+ sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+ sblock.fs_nrpos = 8;
+ sblock.fs_postbloff =
+ (char *)(&sblock.fs_opostbl[0][0]) -
+ (char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty) {
+ bcopy((char *)&sblock, (char *)&altsblock,
+ (size_t)sblock.fs_sbsize);
+ flush(fswritefd, &asblk);
+ }
+ /*
+ * read in the summary info.
+ */
+ asked = 0;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ size = sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize;
+ sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size);
+ if (bread(fsreadfd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ size) != 0 && !asked) {
+ pfatal("BAD SUMMARY INFORMATION");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ asked++;
+ }
+ }
+ /*
+ * allocate and initialize the necessary maps
+ */
+ bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short));
+ blockmap = calloc((unsigned)bmapsize, sizeof (char));
+ if (blockmap == NULL) {
+ printf("cannot alloc %u bytes for blockmap\n",
+ (unsigned)bmapsize);
+ goto badsb;
+ }
+ statemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (statemap == NULL) {
+ printf("cannot alloc %u bytes for statemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ typemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (typemap == NULL) {
+ printf("cannot alloc %u bytes for typemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short));
+ if (lncntp == NULL) {
+ printf("cannot alloc %u bytes for lncntp\n",
+ (unsigned)(maxino + 1) * sizeof(short));
+ goto badsb;
+ }
+ numdirs = sblock.fs_cstotal.cs_ndir;
+ inplast = 0;
+ listmax = numdirs + 10;
+ inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+ sizeof(struct inoinfo *));
+ inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+ sizeof(struct inoinfo *));
+ if (inpsort == NULL || inphead == NULL) {
+ printf("cannot alloc %u bytes for inphead\n",
+ (unsigned)numdirs * sizeof(struct inoinfo *));
+ goto badsb;
+ }
+ bufinit();
+ return (1);
+
+badsb:
+ ckfini();
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+readsb(listerr)
+ int listerr;
+{
+ daddr_t super = bflag ? bflag : SBOFF / dev_bsize;
+
+ if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0)
+ return (0);
+ sblk.b_bno = super;
+ sblk.b_size = SBSIZE;
+ /*
+ * run a few consistency checks of the super block
+ */
+ if (sblock.fs_magic != FS_MAGIC)
+ { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); }
+ if (sblock.fs_ncg < 1)
+ { badsb(listerr, "NCG OUT OF RANGE"); return (0); }
+ if (sblock.fs_cpg < 1)
+ { badsb(listerr, "CPG OUT OF RANGE"); return (0); }
+ if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
+ (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
+ { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); }
+ if (sblock.fs_sbsize > SBSIZE)
+ { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); }
+ /*
+ * Compute block size that the filesystem is based on,
+ * according to fsbtodb, and adjust superblock block number
+ * so we can tell if this is an alternate later.
+ */
+ super *= dev_bsize;
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ sblk.b_bno = super / dev_bsize;
+ if (bflag) {
+ havesb = 1;
+ return (1);
+ }
+ /*
+ * Set all possible fields that could differ, then do check
+ * of whole super block against an alternate super block.
+ * When an alternate super-block is specified this check is skipped.
+ */
+ getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
+ if (asblk.b_errs)
+ return (0);
+ altsblock.fs_link = sblock.fs_link;
+ altsblock.fs_rlink = sblock.fs_rlink;
+ altsblock.fs_time = sblock.fs_time;
+ altsblock.fs_cstotal = sblock.fs_cstotal;
+ altsblock.fs_cgrotor = sblock.fs_cgrotor;
+ altsblock.fs_fmod = sblock.fs_fmod;
+ altsblock.fs_clean = sblock.fs_clean;
+ altsblock.fs_ronly = sblock.fs_ronly;
+ altsblock.fs_flags = sblock.fs_flags;
+ altsblock.fs_maxcontig = sblock.fs_maxcontig;
+ altsblock.fs_minfree = sblock.fs_minfree;
+ altsblock.fs_optim = sblock.fs_optim;
+ altsblock.fs_rotdelay = sblock.fs_rotdelay;
+ altsblock.fs_maxbpg = sblock.fs_maxbpg;
+ bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp,
+ sizeof sblock.fs_csp);
+ bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt,
+ sizeof sblock.fs_fsmnt);
+ bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon,
+ sizeof sblock.fs_sparecon);
+ /*
+ * The following should not have to be copied.
+ */
+ altsblock.fs_fsbtodb = sblock.fs_fsbtodb;
+ altsblock.fs_interleave = sblock.fs_interleave;
+ altsblock.fs_npsect = sblock.fs_npsect;
+ altsblock.fs_nrpos = sblock.fs_nrpos;
+ altsblock.fs_qbmask = sblock.fs_qbmask;
+ altsblock.fs_qfmask = sblock.fs_qfmask;
+ altsblock.fs_state = sblock.fs_state;
+ altsblock.fs_maxfilesize = sblock.fs_maxfilesize;
+ if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) {
+ badsb(listerr,
+ "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+ return (0);
+ }
+ havesb = 1;
+ return (1);
+}
+
+badsb(listerr, s)
+ int listerr;
+ char *s;
+{
+
+ if (!listerr)
+ return;
+ if (preen)
+ printf("%s: ", cdevname);
+ pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = index(dev, '\0') - 1;
+ if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ bzero((char *)fs, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_cpg = pp->p_cpg;
+ fs->fs_size = pp->p_size;
+ fs->fs_ntrak = lp->d_ntracks;
+ fs->fs_nsect = lp->d_nsectors;
+ fs->fs_spc = lp->d_secpercyl;
+ fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ fs->fs_cgmask = 0xffffffff;
+ for (i = fs->fs_ntrak; i > 1; i >>= 1)
+ fs->fs_cgmask <<= 1;
+ if (!POWEROF2(fs->fs_ntrak))
+ fs->fs_cgmask <<= 1;
+ fs->fs_cgoffset = roundup(
+ howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+ fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+ fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+ for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ifs/utilities.c b/sbin/fsck_ifs/utilities.c
new file mode 100644
index 0000000..64a4cac
--- /dev/null
+++ b/sbin/fsck_ifs/utilities.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1980, 1986, 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.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+ftypeok(dp)
+ struct dinode *dp;
+{
+ switch (dp->di_mode & IFMT) {
+
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ return (1);
+
+ default:
+ if (debug)
+ printf("bad file type 0%o\n", dp->di_mode);
+ return (0);
+ }
+}
+
+reply(question)
+ char *question;
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ pfatal("INTERNAL ERROR: GOT TO reply()");
+ persevere = !strcmp(question, "CONTINUE");
+ printf("\n");
+ if (!persevere && (nflag || fswritefd < 0)) {
+ printf("%s? no\n\n", question);
+ return (0);
+ }
+ if (yflag || (persevere && nflag)) {
+ printf("%s? yes\n\n", question);
+ return (1);
+ }
+ do {
+ printf("%s? [yn] ", question);
+ (void) fflush(stdout);
+ c = getc(stdin);
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ printf("\n");
+ if (c == 'y' || c == 'Y')
+ return (1);
+ return (0);
+}
+
+/*
+ * Malloc buffers and set up cache.
+ */
+bufinit()
+{
+ register struct bufarea *bp;
+ long bufcnt, i;
+ char *bufp;
+
+ pbp = pdirbp = (struct bufarea *)0;
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bufp == 0)
+ errexit("cannot allocate buffer pool\n");
+ cgblk.b_un.b_buf = bufp;
+ initbarea(&cgblk);
+ bufhead.b_next = bufhead.b_prev = &bufhead;
+ bufcnt = MAXBUFSPACE / sblock.fs_bsize;
+ if (bufcnt < MINBUFS)
+ bufcnt = MINBUFS;
+ for (i = 0; i < bufcnt; i++) {
+ bp = (struct bufarea *)malloc(sizeof(struct bufarea));
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ if (i >= MINBUFS)
+ break;
+ errexit("cannot allocate buffer pool\n");
+ }
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ initbarea(bp);
+ }
+ bufhead.b_size = i; /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+ register struct bufarea *bp;
+
+ for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+ if (bp->b_bno == fsbtodb(&sblock, blkno))
+ goto foundit;
+ for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+ if ((bp->b_flags & B_INUSE) == 0)
+ break;
+ if (bp == &bufhead)
+ errexit("deadlocked buffer pool\n");
+ getblk(bp, blkno, size);
+ /* fall through */
+foundit:
+ totalreads++;
+ bp->b_prev->b_next = bp->b_next;
+ bp->b_next->b_prev = bp->b_prev;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ bp->b_flags |= B_INUSE;
+ return (bp);
+}
+
+void
+getblk(bp, blk, size)
+ register struct bufarea *bp;
+ daddr_t blk;
+ long size;
+{
+ daddr_t dblk;
+
+ dblk = fsbtodb(&sblock, blk);
+ if (bp->b_bno != dblk) {
+ flush(fswritefd, bp);
+ diskreads++;
+ bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ bp->b_bno = dblk;
+ bp->b_size = size;
+ }
+}
+
+flush(fd, bp)
+ int fd;
+ register struct bufarea *bp;
+{
+ register int i, j;
+
+ if (!bp->b_dirty)
+ return;
+ if (bp->b_errs != 0)
+ pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+ (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+ bp->b_bno);
+ bp->b_dirty = 0;
+ bp->b_errs = 0;
+ bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ if (bp != &sblk)
+ return;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ bwrite(fswritefd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize);
+ }
+}
+
+rwerror(mesg, blk)
+ char *mesg;
+ daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ errexit("Program terminated\n");
+}
+
+ckfini()
+{
+ register struct bufarea *bp, *nbp;
+ int cnt = 0;
+
+ if (fswritefd < 0) {
+ (void)close(fsreadfd);
+ return;
+ }
+ flush(fswritefd, &sblk);
+ if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+ !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+ sblk.b_bno = SBOFF / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ flush(fswritefd, &cgblk);
+ free(cgblk.b_un.b_buf);
+ for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+ cnt++;
+ flush(fswritefd, bp);
+ nbp = bp->b_prev;
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ if (bufhead.b_size != cnt)
+ errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (debug)
+ printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+ totalreads, (int)(diskreads * 100 / totalreads));
+ (void)close(fsreadfd);
+ (void)close(fswritefd);
+}
+
+bread(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ char *cp;
+ int i, errs;
+ off_t offset;
+
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (read(fd, buf, (int)size) == size)
+ return (0);
+ rwerror("READ", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ errs = 0;
+ bzero(buf, (size_t)size);
+ printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+ for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+ if (read(fd, cp, (int)secsize) != secsize) {
+ (void)lseek(fd, offset + i + secsize, 0);
+ if (secsize != dev_bsize && dev_bsize != 1)
+ printf(" %ld (%ld),",
+ (blk * dev_bsize + i) / secsize,
+ blk + i / dev_bsize);
+ else
+ printf(" %ld,", blk + i / dev_bsize);
+ errs++;
+ }
+ }
+ printf("\n");
+ return (errs);
+}
+
+bwrite(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ int i;
+ char *cp;
+ off_t offset;
+
+ if (fd < 0)
+ return;
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (write(fd, buf, (int)size) == size) {
+ fsmodified = 1;
+ return;
+ }
+ rwerror("WRITE", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+ for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+ if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ (void)lseek(fd, offset + i + dev_bsize, 0);
+ printf(" %ld,", blk + i / dev_bsize);
+ }
+ printf("\n");
+ return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+allocblk(frags)
+ long frags;
+{
+ register int i, j, k;
+
+ if (frags <= 0 || frags > sblock.fs_frag)
+ return (0);
+ for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (testbmap(i + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (testbmap(i + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ for (k = 0; k < frags; k++)
+ setbmap(i + j + k);
+ n_blks += frags;
+ return (i + j);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Free a previously allocated block
+ */
+freeblk(blkno, frags)
+ daddr_t blkno;
+ long frags;
+{
+ struct inodesc idesc;
+
+ idesc.id_blkno = blkno;
+ idesc.id_numfrags = frags;
+ (void)pass4check(&idesc);
+}
+
+/*
+ * Find a pathname
+ */
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+ extern int findname();
+
+ if (curdir == ino && ino == ROOTINO) {
+ (void)strcpy(namebuf, "/");
+ return;
+ }
+ if (busy ||
+ (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
+ (void)strcpy(namebuf, "?");
+ return;
+ }
+ busy = 1;
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ cp = &namebuf[MAXPATHLEN - 1];
+ *cp = '\0';
+ if (curdir != ino) {
+ idesc.id_parent = curdir;
+ goto namelookup;
+ }
+ while (ino != ROOTINO) {
+ idesc.id_number = ino;
+ idesc.id_func = findino;
+ idesc.id_name = "..";
+ if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ break;
+ namelookup:
+ idesc.id_number = idesc.id_parent;
+ idesc.id_parent = ino;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ break;
+ len = strlen(namebuf);
+ cp -= len;
+ bcopy(namebuf, cp, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch()
+{
+ if (!doinglevel2)
+ ckfini();
+ exit(12);
+}
+
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit()
+{
+ extern returntosingle;
+
+ printf("returning to single-user after filesystem check\n");
+ returntosingle = 1;
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+void
+voidquit()
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+dofix(idesc, msg)
+ register struct inodesc *idesc;
+ char *msg;
+{
+
+ switch (idesc->id_fix) {
+
+ case DONTKNOW:
+ if (idesc->id_type == DATA)
+ direrror(idesc->id_number, msg);
+ else
+ pwarn(msg);
+ if (preen) {
+ printf(" (SALVAGED)\n");
+ idesc->id_fix = FIX;
+ return (ALTERED);
+ }
+ if (reply("SALVAGE") == 0) {
+ idesc->id_fix = NOFIX;
+ return (0);
+ }
+ idesc->id_fix = FIX;
+ return (ALTERED);
+
+ case FIX:
+ return (ALTERED);
+
+ case NOFIX:
+ case IGNORE:
+ return (0);
+
+ default:
+ errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+errexit(s1, s2, s3, s4)
+ char *s1;
+{
+ printf(s1, s2, s3, s4);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+pfatal(s, a1, a2, a3)
+ char *s;
+{
+
+ if (preen) {
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ printf(s, a1, a2, a3);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+pwarn(s, a1, a2, a3, a4, a5, a6)
+ char *s;
+{
+
+ if (preen)
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3, a4, a5, a6);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+panic(s)
+ char *s;
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(s);
+}
+#endif
diff --git a/sbin/mount_ifs/Makefile b/sbin/mount_ifs/Makefile
new file mode 100644
index 0000000..b3efd4e
--- /dev/null
+++ b/sbin/mount_ifs/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.5 (Berkeley) 3/27/94
+
+PROG= mount
+SRCS= mount.c mount_ufs.c getmntopts.c
+MAN8= mount.0
+# We do NOT install the getmntopts.3 man page.
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ifs/getmntopts.3 b/sbin/mount_ifs/getmntopts.3
new file mode 100644
index 0000000..642c57a
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.3
@@ -0,0 +1,163 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)getmntopts.3 8.1 (Berkeley) 3/27/94
+.\"
+.Dd March 27, 1994
+.Dt GETMNTOPTS 3
+.Os BSD 4.4
+.Sh NAME
+.Nm getmntopts
+.Nd scan mount options
+.Sh SYNOPSIS
+.Fd #include <mntopts.h>
+.Ft void
+.Fn getmntopts "char *options" "struct mntopt *mopts" "int *flagp"
+.Sh DESCRIPTION
+The
+.Nm getmntopts
+function takes a comma separated option list and a list
+of valid option names, and computes the bitmask
+corresponding to the requested set of options.
+.Pp
+The string
+.Dv options
+is broken down into a sequence of comma separated tokens.
+Each token is looked up in the table described by
+.Dv mopts
+and the bits in
+the word referenced by
+.Dv flagp
+are updated.
+The flag word is not initialized by
+.Nm getmntopt .
+The table,
+.Dv mopts ,
+has the following format:
+.Bd -literal
+struct mntopt {
+ char *m_option; /* option name */
+ int m_inverse; /* is this a negative option, eg "dev" */
+ int m_flag; /* bit to set, eg MNT_RDONLY */
+};
+.Ed
+.Pp
+The members of this structure are:
+.Bl -tag -width m_inverse
+.It Fa m_option
+the option name,
+for example
+.Dq suid .
+.It Fa m_inverse
+tells
+.Nm getmntopts
+that the name has the inverse meaning of the
+bit.
+For example,
+.Dq suid
+is the string, whereas the
+mount flag is
+.Dv MNT_NOSUID .
+In this case, the sense of the string and the flag
+are inverted, so the
+.Dv m_inverse
+flag should be set.
+.It Fa m_flag
+the value of the bit to be set or cleared in
+the flag word when the option is recognized.
+The bit is set when the option is discovered,
+but cleared if the option name was preceded
+by the letters
+.Dq no .
+The
+.Dv m_inverse
+flag causes these two operations to be reversed.
+.El
+.Pp
+Each of the user visible
+.Dv MNT_
+flags has a corresponding
+.Dv MOPT_
+macro which defines an appropriate
+.Li "struct mntopt"
+entry.
+To simplify the program interface and ensure consistency across all
+programs, a general purpose macro,
+.Dv MOPT_STDOPTS ,
+is defined which
+contains an entry for all the generic VFS options.
+In addition, the macros
+.Dv MOPT_FORCE
+and
+.Dv MOPT_UPDATE
+exist to enable the
+.Dv MNT_FORCE
+and
+.Dv MNT_UPDATE
+flags to be set.
+Finally, the table must be terminated by an entry with a NULL
+first element.
+.Sh EXAMPLES
+Most commands will use the standard option set.
+Local filesystems which support the
+.Dv MNT_UPDATE
+flag, would also have an
+.Dv MOPT_UPDATE
+entry.
+This can be declared and used as follows:
+.Bd -literal
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+ ...
+ mntflags = 0;
+ ...
+ getmntopts(options, mopts, &mntflags)
+ ...
+.Ed
+.Sh DIAGNOSTICS
+The
+.Nm getmntopts
+function displays an error message and exits if an
+unrecognized option is encountered.
+.Sh SEE ALSO
+.Xr err 3 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Fn getmntopts
+function appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_ifs/getmntopts.c b/sbin/mount_ifs/getmntopts.c
new file mode 100644
index 0000000..2c84ae5
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 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 sccsid[] = "@(#)getmntopts.c 8.1 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mntopts.h"
+
+void
+getmntopts(options, m0, flagp)
+ const char *options;
+ const struct mntopt *m0;
+ int *flagp;
+{
+ const struct mntopt *m;
+ int negative;
+ char *opt, *optbuf;
+
+ /* Copy option string, since it is about to be torn asunder... */
+ if ((optbuf = strdup(options)) == NULL)
+ err(1, NULL);
+
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ /* Check for "no" prefix. */
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ negative = 1;
+ opt += 2;
+ } else
+ negative = 0;
+
+ /* Scan option table. */
+ for (m = m0; m->m_option != NULL; ++m)
+ if (strcasecmp(opt, m->m_option) == 0)
+ break;
+
+ /* Save flag, or fail if option is not recognised. */
+ if (m->m_option) {
+ if (negative == m->m_inverse)
+ *flagp |= m->m_flag;
+ else
+ *flagp &= ~m->m_flag;
+ } else
+ errx(1, "-o %s: option not supported", opt);
+ }
+
+ free(optbuf);
+}
diff --git a/sbin/mount_ifs/mntopts.h b/sbin/mount_ifs/mntopts.h
new file mode 100644
index 0000000..a89f63d
--- /dev/null
+++ b/sbin/mount_ifs/mntopts.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)mntopts.h 8.3 (Berkeley) 3/27/94
+ */
+
+struct mntopt {
+ const char *m_option; /* option name */
+ int m_inverse; /* if a negative option, eg "dev" */
+ int m_flag; /* bit to set, eg. MNT_RDONLY */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC }
+#define MOPT_NODEV { "dev", 1, MNT_NODEV }
+#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC }
+#define MOPT_NOSUID { "suid", 1, MNT_NOSUID }
+#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY }
+#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS }
+#define MOPT_UNION { "union", 0, MNT_UNION }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 1, MNT_FORCE }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE }
+
+/* Support for old-style "ro", "rw" flags. */
+#define MOPT_RO { "ro", 0, MNT_RDONLY }
+#define MOPT_RW { "rw", 1, MNT_RDONLY }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NODEV, \
+ MOPT_NOEXEC, \
+ MOPT_NOSUID, \
+ MOPT_RDONLY, \
+ MOPT_UNION
+
+void getmntopts __P((const char *, const struct mntopt *, int *));
diff --git a/sbin/mount_ifs/mount.8 b/sbin/mount_ifs/mount.8
new file mode 100644
index 0000000..e2c53c8
--- /dev/null
+++ b/sbin/mount_ifs/mount.8
@@ -0,0 +1,264 @@
+.\" Copyright (c) 1980, 1989, 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.
+.\"
+.\" @(#)mount.8 8.7 (Berkeley) 3/27/94
+.\"
+.Dd March 27, 1994
+.Dt MOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm mount
+.Nd mount file systems
+.Sh SYNOPSIS
+.Nm mount
+.Op Fl adfruvw
+.Op Fl t Ar ufs | lfs | external_type
+.Nm mount
+.Op Fl dfruvw
+.Ar special | node
+.Nm mount
+.Op Fl dfruvw
+.Op Fl o Ar options
+.Op Fl t Ar ufs | lfs | external_type
+.Ar special node
+.Sh DESCRIPTION
+The
+.Nm mount
+command
+calls the
+.Xr mount 2
+system call to prepare and graft a
+.Ar "special device"
+or the remote node (rhost:path) on to the file system tree at the point
+.Ar node .
+If either
+.Ar special
+or
+.Ar node
+are not provided, the appropriate information is taken from the
+.Xr fstab 5
+file.
+.Pp
+The system maintains a list of currently mounted file systems.
+If no arguments are given to
+.Nm mount,
+this list is printed.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Causes everything to be done except for the actual system call.
+This option is useful in conjunction with the
+.Fl v
+flag to
+determine what the
+.Nm mount
+command is trying to do.
+.It Fl f
+Forces the revocation of write access when trying to downgrade
+a filesystem mount status from read-write to read-only.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+The following options are available:
+.Bl -tag -width indent
+.It async
+All
+.Tn I/O
+to the file system should be done asynchronously.
+This is a
+.Em dangerous
+flag to set,
+and should not be used unless you are prepared to recreate the file
+system should your system crash.
+.It force
+The same as
+.Fl f ;
+forces the revocation of write access when trying to downgrade
+a filesystem mount status from read-write to read-only.
+.It nodev
+Do not interpret character or block special devices on the file system.
+This option is useful for a server that has file systems containing
+special devices for architectures other than its own.
+.It noexec
+Do not allow execution of any binaries on the mounted file system.
+This option is useful for a server that has file systems containing
+binaries for architectures other than its own.
+.It nosuid
+Do not allow set-user-identifier or set-group-identifier bits to take effect.
+.It rdonly
+The same as
+.Fl r ;
+mount the file system read-only (even the super-user may not write it).
+.It sync
+All
+.Tn I/O
+to the file system should be done synchronously.
+.It update
+The same as
+.Fl u ;
+indicate that the status of an already mounted file system should be changed.
+.It union
+Causes the namespace at the mount point to appear as the union
+of the mounted filesystem root and the existing directory.
+Lookups will be done in the mounted filesystem first.
+If those operations fail due to a non-existent file the underlying
+directory is then accessed.
+All creates are done in the mounted filesystem.
+.El
+.Pp
+Any additional options specific to a filesystem type that is not
+one of the internally known types (see the
+.Fl t
+option) may be passed as a comma separated list; these options are
+distinguished by a leading
+.Dq \&-
+(dash).
+Options that take a value are specified using the syntax -option=value.
+For example, the mount command:
+.Bd -literal -offset indent
+mount -t mfs -o nosuid,-N,-s=4000 /dev/dk0b /tmp
+.Ed
+.Pp
+causes
+.Nm mount
+to execute the equivalent of:
+.Bd -literal -offset indent
+/sbin/mount_mfs -o nosuid -N -s 4000 /dev/dk0b /tmp
+.Ed
+.It Fl r
+The file system is to be mounted read-only.
+Mount the file system read-only (even the super-user may not write it).
+The same as the
+.Dq rdonly
+argument to the
+.Fl o
+option.
+.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type"
+The argument following the
+.Fl t
+is used to indicate the file system type.
+The type
+.Ar ufs
+is the default.
+The \fI-t\fP option can be used
+to indicate that the actions should only be taken on
+filesystems of the specified type.
+More than one type may be specified in a comma separated list.
+The list of filesystem types can be prefixed with
+.Dq no
+to specify the filesystem types for which action should
+.Em not
+be taken.
+For example, the
+.Nm mount
+command:
+.Bd -literal -offset indent
+mount -a -t nonfs,mfs
+.Ed
+.Pp
+mounts all filesystems except those of type
+.Tn NFS
+and
+.Tn MFS .
+.Pp
+If the type is not one of the internally known types,
+mount will attempt to execute a program in
+.Pa /sbin/mount_ Ns Em XXX
+where
+.Em XXX
+is replaced by the type name.
+For example, nfs filesystems are mounted by the program
+.Pa /sbin/mount_nfs .
+.It Fl u
+The
+.Fl u
+flag indicates that the status of an already mounted file
+system should be changed.
+Any of the options discussed above (the
+.Fl o
+option)
+may be changed;
+also a file system can be changed from read-only to read-write
+or vice versa.
+An attempt to change from read-write to read-only will fail if any
+files on the filesystem are currently open for writing unless the
+.Fl f
+flag is also specified.
+The set of options is determined by first extracting the options
+for the file system from the
+.Xr fstab
+table,
+then applying any options specified by the
+.Fl o
+argument,
+and finally applying the
+.Fl r
+or
+.Fl w
+option.
+.It Fl v
+Verbose mode.
+.It Fl w
+The file system object is to be read and write.
+.Pp
+The options specific to NFS filesystems are described in the
+.Xr mount_nfs 8
+manual page.
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+file system table
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr fstab 5 ,
+.Xr mount_cd9660 8 ,
+.Xr mount_fdesc 8 ,
+.Xr mount_kernfs 8 ,
+.Xr mount_lfs 8 ,
+.Xr mount_lofs 8 ,
+.Xr mount_mfs 8 ,
+.Xr mount_nfs 8 ,
+.Xr mount_null 8 ,
+.Xr mount_portal 8 ,
+.Xr mount_procfs 8 ,
+.Xr mount_umap 8 ,
+.Xr mount_union 8 ,
+.Xr umount 8
+.Sh BUGS
+It is possible for a corrupted file system to cause a crash.
+.Sh HISTORY
+A
+.Nm mount
+command appeared in
+.At v6 .
diff --git a/sbin/mount_ifs/mount.c b/sbin/mount_ifs/mount.c
new file mode 100644
index 0000000..f0ff782
--- /dev/null
+++ b/sbin/mount_ifs/mount.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 1980, 1989, 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, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount.c 8.19 (Berkeley) 4/19/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+int debug, verbose, skipvfs;
+
+int badvfsname __P((const char *, const char **));
+int badvfstype __P((int, const char **));
+char *catopt __P((char *, const char *));
+struct statfs
+ *getmntpt __P((const char *));
+const char
+ **makevfslist __P((char *));
+void mangle __P((char *, int *, const char **));
+int mountfs __P((const char *, const char *, const char *,
+ int, const char *, const char *));
+void prmount __P((const char *, const char *, int));
+void usage __P((void));
+
+/* From mount_ufs.c. */
+int mount_ufs __P((int, char * const *));
+
+/* Map from mount otions to printable formats. */
+static struct opt {
+ int o_opt;
+ const char *o_name;
+} optnames[] = {
+ { MNT_ASYNC, "asynchronous" },
+ { MNT_EXPORTED, "NFS exported" },
+ { MNT_LOCAL, "local" },
+ { MNT_NODEV, "nodev" },
+ { MNT_NOEXEC, "noexec" },
+ { MNT_NOSUID, "nosuid" },
+ { MNT_QUOTA, "with quotas" },
+ { MNT_RDONLY, "read-only" },
+ { MNT_SYNCHRONOUS, "synchronous" },
+ { MNT_UNION, "union" },
+ { MNT_USER, "user mount" },
+ { NULL }
+};
+
+int
+main(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ const char *mntonname, **vfslist, *vfstype;
+ struct fstab *fs;
+ struct statfs *mntbuf;
+ FILE *mountdfp;
+ pid_t pid;
+ int all, ch, i, init_flags, mntsize, rval;
+ char *options;
+
+ all = init_flags = 0;
+ options = NULL;
+ vfslist = NULL;
+ vfstype = "ufs";
+ while ((ch = getopt(argc, argv, "adfo:rwt:uv")) != EOF)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ init_flags |= MNT_FORCE;
+ break;
+ case 'o':
+ if (*optarg)
+ options = catopt(options, optarg);
+ break;
+ case 'r':
+ init_flags |= MNT_RDONLY;
+ break;
+ case 't':
+ if (vfslist != NULL)
+ errx(1, "only one -t option may be specified.");
+ vfslist = makevfslist(optarg);
+ vfstype = optarg;
+ break;
+ case 'u':
+ init_flags |= MNT_UPDATE;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ init_flags &= ~MNT_RDONLY;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BADTYPE(type) \
+ (strcmp(type, FSTAB_RO) && \
+ strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
+
+ rval = 0;
+ switch (argc) {
+ case 0:
+ if (all)
+ while ((fs = getfsent()) != NULL) {
+ if (BADTYPE(fs->fs_type))
+ continue;
+ if (badvfsname(fs->fs_vfstype, vfslist))
+ continue;
+ if (mountfs(fs->fs_vfstype, fs->fs_spec,
+ fs->fs_file, init_flags, options,
+ fs->fs_mntops))
+ rval = 1;
+ }
+ else {
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (badvfstype(mntbuf[i].f_type, vfslist))
+ continue;
+ prmount(mntbuf[i].f_mntfromname,
+ mntbuf[i].f_mntonname, mntbuf[i].f_flags);
+ }
+ }
+ exit(rval);
+ case 1:
+ if (vfslist != NULL)
+ usage();
+
+ if (init_flags & MNT_UPDATE) {
+ if ((mntbuf = getmntpt(*argv)) == NULL)
+ errx(1,
+ "unknown special file or file system %s.",
+ *argv);
+ if ((fs = getfsfile(mntbuf->f_mntonname)) == NULL)
+ errx(1, "can't find fstab entry for %s.",
+ *argv);
+ /* If it's an update, ignore the fstab file options. */
+ fs->fs_mntops = NULL;
+ mntonname = mntbuf->f_mntonname;
+ } else {
+ if ((fs = getfsfile(*argv)) == NULL &&
+ (fs = getfsspec(*argv)) == NULL)
+ errx(1,
+ "%s: unknown special file or file system.",
+ *argv);
+ if (BADTYPE(fs->fs_type))
+ errx(1, "%s has unknown file system type.",
+ *argv);
+ mntonname = fs->fs_file;
+ }
+ rval = mountfs(fs->fs_vfstype, fs->fs_spec,
+ mntonname, init_flags, options, fs->fs_mntops);
+ break;
+ case 2:
+ /*
+ * If -t flag has not been specified, and spec contains either
+ * a ':' or a '@' then assume that an NFS filesystem is being
+ * specified ala Sun.
+ */
+ if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL)
+ vfstype = "nfs";
+ rval = mountfs(vfstype,
+ argv[0], argv[1], init_flags, options, NULL);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * If the mount was successfully, and done by root, tell mountd the
+ * good news. Pid checks are probably unnecessary, but don't hurt.
+ */
+ if (rval == 0 && getuid() == 0 &&
+ (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
+ if (fscanf(mountdfp, "%ld", &pid) == 1 &&
+ pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
+ err(1, "signal mountd");
+ (void)fclose(mountdfp);
+ }
+
+ exit(rval);
+}
+
+int
+mountfs(vfstype, spec, name, flags, options, mntopts)
+ const char *vfstype, *spec, *name, *options, *mntopts;
+ int flags;
+{
+ /* List of directories containing mount_xxx subcommands. */
+ static const char *edirs[] = {
+ _PATH_SBIN,
+ _PATH_USRSBIN,
+ NULL
+ };
+ const char *argv[100], **edir;
+ struct statfs sf;
+ pid_t pid;
+ int argc, i, status;
+ char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN];
+
+ if (realpath(name, mntpath) == NULL) {
+ warn("%s", mntpath);
+ return (1);
+ }
+
+ name = mntpath;
+
+ if (options == NULL) {
+ if (mntopts == NULL || *mntopts == '\0')
+ options = "rw";
+ else
+ options = mntopts;
+ mntopts = "";
+ }
+ optbuf = catopt(strdup(mntopts), options);
+
+ if (strcmp(name, "/") == 0)
+ flags |= MNT_UPDATE;
+ if (flags & MNT_FORCE)
+ optbuf = catopt(optbuf, "force");
+ if (flags & MNT_RDONLY)
+ optbuf = catopt(optbuf, "ro");
+ /*
+ * XXX
+ * The mount_mfs (newfs) command uses -o to select the
+ * optimisation mode. We don't pass the default "-o rw"
+ * for that reason.
+ */
+ if (flags & MNT_UPDATE)
+ optbuf = catopt(optbuf, "update");
+
+ argc = 0;
+ argv[argc++] = vfstype;
+ mangle(optbuf, &argc, argv);
+ argv[argc++] = spec;
+ argv[argc++] = name;
+ argv[argc] = NULL;
+
+ if (debug) {
+ (void)printf("exec: mount_%s", vfstype);
+ for (i = 1; i < argc; i++)
+ (void)printf(" %s", argv[i]);
+ (void)printf("\n");
+ return (0);
+ }
+
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ warn("vfork");
+ free(optbuf);
+ return (1);
+ case 0: /* Child. */
+ if (strcmp(vfstype, "ufs") == 0)
+ exit(mount_ufs(argc, (char * const *) argv));
+
+ /* Go find an executable. */
+ edir = edirs;
+ do {
+ (void)snprintf(execname,
+ sizeof(execname), "%s/mount_%s", *edir, vfstype);
+ execv(execname, (char * const *)argv);
+ if (errno != ENOENT)
+ warn("exec %s for %s", execname, name);
+ } while (*++edir != NULL);
+
+ if (errno == ENOENT)
+ warn("exec %s for %s", execname, name);
+ exit(1);
+ /* NOTREACHED */
+ default: /* Parent. */
+ free(optbuf);
+
+ if (waitpid(pid, &status, 0) < 0) {
+ warn("waitpid");
+ return (1);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ return (WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]);
+ return (1);
+ }
+
+ if (verbose) {
+ if (statfs(name, &sf) < 0) {
+ warn("%s", name);
+ return (1);
+ }
+ prmount(sf.f_mntfromname, sf.f_mntonname, sf.f_flags);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+void
+prmount(spec, name, flags)
+ const char *spec, *name;
+ int flags;
+{
+ struct opt *o;
+ int f;
+
+ (void)printf("%s on %s", spec, name);
+
+ flags &= MNT_VISFLAGMASK;
+ for (f = 0, o = optnames; flags && o->o_opt; o++)
+ if (flags & o->o_opt) {
+ (void)printf("%s%s", !f++ ? " (" : ", ", o->o_name);
+ flags &= ~o->o_opt;
+ }
+ (void)printf(f ? ")\n" : "\n");
+}
+
+struct statfs *
+getmntpt(name)
+ const char *name;
+{
+ struct statfs *mntbuf;
+ int i, mntsize;
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++)
+ if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
+ strcmp(mntbuf[i].f_mntonname, name) == 0)
+ return (&mntbuf[i]);
+ return (NULL);
+}
+
+int
+badvfsname(vfsname, vfslist)
+ const char *vfsname;
+ const char **vfslist;
+{
+
+ if (vfslist == NULL)
+ return (0);
+ while (*vfslist != NULL) {
+ if (strcmp(vfsname, *vfslist) == 0)
+ return (skipvfs);
+ ++vfslist;
+ }
+ return (!skipvfs);
+}
+
+int
+badvfstype(vfstype, vfslist)
+ int vfstype;
+ const char **vfslist;
+{
+static const char *vfsnames[] = INITMOUNTNAMES;
+
+ if ((vfstype < 0) || (vfstype > MOUNT_MAXTYPE))
+ return (0);
+
+ return (badvfsname(vfsnames[vfstype], vfslist));
+}
+
+const char **
+makevfslist(fslist)
+ char *fslist;
+{
+ const char **av;
+ int i;
+ char *nextcp;
+
+ if (fslist == NULL)
+ return (NULL);
+ if (fslist[0] == 'n' && fslist[1] == 'o') {
+ fslist += 2;
+ skipvfs = 1;
+ }
+ for (i = 0, nextcp = fslist; *nextcp; nextcp++)
+ if (*nextcp == ',')
+ i++;
+ if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
+ warn(NULL);
+ return (NULL);
+ }
+ nextcp = fslist;
+ i = 0;
+ av[i++] = nextcp;
+ while ((nextcp = strchr(nextcp, ',')) != NULL) {
+ *nextcp++ = '\0';
+ av[i++] = nextcp;
+ }
+ av[i++] = NULL;
+ return (av);
+}
+
+char *
+catopt(s0, s1)
+ char *s0;
+ const char *s1;
+{
+ size_t i;
+ char *cp;
+
+ if (s0 && *s0) {
+ i = strlen(s0) + strlen(s1) + 1 + 1;
+ if ((cp = malloc(i)) == NULL)
+ err(1, NULL);
+ (void)snprintf(cp, i, "%s,%s", s0, s1);
+ } else
+ cp = strdup(s1);
+
+ if (s0)
+ free(s0);
+ return (cp);
+}
+
+void
+mangle(options, argcp, argv)
+ char *options;
+ int *argcp;
+ const char **argv;
+{
+ char *p, *s;
+ int argc;
+
+ argc = *argcp;
+ for (s = options; (p = strsep(&s, ",")) != NULL;)
+ if (*p != '\0')
+ if (*p == '-') {
+ argv[argc++] = p;
+ p = strchr(p, '=');
+ if (p) {
+ *p = '\0';
+ argv[argc++] = p+1;
+ }
+ } else if (strcmp(p, "rw") != 0) {
+ argv[argc++] = "-o";
+ argv[argc++] = p;
+ }
+
+ *argcp = argc;
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: mount %s %s\n mount %s\n mount %s\n",
+ "[-dfruvw] [-o options] [-t ufs | external_type]",
+ "special node",
+ "[-adfruvw] [-t ufs | external_type]",
+ "[-dfruvw] special | node");
+ exit(1);
+}
diff --git a/sbin/mount_ifs/mount_ufs.c b/sbin/mount_ifs/mount_ufs.c
new file mode 100644
index 0000000..babb760
--- /dev/null
+++ b/sbin/mount_ifs/mount_ufs.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 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) 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_ufs.c 8.2 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+void ufs_usage __P((void));
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ MOPT_SYNC,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+int
+mount_ufs(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ extern int optreset;
+ struct ufs_args args;
+ int ch, mntflags;
+ char *fs_name;
+
+ mntflags = 0;
+ optind = optreset = 1; /* Reset for parse of new argv. */
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case '?':
+ default:
+ ufs_usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ ufs_usage();
+
+ args.fspec = argv[0]; /* The name of the device file. */
+ fs_name = argv[1]; /* The mount point. */
+
+#define DEFAULT_ROOTUID -2
+ args.export.ex_root = DEFAULT_ROOTUID;
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+
+ if (mount(MOUNT_UFS, fs_name, mntflags, &args) < 0) {
+ (void)fprintf(stderr, "%s on %s: ", args.fspec, fs_name);
+ switch (errno) {
+ case EMFILE:
+ (void)fprintf(stderr, "mount table full.\n");
+ break;
+ case EINVAL:
+ if (mntflags & MNT_UPDATE)
+ (void)fprintf(stderr,
+ "Specified device does not match mounted device.\n");
+ else
+ (void)fprintf(stderr,
+ "Incorrect super block.\n");
+ break;
+ default:
+ (void)fprintf(stderr, "%s\n", strerror(errno));
+ break;
+ }
+ return (1);
+ }
+ return (0);
+}
+
+void
+ufs_usage()
+{
+ (void)fprintf(stderr, "usage: mount_ufs [-o options] special node\n");
+ exit(1);
+}
diff --git a/sbin/mount_ifs/pathnames.h b/sbin/mount_ifs/pathnames.h
new file mode 100644
index 0000000..45a4a01
--- /dev/null
+++ b/sbin/mount_ifs/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 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.
+ *
+ * @(#)pathnames.h 8.2 (Berkeley) 3/27/94
+ */
+
+#define _PATH_SBIN "/sbin"
+#define _PATH_USRSBIN "/usr/sbin"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/sbin/mount_nullfs/Makefile b/sbin/mount_nullfs/Makefile
new file mode 100644
index 0000000..aae87e1
--- /dev/null
+++ b/sbin/mount_nullfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_null
+SRCS= mount_null.c getmntopts.c
+MAN8= mount_null.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I/sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8
new file mode 100644
index 0000000..6c8106f
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -0,0 +1,220 @@
+.\"
+.\" Copyright (c) 1992, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" John Heidemann of the UCLA Ficus project.
+.\"
+.\"
+.\" 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.
+.\"
+.\" @(#)mount_null.8 8.4 (Berkeley) 4/19/94
+.\"
+.\"
+.Dd April 19, 1994
+.Dt MOUNT_NULL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_null
+.Nd demonstrate the use of a null file system layer
+.Sh SYNOPSIS
+.Nm mount_null
+.Op Fl o Ar options
+.Ar target
+.Ar mount-point
+.Sh DESCRIPTION
+The
+.Nm mount_null
+command creates a
+null layer, duplicating a sub-tree of the file system
+name space under another part of the global file system namespace.
+In this respect, it is
+similar to the loopback file system (see
+.Xr mount_lofs 8 ) .
+It differs from
+the loopback file system in two respects: it is implemented using
+a stackable layers techniques, and it's
+.Do
+null-node
+.Dc s
+stack above
+all lower-layer vnodes, not just over directory vnodes.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The null layer has two purposes.
+First, it serves as a demonstration of layering by proving a layer
+which does nothing.
+(It actually does everything the loopback file system does,
+which is slightly more than nothing.)
+Second, the null layer can serve as a prototype layer.
+Since it provides all necessary layer framework,
+new file system layers can be created very easily be starting
+with a null layer.
+.Pp
+The remainder of this man page examines the null layer as a basis
+for constructing new layers.
+.\"
+.\"
+.Sh INSTANTIATING NEW NULL LAYERS
+New null layers are created with
+.Xr mount_null 8 .
+.Xr Mount_null 8
+takes two arguments, the pathname
+of the lower vfs (target-pn) and the pathname where the null
+layer will appear in the namespace (mount-point-pn). After
+the null layer is put into place, the contents
+of target-pn subtree will be aliased under mount-point-pn.
+.\"
+.\"
+.Sh OPERATION OF A NULL LAYER
+The null layer is the minimum file system layer,
+simply bypassing all possible operations to the lower layer
+for processing there. The majority of its activity centers
+on the bypass routine, though which nearly all vnode operations
+pass.
+.Pp
+The bypass routine accepts arbitrary vnode operations for
+handling by the lower layer. It begins by examing vnode
+operation arguments and replacing any null-nodes by their
+lower-layer equivalents. It then invokes the operation
+on the lower layer. Finally, it replaces the null-nodes
+in the arguments and, if a vnode is returned by the operation,
+stacks a null-node on top of the returned vnode.
+.Pp
+Although bypass handles most operations,
+.Em vop_getattr ,
+.Em vop_inactive ,
+.Em vop_reclaim ,
+and
+.Em vop_print
+are not bypassed.
+.Em Vop_getattr
+must change the fsid being returned.
+.Em Vop_inactive
+and vop_reclaim are not bypassed so that
+they can handle freeing null-layer specific data.
+.Em Vop_print
+is not bypassed to avoid excessive debugging
+information.
+.\"
+.\"
+.Sh INSTANTIATING VNODE STACKS
+Mounting associates the null layer with a lower layer,
+in effect stacking two VFSes. Vnode stacks are instead
+created on demand as files are accessed.
+.Pp
+The initial mount creates a single vnode stack for the
+root of the new null layer. All other vnode stacks
+are created as a result of vnode operations on
+this or other null vnode stacks.
+.Pp
+New vnode stacks come into existence as a result of
+an operation which returns a vnode.
+The bypass routine stacks a null-node above the new
+vnode before returning it to the caller.
+.Pp
+For example, imagine mounting a null layer with
+.Bd -literal -offset indent
+mount_null /usr/include /dev/layer/null
+.Ed
+Changing directory to
+.Pa /dev/layer/null
+will assign
+the root null-node (which was created when the null layer was mounted).
+Now consider opening
+.Pa sys .
+A vop_lookup would be
+done on the root null-node. This operation would bypass through
+to the lower layer which would return a vnode representing
+the UFS
+.Pa sys .
+Null_bypass then builds a null-node
+aliasing the UFS
+.Pa sys
+and returns this to the caller.
+Later operations on the null-node
+.Pa sys
+will repeat this
+process when constructing other vnode stacks.
+.\"
+.\"
+.Sh CREATING OTHER FILE SYSTEM LAYERS
+One of the easiest ways to construct new file system layers is to make
+a copy of the null layer, rename all files and variables, and
+then begin modifyng the copy. Sed can be used to easily rename
+all variables.
+.Pp
+The umap layer is an example of a layer descended from the
+null layer.
+.\"
+.\"
+.Sh INVOKING OPERATIONS ON LOWER LAYERS
+There are two techniques to invoke operations on a lower layer
+when the operation cannot be completely bypassed. Each method
+is appropriate in different situations. In both cases,
+it is the responsibility of the aliasing layer to make
+the operation arguments "correct" for the lower layer
+by mapping an vnode arguments to the lower layer.
+.Pp
+The first approach is to call the aliasing layer's bypass routine.
+This method is most suitable when you wish to invoke the operation
+currently being handled on the lower layer. It has the advantage
+the the bypass routine already must do argument mapping.
+An example of this is
+.Em null_getattrs
+in the null layer.
+.Pp
+A second approach is to directly invoked vnode operations on
+the lower layer with the
+.Em VOP_OPERATIONNAME
+interface.
+The advantage of this method is that it is easy to invoke
+arbitrary operations on the lower layer. The disadvantage
+is that vnodes arguments must be manually mapped.
+.\"
+.\"
+.Sh SEE ALSO
+.Xr mount 8
+.sp
+UCLA Technical Report CSD-910056,
+.Em "Stackable Layers: an Architecture for File System Development" .
+.Sh HISTORY
+The
+.Nm mount_null
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c
new file mode 100644
index 0000000..e8c26df
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_null.c 8.5 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <miscfs/nullfs/null.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+int subdir __P((const char *, const char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct null_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ if (realpath(argv[0], target) == 0)
+ err(1, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(1, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ if (mount(MOUNT_NULL, argv[1], mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_null [-o options] target_fs mount_point\n");
+ exit(1);
+}
diff --git a/sbin/mount_portalfs/Makefile b/sbin/mount_portalfs/Makefile
new file mode 100644
index 0000000..1f17d24
--- /dev/null
+++ b/sbin/mount_portalfs/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_portal
+SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_tcp.c
+MAN8= mount_portal.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I/sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+DPADD= $(LIBCOMPAT)
+LDADD= -lcompat
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_portalfs/activate.c b/sbin/mount_portalfs/activate.c
new file mode 100644
index 0000000..3361798
--- /dev/null
+++ b/sbin/mount_portalfs/activate.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)activate.c 8.2 (Berkeley) 3/27/94
+ *
+ * $Id: activate.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include "portald.h"
+
+/*
+ * Scan the providers list and call the
+ * appropriate function.
+ */
+static int activate_argv(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ provider *pr;
+
+ for (pr = providers; pr->pr_match; pr++)
+ if (strcmp(v[0], pr->pr_match) == 0)
+ return ((*pr->pr_func)(pcr, key, v, so, fdp));
+
+ return (ENOENT);
+}
+
+static int get_request(so, pcr, key, klen)
+int so;
+struct portal_cred *pcr;
+char *key;
+int klen;
+{
+ struct iovec iov[2];
+ struct msghdr msg;
+ int n;
+
+ iov[0].iov_base = (caddr_t) pcr;
+ iov[0].iov_len = sizeof(*pcr);
+ iov[1].iov_base = key;
+ iov[1].iov_len = klen;
+
+ bzero((char *) &msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ n = recvmsg(so, &msg, 0);
+ if (n < 0)
+ return (errno);
+
+ if (n <= sizeof(*pcr))
+ return (EINVAL);
+
+ n -= sizeof(*pcr);
+ key[n] = '\0';
+
+ return (0);
+}
+
+static void send_reply(so, fd, error)
+int so;
+int fd;
+int error;
+{
+ int n;
+ struct iovec iov;
+ struct msghdr msg;
+ struct {
+ struct cmsghdr cmsg;
+ int fd;
+ } ctl;
+
+ /*
+ * Line up error code. Don't worry about byte ordering
+ * because we must be sending to the local machine.
+ */
+ iov.iov_base = (caddr_t) &error;
+ iov.iov_len = sizeof(error);
+
+ /*
+ * Build a msghdr
+ */
+ bzero((char *) &msg, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /*
+ * If there is a file descriptor to send then
+ * construct a suitable rights control message.
+ */
+ if (fd >= 0) {
+ ctl.fd = fd;
+ ctl.cmsg.cmsg_len = sizeof(ctl);
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ msg.msg_control = (caddr_t) &ctl;
+ msg.msg_controllen = ctl.cmsg.cmsg_len;
+ }
+
+ /*
+ * Send to kernel...
+ */
+ if ((n = sendmsg(so, &msg, MSG_EOR)) < 0)
+ syslog(LOG_ERR, "send: %s", strerror(errno));
+#ifdef DEBUG
+ fprintf(stderr, "sent %d bytes\n", n);
+#endif
+ sleep(1); /*XXX*/
+#ifdef notdef
+ if (shutdown(so, 2) < 0)
+ syslog(LOG_ERR, "shutdown: %s", strerror(errno));
+#endif
+ /*
+ * Throw away the open file descriptor
+ */
+ (void) close(fd);
+}
+
+void activate(q, so)
+qelem *q;
+int so;
+{
+ struct portal_cred pcred;
+ char key[MAXPATHLEN+1];
+ int error;
+ char **v;
+ int fd = -1;
+
+ /*
+ * Read the key from the socket
+ */
+ error = get_request(so, &pcred, key, sizeof(key));
+ if (error) {
+ syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error));
+ goto drop;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "lookup key %s\n", key);
+#endif
+
+ /*
+ * Find a match in the configuration file
+ */
+ v = conf_match(q, key);
+
+ /*
+ * If a match existed, then find an appropriate portal
+ * otherwise simply return ENOENT.
+ */
+ if (v) {
+ error = activate_argv(&pcred, key, v, so, &fd);
+ if (error)
+ fd = -1;
+ else if (fd < 0)
+ error = -1;
+ } else {
+ error = ENOENT;
+ }
+
+ if (error >= 0)
+ send_reply(so, fd, error);
+
+drop:;
+ close(so);
+}
diff --git a/sbin/mount_portalfs/conf.c b/sbin/mount_portalfs/conf.c
new file mode 100644
index 0000000..18833b6
--- /dev/null
+++ b/sbin/mount_portalfs/conf.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)conf.c 8.2 (Berkeley) 3/27/94
+ *
+ * $Id: conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <regexp.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+#define ALLOC(ty) (xmalloc(sizeof(ty)))
+
+typedef struct path path;
+struct path {
+ qelem p_q; /* 2-way linked list */
+ int p_lno; /* Line number of this record */
+ char *p_args; /* copy of arg string (malloc) */
+ char *p_key; /* Pathname to match (also p_argv[0]) */
+ regexp *p_re; /* RE to match against pathname (malloc) */
+ int p_argc; /* number of elements in arg string */
+ char **p_argv; /* argv[] pointers into arg string (malloc) */
+};
+
+static char *conf_file; /* XXX for regerror */
+static path *curp; /* XXX for regerror */
+
+/*
+ * Add an element to a 2-way list,
+ * just after (pred)
+ */
+static void ins_que(elem, pred)
+qelem *elem, *pred;
+{
+ qelem *p = pred->q_forw;
+ elem->q_back = pred;
+ elem->q_forw = p;
+ pred->q_forw = elem;
+ p->q_back = elem;
+}
+
+/*
+ * Remove an element from a 2-way list
+ */
+static void rem_que(elem)
+qelem *elem;
+{
+ qelem *p = elem->q_forw;
+ qelem *p2 = elem->q_back;
+ p2->q_forw = p;
+ p->q_back = p2;
+}
+
+/*
+ * Error checking malloc
+ */
+static void *xmalloc(siz)
+unsigned siz;
+{
+ void *p = malloc(siz);
+ if (p)
+ return (p);
+ syslog(LOG_ALERT, "malloc: failed to get %d bytes", siz);
+ exit(1);
+}
+
+/*
+ * Insert the path in the list.
+ * If there is already an element with the same key then
+ * the *second* one is ignored (return 0). If the key is
+ * not found then the path is added to the end of the list
+ * and 1 is returned.
+ */
+static int pinsert(p0, q0)
+path *p0;
+qelem *q0;
+{
+ qelem *q;
+
+ if (p0->p_argc == 0)
+ return (0);
+
+ for (q = q0->q_forw; q != q0; q = q->q_forw) {
+ path *p = (path *) q;
+ if (strcmp(p->p_key, p0->p_key) == 0)
+ return (0);
+ }
+ ins_que(&p0->p_q, q0->q_back);
+ return (1);
+
+}
+
+void regerror(s)
+const char *s;
+{
+ syslog(LOG_ERR, "%s:%s: regcomp %s: %s",
+ conf_file, curp->p_lno, curp->p_key, s);
+}
+
+static path *palloc(cline, lno)
+char *cline;
+int lno;
+{
+ int c;
+ char *s;
+ char *key;
+ path *p;
+ char **ap;
+
+ /*
+ * Implement comment chars
+ */
+ s = strchr(cline, '#');
+ if (s)
+ *s = 0;
+
+ /*
+ * Do a pass through the string to count the number
+ * of arguments
+ */
+ c = 0;
+ key = strdup(cline);
+ for (s = key; s != NULL; ) {
+ char *val;
+ while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
+ ;
+ if (val)
+ c++;
+ }
+ c++;
+ free(key);
+
+ if (c <= 1)
+ return (0);
+
+ /*
+ * Now do another pass and generate a new path structure
+ */
+ p = ALLOC(path);
+ p->p_argc = 0;
+ p->p_argv = xmalloc(c * sizeof(char *));
+ p->p_args = strdup(cline);
+ ap = p->p_argv;
+ for (s = p->p_args; s != NULL; ) {
+ char *val;
+ while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
+ ;
+ if (val) {
+ *ap++ = val;
+ p->p_argc++;
+ }
+ }
+ *ap = 0;
+
+#ifdef DEBUG
+ for (c = 0; c < p->p_argc; c++)
+ printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]);
+#endif
+
+ p->p_key = p->p_argv[0];
+ if (strpbrk(p->p_key, RE_CHARS)) {
+ curp = p; /* XXX */
+ p->p_re = regcomp(p->p_key);
+ curp = 0; /* XXX */
+ } else {
+ p->p_re = 0;
+ }
+ p->p_lno = lno;
+
+ return (p);
+}
+
+/*
+ * Free a path structure
+ */
+static void pfree(p)
+path *p;
+{
+ free(p->p_args);
+ if (p->p_re)
+ free((char *) p->p_re);
+ free((char *) p->p_argv);
+ free((char *) p);
+}
+
+/*
+ * Discard all currently held path structures on q0.
+ * and add all the ones on xq.
+ */
+static void preplace(q0, xq)
+qelem *q0;
+qelem *xq;
+{
+ /*
+ * While the list is not empty,
+ * take the first element off the list
+ * and free it.
+ */
+ while (q0->q_forw != q0) {
+ qelem *q = q0->q_forw;
+ rem_que(q);
+ pfree((path *) q);
+ }
+ while (xq->q_forw != xq) {
+ qelem *q = xq->q_forw;
+ rem_que(q);
+ ins_que(q, q0);
+ }
+}
+
+/*
+ * Read the lines from the configuration file and
+ * add them to the list of paths.
+ */
+static void readfp(q0, fp)
+qelem *q0;
+FILE *fp;
+{
+ char cline[LINE_MAX];
+ int nread = 0;
+ qelem q;
+
+ /*
+ * Make a new empty list.
+ */
+ q.q_forw = q.q_back = &q;
+
+ /*
+ * Read the lines from the configuration file.
+ */
+ while (fgets(cline, sizeof(cline), fp)) {
+ path *p = palloc(cline, nread+1);
+ if (p && !pinsert(p, &q))
+ pfree(p);
+ nread++;
+ }
+
+ /*
+ * If some records were read, then throw
+ * away the old list and replace with the
+ * new one.
+ */
+ if (nread)
+ preplace(q0, &q);
+}
+
+/*
+ * Read the configuration file (conf) and replace
+ * the existing path list with the new version.
+ * If the file is not readable, then no changes take place
+ */
+void conf_read(q, conf)
+qelem *q;
+char *conf;
+{
+ FILE *fp = fopen(conf, "r");
+ if (fp) {
+ conf_file = conf; /* XXX */
+ readfp(q, fp);
+ conf_file = 0; /* XXX */
+ (void) fclose(fp);
+ } else {
+ syslog(LOG_ERR, "open config file \"%s\": %s", conf, strerror(errno));
+ }
+}
+
+
+char **conf_match(q0, key)
+qelem *q0;
+char *key;
+{
+ qelem *q;
+
+ for (q = q0->q_forw; q != q0; q = q->q_forw) {
+ path *p = (path *) q;
+ if (p->p_re) {
+ if (regexec(p->p_re, key))
+ return (p->p_argv+1);
+ } else {
+ if (strncmp(p->p_key, key, strlen(p->p_key)) == 0)
+ return (p->p_argv+1);
+ }
+ }
+
+ return (0);
+}
diff --git a/sbin/mount_portalfs/mount_portalfs.8 b/sbin/mount_portalfs/mount_portalfs.8
new file mode 100644
index 0000000..0df6531
--- /dev/null
+++ b/sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,136 @@
+.\"
+.\" Copyright (c) 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry.
+.\"
+.\" 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.
+.\"
+.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_PORTAL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_portal
+.Nd mount the portal daemon
+.Sh SYNOPSIS
+.Nm mount_portal
+.Op Fl o Ar options
+.Ar /etc/portal.conf
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_portal
+command attaches an instance of the portal daemon
+to the global filesystem namespace.
+The conventional mount point is
+.Pa /p .
+.PA /dev .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The portal daemon provides an
+.Em open
+service.
+Objects opened under the portal mount point are
+dynamically created by the portal daemon according
+to rules specified in the named configuration file.
+Using this mechanism allows descriptors such as sockets
+to be made available in the filesystem namespace.
+.Pp
+The portal daemon works by being passed the full pathname
+of the object being opened.
+The daemon creates an appropriate descriptor according
+to the rules in the configuration file, and then passes the descriptor back
+to the calling process as the result of the open system call.
+.Sh NAMESPACE
+By convention, the portal daemon divides the namespace into sub-namespaces,
+each of which handles objects of a particular type.
+.Pp
+Currently, two sub-namespaces are implemented:
+.Pa tcp
+and
+.Pa fs .
+The
+.Pa tcp
+namespace takes a hostname and a port (slash separated) and
+creates an open TCP/IP connection.
+The
+.Pa fs
+namespace opens the named file, starting back at the root directory.
+This can be used to provide a controlled escape path from
+a chrooted environment.
+.Sh "CONFIGURATION FILE"
+The configuration file contains a list of rules.
+Each rule takes one line and consists of two or more
+whitespace separated fields.
+A hash (``#'') character causes the remainder of a line to
+be ignored. Blank lines are ignored.
+.Pp
+The first field is a pathname prefix to match
+against the requested pathname.
+If a match is found, the second field
+tells the daemon what type of object to create.
+Subsequent fields are passed to the creation function.
+.Bd -literal
+# @(#)portal.conf 5.1 (Berkeley) 7/13/92
+tcp/ tcp tcp/
+fs/ file fs/
+.Ed
+.Sh FILES
+.Bl -tag -width /p/* -compact
+.It Pa /p/*
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh CAVEATS
+This filesystem may not be NFS-exported.
+.Sh HISTORY
+The
+.Nm mount_portal
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_portalfs/mount_portalfs.c b/sbin/mount_portalfs/mount_portalfs.c
new file mode 100644
index 0000000..ae5345d
--- /dev/null
+++ b/sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_portal.c 8.4 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+#include "portald.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void));
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sigchld(sig)
+int sig;
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ if (pid < 0)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct portal_args args;
+ struct sockaddr_un un;
+ char *conf;
+ char *mountpt;
+ int mntflags = 0;
+ char tag[32];
+
+ qelem q;
+ int rc;
+ int so;
+ int error = 0;
+
+ /*
+ * Crack command line args
+ */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "o:")) != EOF) {
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ default:
+ error = 1;
+ break;
+ }
+ }
+
+ if (optind != (argc - 2))
+ error = 1;
+
+ if (error)
+ usage();
+
+ /*
+ * Get config file and mount point
+ */
+ conf = argv[optind];
+ mountpt = argv[optind+1];
+
+ /*
+ * Construct the listening socket
+ */
+ un.sun_family = AF_UNIX;
+ if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) {
+ fprintf(stderr, "mount_portal: portal socket name too long\n");
+ exit(1);
+ }
+ strcpy(un.sun_path, _PATH_TMPPORTAL);
+ mktemp(un.sun_path);
+ un.sun_len = strlen(un.sun_path);
+
+ so = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (so < 0) {
+ fprintf(stderr, "mount_portal: socket: %s\n", strerror(errno));
+ exit(1);
+ }
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+ (void) unlink(un.sun_path);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ rc = mount(MOUNT_PORTAL, mountpt, mntflags, &args);
+ if (rc < 0)
+ err(1, NULL);
+
+#ifdef notdef
+ /*
+ * Everything is ready to go - now is a good time to fork
+ */
+ daemon(0, 0);
+#endif
+
+ /*
+ * Start logging (and change name)
+ */
+ openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ q.q_forw = q.q_back = &q;
+ readcf = 1;
+
+ signal(SIGCHLD, sigchld);
+
+ /*
+ * Just loop waiting for new connections and activating them
+ */
+ for (;;) {
+ struct sockaddr_un un2;
+ int len2 = sizeof(un2);
+ int so2;
+ pid_t pid;
+ fd_set fdset;
+ int rc;
+
+ /*
+ * Check whether we need to re-read the configuration file
+ */
+ if (readcf) {
+ readcf = 0;
+ conf_read(&q, conf);
+ continue;
+ }
+
+ /*
+ * Accept a new connection
+ * Will get EINTR if a signal has arrived, so just
+ * ignore that error code
+ */
+ FD_SET(so, &fdset);
+ rc = select(so+1, &fdset, (void *) 0, (void *) 0, (void *) 0);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %s", strerror(errno));
+ exit(1);
+ }
+ if (rc == 0)
+ break;
+ so2 = accept(so, (struct sockaddr *) &un2, &len2);
+ if (so2 < 0) {
+ /*
+ * The unmount function does a shutdown on the socket
+ * which will generated ECONNABORTED on the accept.
+ */
+ if (errno == ECONNABORTED)
+ break;
+ if (errno != EINTR) {
+ syslog(LOG_ERR, "accept: %s", strerror(errno));
+ exit(1);
+ }
+ continue;
+ }
+
+ /*
+ * Now fork a new child to deal with the connection
+ */
+ eagain:;
+ switch (pid = fork()) {
+ case -1:
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto eagain;
+ }
+ syslog(LOG_ERR, "fork: %s", strerror(errno));
+ break;
+ case 0:
+ (void) close(so);
+ activate(&q, so2);
+ break;
+ default:
+ (void) close(so2);
+ break;
+ }
+ }
+ syslog(LOG_INFO, "%s unmounted", mountpt);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_portal [-o options] config mount-point\n");
+ exit(1);
+}
diff --git a/sbin/mount_portalfs/pathnames.h b/sbin/mount_portalfs/pathnames.h
new file mode 100644
index 0000000..2532114
--- /dev/null
+++ b/sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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
+ *
+ * $Id: pathnames.h,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */
diff --git a/sbin/mount_portalfs/portal.conf b/sbin/mount_portalfs/portal.conf
new file mode 100644
index 0000000..5b5a773
--- /dev/null
+++ b/sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id: portal.conf,v 1.1 1992/05/27 06:50:13 jsp Exp jsp $
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe
+foo/ exec ./bar bar baz
diff --git a/sbin/mount_portalfs/portald.h b/sbin/mount_portalfs/portald.h
new file mode 100644
index 0000000..fbe111b
--- /dev/null
+++ b/sbin/mount_portalfs/portald.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)portald.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: portald.h,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <sys/cdefs.h>
+#include <miscfs/portal/portal.h>
+
+/*
+ * Meta-chars in an RE. Paths in the config file containing
+ * any of these characters will be matched using regexec, other
+ * paths will be prefix-matched.
+ */
+#define RE_CHARS ".|()[]*+?\\^$"
+
+typedef struct qelem qelem;
+
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+typedef struct provider provider;
+struct provider {
+ char *pr_match;
+ int (*pr_func) __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_file __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_tcp __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+
+/*
+ * Global functions
+ */
+extern void activate __P((qelem *q, int so));
+extern char **conf_match __P((qelem *q, char *key));
+extern void conf_read __P((qelem *q, char *conf));
diff --git a/sbin/mount_portalfs/pt_conf.c b/sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..d1eba94
--- /dev/null
+++ b/sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: pt_conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "portald.h"
+
+provider providers[] = {
+ { "exec", portal_exec },
+ { "file", portal_file },
+ { "tcp", portal_tcp },
+ { 0, 0 }
+};
diff --git a/sbin/mount_portalfs/pt_exec.c b/sbin/mount_portalfs/pt_exec.c
new file mode 100644
index 0000000..06e3382
--- /dev/null
+++ b/sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: pt_exec.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+int portal_exec(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ return (ENOEXEC);
+}
+
diff --git a/sbin/mount_portalfs/pt_file.c b/sbin/mount_portalfs/pt_file.c
new file mode 100644
index 0000000..ace35c0
--- /dev/null
+++ b/sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)pt_file.c 8.2 (Berkeley) 3/27/94
+ *
+ * $Id: pt_file.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+int portal_file(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ int fd;
+ char pbuf[MAXPATHLEN];
+ int error;
+ int gidset[NGROUPS];
+ int i;
+
+ pbuf[0] = '/';
+ strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
+
+#ifdef DEBUG
+ printf("path = %s, uid = %d, gid = %d\n", pbuf, pcr->pcr_uid, pcr->pcr_groups[0]);
+#endif
+
+ for (i = 0; i < pcr->pcr_ngroups; i++)
+ gidset[i] = pcr->pcr_groups[i];
+
+ if (setgroups(pcr->pcr_ngroups, gidset) < 0)
+ return (errno);
+
+ if (seteuid(pcr->pcr_uid) < 0)
+ return (errno);
+
+ fd = open(pbuf, O_RDWR|O_CREAT, 0666);
+ if (fd < 0)
+ error = errno;
+ else
+ error = 0;
+
+ if (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */
+ error = errno;
+ syslog(LOG_ERR, "setcred: %s", strerror(error));
+ if (fd >= 0) {
+ (void) close(fd);
+ fd = -1;
+ }
+ }
+
+ if (error == 0)
+ *fdp = fd;
+
+#ifdef DEBUG
+ fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error);
+#endif
+
+ return (error);
+}
diff --git a/sbin/mount_portalfs/pt_tcp.c b/sbin/mount_portalfs/pt_tcp.c
new file mode 100644
index 0000000..18a53ce
--- /dev/null
+++ b/sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)pt_tcp.c 8.3 (Berkeley) 3/27/94
+ *
+ * $Id: pt_tcp.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "portald.h"
+
+/*
+ * Key will be tcp/host/port[/"priv"]
+ * Create a TCP socket connected to the
+ * requested host and port.
+ * Some trailing suffix values have special meanings.
+ * An unrecognised suffix is an error.
+ */
+int portal_tcp(pcr, key, v, kso, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int kso;
+int *fdp;
+{
+ char host[MAXHOSTNAMELEN];
+ char port[MAXHOSTNAMELEN];
+ char *p = key + (v[1] ? strlen(v[1]) : 0);
+ char *q;
+ struct hostent *hp;
+ struct servent *sp;
+ struct in_addr **ipp;
+ struct in_addr *ip[2];
+ struct in_addr ina;
+ int s_port;
+ int priv = 0;
+ struct sockaddr_in sain;
+
+ q = strchr(p, '/');
+ if (q == 0 || q - p >= sizeof(host))
+ return (EINVAL);
+ *q = '\0';
+ strcpy(host, p);
+ p = q + 1;
+
+ q = strchr(p, '/');
+ if (q)
+ *q = '\0';
+ if (strlen(p) >= sizeof(port))
+ return (EINVAL);
+ strcpy(port, p);
+ if (q) {
+ p = q + 1;
+ if (strcmp(p, "priv") == 0) {
+ if (pcr->pcr_uid == 0)
+ priv = 1;
+ else
+ return (EPERM);
+ } else {
+ return (EINVAL);
+ }
+ }
+
+ hp = gethostbyname(host);
+ if (hp != 0) {
+ ipp = (struct in_addr **) hp->h_addr_list;
+ } else {
+ ina.s_addr = inet_addr(host);
+ if (ina.s_addr == INADDR_NONE)
+ return (EINVAL);
+ ip[0] = &ina;
+ ip[1] = 0;
+ ipp = ip;
+ }
+
+ sp = getservbyname(port, "tcp");
+ if (sp != 0)
+ s_port = sp->s_port;
+ else {
+ s_port = atoi(port);
+ if (s_port == 0)
+ return (EINVAL);
+ }
+
+ bzero(&sain, sizeof(sain));
+ sain.sin_len = sizeof(sain);
+ sain.sin_family = AF_INET;
+ sain.sin_port = s_port;
+
+ while (ipp[0]) {
+ int so;
+
+ if (priv)
+ so = rresvport((int *) 0);
+ else
+ so = socket(AF_INET, SOCK_STREAM, 0);
+ if (so < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ return (errno);
+ }
+
+ sain.sin_addr = *ipp[0];
+ if (connect(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) {
+ *fdp = so;
+ return (0);
+ }
+ (void) close(so);
+
+ ipp++;
+ }
+
+ return (errno);
+}
diff --git a/sbin/mount_umapfs/Makefile b/sbin/mount_umapfs/Makefile
new file mode 100644
index 0000000..af1bf2f
--- /dev/null
+++ b/sbin/mount_umapfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_umap
+SRCS= mount_umap.c getmntopts.c
+MAN8= mount_umap.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I/sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_umapfs/mount_umapfs.8 b/sbin/mount_umapfs/mount_umapfs.8
new file mode 100644
index 0000000..e90ce26
--- /dev/null
+++ b/sbin/mount_umapfs/mount_umapfs.8
@@ -0,0 +1,131 @@
+.\" Copyright (c) 1992, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry and from John Heidemann of the UCLA Ficus project.
+.\"
+.\" 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.
+.\"
+.\" @(#)mount_umap.8 8.3 (Berkeley) 3/27/94
+.\"
+.Dd "March 27, 1994"
+.Dt MOUNT_UMAP 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_umap
+.Nd sample file system layer
+.Sh SYNOPSIS
+.Nm mount_umap
+.Op Fl o Ar options
+.Ar target
+.Ar mount-point
+.Ar uid-mapfile
+.Ar gid-mapfile
+.Sh DESCRIPTION
+The
+.Nm mount_umap
+command is used to mount a sub-tree of an existing file system
+that uses a different set of uids and gids than the local system.
+Such a file system could be mounted from a remote site via NFS or
+it could be a file system on removable media brought from some
+foreign location that uses a different password file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The
+.Nm mount_umap
+command uses a set of files provided by the user to make correspondences
+between uids and gids in the sub-tree's original environment and
+some other set of ids in the local environment. For instance, user
+smith might have uid 1000 in the original environment, while having
+uid 2000 in the local environment. The
+.Nm mount_umap
+command allows the subtree from smith's original environment to be
+mapped in such a way that all files with owning uid 1000 look like
+they are actually owned by uid 2000.
+.Pp
+.Em target
+should be the current location of the sub-tree in the
+local system's name space.
+.Em mount-point
+should be a directory
+where the mapped subtree is to be placed.
+.Em uid-mapfile
+and
+.Em gid-mapfile
+describe the mappings to be made between identifiers.
+Briefly, the format of these files is a count of the number of
+mappings on the first line, with each subsequent line containing
+a single mapping. Each of these mappings consists of an id from
+the original environment and the corresponding id in the local environment,
+separated by white space.
+.Em uid-mapfile
+should contain all uid
+mappings, and
+.Em gid-mapfile
+should contain all gid mappings.
+Any uids not mapped in
+.Em uid-mapfile
+will be treated as user NOBODY,
+and any gids not mapped in
+.Em gid-mapfile
+will be treated as group
+NULLGROUP. At most 64 uids can be mapped for a given subtree, and
+at most 16 groups can be mapped by a given subtree.
+.Pp
+The mapfiles can be located anywhere in the file hierarchy, but they
+must be owned by root, and they must be writable only by root.
+.Nm mount_umap
+will refuse to map the sub-tree if the ownership or permissions on
+these files are improper. It will also balk if the count of mappings
+in the first line of the map files is not correct.
+.Pp
+The layer created by the
+.Nm mount_umap
+command is meant to serve as a simple example of file system layering.
+It is not meant for production use. The implementation is not very
+sophisticated.
+.Sh SEE ALSO
+.Xr mount 8 ,
+.Xr mount_null 8 ,
+.Xr mount_lofs 8
+.Sh HISTORY
+The
+.Nm mount_umap
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_umapfs/mount_umapfs.c b/sbin/mount_umapfs/mount_umapfs.c
new file mode 100644
index 0000000..a069fe5
--- /dev/null
+++ b/sbin/mount_umapfs/mount_umapfs.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_umap.c 8.3 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <miscfs/umapfs/umap.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+#define ROOTUSER 0
+/*
+ * This define controls whether any user but the superuser can own and
+ * write mapfiles. If other users can, system security can be gravely
+ * compromised. If this is not a concern, undefine SECURITY.
+ */
+#define MAPSECURITY 1
+
+/*
+ * This routine provides the user interface to mounting a umap layer.
+ * It takes 4 mandatory parameters. The mandatory arguments are the place
+ * where the next lower level is mounted, the place where the umap layer is to
+ * be mounted, the name of the user mapfile, and the name of the group
+ * mapfile. The routine checks the ownerships and permissions on the
+ * mapfiles, then opens and reads them. Then it calls mount(), which
+ * will, in turn, call the umap version of mount.
+ */
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static char not[] = "; not mounted.";
+ struct stat statbuf;
+ struct umap_args args;
+ FILE *fp, *gfp;
+ u_long gmapdata[GMAPFILEENTRIES][2], mapdata[MAPFILEENTRIES][2];
+ int ch, count, gnentries, mntflags, nentries;
+ char *gmapfile, *mapfile, *source, *target, buf[20];
+
+ mntflags = 0;
+ mapfile = gmapfile = NULL;
+ while ((ch = getopt(argc, argv, "g:o:u:")) != EOF)
+ switch (ch) {
+ case 'g':
+ gmapfile = optarg;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case 'u':
+ mapfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 || mapfile == NULL || gmapfile == NULL)
+ usage();
+
+ source = argv[0];
+ target = argv[1];
+
+ /* Read in uid mapping data. */
+ if ((fp = fopen(mapfile, "r")) == NULL)
+ err(1, "%s%s", mapfile, not);
+
+#ifdef MAPSECURITY
+ /*
+ * Check that group and other don't have write permissions on
+ * this mapfile, and that the mapfile belongs to root.
+ */
+ if (fstat(fileno(fp), &statbuf))
+ err(1, "%s%s", mapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(1, "%s: improper write permissions (%s)%s",
+ mapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(1, "%s does not belong to root%s", mapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(fp, "%d\n", &nentries)) != 1)
+ errx(1, "%s: nentries not found%s", mapfile, not);
+ if (nentries > MAPFILEENTRIES)
+ errx(1,
+ "maximum number of entries is %d%s", MAPFILEENTRIES, not);
+#if 0
+ (void)printf("reading %d entries\n", nentries);
+#endif
+ for (count = 0; count < nentries; ++count) {
+ if ((fscanf(fp, "%lu %lu\n",
+ &(mapdata[count][0]), &(mapdata[count][1]))) != 2) {
+ if (ferror(fp))
+ err(1, "%s%s", mapfile, not);
+ if (feof(fp))
+ errx(1, "%s: unexpected end-of-file%s",
+ mapfile, not);
+ errx(1, "%s: illegal format (line %d)%s",
+ mapfile, count + 2, not);
+ }
+#if 0
+ /* Fix a security hole. */
+ if (mapdata[count][1] == 0)
+ errx(1, "mapping id 0 not permitted (line %d)%s",
+ count + 2, not);
+#endif
+ }
+
+ /* Read in gid mapping data. */
+ if ((gfp = fopen(gmapfile, "r")) == NULL)
+ err(1, "%s%s", gmapfile, not);
+
+#ifdef MAPSECURITY
+ /*
+ * Check that group and other don't have write permissions on
+ * this group mapfile, and that the file belongs to root.
+ */
+ if (fstat(fileno(gfp), &statbuf))
+ err(1, "%s%s", gmapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(1, "%s: improper write permissions (%s)%s",
+ gmapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(1, "%s does not belong to root%s", gmapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(fp, "%d\n", &gnentries)) != 1)
+ errx(1, "nentries not found%s", gmapfile, not);
+ if (gnentries > MAPFILEENTRIES)
+ errx(1,
+ "maximum number of entries is %d%s", GMAPFILEENTRIES, not);
+#if 0
+ (void)printf("reading %d group entries\n", gnentries);
+#endif
+
+ for (count = 0; count < gnentries; ++count)
+ if ((fscanf(fp, "%lu %lu\n",
+ &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) {
+ if (ferror(fp))
+ err(1, "%s%s", gmapfile, not);
+ if (feof(fp))
+ errx(1, "%s: unexpected end-of-file%s",
+ gmapfile, not);
+ errx(1, "%s: illegal format (line %d)%s",
+ gmapfile, count + 2, not);
+ }
+
+
+ /* Setup mount call args. */
+ args.target = source;
+ args.nentries = nentries;
+ args.mapdata = mapdata;
+ args.gnentries = gnentries;
+ args.gmapdata = gmapdata;
+
+ if (mount(MOUNT_UMAP, argv[1], mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mount_umap [-o options] -u usermap -g groupmap target_fs mount_point\n");
+ exit(1);
+}
diff --git a/sbin/mount_umapfs/umap_manual b/sbin/mount_umapfs/umap_manual
new file mode 100644
index 0000000..059939f
--- /dev/null
+++ b/sbin/mount_umapfs/umap_manual
@@ -0,0 +1,175 @@
+
+\appendix
+\section{The umap Layer} \label{sect:umap}
+
+\subsection{Introduction}
+
+Normally, the file system is expected to span a single administrative domain.
+An administrative domain, for these purposes, is a machine or set of
+machines that share common password file information, usually through
+the yellow pages mechanism. File hierarchies that span more
+than one domain leads to certain problems, since the same numerical
+UID in one domain may correspond to a different user in another domain.
+If the system administrator is very careful to ensure that both domains
+contain identical user ID information, The umap layer can be used to
+run between those domains without changes
+
+The umap layer is a file system layer that sits on top of the normal
+file layer. The umap layer maps Unix-style UIDs from
+one domain into the UIDs in the other domain. By setting up the mappings
+properly, the same user with different UIDs in two domains can be seen
+as the same user, from the system point of view, or, conversely, two
+different users with the same UID in the two domains can be distinguished.
+
+First, we define some terms. ``User'' refers to the human (or daemon) that
+has privileges to login, run programs, and access files. ``UID''refers to
+the numerical identifier that uniquely identifies the user within a
+single domain. ``Login name'' refers to the character string the user
+types to log into the system. ``GID'' refers to the numerical group
+identifier used by Unix systems to identify groups of users. ``Group
+name'' is the character string name attached to a particular GID in the
+local {\sf /etc/groups} file or the yellow pages groups file.
+
+In order for the umap layer to work properly, all users
+in either domain must have password file entries in both domains.
+They do not, however, have to have the same numerical UID, nor even the
+same character string login name (the latter is highly recommended,
+if possible, however). Any user not having a UID in one domain will be
+treated as the special user NOBODY by the other domain, probably with
+undesirable consequences. Any user not owning any files in the shared
+sub-trees need not be given a UID in the other domain.
+
+Groups work similarly. The umap layer can translate group ID's between
+domains in the same manner as UID's. Again, any group that wishes to
+participate must have a group ID in both domains,
+though it need not be the same GID in both. If a group in one domain is not
+known in the other domain, that group will be treated as being NULLGROUP.
+The umap layer has no provisions for enrolling UID's from other domains
+as group members, but, since each user from each domain must have some
+UID in every domain, the UID in the local domain can be used to enroll
+the user in the local groups.
+
+NOBODY and NULLGROUP are special reserved UID's and GID's, respectively.
+NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator
+wants to have an appropriate text string appear when these UID's are
+encountered by programs like {\sf ls -l}, he should add these values to
+the password and {\sf /etc/groups} file, or to the appropriate yellow pages.
+If these IDs are already in use in that domain, different values can be
+used for NOBODY and NULLGROUP, but that will require a recompilation of
+the umap layer code and, as a result, the entire kernel. These
+values are defined in the {\sf umap\_info.h} file, kept with the rest of the
+umap source code.
+
+When the umap layer is in use, one of the participating domains is declared
+to be the master. All UID and GID information stored for participating files
+will be stored in vnodes using its mappings, no matter what site the copies of
+the files are stored at. The master domain therefore need not run a copy
+of the umap layer, as it already has all of the correct mappings. All
+other domains must run a umap layer on top of any other layers they use.
+
+\subsection{Setting Up a umap Layer}
+
+The system administrator of a system needing to use the umap layer
+must take several actions.
+First, he must create files containing the necessary UID
+and GID mappings. There is a separate file for user and group IDs. The
+format of the files is the same. The first line contains the total number
+of entries in the file. Each subsequent line contains one mapping. A
+mapping line consists of two numerical UIDs, separated by white space.
+The first is the UID of a user on the local machine. The second is the
+UID for the same user on the master machine. The maximum number of users
+that can be mapped for a single shared sub-tree is 64. The maximum number of
+groups that can be mapped for a single sub-tree is 16. These constants
+are set in the {\sf umap\_info.h} file, and can be changed, but changing them
+requires recompilation. Separate mapping files can be used for each shared
+subtree, or the same mapping files can be shared by several sub-trees.
+
+Below is a sample UID mapping file. There are four entries. UID 5 is mapped
+to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this
+machine, the UID's for users 5, 521, and 7000 are the same as on the master,
+but UID 2002 is for a user whose UID on the master machine is 604. All
+files in the sub-tree belonging to that user have UID 604 in their inodes,
+even on this machine, but the umap layer will ensure that anyone running
+under UID 2002 will have all files in this sub-tree owned by 604 treated as if
+they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree
+will show the login name associated with UID 2002 as the owner.
+
+\noindent4\newline
+5 5\newline
+521 521\newline
+2002 604\newline
+7000 7000\newline
+
+The user and group mapping files should be owned by the root user, and
+should be writable only by that user. If they are not owned by root, or
+are writable by some other user, the umap mounting command will abort.
+
+Normally, the sub-treeis grafted directly into the place in
+the file hierarchy where the it should appear to users.Using the umap
+layer requires that the sub-tree be grafted somewhere else, and
+the umap layer be mounted in the desired position in the file hierarchy.
+Depending on the situation, the underlying sub-tree can be wherever is
+convenient.
+
+\subsection{Troubleshooting umap Layer Problems}
+
+The umap layer code was not built with special convenience or
+robustness in mind, as it is expected to be superseded with a better
+user ID mapping strategy in the near future. As a result, it is not
+very forgiving of errors in being set up. Here are some possible
+problems, and what to do about them.
+
+\begin{itemize}
+
+
+\item{Problem: A file belongs to NOBODY, or group NULLGROUP.
+
+Fixes: The mapping files don't know about this file's real user or group.
+Either they are not in the mapping files, or the counts on the number of
+entries in the mapping files are too low, so entries at the end (including
+these) are being ignored. Add the entries or fix the counts, and either
+unmount and remount the sub-tree, or reboot.}
+
+\item{Problem: A normal operation does not work.
+
+Fixes: Possibly, some mapping has not been set properly. Check to
+see which files are used by the operation and who they appear to be
+owned by. If they are owned by NOBODY or some other suspicious user,
+there may be a problem in the mapping files. Be sure to check groups,
+too. As above, if the counts of mappings in the mapping files are lower
+than the actual numbers of pairs, pairs at the end of the file will be
+ignored. If any changes are made in the mapping files, you will need to
+either unmount and remount or reboot before they will take effect.
+
+Another possible problem can arise because not all Unix utilities
+rely exclusively on numeric UID for identification. For instance,
+SCCS saves the login name in files. If a user's login name on two machines
+isn't the same, SCCS may veto an operation even though Unix file permissions,
+as checked by the umap layer, may say it's OK. There's not much to be
+done in such cases, unless the login name can be changed or one fiddles
+improperly with SCCS information. There may be other, undiscovered cases
+where similar problems arise, some of which may be even harder to handle.}
+
+\item{Problem: Someone has access permissions he should not have.
+
+Fixes: This is probably caused by a mistake in the mapping files. Check
+both user and group mapping files. If any changes are made in the mapping
+files, you will need to unmount and remount the sub-tree or reboot before they
+will take effect.}
+
+\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file.
+
+Fixes: Probably a mistake in the mapping files. In particular, if
+two local UIDs are mapped to a single master UID, stat calls will assign
+ownership to the first local UID occurring in the file, which may or may
+not be what was intended. (Generally speaking, mapping two local UIDs to
+a single master UID is a bad idea, but the software will not prevent it.
+Similarly, mapping a single local UID to two master UIDs is a bad idea,
+but will not be prevented. In this case, only the first mapping of the
+local UID will be done. The second, and all subsequent ones, will be
+ignored.) If any changes are made in the mapping files, you will need to
+unmount and remount the sub-tree or reboot before they will take effect.}
+
+\end{itemize}
+
+\end{document}
diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile
new file mode 100644
index 0000000..a6b33af
--- /dev/null
+++ b/sbin/mount_unionfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_union
+SRCS= mount_union.c getmntopts.c
+MAN8= mount_union.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I/sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_unionfs/mount_unionfs.8 b/sbin/mount_unionfs/mount_unionfs.8
new file mode 100644
index 0000000..65a6497
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.8
@@ -0,0 +1,204 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software donated to Berkeley by
+.\" Jan-Simon Pendry.
+.\"
+.\" 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.
+.\"
+.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_UNION 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_union
+.Nd mount union filesystems
+.Sh SYNOPSIS
+.Nm mount_union
+.Op Fl br
+.Op Fl o Ar options
+.Ar directory
+.Ar uniondir
+.Sh DESCRIPTION
+The
+.Nm mount_union
+command
+attaches
+.Ar directory
+above
+.Ar uniondir
+in such a way that the contents of both directory trees remain visible.
+By default,
+.Ar directory
+becomes the
+.Em upper
+layer and
+.Ar uniondir
+becomes the
+.Em lower
+layer.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Invert the default position, so that
+.Ar directory
+becomes the lower layer and
+.Ar uniondir
+becomes the upper layer.
+However,
+.Ar uniondir
+remains the mount point.
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.It Fl r
+Hide the lower layer completely in the same way as mounting with
+.Xr mount_lofs 8
+or
+.Xr mount_null 8 .
+.El
+.Pp
+To enforce filesystem security, the user mounting the filesystem
+must be superuser or else have write permission on the mounted-on
+directory.
+.Pp
+Filenames are looked up in the upper layer and then in the
+lower layer.
+If a directory is found in the lower layer, and there is no entry
+in the upper layer, then a
+.Em shadow
+directory will be created in the upper layer.
+It will be owned by the user who originally did the union mount,
+with mode
+.Dq rwxrwxrwx
+(0777) modified by the umask in effect at that time.
+.Pp
+If a file exists in the upper layer then there is no way to access
+a file with the same name in the lower layer.
+If necessary, a combination of loopback and union mounts can be made
+which will still allow the lower files to be accessed by a different
+pathname.
+.Pp
+Except in the case of a directory,
+access to an object is granted via the normal filesystem access checks.
+For directories, the current user must have access to both the upper
+and lower directories (should they both exist).
+.Pp
+Requests to create or modify objects in
+.Ar uniondir
+are passed to the upper layer with the exception of a few special cases.
+An attempt to open for writing a file which exists in the lower layer
+causes a copy of the
+.Em entire
+file to be made to the upper layer, and then for the upper layer copy
+to be opened.
+Similarly, an attempt to truncate a lower layer file to zero length
+causes an empty file to be created in the upper layer.
+Any other operation which would ultimately require modification to
+the lower layer fails with
+.Dv EROFS .
+.Pp
+The union filesystem manipulates the namespace, rather than
+individual filesystems.
+The union operation applies recursively down the directory tree
+now rooted at
+.Ar uniondir .
+Thus any filesystems which are mounted under
+.Ar uniondir
+will take part in the union operation.
+This differs from the
+.Em union
+option to
+.Xr mount 8
+which only applies the union operation to the mount point itself,
+and then only for lookups.
+.Sh EXAMPLES
+The commands
+.Bd -literal -offset indent
+mount -t cd9660 -o ro /dev/cd0a /usr/src
+mount -t union -o /var/obj /usr/src
+.Ed
+.Pp
+mount the CD-ROM drive
+.Pa /dev/cd0a
+on
+.Pa /usr/src
+and then attaches
+.Pa /var/obj
+on top.
+For most purposes the effect of this is to make the
+source tree appear writable
+even though it is stored on a CD-ROM.
+.Pp
+The command
+.Bd -literal -offset indent
+mount -t union -o -b /sys $HOME/sys
+.Ed
+.Pp
+attaches the system source tree below the
+.Pa sys
+directory in the user's home directory.
+This allows individual users to make private changes
+to the source, and build new kernels, without those
+changes becoming visible to other users.
+Note that the files in the lower layer remain
+accessible via
+.Pa /sys .
+.Sh SEE ALSO
+.Xr intro 2 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr mount_lofs 8 ,
+.Xr mount_null 8
+.Sh BUGS
+Without whiteout support from the filesystem backing the upper layer,
+there is no way that delete and rename operations on lower layer
+objects can be done.
+.Dv EROFS
+is returned for this kind of operations along with any others
+which would make modifications to the lower layer, such as
+.Xr chmod 1 .
+.Pp
+Running
+.Xr find 1
+over a union tree has the side-effect of creating
+a tree of shadow directories in the upper layer.
+.Sh HISTORY
+The
+.Nm mount_union
+command first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c
new file mode 100644
index 0000000..90d075b
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <miscfs/union/union.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+int subdir __P((const char *, const char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct union_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+
+ mntflags = 0;
+ args.mntflags = UNMNT_ABOVE;
+ while ((ch = getopt(argc, argv, "bo:r")) != EOF)
+ switch (ch) {
+ case 'b':
+ args.mntflags &= ~UNMNT_OPMASK;
+ args.mntflags |= UNMNT_BELOW;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case 'r':
+ args.mntflags &= ~UNMNT_OPMASK;
+ args.mntflags |= UNMNT_REPLACE;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ if (realpath(argv[0], target) == 0)
+ err(1, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(1, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ if (mount(MOUNT_UNION, argv[1], mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_union [-br] [-o options] target_fs mount_point\n");
+ exit(1);
+}
diff --git a/sbin/quotacheck/preen.c b/sbin/quotacheck/preen.c
new file mode 100644
index 0000000..005a65d
--- /dev/null
+++ b/sbin/quotacheck/preen.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1990, 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[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char *rawname(), *unrawname(), *blockcheck();
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted filesystem name */
+ long auxdata; /* auxillary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+char hotroot;
+
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen, maxrun;
+ int (*docheck)(), (*chkit)();
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+ if (name = blockcheck(fsp->fs_spec)) {
+ if (sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0))
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while (ret = startdisk(dk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while (ret = startdisk(nextdisk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (p = name + strlen(name) - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+ if (p < name)
+ len = strlen(name);
+
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+addpart(name, fsname, auxdata)
+ char *name, *fsname;
+ long auxdata;
+{
+ struct disk *dk = finddisk(name);
+ register struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)();
+{
+ register struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
+char *
+blockcheck(name)
+ char *name;
+{
+ struct stat stslash, stblock, stchar;
+ char *raw;
+ int retried = 0;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (0);
+ }
+retry:
+ if (stat(name, &stblock) < 0) {
+ perror(name);
+ printf("Can't stat %s\n", name);
+ return (0);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(name);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (name);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (name);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ name = unrawname(name);
+ retried++;
+ goto retry;
+ }
+ printf("Can't make sense out of name %s\n", name);
+ return (0);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
OpenPOWER on IntegriCloud