aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/Makefile10
-rw-r--r--sbin/Makefile.inc3
-rw-r--r--sbin/badsect/Makefile6
-rw-r--r--sbin/badsect/badsect.8132
-rw-r--r--sbin/badsect/badsect.c209
-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/clri/Makefile6
-rw-r--r--sbin/clri/clri.879
-rw-r--r--sbin/clri/clri.c140
-rw-r--r--sbin/disklabel/Makefile14
-rw-r--r--sbin/disklabel/disklabel.5.5384
-rw-r--r--sbin/disklabel/disklabel.8343
-rw-r--r--sbin/disklabel/disklabel.c1314
-rw-r--r--sbin/disklabel/dkcksum.c53
-rw-r--r--sbin/disklabel/pathnames.h40
-rw-r--r--sbin/dmesg/Makefile10
-rw-r--r--sbin/dmesg/dmesg.863
-rw-r--r--sbin/dmesg/dmesg.c157
-rw-r--r--sbin/dump/Makefile25
-rw-r--r--sbin/dump/dump.8335
-rw-r--r--sbin/dump/dump.h212
-rw-r--r--sbin/dump/dumprmt.c380
-rw-r--r--sbin/dump/itime.c271
-rw-r--r--sbin/dump/main.c560
-rw-r--r--sbin/dump/optr.c538
-rw-r--r--sbin/dump/pathnames.h42
-rw-r--r--sbin/dump/tape.c859
-rw-r--r--sbin/dump/traverse.c613
-rw-r--r--sbin/dump/unctime.c157
-rw-r--r--sbin/dumpfs/Makefile6
-rw-r--r--sbin/dumpfs/dumpfs.862
-rw-r--r--sbin/dumpfs/dumpfs.c315
-rw-r--r--sbin/dumplfs/Makefile9
-rw-r--r--sbin/dumplfs/dumplfs.859
-rw-r--r--sbin/dumplfs/dumplfs.c612
-rw-r--r--sbin/dumplfs/extern.h39
-rw-r--r--sbin/dumplfs/misc.c90
-rw-r--r--sbin/fastboot/Makefile12
-rw-r--r--sbin/fastboot/fastboot.869
-rw-r--r--sbin/fastboot/fastboot.sh38
-rw-r--r--sbin/fastboot/fasthalt.sh38
-rw-r--r--sbin/fsck/Makefile9
-rw-r--r--sbin/fsck/SMM.doc/0.t150
-rw-r--r--sbin/fsck/SMM.doc/1.t83
-rw-r--r--sbin/fsck/SMM.doc/2.t265
-rw-r--r--sbin/fsck/SMM.doc/3.t439
-rw-r--r--sbin/fsck/SMM.doc/4.t1424
-rw-r--r--sbin/fsck/SMM.doc/Makefile7
-rw-r--r--sbin/fsck/dir.c681
-rw-r--r--sbin/fsck/fsck.8291
-rw-r--r--sbin/fsck/fsck.h215
-rw-r--r--sbin/fsck/inode.c543
-rw-r--r--sbin/fsck/main.c318
-rw-r--r--sbin/fsck/pass1.c314
-rw-r--r--sbin/fsck/pass1b.c99
-rw-r--r--sbin/fsck/pass2.c430
-rw-r--r--sbin/fsck/pass3.c71
-rw-r--r--sbin/fsck/pass4.c133
-rw-r--r--sbin/fsck/pass5.c319
-rw-r--r--sbin/fsck/preen.c354
-rw-r--r--sbin/fsck/setup.c466
-rw-r--r--sbin/fsck/utilities.c566
-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/ifconfig/Makefile6
-rw-r--r--sbin/ifconfig/ifconfig.8275
-rw-r--r--sbin/ifconfig/ifconfig.c679
-rw-r--r--sbin/init/Makefile11
-rw-r--r--sbin/init/NOTES112
-rw-r--r--sbin/init/init.8291
-rw-r--r--sbin/init/init.c1297
-rw-r--r--sbin/init/pathnames.h42
-rw-r--r--sbin/mknod/Makefile6
-rw-r--r--sbin/mknod/mknod.899
-rw-r--r--sbin/mknod/mknod.c82
-rw-r--r--sbin/mount/Makefile8
-rw-r--r--sbin/mount/getmntopts.3163
-rw-r--r--sbin/mount/getmntopts.c87
-rw-r--r--sbin/mount/mntopts.h72
-rw-r--r--sbin/mount/mount.8264
-rw-r--r--sbin/mount/mount.c512
-rw-r--r--sbin/mount/mount_ufs.c131
-rw-r--r--sbin/mount/pathnames.h38
-rw-r--r--sbin/mount_cd9660/Makefile11
-rw-r--r--sbin/mount_cd9660/mount_cd9660.899
-rw-r--r--sbin/mount_cd9660/mount_cd9660.c130
-rw-r--r--sbin/mount_fdesc/Makefile11
-rw-r--r--sbin/mount_fdesc/mount_fdesc.8171
-rw-r--r--sbin/mount_fdesc/mount_fdesc.c100
-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_kernfs/Makefile11
-rw-r--r--sbin/mount_kernfs/mount_kernfs.8131
-rw-r--r--sbin/mount_kernfs/mount_kernfs.c100
-rw-r--r--sbin/mount_lfs/Makefile11
-rw-r--r--sbin/mount_lfs/mount_lfs.8125
-rw-r--r--sbin/mount_lfs/mount_lfs.c147
-rw-r--r--sbin/mount_lfs/pathnames.h36
-rw-r--r--sbin/mount_nfs/Makefile14
-rw-r--r--sbin/mount_nfs/mount_nfs.8222
-rw-r--r--sbin/mount_nfs/mount_nfs.c551
-rw-r--r--sbin/mount_null/Makefile11
-rw-r--r--sbin/mount_null/mount_null.8220
-rw-r--r--sbin/mount_null/mount_null.c129
-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_portal/Makefile15
-rw-r--r--sbin/mount_portal/activate.c215
-rw-r--r--sbin/mount_portal/conf.c329
-rw-r--r--sbin/mount_portal/mount_portal.8136
-rw-r--r--sbin/mount_portal/mount_portal.c261
-rw-r--r--sbin/mount_portal/pathnames.h44
-rw-r--r--sbin/mount_portal/portal.conf7
-rw-r--r--sbin/mount_portal/portald.h82
-rw-r--r--sbin/mount_portal/pt_conf.c51
-rw-r--r--sbin/mount_portal/pt_exec.c61
-rw-r--r--sbin/mount_portal/pt_file.c106
-rw-r--r--sbin/mount_portal/pt_tcp.c158
-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_procfs/Makefile11
-rw-r--r--sbin/mount_procfs/mount_procfs.8253
-rw-r--r--sbin/mount_procfs/mount_procfs.c100
-rw-r--r--sbin/mount_umap/Makefile11
-rw-r--r--sbin/mount_umap/mount_umap.8131
-rw-r--r--sbin/mount_umap/mount_umap.c232
-rw-r--r--sbin/mount_umap/sample.group.mapfile2
-rw-r--r--sbin/mount_umap/sample.user.mapfile3
-rw-r--r--sbin/mount_umap/umap_manual175
-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/sample.group.mapfile2
-rw-r--r--sbin/mount_umapfs/sample.user.mapfile3
-rw-r--r--sbin/mount_umapfs/umap_manual175
-rw-r--r--sbin/mount_union/Makefile14
-rw-r--r--sbin/mount_union/mount_union.8204
-rw-r--r--sbin/mount_union/mount_union.c140
-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/mountd/Makefile10
-rw-r--r--sbin/mountd/exports.5250
-rw-r--r--sbin/mountd/mountd.8100
-rw-r--r--sbin/mountd/mountd.c2005
-rw-r--r--sbin/mountd/netgroup.591
-rw-r--r--sbin/mountd/pathnames.h39
-rw-r--r--sbin/newfs/Makefile14
-rw-r--r--sbin/newfs/mkfs.c1227
-rw-r--r--sbin/newfs/newfs.8282
-rw-r--r--sbin/newfs/newfs.c678
-rw-r--r--sbin/newlfs/Makefile9
-rw-r--r--sbin/newlfs/config.h134
-rw-r--r--sbin/newlfs/extern.h45
-rw-r--r--sbin/newlfs/lfs.c673
-rw-r--r--sbin/newlfs/misc.c83
-rw-r--r--sbin/newlfs/newfs.c466
-rw-r--r--sbin/newlfs/newlfs.895
-rw-r--r--sbin/nfsd/Makefile9
-rw-r--r--sbin/nfsd/nfsd.8114
-rw-r--r--sbin/nfsd/nfsd.c589
-rw-r--r--sbin/nfsiod/Makefile7
-rw-r--r--sbin/nfsiod/nfsiod.874
-rw-r--r--sbin/nfsiod/nfsiod.c167
-rw-r--r--sbin/nologin/Makefile14
-rw-r--r--sbin/nologin/nologin.854
-rw-r--r--sbin/nologin/nologin.sh38
-rw-r--r--sbin/ping/Makefile8
-rw-r--r--sbin/ping/ping.8327
-rw-r--r--sbin/ping/ping.c986
-rw-r--r--sbin/quotacheck/Makefile8
-rw-r--r--sbin/quotacheck/preen.c354
-rw-r--r--sbin/quotacheck/quotacheck.8157
-rw-r--r--sbin/quotacheck/quotacheck.c636
-rw-r--r--sbin/reboot/Makefile18
-rw-r--r--sbin/reboot/boot_hp300.8117
-rw-r--r--sbin/reboot/boot_i386.8126
-rw-r--r--sbin/reboot/boot_sparc.889
-rw-r--r--sbin/reboot/boot_tahoe.8152
-rw-r--r--sbin/reboot/boot_vax.8322
-rw-r--r--sbin/reboot/reboot.888
-rw-r--r--sbin/reboot/reboot.c204
-rw-r--r--sbin/restore/Makefile17
-rw-r--r--sbin/restore/dirs.c747
-rw-r--r--sbin/restore/extern.h108
-rw-r--r--sbin/restore/interactive.c755
-rw-r--r--sbin/restore/main.c345
-rw-r--r--sbin/restore/pathnames.h43
-rw-r--r--sbin/restore/restore.8405
-rw-r--r--sbin/restore/restore.c819
-rw-r--r--sbin/restore/restore.h137
-rw-r--r--sbin/restore/symtab.c629
-rw-r--r--sbin/restore/tape.c1348
-rw-r--r--sbin/restore/utilities.c395
-rw-r--r--sbin/route/Makefile25
-rw-r--r--sbin/route/ccitt_addr.c175
-rw-r--r--sbin/route/keywords44
-rw-r--r--sbin/route/route.8324
-rw-r--r--sbin/route/route.c1415
-rw-r--r--sbin/savecore/Makefile8
-rw-r--r--sbin/savecore/savecore.8124
-rw-r--r--sbin/savecore/savecore.c649
-rw-r--r--sbin/scsiformat/Makefile7
-rw-r--r--sbin/scsiformat/scsiformat.882
-rw-r--r--sbin/scsiformat/scsiformat.c664
-rw-r--r--sbin/shutdown/Makefile9
-rw-r--r--sbin/shutdown/pathnames.h41
-rw-r--r--sbin/shutdown/shutdown.8162
-rw-r--r--sbin/shutdown/shutdown.c492
-rw-r--r--sbin/slattach/Makefile7
-rw-r--r--sbin/slattach/slattach.890
-rw-r--r--sbin/slattach/slattach.c173
-rw-r--r--sbin/startslip/Makefile6
-rw-r--r--sbin/startslip/startslip.1105
-rw-r--r--sbin/startslip/startslip.c452
-rw-r--r--sbin/swapon/Makefile6
-rw-r--r--sbin/swapon/swapon.890
-rw-r--r--sbin/swapon/swapon.c120
-rw-r--r--sbin/tunefs/Makefile6
-rw-r--r--sbin/tunefs/tunefs.8138
-rw-r--r--sbin/tunefs/tunefs.c284
-rw-r--r--sbin/umount/Makefile7
-rw-r--r--sbin/umount/umount.8123
-rw-r--r--sbin/umount/umount.c421
-rw-r--r--usr.sbin/mount_portalfs/Makefile15
-rw-r--r--usr.sbin/mount_portalfs/activate.c215
-rw-r--r--usr.sbin/mount_portalfs/conf.c329
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.8136
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c261
-rw-r--r--usr.sbin/mount_portalfs/pathnames.h44
-rw-r--r--usr.sbin/mount_portalfs/portal.conf7
-rw-r--r--usr.sbin/mount_portalfs/portald.h82
-rw-r--r--usr.sbin/mount_portalfs/pt_conf.c51
-rw-r--r--usr.sbin/mount_portalfs/pt_exec.c61
-rw-r--r--usr.sbin/mount_portalfs/pt_file.c106
-rw-r--r--usr.sbin/mount_portalfs/pt_tcp.c158
-rw-r--r--usr.sbin/mountd/Makefile10
-rw-r--r--usr.sbin/mountd/exports.5250
-rw-r--r--usr.sbin/mountd/mountd.8100
-rw-r--r--usr.sbin/mountd/mountd.c2005
-rw-r--r--usr.sbin/mountd/netgroup.591
-rw-r--r--usr.sbin/mountd/pathnames.h39
-rw-r--r--usr.sbin/nfsd/Makefile9
-rw-r--r--usr.sbin/nfsd/nfsd.8114
-rw-r--r--usr.sbin/nfsd/nfsd.c589
-rw-r--r--usr.sbin/nologin/Makefile14
-rw-r--r--usr.sbin/nologin/nologin.854
-rw-r--r--usr.sbin/nologin/nologin.sh38
304 files changed, 70407 insertions, 0 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
new file mode 100644
index 000000000000..919fba6dec9f
--- /dev/null
+++ b/sbin/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.5 (Berkeley) 3/31/94
+
+SUBDIR= XNSrouted badsect clri disklabel dmesg dump dumpfs dumplfs fastboot \
+ fsck fsdb icheck ifconfig init mknod mount mount_cd9660 mount_fdesc \
+ mount_kernfs mount_lfs mount_nfs mount_null mount_portal \
+ mount_procfs mount_umap mount_union mountd ncheck newfs newlfs nfsd \
+ nfsiod nologin ping quotacheck reboot restore route routed savecore \
+ scsiformat shutdown slattach startslip swapon tunefs umount
+
+.include <bsd.subdir.mk>
diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc
new file mode 100644
index 000000000000..79c318e633f1
--- /dev/null
+++ b/sbin/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93
+
+BINDIR?= /sbin
diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile
new file mode 100644
index 000000000000..ff9f4f1d22cb
--- /dev/null
+++ b/sbin/badsect/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= badsect
+MAN8= badsect.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/badsect/badsect.8 b/sbin/badsect/badsect.8
new file mode 100644
index 000000000000..840011d00225
--- /dev/null
+++ b/sbin/badsect/badsect.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)badsect.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt BADSECT 8
+.Os BSD 4
+.Sh NAME
+.Nm badsect
+.Nd create files to contain bad sectors
+.Sh SYNOPSIS
+.Nm /etc/badsect
+.Ar bbdir sector ...
+.Sh DESCRIPTION
+.Nm Badsect
+makes a file to contain a bad sector. Normally, bad sectors
+are made inaccessible by the standard formatter, which provides
+a forwarding table for bad sectors to the driver; see
+.Xr bad144 8
+for details.
+If a driver supports the bad blocking standard it is much preferable to
+use that method to isolate bad blocks, since the bad block forwarding
+makes the pack appear perfect, and such packs can then be copied with
+.Xr dd 1 .
+The technique used by this program is also less general than
+bad block forwarding, as
+.Nm badsect
+can't make amends for
+bad blocks in the i-list of file systems or in swap areas.
+.Pp
+On some disks,
+adding a sector which is suddenly bad to the bad sector table
+currently requires the running of the standard
+.Tn DEC
+formatter.
+Thus to deal with a newly bad block
+or on disks where the drivers
+do not support the bad-blocking standard
+.Nm badsect
+may be used to good effect.
+.Pp
+.Nm Badsect
+is used on a quiet file system in the following way:
+First mount the file system, and change to its root directory.
+Make a directory
+.Li BAD
+there. Run
+.Nm badsect
+giving as argument the
+.Ar BAD
+directory followed by
+all the bad sectors you wish to add.
+(The sector numbers must be relative to the beginning of
+the file system, but this is not hard as the system reports
+relative sector numbers in its console error messages.)
+Then change back to the root directory, unmount the file system
+and run
+.Xr fsck 8
+on the file system. The bad sectors should show up in two files
+or in the bad sector files and the free list. Have
+.Xr fsck
+remove files containing the offending bad sectors, but
+.Em do not
+have it remove the
+.Pa BAD/ Ns Em nnnnn
+files.
+This will leave the bad sectors in only the
+.Li BAD
+files.
+.Pp
+.Nm Badsect
+works by giving the specified sector numbers in a
+.Xr mknod 2
+system call,
+creating an illegal file whose first block address is the block containing
+bad sector and whose name is the bad sector number.
+When it is discovered by
+.Xr fsck
+it will ask
+.Dq Li "HOLD BAD BLOCK ?"
+A positive response will cause
+.Xr fsck
+to convert the inode to a regular file containing the bad block.
+.Sh SEE ALSO
+.Xr bad144 8 ,
+.Xr fsck 8 ,
+.Xr format 8
+.Sh DIAGNOSTICS
+.Nm Badsect
+refuses to attach a block that
+resides in a critical area or is out of range of the file system.
+A warning is issued if the block is already in use.
+.Sh BUGS
+If more than one sector which comprise a file system fragment are bad,
+you should specify only one of them to
+.Nm badsect ,
+as the blocks in the bad sector files actually cover all the sectors in a
+file system fragment.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.1 .
diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c
new file mode 100644
index 000000000000..fa978b10a79f
--- /dev/null
+++ b/sbin/badsect/badsect.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1981, 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1981, 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)badsect.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * badsect
+ *
+ * Badsect takes a list of file-system relative sector numbers
+ * and makes files containing the blocks of which these sectors are a part.
+ * It can be used to contain sectors which have problems if these sectors
+ * are not part of the bad file for the pack (see bad144). For instance,
+ * this program can be used if the driver for the file system in question
+ * does not support bad block forwarding.
+ */
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/stat.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+union {
+ struct fs fs;
+ char fsx[SBSIZE];
+} ufs;
+#define sblock ufs.fs
+union {
+ struct cg cg;
+ char cgx[MAXBSIZE];
+} ucg;
+#define acg ucg.cg
+struct fs *fs;
+int fso, fsi;
+int errs;
+long dev_bsize = 1;
+
+char buf[MAXBSIZE];
+
+void rdfs __P((daddr_t, int, char *));
+int chkuse __P((daddr_t, int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ daddr_t number;
+ struct stat stbuf, devstat;
+ register struct direct *dp;
+ DIR *dirp;
+ char name[BUFSIZ];
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: badsect bbdir blkno [ blkno ]\n");
+ exit(1);
+ }
+ if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0) {
+ perror(argv[1]);
+ exit(2);
+ }
+ strcpy(name, _PATH_DEV);
+ if ((dirp = opendir(name)) == NULL) {
+ perror(name);
+ exit(3);
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ strcpy(&name[5], dp->d_name);
+ if (stat(name, &devstat) < 0) {
+ perror(name);
+ exit(4);
+ }
+ if (stbuf.st_dev == devstat.st_rdev &&
+ (devstat.st_mode & IFMT) == IFBLK)
+ break;
+ }
+ closedir(dirp);
+ if (dp == NULL) {
+ printf("Cannot find dev 0%o corresponding to %s\n",
+ stbuf.st_rdev, argv[1]);
+ exit(5);
+ }
+ if ((fsi = open(name, 0)) < 0) {
+ perror(name);
+ exit(6);
+ }
+ fs = &sblock;
+ rdfs(SBOFF, SBSIZE, (char *)fs);
+ dev_bsize = fs->fs_fsize / fsbtodb(fs, 1);
+ for (argc -= 2, argv += 2; argc > 0; argc--, argv++) {
+ number = atoi(*argv);
+ if (chkuse(number, 1))
+ continue;
+ if (mknod(*argv, IFMT|0600, dbtofsb(fs, number)) < 0) {
+ perror(*argv);
+ errs++;
+ }
+ }
+ printf("Don't forget to run ``fsck %s''\n", name);
+ exit(errs);
+}
+
+int
+chkuse(blkno, cnt)
+ daddr_t blkno;
+ int cnt;
+{
+ int cg;
+ daddr_t fsbn, bn;
+
+ fsbn = dbtofsb(fs, blkno);
+ if ((unsigned)(fsbn+cnt) > fs->fs_size) {
+ printf("block %d out of range of file system\n", blkno);
+ return (1);
+ }
+ cg = dtog(fs, fsbn);
+ if (fsbn < cgdmin(fs, cg)) {
+ if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) {
+ printf("block %d in non-data area: cannot attach\n",
+ blkno);
+ return (1);
+ }
+ } else {
+ if ((fsbn+cnt) > cgbase(fs, cg+1)) {
+ printf("block %d in non-data area: cannot attach\n",
+ blkno);
+ return (1);
+ }
+ }
+ rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)sblock.fs_cgsize,
+ (char *)&acg);
+ if (!cg_chkmagic(&acg)) {
+ fprintf(stderr, "cg %d: bad magic number\n", cg);
+ errs++;
+ return (1);
+ }
+ bn = dtogd(fs, fsbn);
+ if (isclr(cg_blksfree(&acg), bn))
+ printf("Warning: sector %d is in use\n", blkno);
+ return (0);
+}
+
+/*
+ * read a block from the file system
+ */
+void
+rdfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+
+ if (lseek(fsi, (off_t)bno * dev_bsize, SEEK_SET) < 0) {
+ printf("seek error: %ld\n", bno);
+ perror("rdfs");
+ exit(1);
+ }
+ n = read(fsi, bf, size);
+ if (n != size) {
+ printf("read error: %ld\n", bno);
+ perror("rdfs");
+ exit(1);
+ }
+}
diff --git a/sbin/bsdlabel/Makefile b/sbin/bsdlabel/Makefile
new file mode 100644
index 000000000000..3f4749c70f09
--- /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 000000000000..bf14e0f23b04
--- /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 000000000000..4a2934078584
--- /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 000000000000..fb6f6cd14d58
--- /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 000000000000..f7a1e4917df6
--- /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 000000000000..def3297d205e
--- /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/clri/Makefile b/sbin/clri/Makefile
new file mode 100644
index 000000000000..deb3cbb17266
--- /dev/null
+++ b/sbin/clri/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= clri
+MAN8= clri.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8
new file mode 100644
index 000000000000..df313a778cf3
--- /dev/null
+++ b/sbin/clri/clri.8
@@ -0,0 +1,79 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)clri.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt CLRI 8
+.Os BSD 4
+.Sh NAME
+.Nm clri
+.Nd clear an inode
+.Sh SYNOPSIS
+.Nm clri
+.Ar special_device inode_number ...
+.Sh DESCRIPTION
+.Bf -symbolic
+.Nm Clri
+is obsoleted for normal file system repair work by
+.Xr fsck 8 .
+.Ef
+.Pp
+.Nm Clri
+zeros out the inodes with the specified inode number(s)
+on the filesystem residing on the given
+.Ar special_device .
+The
+.Xr fsck 8
+utility is usually run after
+.Nm clri
+to reclaim the zero'ed inode(s) and the
+blocks previously claimed by those inode(s).
+Both read and write permission are required on the specified
+.Ar special_device .
+.Pp
+The primary purpose of this routine
+is to remove a file which
+for some reason is not being properly handled by
+.Xr fsck 8 .
+Once removed,
+it is anticipated that
+.Xr fsck 8
+will be able to clean up the resulting mess.
+.Sh "SEE ALSO"
+.Xr fsck 8 ,
+.Xr fsdb 8 ,
+.Xr icheck 8 ,
+.Xr ncheck 8
+.Sh BUGS
+If the file is open, the work of
+.Nm clri
+will be lost when the inode is written back to disk from the inode cache.
diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c
new file mode 100644
index 000000000000..c2bdcccc1d9d
--- /dev/null
+++ b/sbin/clri/clri.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rich $alz of BBN Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct fs *sbp;
+ register struct dinode *ip;
+ register int fd;
+ struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)];
+ long generation, bsize;
+ off_t offset;
+ int inonum;
+ char *fs, sblock[SBSIZE];
+
+ if (argc < 3) {
+ (void)fprintf(stderr, "usage: clri filesystem inode ...\n");
+ exit(1);
+ }
+
+ fs = *++argv;
+
+ /* get the superblock. */
+ if ((fd = open(fs, O_RDWR, 0)) < 0)
+ err(1, "%s", fs);
+ if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0)
+ err(1, "%s", fs);
+ if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) {
+ (void)fprintf(stderr,
+ "clri: %s: can't read the superblock.\n", fs);
+ exit(1);
+ }
+
+ sbp = (struct fs *)sblock;
+ if (sbp->fs_magic != FS_MAGIC) {
+ (void)fprintf(stderr,
+ "clri: %s: superblock magic number 0x%x, not 0x%x.\n",
+ fs, sbp->fs_magic, FS_MAGIC);
+ exit(1);
+ }
+ bsize = sbp->fs_bsize;
+
+ /* remaining arguments are inode numbers. */
+ while (*++argv) {
+ /* get the inode number. */
+ if ((inonum = atoi(*argv)) <= 0) {
+ (void)fprintf(stderr,
+ "clri: %s is not a valid inode number.\n", *argv);
+ exit(1);
+ }
+ (void)printf("clearing %d\n", inonum);
+
+ /* read in the appropriate block. */
+ offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */
+ offset = fsbtodb(sbp, offset); /* fs blk disk blk */
+ offset *= DEV_BSIZE; /* disk blk to bytes */
+
+ /* seek and read the block */
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ err(1, "%s", fs);
+ if (read(fd, ibuf, bsize) != bsize)
+ err(1, "%s", fs);
+
+ /* get the inode within the block. */
+ ip = &ibuf[ino_to_fsbo(sbp, inonum)];
+
+ /* clear the inode, and bump the generation count. */
+ generation = ip->di_gen + 1;
+ memset(ip, 0, sizeof(*ip));
+ ip->di_gen = generation;
+
+ /* backup and write the block */
+ if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0)
+ err(1, "%s", fs);
+ if (write(fd, ibuf, bsize) != bsize)
+ err(1, "%s", fs);
+ (void)fsync(fd);
+ }
+ (void)close(fd);
+ exit(0);
+}
diff --git a/sbin/disklabel/Makefile b/sbin/disklabel/Makefile
new file mode 100644
index 000000000000..3f4749c70f09
--- /dev/null
+++ b/sbin/disklabel/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/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5.5
new file mode 100644
index 000000000000..fb6f6cd14d58
--- /dev/null
+++ b/sbin/disklabel/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/disklabel/disklabel.8 b/sbin/disklabel/disklabel.8
new file mode 100644
index 000000000000..bf14e0f23b04
--- /dev/null
+++ b/sbin/disklabel/disklabel.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/disklabel/disklabel.c b/sbin/disklabel/disklabel.c
new file mode 100644
index 000000000000..4a2934078584
--- /dev/null
+++ b/sbin/disklabel/disklabel.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/disklabel/dkcksum.c b/sbin/disklabel/dkcksum.c
new file mode 100644
index 000000000000..f7a1e4917df6
--- /dev/null
+++ b/sbin/disklabel/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/disklabel/pathnames.h b/sbin/disklabel/pathnames.h
new file mode 100644
index 000000000000..def3297d205e
--- /dev/null
+++ b/sbin/disklabel/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/dmesg/Makefile b/sbin/dmesg/Makefile
new file mode 100644
index 000000000000..fc5edce2ae16
--- /dev/null
+++ b/sbin/dmesg/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= dmesg
+MAN8= dmesg.0
+BINGRP= kmem
+BINMODE=2555
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+.include <bsd.prog.mk>
diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8
new file mode 100644
index 000000000000..7dd0acf633ed
--- /dev/null
+++ b/sbin/dmesg/dmesg.8
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt DMESG 8
+.Os BSD 4
+.Sh NAME
+.Nm dmesg
+.Nd "display the system message buffer"
+.Sh SYNOPSIS
+.Nm dmesg
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Sh DESCRIPTION
+.Nm Dmesg
+displays the contents of the system message buffer.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default ``/dev/kmem''.
+.It Fl N
+Extract the name list from the specified system instead of the default
+``/vmunix''.
+.El
+.Sh SEE ALSO
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c
new file mode 100644
index 000000000000..d789f5dcaa37
--- /dev/null
+++ b/sbin/dmesg/dmesg.c
@@ -0,0 +1,157 @@
+/*-
+ * 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 copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/msgbuf.h>
+
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+
+struct nlist nl[] = {
+#define X_MSGBUF 0
+ { "_msgbufp" },
+ { NULL },
+};
+
+void usage __P((void));
+
+#define KREAD(addr, var) \
+ kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var)
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch, newl, skip;
+ register char *p, *ep;
+ struct msgbuf *bufp, cur;
+ char *memf, *nlistf;
+ kvm_t *kd;
+ char buf[5];
+
+ memf = nlistf = NULL;
+ while ((ch = getopt(argc, argv, "M:N:")) != EOF)
+ switch(ch) {
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (memf != NULL || nlistf != NULL)
+ setgid(getgid());
+
+ /* Read in kernel message buffer, do sanity checks. */
+ if ((kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg")) == NULL)
+ exit (1);
+ if (kvm_nlist(kd, nl) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ if (nl[X_MSGBUF].n_type == 0)
+ errx(1, "%s: msgbufp not found", nlistf ? nlistf : "namelist");
+ if (KREAD(nl[X_MSGBUF].n_value, bufp) || KREAD((long)bufp, cur))
+ errx(1, "kvm_read: %s", kvm_geterr(kd));
+ kvm_close(kd);
+ if (cur.msg_magic != MSG_MAGIC)
+ errx(1, "magic number incorrect");
+ if (cur.msg_bufx >= MSG_BSIZE)
+ cur.msg_bufx = 0;
+
+ /*
+ * The message buffer is circular; start at the read pointer, and
+ * go to the write pointer - 1.
+ */
+ p = cur.msg_bufc + cur.msg_bufx;
+ ep = cur.msg_bufc + cur.msg_bufx - 1;
+ for (newl = skip = 0; p != ep; ++p) {
+ if (p == cur.msg_bufc + MSG_BSIZE)
+ p = cur.msg_bufc;
+ ch = *p;
+ /* Skip "\n<.*>" syslog sequences. */
+ if (skip) {
+ if (ch == '>')
+ newl = skip = 0;
+ continue;
+ }
+ if (newl && ch == '<') {
+ skip = 1;
+ continue;
+ }
+ if (ch == '\0')
+ continue;
+ newl = ch == '\n';
+ (void)vis(buf, ch, 0, 0);
+ if (buf[1] == 0)
+ (void)putchar(buf[0]);
+ else
+ (void)printf("%s", buf);
+ }
+ if (!newl)
+ (void)putchar('\n');
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: dmesg [-M core] [-N system]\n");
+ exit(1);
+}
diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile
new file mode 100644
index 000000000000..78ab5d8be25d
--- /dev/null
+++ b/sbin/dump/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+# dump.h header file
+# itime.c reads /etc/dumpdates
+# main.c driver
+# optr.c operator interface
+# dumprmt.c handles remote tape via rmt(8)
+# tape.c handles the mag tape and opening/closing
+# traverse.c traverses the file system
+# unctime.c undo ctime
+#
+# DEBUG use local directory to find ddate and dumpdates
+# TDEBUG trace out the process forking
+
+PROG= dump
+LINKS= ${BINDIR}/dump ${BINDIR}/rdump
+CFLAGS+=-DRDUMP
+SRCS= itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c
+BINOWN= root
+BINGRP= tty
+BINMODE=6555
+MAN8= dump.0
+MLINKS+=dump.8 rdump.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8
new file mode 100644
index 000000000000..2cd9335c454e
--- /dev/null
+++ b/sbin/dump/dump.8
@@ -0,0 +1,335 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dump.8 8.1 (Berkeley) 6/16/93
+.\"
+.Dd June 16, 1993
+.Dt DUMP 8
+.Os BSD 4
+.Sh NAME
+.Nm dump
+.Nd filesystem backup
+.Sh SYNOPSIS
+.Nm dump
+.Op Cm 0123456789BbhfusTdWn Op Ar argument ...
+.Op Ar filesystem
+.Sh DESCRIPTION
+.Nm Dump
+examines files
+on a filesystem
+and determines which files
+need to be backed up. These files
+are copied to the given disk, tape or other
+storage medium for safe keeping (see the
+.Cm f
+option below for doing remote backups).
+A dump that is larger than the output medium is broken into
+multiple volumes.
+On most media the size is determined by writing until an
+end-of-media indication is returned.
+On media that cannot reliably return an end-of-media indication
+(such as some cartridge tape drives)
+each volume is of a fixed size;
+the actual size is determined by the tape size and density and/or
+block count options below.
+By default, the same output file name is used for each volume
+after prompting the operator to change media.
+.Pp
+The following options are supported by
+.Nm dump:
+.Bl -tag -width 4n
+.It Cm 0\-9
+Dump levels.
+A level 0, full backup,
+guarantees the entire file system is copied
+(but see also the
+.Cm h
+option below).
+A level number above 0,
+incremental backup,
+tells dump to
+copy all files new or modified since the
+last dump of the same or lower level. The default
+level is 9.
+.It Cm B Ar records
+The number of dump records per volume.
+This option overrides the calculation of tape size
+based on length and density.
+.It Cm b Ar blocksize
+The number of kilobytes per dump record.
+.It Cm h Ar level
+Honor the user
+.Dq nodump
+flag
+.Dp Dv UF_NODUMP
+only for dumps at or above the given
+.Ar level .
+The default honor level is 1,
+so that incremental backups omit such files
+but full backups retain them.
+.It Cm f Ar file
+Write the backup to
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/rmt12
+(a tape drive),
+.Pa /dev/rsd1c
+(a disk drive),
+an ordinary file,
+or
+.Ql Fl
+(the standard output).
+Multiple file names may be given as a single argument separated by commas.
+Each file will be used for one dump volume in the order listed;
+if the dump requires more volumes than the number of names given,
+the last file name will used for all remaining volumes after prompting
+for media changes.
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm dump
+writes to the named file on the remote host using
+.Xr rmt 8 .
+.It Cm d Ar density
+Set tape density to
+.Ar density .
+The default is 1600BPI.
+.It Cm n
+Whenever
+.Nm dump
+requires operator attention,
+notify all operators in the group
+.Dq operator
+by means similar to a
+.Xr wall 1 .
+.It Cm s Ar feet
+Attempt to calculate the amount of tape needed
+at a particular density.
+If this amount is exceeded,
+.Nm dump
+prompts for a new tape.
+It is recommended to be a bit conservative on this option.
+The default tape length is 2300 feet.
+.It Cm u
+Update the file
+.Pa /etc/dumpdates
+after a successful dump.
+The format of
+.Pa /etc/dumpdates
+is readable by people, consisting of one
+free format record per line:
+filesystem name,
+increment level
+and
+.Xr ctime 3
+format dump date.
+There may be only one entry per filesystem at each level.
+The file
+.Pa /etc/dumpdates
+may be edited to change any of the fields,
+if necessary.
+.It Cm T Ar date
+Use the specified date as the starting time for the dump
+instead of the time determined from looking in
+.Pa /etc/dumpdates .
+The format of date is the same as that of
+.Xr ctime 3 .
+This option is useful for automated dump scripts that wish to
+dump over a specific period of time.
+The
+.Cm T
+option is mutually exclusive from the
+.Cm u
+option.
+.It Cm W
+.Nm Dump
+tells the operator what file systems need to be dumped.
+This information is gleaned from the files
+.Pa /etc/dumpdates
+and
+.Pa /etc/fstab .
+The
+.Cm W
+option causes
+.Nm dump
+to print out, for each file system in
+.Pa /etc/dumpdates
+the most recent dump date and level,
+and highlights those file systems that should be dumped.
+If the
+.Cm W
+option is set, all other options are ignored, and
+.Nm dump
+exits immediately.
+.It Cm w
+Is like W, but prints only those filesystems which need to be dumped.
+.El
+.Pp
+.Nm Dump
+requires operator intervention on these conditions:
+end of tape,
+end of dump,
+tape write error,
+tape open error or
+disk read error (if there are more than a threshold of 32).
+In addition to alerting all operators implied by the
+.Cm n
+key,
+.Nm dump
+interacts with the operator on
+.Em dump's
+control terminal at times when
+.Nm dump
+can no longer proceed,
+or if something is grossly wrong.
+All questions
+.Nm dump
+poses
+.Em must
+be answered by typing
+.Dq yes
+or
+.Dq no ,
+appropriately.
+.Pp
+Since making a dump involves a lot of time and effort for full dumps,
+.Nm dump
+checkpoints itself at the start of each tape volume.
+If writing that volume fails for some reason,
+.Nm dump
+will,
+with operator permission,
+restart itself from the checkpoint
+after the old tape has been rewound and removed,
+and a new tape has been mounted.
+.Pp
+.Nm Dump
+tells the operator what is going on at periodic intervals,
+including usually low estimates of the number of blocks to write,
+the number of tapes it will take, the time to completion, and
+the time to the tape change.
+The output is verbose,
+so that others know that the terminal
+controlling
+.Nm dump
+is busy,
+and will be for some time.
+.Pp
+In the event of a catastrophic disk event, the time required
+to restore all the necessary backup tapes or files to disk
+can be kept to a minimum by staggering the incremental dumps.
+An efficient method of staggering incremental dumps
+to minimize the number of tapes follows:
+.Bl -bullet -offset indent
+.It
+Always start with a level 0 backup, for example:
+.Bd -literal -offset indent
+/etc/dump 0uf /dev/nrst1 /usr/src
+.Ed
+.Pp
+This should be done at set intervals, say once a month or once every two months,
+and on a set of fresh tapes that is saved forever.
+.It
+After a level 0, dumps of active file
+systems are taken on a daily basis,
+using a modified Tower of Hanoi algorithm,
+with this sequence of dump levels:
+.Bd -literal -offset indent
+3 2 5 4 7 6 9 8 9 9 ...
+.Ed
+.Pp
+For the daily dumps, it should be possible to use a fixed number of tapes
+for each day, used on a weekly basis.
+Each week, a level 1 dump is taken, and
+the daily Hanoi sequence repeats beginning with 3.
+For weekly dumps, another fixed set of tapes per dumped file system is
+used, also on a cyclical basis.
+.El
+.Pp
+After several months or so, the daily and weekly tapes should get
+rotated out of the dump cycle and fresh tapes brought in.
+.Sh FILES
+.Bl -tag -width /etc/dumpdates -compact
+.It Pa /dev/rmt8
+default tape unit to dump to
+.It Pa /etc/dumpdates
+dump date records
+.It Pa /etc/fstab
+dump table: file systems and frequency
+.It Pa /etc/group
+to find group
+.Em operator
+.El
+.Sh SEE ALSO
+.Xr restore 8 ,
+.Xr rmt 8 ,
+.Xr dump 5 ,
+.Xr fstab 5
+.Sh DIAGNOSTICS
+Many, and verbose.
+.Pp
+Dump exits with zero status on success.
+Startup errors are indicated with an exit code of 1;
+abnormal termination is indicated with an exit code of 3.
+.Sh BUGS
+.Pp
+Fewer than 32 read errors on the filesystem are ignored.
+Each reel requires a new process, so parent processes for
+reels already written just hang around until the entire tape
+is written.
+.Pp
+.Nm Dump
+with the
+.Cm W
+or
+.Cm w
+options does not report filesystems that have never been recorded
+in
+.Pa /etc/dumpdates ,
+even if listed in
+.Pa /etc/fstab .
+.Pp
+It would be nice if
+.Nm dump
+knew about the dump sequence,
+kept track of the tapes scribbled on,
+told the operator which tape to mount when,
+and provided more assistance
+for the operator running
+.Xr restore .
+.Sh HISTORY
+A
+.Nm dump
+command appeared in Version 6 AT&T UNIX.
diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h
new file mode 100644
index 000000000000..9060b00e9477
--- /dev/null
+++ b/sbin/dump/dump.h
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dump.h 8.1 (Berkeley) 6/5/93
+ */
+
+#define MAXINOPB (MAXBSIZE / sizeof(struct dinode))
+#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t))
+
+/*
+ * Dump maps used to describe what is to be dumped.
+ */
+int mapsize; /* size of the state maps */
+char *usedinomap; /* map of allocated inodes */
+char *dumpdirmap; /* map of directories to be dumped */
+char *dumpinomap; /* map of files to be dumped */
+/*
+ * Map manipulation macros.
+ */
+#define SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY)
+#define CLRINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] &= ~(1 << ((u_int)((ino) - 1) % NBBY))
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY)))
+
+/*
+ * All calculations done in 0.1" units!
+ */
+char *disk; /* name of the disk file */
+char *tape; /* name of the tape file */
+char *dumpdates; /* name of the file containing dump date information*/
+char *temp; /* name of the file for doing rewrite of dumpdates */
+char lastlevel; /* dump level of previous dump */
+char level; /* dump level of this dump */
+int uflag; /* update flag */
+int diskfd; /* disk file descriptor */
+int tapefd; /* tape file descriptor */
+int pipeout; /* true => output to standard output */
+ino_t curino; /* current inumber; used globally */
+int newtape; /* new tape flag */
+int density; /* density in 0.1" units */
+long tapesize; /* estimated tape size, blocks */
+long tsize; /* tape size in 0.1" units */
+long asize; /* number of 0.1" units written on current tape */
+int etapes; /* estimated number of tapes */
+int nonodump; /* if set, do not honor UF_NODUMP user flags */
+
+int notify; /* notify operator flag */
+int blockswritten; /* number of blocks written on current tape */
+int tapeno; /* current tape number */
+time_t tstart_writing; /* when started writing the first tape block */
+struct fs *sblock; /* the file system super block */
+char sblock_buf[MAXBSIZE];
+long dev_bsize; /* block size of underlying disk device */
+int dev_bshift; /* log2(dev_bsize) */
+int tp_bshift; /* log2(TP_BSIZE) */
+
+#ifndef __P
+#include <sys/cdefs.h>
+#endif
+
+/* operator interface functions */
+void broadcast __P((char *message));
+void lastdump __P((int arg)); /* int should be char */
+void msg __P((const char *fmt, ...));
+void msgtail __P((const char *fmt, ...));
+int query __P((char *question));
+void quit __P((const char *fmt, ...));
+void set_operators __P((void));
+void timeest __P((void));
+time_t unctime __P((char *str));
+
+/* mapping rouintes */
+struct dinode;
+long blockest __P((struct dinode *dp));
+int mapfiles __P((ino_t maxino, long *tapesize));
+int mapdirs __P((ino_t maxino, long *tapesize));
+
+/* file dumping routines */
+void blksout __P((daddr_t *blkp, int frags, ino_t ino));
+void bread __P((daddr_t blkno, char *buf, int size));
+void dumpino __P((struct dinode *dp, ino_t ino));
+void dumpmap __P((char *map, int type, ino_t ino));
+void writeheader __P((ino_t ino));
+
+/* tape writing routines */
+int alloctape __P((void));
+void close_rewind __P((void));
+void dumpblock __P((daddr_t blkno, int size));
+void startnewtape __P((int top));
+void trewind __P((void));
+void writerec __P((char *dp, int isspcl));
+
+__dead void Exit __P((int status));
+void dumpabort __P((int signo));
+void getfstab __P((void));
+
+char *rawname __P((char *cp));
+struct dinode *getino __P((ino_t inum));
+
+/* rdump routines */
+#ifdef RDUMP
+void rmtclose __P((void));
+int rmthost __P((char *host));
+int rmtopen __P((char *tape, int mode));
+int rmtwrite __P((char *buf, int count));
+#endif /* RDUMP */
+
+void interrupt __P((int signo)); /* in case operator bangs on console */
+
+/*
+ * Exit status codes
+ */
+#define X_FINOK 0 /* normal exit */
+#define X_REWRITE 2 /* restart writing from the check point */
+#define X_ABORT 3 /* abort dump; don't attempt checkpointing */
+
+#define OPGRENT "operator" /* group entry to notify */
+#define DIALUP "ttyd" /* prefix for dialups */
+
+struct fstab *fstabsearch __P((char *key)); /* search fs_file and fs_spec */
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+/*
+ * The contents of the file _PATH_DUMPDATES is maintained both on
+ * a linked list, and then (eventually) arrayified.
+ */
+struct dumpdates {
+ char dd_name[NAME_MAX+3];
+ char dd_level;
+ time_t dd_ddate;
+};
+struct dumptime {
+ struct dumpdates dt_value;
+ struct dumptime *dt_next;
+};
+struct dumptime *dthead; /* head of the list version */
+int nddates; /* number of records (might be zero) */
+int ddates_in; /* we have read the increment file */
+struct dumpdates **ddatev; /* the arrayfied version */
+void initdumptimes __P((void));
+void getdumptime __P((void));
+void putdumptime __P((void));
+#define ITITERATE(i, ddp) \
+ for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i])
+
+void sig __P((int signo));
+
+/*
+ * Compatibility with old systems.
+ */
+#ifdef COMPAT
+#include <sys/file.h>
+extern char *index(), *rindex(), *strdup();
+extern char *ctime();
+extern int read(), write();
+extern int errno;
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP "/etc/utmp"
+#endif
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
+
+#ifdef sunos
+extern char *calloc();
+extern char *malloc();
+extern long atol();
+extern char *strcpy();
+extern char *strncpy();
+extern char *strcat();
+extern time_t time();
+extern void endgrent();
+extern __dead void exit();
+extern off_t lseek();
+extern const char *strerror();
+#endif
diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c
new file mode 100644
index 000000000000..22acbfd443cb
--- /dev/null
+++ b/sbin/dump/dumprmt.c
@@ -0,0 +1,380 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumprmt.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "pathnames.h"
+#include "dump.h"
+
+#define TS_CLOSED 0
+#define TS_OPEN 1
+
+static int rmtstate = TS_CLOSED;
+static int rmtape;
+static char *rmtpeer;
+
+static int okname __P((char *));
+static int rmtcall __P((char *, char *));
+static void rmtconnaborted __P((/* int, int */));
+static int rmtgetb __P((void));
+static void rmtgetconn __P((void));
+static void rmtgets __P((char *, int));
+static int rmtreply __P((char *));
+
+extern int ntrec; /* blocking factor on tape */
+
+int
+rmthost(host)
+ char *host;
+{
+
+ rmtpeer = malloc(strlen(host) + 1);
+ if (rmtpeer)
+ strcpy(rmtpeer, host);
+ else
+ rmtpeer = host;
+ signal(SIGPIPE, rmtconnaborted);
+ rmtgetconn();
+ if (rmtape < 0)
+ return (0);
+ return (1);
+}
+
+static void
+rmtconnaborted()
+{
+
+ (void) fprintf(stderr, "rdump: Lost connection to remote host.\n");
+ exit(1);
+}
+
+void
+rmtgetconn()
+{
+ register char *cp;
+ static struct servent *sp = NULL;
+ static struct passwd *pwd = NULL;
+#ifdef notdef
+ static int on = 1;
+#endif
+ char *tuser;
+ int size;
+ int maxseg;
+
+ if (sp == NULL) {
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL) {
+ (void) fprintf(stderr,
+ "rdump: shell/tcp: unknown service\n");
+ exit(1);
+ }
+ pwd = getpwuid(getuid());
+ if (pwd == NULL) {
+ (void) fprintf(stderr, "rdump: who are you?\n");
+ exit(1);
+ }
+ }
+ if ((cp = index(rmtpeer, '@')) != NULL) {
+ tuser = rmtpeer;
+ *cp = '\0';
+ if (!okname(tuser))
+ exit(1);
+ rmtpeer = ++cp;
+ } else
+ tuser = pwd->pw_name;
+ rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser,
+ _PATH_RMT, (int *)0);
+ size = ntrec * TP_BSIZE;
+ if (size > 60 * 1024) /* XXX */
+ size = 60 * 1024;
+ /* Leave some space for rmt request/response protocol */
+ size += 2 * 1024;
+ while (size > TP_BSIZE &&
+ setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
+ size -= TP_BSIZE;
+ (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
+ maxseg = 1024;
+ if (setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG,
+ &maxseg, sizeof (maxseg)) < 0)
+ perror("TCP_MAXSEG setsockopt");
+
+#ifdef notdef
+ if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+ perror("TCP_NODELAY setsockopt");
+#endif
+}
+
+static int
+okname(cp0)
+ char *cp0;
+{
+ register char *cp;
+ register int c;
+
+ for (cp = cp0; *cp; cp++) {
+ c = *cp;
+ if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
+ (void) fprintf(stderr, "rdump: invalid user name %s\n",
+ cp0);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int
+rmtopen(tape, mode)
+ char *tape;
+ int mode;
+{
+ char buf[256];
+
+ (void)sprintf(buf, "O%s\n%d\n", tape, mode);
+ rmtstate = TS_OPEN;
+ return (rmtcall(tape, buf));
+}
+
+void
+rmtclose()
+{
+
+ if (rmtstate != TS_OPEN)
+ return;
+ rmtcall("close", "C\n");
+ rmtstate = TS_CLOSED;
+}
+
+int
+rmtread(buf, count)
+ char *buf;
+ int count;
+{
+ char line[30];
+ int n, i, cc;
+ extern errno;
+
+ (void)sprintf(line, "R%d\n", count);
+ n = rmtcall("read", line);
+ if (n < 0) {
+ errno = n;
+ return (-1);
+ }
+ for (i = 0; i < n; i += cc) {
+ cc = read(rmtape, buf+i, n - i);
+ if (cc <= 0) {
+ rmtconnaborted();
+ }
+ }
+ return (n);
+}
+
+int
+rmtwrite(buf, count)
+ char *buf;
+ int count;
+{
+ char line[30];
+
+ (void)sprintf(line, "W%d\n", count);
+ write(rmtape, line, strlen(line));
+ write(rmtape, buf, count);
+ return (rmtreply("write"));
+}
+
+void
+rmtwrite0(count)
+ int count;
+{
+ char line[30];
+
+ (void)sprintf(line, "W%d\n", count);
+ write(rmtape, line, strlen(line));
+}
+
+void
+rmtwrite1(buf, count)
+ char *buf;
+ int count;
+{
+
+ write(rmtape, buf, count);
+}
+
+int
+rmtwrite2()
+{
+
+ return (rmtreply("write"));
+}
+
+int
+rmtseek(offset, pos)
+ int offset, pos;
+{
+ char line[80];
+
+ (void)sprintf(line, "L%d\n%d\n", offset, pos);
+ return (rmtcall("seek", line));
+}
+
+struct mtget mts;
+
+struct mtget *
+rmtstatus()
+{
+ register int i;
+ register char *cp;
+
+ if (rmtstate != TS_OPEN)
+ return (NULL);
+ rmtcall("status", "S\n");
+ for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
+ *cp++ = rmtgetb();
+ return (&mts);
+}
+
+int
+rmtioctl(cmd, count)
+ int cmd, count;
+{
+ char buf[256];
+
+ if (count < 0)
+ return (-1);
+ (void)sprintf(buf, "I%d\n%d\n", cmd, count);
+ return (rmtcall("ioctl", buf));
+}
+
+static int
+rmtcall(cmd, buf)
+ char *cmd, *buf;
+{
+
+ if (write(rmtape, buf, strlen(buf)) != strlen(buf))
+ rmtconnaborted();
+ return (rmtreply(cmd));
+}
+
+static int
+rmtreply(cmd)
+ char *cmd;
+{
+ register char *cp;
+ char code[30], emsg[BUFSIZ];
+
+ rmtgets(code, sizeof (code));
+ if (*code == 'E' || *code == 'F') {
+ rmtgets(emsg, sizeof (emsg));
+ msg("%s: %s", cmd, emsg);
+ if (*code == 'F') {
+ rmtstate = TS_CLOSED;
+ return (-1);
+ }
+ return (-1);
+ }
+ if (*code != 'A') {
+ /* Kill trailing newline */
+ cp = code + strlen(code);
+ if (cp > code && *--cp == '\n')
+ *cp = '\0';
+
+ msg("Protocol to remote tape server botched (code \"%s\").\n",
+ code);
+ rmtconnaborted();
+ }
+ return (atoi(code + 1));
+}
+
+int
+rmtgetb()
+{
+ char c;
+
+ if (read(rmtape, &c, 1) != 1)
+ rmtconnaborted();
+ return (c);
+}
+
+/* Get a line (guaranteed to have a trailing newline). */
+void
+rmtgets(line, len)
+ char *line;
+ int len;
+{
+ register char *cp = line;
+
+ while (len > 1) {
+ *cp = rmtgetb();
+ if (*cp == '\n') {
+ cp[1] = '\0';
+ return;
+ }
+ cp++;
+ len--;
+ }
+ *cp = '\0';
+ msg("Protocol to remote tape server botched.\n");
+ msg("(rmtgets got \"%s\").\n", line);
+ rmtconnaborted();
+}
diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c
new file mode 100644
index 000000000000..94656ac649e3
--- /dev/null
+++ b/sbin/dump/itime.c
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+
+struct dumpdates **ddatev = 0;
+int nddates = 0;
+int ddates_in = 0;
+struct dumptime *dthead = 0;
+
+static void dumprecout __P((FILE *, struct dumpdates *));
+static int getrecord __P((FILE *, struct dumpdates *));
+static int makedumpdate __P((struct dumpdates *, char *));
+static void readdumptimes __P((FILE *));
+
+void
+initdumptimes()
+{
+ FILE *df;
+
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ if (errno != ENOENT) {
+ quit("cannot read %s: %s\n", dumpdates,
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ /*
+ * Dumpdates does not exist, make an empty one.
+ */
+ msg("WARNING: no file `%s', making an empty one\n", dumpdates);
+ if ((df = fopen(dumpdates, "w")) == NULL) {
+ quit("cannot create %s: %s\n", dumpdates,
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ (void) fclose(df);
+ if ((df = fopen(dumpdates, "r")) == NULL) {
+ quit("cannot read %s even after creating it: %s\n",
+ dumpdates, strerror(errno));
+ /* NOTREACHED */
+ }
+ }
+ (void) flock(fileno(df), LOCK_SH);
+ readdumptimes(df);
+ (void) fclose(df);
+}
+
+static void
+readdumptimes(df)
+ FILE *df;
+{
+ register int i;
+ register struct dumptime *dtwalk;
+
+ for (;;) {
+ dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime));
+ if (getrecord(df, &(dtwalk->dt_value)) < 0)
+ break;
+ nddates++;
+ dtwalk->dt_next = dthead;
+ dthead = dtwalk;
+ }
+
+ ddates_in = 1;
+ /*
+ * arrayify the list, leaving enough room for the additional
+ * record that we may have to add to the ddate structure
+ */
+ ddatev = (struct dumpdates **)
+ calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *));
+ dtwalk = dthead;
+ for (i = nddates - 1; i >= 0; i--, dtwalk = dtwalk->dt_next)
+ ddatev[i] = &dtwalk->dt_value;
+}
+
+void
+getdumptime()
+{
+ register struct dumpdates *ddp;
+ register int i;
+ char *fname;
+
+ fname = disk;
+#ifdef FDEBUG
+ msg("Looking for name %s in dumpdates = %s for level = %c\n",
+ fname, dumpdates, level);
+#endif
+ spcl.c_ddate = 0;
+ lastlevel = '0';
+
+ initdumptimes();
+ /*
+ * Go find the entry with the same name for a lower increment
+ * and older date
+ */
+ ITITERATE(i, ddp) {
+ if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0)
+ continue;
+ if (ddp->dd_level >= level)
+ continue;
+ if (ddp->dd_ddate <= spcl.c_ddate)
+ continue;
+ spcl.c_ddate = ddp->dd_ddate;
+ lastlevel = ddp->dd_level;
+ }
+}
+
+void
+putdumptime()
+{
+ FILE *df;
+ register struct dumpdates *dtwalk;
+ register int i;
+ int fd;
+ char *fname;
+
+ if(uflag == 0)
+ return;
+ if ((df = fopen(dumpdates, "r+")) == NULL)
+ quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno));
+ fd = fileno(df);
+ (void) flock(fd, LOCK_EX);
+ fname = disk;
+ free((char *)ddatev);
+ ddatev = 0;
+ nddates = 0;
+ dthead = 0;
+ ddates_in = 0;
+ readdumptimes(df);
+ if (fseek(df, 0L, 0) < 0)
+ quit("fseek: %s\n", strerror(errno));
+ spcl.c_ddate = 0;
+ ITITERATE(i, dtwalk) {
+ if (strncmp(fname, dtwalk->dd_name,
+ sizeof (dtwalk->dd_name)) != 0)
+ continue;
+ if (dtwalk->dd_level != level)
+ continue;
+ goto found;
+ }
+ /*
+ * construct the new upper bound;
+ * Enough room has been allocated.
+ */
+ dtwalk = ddatev[nddates] =
+ (struct dumpdates *)calloc(1, sizeof (struct dumpdates));
+ nddates += 1;
+ found:
+ (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name));
+ dtwalk->dd_level = level;
+ dtwalk->dd_ddate = spcl.c_date;
+
+ ITITERATE(i, dtwalk) {
+ dumprecout(df, dtwalk);
+ }
+ if (fflush(df))
+ quit("%s: %s\n", dumpdates, strerror(errno));
+ if (ftruncate(fd, ftell(df)))
+ quit("ftruncate (%s): %s\n", dumpdates, strerror(errno));
+ (void) fclose(df);
+ msg("level %c dump on %s", level,
+ spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+}
+
+static void
+dumprecout(file, what)
+ FILE *file;
+ struct dumpdates *what;
+{
+
+ if (fprintf(file, DUMPOUTFMT,
+ what->dd_name,
+ what->dd_level,
+ ctime(&what->dd_ddate)) < 0)
+ quit("%s: %s\n", dumpdates, strerror(errno));
+}
+
+int recno;
+
+static int
+getrecord(df, ddatep)
+ FILE *df;
+ struct dumpdates *ddatep;
+{
+ char tbuf[BUFSIZ];
+
+ recno = 0;
+ if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf)
+ return(-1);
+ recno++;
+ if (makedumpdate(ddatep, tbuf) < 0)
+ msg("Unknown intermediate format in %s, line %d\n",
+ dumpdates, recno);
+
+#ifdef FDEBUG
+ msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level,
+ ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate));
+#endif
+ return(0);
+}
+
+static int
+makedumpdate(ddp, tbuf)
+ struct dumpdates *ddp;
+ char *tbuf;
+{
+ char un_buf[128];
+
+ (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf);
+ ddp->dd_ddate = unctime(un_buf);
+ if (ddp->dd_ddate < 0)
+ return(-1);
+ return(0);
+}
diff --git a/sbin/dump/main.c b/sbin/dump/main.c
new file mode 100644
index 000000000000..40d668e13e82
--- /dev/null
+++ b/sbin/dump/main.c
@@ -0,0 +1,560 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/15/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+#include "pathnames.h"
+
+#ifndef SBOFF
+#define SBOFF (SBLOCK * DEV_BSIZE)
+#endif
+
+int notify = 0; /* notify operator flag */
+int blockswritten = 0; /* number of blocks written on current tape */
+int tapeno = 0; /* current tape number */
+int density = 0; /* density in bytes/0.1" */
+int ntrec = NTREC; /* # tape blocks in each tape record */
+int cartridge = 0; /* Assume non-cartridge tape */
+long dev_bsize = 1; /* recalculated below */
+long blocksperfile; /* output blocks per file */
+char *host = NULL; /* remote host (if any) */
+
+static long numarg __P((int, char *, long, long, int *, char ***));
+static __dead void missingarg __P((int, char *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register ino_t ino;
+ register int dirty;
+ register struct dinode *dp;
+ register struct fstab *dt;
+ register char *map;
+ register char *cp;
+ int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
+ ino_t maxino;
+
+ spcl.c_date = 0;
+ (void)time((time_t *)&spcl.c_date);
+
+ tsize = 0; /* Default later, based on 'c' option for cart tapes */
+ tape = _PATH_DEFTAPE;
+ dumpdates = _PATH_DUMPDATES;
+ temp = _PATH_DTMP;
+ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
+ quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
+ level = '0';
+ if (argc == 1) {
+ (void) fprintf(stderr, "Must specify a key.\n");
+ Exit(X_ABORT);
+ }
+ argv++;
+ argc -= 2;
+ for (cp = *argv++; cp != NULL && *cp != '\0'; cp++) {
+ switch (*cp) {
+ case '-':
+ break;
+
+ case 'w':
+ lastdump('w'); /* tell us only what has to be done */
+ exit(0);
+
+ case 'W': /* what to do */
+ lastdump('W'); /* tell us state of what is done */
+ exit(0); /* do nothing else */
+
+ case 'f': /* output file */
+ if (argc < 1)
+ missingarg('f', "output file");
+ tape = *argv++;
+ argc--;
+ break;
+
+ case 'd': /* density, in bits per inch */
+ density = numarg('d', "density",
+ 10L, 327670L, &argc, &argv) / 10;
+ if (density >= 625 && !bflag)
+ ntrec = HIGHDENSITYTREC;
+ break;
+
+ case 's': /* tape size, feet */
+ tsize = numarg('s', "size",
+ 1L, 0L, &argc, &argv) * 12 * 10;
+ break;
+
+ case 'T': /* time of last dump */
+ if (argc < 1)
+ missingarg('T', "time of last dump");
+ spcl.c_ddate = unctime(*argv);
+ if (spcl.c_ddate < 0) {
+ (void)fprintf(stderr, "bad time \"%s\"\n",
+ *argv);
+ exit(X_ABORT);
+ }
+ Tflag = 1;
+ lastlevel = '?';
+ argc--;
+ argv++;
+ break;
+
+ case 'b': /* blocks per tape write */
+ ntrec = numarg('b', "number of blocks per write",
+ 1L, 1000L, &argc, &argv);
+ break;
+
+ case 'B': /* blocks per output file */
+ blocksperfile = numarg('B', "number of blocks per file",
+ 1L, 0L, &argc, &argv);
+ break;
+
+ case 'c': /* Tape is cart. not 9-track */
+ cartridge = 1;
+ break;
+
+ /* dump level */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ level = *cp;
+ break;
+
+ case 'u': /* update /etc/dumpdates */
+ uflag = 1;
+ break;
+
+ case 'n': /* notify operators */
+ notify = 1;
+ break;
+
+ case 'h':
+ honorlevel = numarg('h', "honor level",
+ 0L, 10L, &argc, &argv);
+ break;
+
+ default:
+ (void)fprintf(stderr, "bad key '%c'\n", *cp);
+ exit(X_ABORT);
+ }
+ }
+ if (argc < 1) {
+ (void)fprintf(stderr, "Must specify disk or filesystem\n");
+ exit(X_ABORT);
+ }
+ disk = *argv++;
+ argc--;
+ if (argc >= 1) {
+ (void)fprintf(stderr, "Unknown arguments to dump:");
+ while (argc--)
+ (void)fprintf(stderr, " %s", *argv++);
+ (void)fprintf(stderr, "\n");
+ exit(X_ABORT);
+ }
+ if (Tflag && uflag) {
+ (void)fprintf(stderr,
+ "You cannot use the T and u flags together.\n");
+ exit(X_ABORT);
+ }
+ if (strcmp(tape, "-") == 0) {
+ pipeout++;
+ tape = "standard output";
+ }
+
+ if (blocksperfile)
+ blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
+ else {
+ /*
+ * Determine how to default tape size and density
+ *
+ * density tape size
+ * 9-track 1600 bpi (160 bytes/.1") 2300 ft.
+ * 9-track 6250 bpi (625 bytes/.1") 2300 ft.
+ * cartridge 8000 bpi (100 bytes/.1") 1700 ft.
+ * (450*4 - slop)
+ */
+ if (density == 0)
+ density = cartridge ? 100 : 160;
+ if (tsize == 0)
+ tsize = cartridge ? 1700L*120L : 2300L*120L;
+ }
+
+ if (index(tape, ':')) {
+ host = tape;
+ tape = index(host, ':');
+ *tape++ = '\0';
+#ifdef RDUMP
+ if (rmthost(host) == 0)
+ exit(X_ABORT);
+#else
+ (void)fprintf(stderr, "remote dump not enabled\n");
+ exit(X_ABORT);
+#endif
+ }
+ (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */
+
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, sig);
+ if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
+ signal(SIGTRAP, sig);
+ if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
+ signal(SIGFPE, sig);
+ if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
+ signal(SIGBUS, sig);
+ if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
+ signal(SIGSEGV, sig);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, sig);
+ if (signal(SIGINT, interrupt) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+
+ set_operators(); /* /etc/group snarfed */
+ getfstab(); /* /etc/fstab snarfed */
+ /*
+ * disk can be either the full special file name,
+ * the suffix of the special file name,
+ * the special name missing the leading '/',
+ * the file system name with or without the leading '/'.
+ */
+ dt = fstabsearch(disk);
+ if (dt != NULL) {
+ disk = rawname(dt->fs_spec);
+ (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN);
+ (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN);
+ } else {
+ (void)strncpy(spcl.c_dev, disk, NAMELEN);
+ (void)strncpy(spcl.c_filesys, "an unlisted file system",
+ NAMELEN);
+ }
+ (void)strcpy(spcl.c_label, "none");
+ (void)gethostname(spcl.c_host, NAMELEN);
+ spcl.c_level = level - '0';
+ spcl.c_type = TS_TAPE;
+ if (!Tflag)
+ getdumptime(); /* /etc/dumpdates snarfed */
+
+ msg("Date of this level %c dump: %s", level,
+ spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date));
+ msg("Date of last level %c dump: %s", lastlevel,
+ spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate));
+ msg("Dumping %s ", disk);
+ if (dt != NULL)
+ msgtail("(%s) ", dt->fs_file);
+ if (host)
+ msgtail("to %s on host %s\n", tape, host);
+ else
+ msgtail("to %s\n", tape);
+
+ if ((diskfd = open(disk, O_RDONLY)) < 0) {
+ msg("Cannot open %s\n", disk);
+ exit(X_ABORT);
+ }
+ sync();
+ sblock = (struct fs *)sblock_buf;
+ bread(SBOFF, (char *) sblock, SBSIZE);
+ if (sblock->fs_magic != FS_MAGIC)
+ quit("bad sblock magic number\n");
+ dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1);
+ dev_bshift = ffs(dev_bsize) - 1;
+ if (dev_bsize != (1 << dev_bshift))
+ quit("dev_bsize (%d) is not a power of 2", dev_bsize);
+ tp_bshift = ffs(TP_BSIZE) - 1;
+ if (TP_BSIZE != (1 << tp_bshift))
+ quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
+#ifdef FS_44INODEFMT
+ if (sblock->fs_inodefmt >= FS_44INODEFMT)
+ spcl.c_flags |= DR_NEWINODEFMT;
+#endif
+ maxino = sblock->fs_ipg * sblock->fs_ncg;
+ mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
+ usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char));
+ tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+
+ nonodump = spcl.c_level < honorlevel;
+
+ msg("mapping (Pass I) [regular files]\n");
+ anydirskipped = mapfiles(maxino, &tapesize);
+
+ msg("mapping (Pass II) [directories]\n");
+ while (anydirskipped) {
+ anydirskipped = mapdirs(maxino, &tapesize);
+ }
+
+ if (pipeout) {
+ tapesize += 10; /* 10 trailer blocks */
+ msg("estimated %ld tape blocks.\n", tapesize);
+ } else {
+ double fetapes;
+
+ if (blocksperfile)
+ fetapes = (double) tapesize / blocksperfile;
+ else if (cartridge) {
+ /* Estimate number of tapes, assuming streaming stops at
+ the end of each block written, and not in mid-block.
+ Assume no erroneous blocks; this can be compensated
+ for with an artificially low tape size. */
+ fetapes =
+ ( tapesize /* blocks */
+ * TP_BSIZE /* bytes/block */
+ * (1.0/density) /* 0.1" / byte */
+ +
+ tapesize /* blocks */
+ * (1.0/ntrec) /* streaming-stops per block */
+ * 15.48 /* 0.1" / streaming-stop */
+ ) * (1.0 / tsize ); /* tape / 0.1" */
+ } else {
+ /* Estimate number of tapes, for old fashioned 9-track
+ tape */
+ int tenthsperirg = (density == 625) ? 3 : 7;
+ fetapes =
+ ( tapesize /* blocks */
+ * TP_BSIZE /* bytes / block */
+ * (1.0/density) /* 0.1" / byte */
+ +
+ tapesize /* blocks */
+ * (1.0/ntrec) /* IRG's / block */
+ * tenthsperirg /* 0.1" / IRG */
+ ) * (1.0 / tsize ); /* tape / 0.1" */
+ }
+ etapes = fetapes; /* truncating assignment */
+ etapes++;
+ /* count the dumped inodes map on each additional tape */
+ tapesize += (etapes - 1) *
+ (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
+ tapesize += etapes + 10; /* headers + 10 trailer blks */
+ msg("estimated %ld tape blocks on %3.2f tape(s).\n",
+ tapesize, fetapes);
+ }
+
+ /*
+ * Allocate tape buffer.
+ */
+ if (!alloctape())
+ quit("can't allocate tape buffers - try a smaller blocking factor.\n");
+
+ startnewtape(1);
+ (void)time((time_t *)&(tstart_writing));
+ dumpmap(usedinomap, TS_CLRI, maxino - 1);
+
+ msg("dumping (Pass III) [directories]\n");
+ dirty = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip directory inodes deleted and maybe reallocated
+ */
+ dp = getino(ino);
+ if ((dp->di_mode & IFMT) != IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ msg("dumping (Pass IV) [regular files]\n");
+ for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
+ int mode;
+
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ dirty = *map++;
+ else
+ dirty >>= 1;
+ if ((dirty & 1) == 0)
+ continue;
+ /*
+ * Skip inodes deleted and reallocated as directories.
+ */
+ dp = getino(ino);
+ mode = dp->di_mode & IFMT;
+ if (mode == IFDIR)
+ continue;
+ (void)dumpino(dp, ino);
+ }
+
+ spcl.c_type = TS_END;
+ for (i = 0; i < ntrec; i++)
+ writeheader(maxino - 1);
+ if (pipeout)
+ msg("DUMP: %ld tape blocks\n",spcl.c_tapea);
+ else
+ msg("DUMP: %ld tape blocks on %d volumes(s)\n",
+ spcl.c_tapea, spcl.c_volume);
+ putdumptime();
+ trewind();
+ broadcast("DUMP IS DONE!\7\7\n");
+ msg("DUMP IS DONE\n");
+ Exit(X_FINOK);
+ /* NOTREACHED */
+}
+
+/*
+ * Pick up a numeric argument. It must be nonnegative and in the given
+ * range (except that a vmax of 0 means unlimited).
+ */
+static long
+numarg(letter, meaning, vmin, vmax, pargc, pargv)
+ int letter;
+ char *meaning;
+ long vmin, vmax;
+ int *pargc;
+ char ***pargv;
+{
+ register char *p;
+ long val;
+ char *str;
+
+ if (--*pargc < 0)
+ missingarg(letter, meaning);
+ str = *(*pargv)++;
+ for (p = str; *p; p++)
+ if (!isdigit(*p))
+ goto bad;
+ val = atol(str);
+ if (val < vmin || (vmax && val > vmax))
+ goto bad;
+ return (val);
+
+bad:
+ (void)fprintf(stderr, "bad '%c' (%s) value \"%s\"\n",
+ letter, meaning, str);
+ exit(X_ABORT);
+}
+
+static __dead void
+missingarg(letter, meaning)
+ int letter;
+ char *meaning;
+{
+
+ (void)fprintf(stderr, "The '%c' flag (%s) requires an argument\n",
+ letter, meaning);
+ exit(X_ABORT);
+}
+
+void
+sig(signo)
+ int signo;
+{
+ switch(signo) {
+ case SIGALRM:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGTERM:
+ case SIGTRAP:
+ if (pipeout)
+ quit("Signal on pipe: cannot recover\n");
+ msg("Rewriting attempted as response to unknown signal.\n");
+ (void)fflush(stderr);
+ (void)fflush(stdout);
+ close_rewind();
+ exit(X_REWRITE);
+ /* NOTREACHED */
+ case SIGSEGV:
+ msg("SIGSEGV: ABORTING!\n");
+ (void)signal(SIGSEGV, SIG_DFL);
+ (void)kill(0, SIGSEGV);
+ /* NOTREACHED */
+ }
+}
+
+char *
+rawname(cp)
+ char *cp;
+{
+ static char rawbuf[MAXPATHLEN];
+ char *dp = rindex(cp, '/');
+
+ if (dp == NULL)
+ return (NULL);
+ *dp = '\0';
+ (void)strcpy(rawbuf, cp);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, dp + 1);
+ return (rawbuf);
+}
+
+#ifdef sunos
+const char *
+strerror(errnum)
+ int errnum;
+{
+ extern int sys_nerr;
+ extern const char *const sys_errlist[];
+
+ if (errnum < sys_nerr)
+ return (sys_errlist[errnum]);
+ else
+ return ("bogus errno in strerror");
+}
+#endif
diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c
new file mode 100644
index 000000000000..6db09085095c
--- /dev/null
+++ b/sbin/dump/optr.c
@@ -0,0 +1,538 @@
+/*-
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#endif
+#include <tzfile.h>
+#ifdef __STDC__
+#include <unistd.h>
+#endif
+#include <utmp.h>
+#ifndef __STDC__
+#include <varargs.h>
+#endif
+
+#include "dump.h"
+#include "pathnames.h"
+
+void alarmcatch __P((/* int, int */));
+int datesort __P((const void *, const void *));
+static void sendmes __P((char *, char *));
+
+/*
+ * Query the operator; This previously-fascist piece of code
+ * no longer requires an exact response.
+ * It is intended to protect dump aborting by inquisitive
+ * people banging on the console terminal to see what is
+ * happening which might cause dump to croak, destroying
+ * a large number of hours of work.
+ *
+ * Every 2 minutes we reprint the message, alerting others
+ * that dump needs attention.
+ */
+static int timeout;
+static char *attnmessage; /* attention message */
+
+int
+query(question)
+ char *question;
+{
+ char replybuffer[64];
+ int back, errcount;
+ FILE *mytty;
+
+ if ((mytty = fopen(_PATH_TTY, "r")) == NULL)
+ quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno));
+ attnmessage = question;
+ timeout = 0;
+ alarmcatch();
+ back = -1;
+ errcount = 0;
+ do {
+ if (fgets(replybuffer, 63, mytty) == NULL) {
+ clearerr(mytty);
+ if (++errcount > 30) /* XXX ugly */
+ quit("excessive operator query failures\n");
+ } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') {
+ back = 1;
+ } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') {
+ back = 0;
+ } else {
+ (void) fprintf(stderr,
+ " DUMP: \"Yes\" or \"No\"?\n");
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ", question);
+ }
+ } while (back < 0);
+
+ /*
+ * Turn off the alarm, and reset the signal to trap out..
+ */
+ (void) alarm(0);
+ if (signal(SIGALRM, sig) == SIG_IGN)
+ signal(SIGALRM, SIG_IGN);
+ (void) fclose(mytty);
+ return(back);
+}
+
+char lastmsg[100];
+
+/*
+ * Alert the console operator, and enable the alarm clock to
+ * sleep for 2 minutes in case nobody comes to satisfy dump
+ */
+void
+alarmcatch()
+{
+ if (notify == 0) {
+ if (timeout == 0)
+ (void) fprintf(stderr,
+ " DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ else
+ msgtail("\7\7");
+ } else {
+ if (timeout) {
+ msgtail("\n");
+ broadcast(""); /* just print last msg */
+ }
+ (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ",
+ attnmessage);
+ }
+ signal(SIGALRM, alarmcatch);
+ (void) alarm(120);
+ timeout = 1;
+}
+
+/*
+ * Here if an inquisitive operator interrupts the dump program
+ */
+void
+interrupt(signo)
+ int signo;
+{
+ msg("Interrupt received.\n");
+ if (query("Do you want to abort dump?"))
+ dumpabort(0);
+}
+
+/*
+ * The following variables and routines manage alerting
+ * operators to the status of dump.
+ * This works much like wall(1) does.
+ */
+struct group *gp;
+
+/*
+ * Get the names from the group entry "operator" to notify.
+ */
+void
+set_operators()
+{
+ if (!notify) /*not going to notify*/
+ return;
+ gp = getgrnam(OPGRENT);
+ (void) endgrent();
+ if (gp == NULL) {
+ msg("No group entry for %s.\n", OPGRENT);
+ notify = 0;
+ return;
+ }
+}
+
+struct tm *localclock;
+
+/*
+ * We fork a child to do the actual broadcasting, so
+ * that the process control groups are not messed up
+ */
+void
+broadcast(message)
+ char *message;
+{
+ time_t clock;
+ FILE *f_utmp;
+ struct utmp utmp;
+ char **np;
+ int pid, s;
+
+ if (!notify || gp == NULL)
+ return;
+
+ switch (pid = fork()) {
+ case -1:
+ return;
+ case 0:
+ break;
+ default:
+ while (wait(&s) != pid)
+ continue;
+ return;
+ }
+
+ clock = time((time_t *)0);
+ localclock = localtime(&clock);
+
+ if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) {
+ msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno));
+ return;
+ }
+
+ while (!feof(f_utmp)) {
+ if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1)
+ break;
+ if (utmp.ut_name[0] == 0)
+ continue;
+ for (np = gp->gr_mem; *np; np++) {
+ if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0)
+ continue;
+ /*
+ * Do not send messages to operators on dialups
+ */
+ if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0)
+ continue;
+#ifdef DEBUG
+ msg("Message to %s at %s\n", *np, utmp.ut_line);
+#endif
+ sendmes(utmp.ut_line, message);
+ }
+ }
+ (void) fclose(f_utmp);
+ Exit(0); /* the wait in this same routine will catch this */
+ /* NOTREACHED */
+}
+
+static void
+sendmes(tty, message)
+ char *tty, *message;
+{
+ char t[50], buf[BUFSIZ];
+ register char *cp;
+ int lmsg = 1;
+ FILE *f_tty;
+
+ (void) strcpy(t, _PATH_DEV);
+ (void) strcat(t, tty);
+
+ if ((f_tty = fopen(t, "w")) != NULL) {
+ setbuf(f_tty, buf);
+ (void) fprintf(f_tty,
+ "\n\
+\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\
+DUMP: NEEDS ATTENTION: ",
+ localclock->tm_hour, localclock->tm_min);
+ for (cp = lastmsg; ; cp++) {
+ if (*cp == '\0') {
+ if (lmsg) {
+ cp = message;
+ if (*cp == '\0')
+ break;
+ lmsg = 0;
+ } else
+ break;
+ }
+ if (*cp == '\n')
+ (void) putc('\r', f_tty);
+ (void) putc(*cp, f_tty);
+ }
+ (void) fclose(f_tty);
+ }
+}
+
+/*
+ * print out an estimate of the amount of time left to do the dump
+ */
+
+time_t tschedule = 0;
+
+void
+timeest()
+{
+ time_t tnow, deltat;
+
+ (void) time((time_t *) &tnow);
+ if (tnow >= tschedule) {
+ tschedule = tnow + 300;
+ if (blockswritten < 500)
+ return;
+ deltat = tstart_writing - tnow +
+ (1.0 * (tnow - tstart_writing))
+ / blockswritten * tapesize;
+ msg("%3.2f%% done, finished in %d:%02d\n",
+ (blockswritten * 100.0) / tapesize,
+ deltat / 3600, (deltat % 3600) / 60);
+ }
+}
+
+void
+#if __STDC__
+msg(const char *fmt, ...)
+#else
+msg(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ (void) vsprintf(lastmsg, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+msgtail(const char *fmt, ...)
+#else
+msgtail(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+quit(const char *fmt, ...)
+#else
+quit(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void) fprintf(stderr," DUMP: ");
+#ifdef TDEBUG
+ (void) fprintf(stderr, "pid=%d ", getpid());
+#endif
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ dumpabort(0);
+}
+
+/*
+ * Tell the operator what has to be done;
+ * we don't actually do it
+ */
+
+struct fstab *
+allocfsent(fs)
+ register struct fstab *fs;
+{
+ register struct fstab *new;
+
+ new = (struct fstab *)malloc(sizeof (*fs));
+ if (new == NULL ||
+ (new->fs_file = strdup(fs->fs_file)) == NULL ||
+ (new->fs_type = strdup(fs->fs_type)) == NULL ||
+ (new->fs_spec = strdup(fs->fs_spec)) == NULL)
+ quit("%s\n", strerror(errno));
+ new->fs_passno = fs->fs_passno;
+ new->fs_freq = fs->fs_freq;
+ return (new);
+}
+
+struct pfstab {
+ struct pfstab *pf_next;
+ struct fstab *pf_fstab;
+};
+
+static struct pfstab *table;
+
+void
+getfstab()
+{
+ register struct fstab *fs;
+ register struct pfstab *pf;
+
+ if (setfsent() == 0) {
+ msg("Can't open %s for dump table information: %s\n",
+ _PATH_FSTAB, strerror(errno));
+ return;
+ }
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_type, FSTAB_RW) &&
+ strcmp(fs->fs_type, FSTAB_RO) &&
+ strcmp(fs->fs_type, FSTAB_RQ))
+ continue;
+ fs = allocfsent(fs);
+ if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL)
+ quit("%s\n", strerror(errno));
+ pf->pf_fstab = fs;
+ pf->pf_next = table;
+ table = pf;
+ }
+ (void) endfsent();
+}
+
+/*
+ * Search in the fstab for a file name.
+ * This file name can be either the special or the path file name.
+ *
+ * The entries in the fstab are the BLOCK special names, not the
+ * character special names.
+ * The caller of fstabsearch assures that the character device
+ * is dumped (that is much faster)
+ *
+ * The file name can omit the leading '/'.
+ */
+struct fstab *
+fstabsearch(key)
+ char *key;
+{
+ register struct pfstab *pf;
+ register struct fstab *fs;
+ char *rn;
+
+ for (pf = table; pf != NULL; pf = pf->pf_next) {
+ fs = pf->pf_fstab;
+ if (strcmp(fs->fs_file, key) == 0 ||
+ strcmp(fs->fs_spec, key) == 0)
+ return (fs);
+ rn = rawname(fs->fs_spec);
+ if (rn != NULL && strcmp(rn, key) == 0)
+ return (fs);
+ if (key[0] != '/') {
+ if (*fs->fs_spec == '/' &&
+ strcmp(fs->fs_spec + 1, key) == 0)
+ return (fs);
+ if (*fs->fs_file == '/' &&
+ strcmp(fs->fs_file + 1, key) == 0)
+ return (fs);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Tell the operator what to do
+ */
+void
+lastdump(arg)
+ char arg; /* w ==> just what to do; W ==> most recent dumps */
+{
+ register int i;
+ register struct fstab *dt;
+ register struct dumpdates *dtwalk;
+ char *lastname, *date;
+ int dumpme;
+ time_t tnow;
+
+ (void) time(&tnow);
+ getfstab(); /* /etc/fstab input */
+ initdumptimes(); /* /etc/dumpdates input */
+ qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort);
+
+ if (arg == 'w')
+ (void) printf("Dump these file systems:\n");
+ else
+ (void) printf("Last dump(s) done (Dump '>' file systems):\n");
+ lastname = "??";
+ ITITERATE(i, dtwalk) {
+ if (strncmp(lastname, dtwalk->dd_name,
+ sizeof(dtwalk->dd_name)) == 0)
+ continue;
+ date = (char *)ctime(&dtwalk->dd_ddate);
+ date[16] = '\0'; /* blast away seconds and year */
+ lastname = dtwalk->dd_name;
+ dt = fstabsearch(dtwalk->dd_name);
+ dumpme = (dt != NULL &&
+ dt->fs_freq != 0 &&
+ dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY));
+ if (arg != 'w' || dumpme)
+ (void) printf(
+ "%c %8s\t(%6s) Last dump: Level %c, Date %s\n",
+ dumpme && (arg != 'w') ? '>' : ' ',
+ dtwalk->dd_name,
+ dt ? dt->fs_file : "",
+ dtwalk->dd_level,
+ date);
+ }
+}
+
+int
+datesort(a1, a2)
+ const void *a1, *a2;
+{
+ struct dumpdates *d1 = *(struct dumpdates **)a1;
+ struct dumpdates *d2 = *(struct dumpdates **)a2;
+ int diff;
+
+ diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name));
+ if (diff == 0)
+ return (d2->dd_ddate - d1->dd_ddate);
+ return (diff);
+}
diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h
new file mode 100644
index 000000000000..e4dff5336062
--- /dev/null
+++ b/sbin/dump/pathnames.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFTAPE "/dev/rmt8"
+#define _PATH_DTMP "/etc/dtmp"
+#define _PATH_DUMPDATES "/etc/dumpdates"
+#define _PATH_LOCK "/tmp/dumplockXXXXXX"
+#define _PATH_RMT "rmt"
diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c
new file mode 100644
index 000000000000..f6a4e5b82bd3
--- /dev/null
+++ b/sbin/dump/tape.c
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tape.c 8.2 (Berkeley) 3/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#else
+int write(), read();
+#endif
+
+#include "dump.h"
+#include "pathnames.h"
+
+int writesize; /* size of malloc()ed buffer for tape */
+long lastspclrec = -1; /* tape block number of last written header */
+int trecno = 0; /* next record to write in current block */
+extern long blocksperfile; /* number of blocks per output file */
+long blocksthisvol; /* number of blocks on current output file */
+extern int ntrec; /* blocking factor on tape */
+extern int cartridge;
+extern char *host;
+char *nexttape;
+
+static int atomic __P((int (*)(), int, char *, int));
+static void doslave __P((int, int));
+static void enslave __P((void));
+static void flushtape __P((void));
+static void killall __P((void));
+static void rollforward __P((void));
+
+/*
+ * Concurrent dump mods (Caltech) - disk block reading and tape writing
+ * are exported to several slave processes. While one slave writes the
+ * tape, the others read disk blocks; they pass control of the tape in
+ * a ring via signals. The parent process traverses the filesystem and
+ * sends writeheader()'s and lists of daddr's to the slaves via pipes.
+ * The following structure defines the instruction packets sent to slaves.
+ */
+struct req {
+ daddr_t dblk;
+ int count;
+};
+int reqsiz;
+
+#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
+struct slave {
+ int tapea; /* header number at start of this chunk */
+ int count; /* count to next header (used for TS_TAPE */
+ /* after EOT) */
+ int inode; /* inode that we are currently dealing with */
+ int fd; /* FD for this slave */
+ int pid; /* PID for this slave */
+ int sent; /* 1 == we've sent this slave requests */
+ int firstrec; /* record number of this block */
+ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
+ struct req *req; /* buffer for requests */
+} slaves[SLAVES+1];
+struct slave *slp;
+
+char (*nextblock)[TP_BSIZE];
+
+int master; /* pid of master, for sending error signals */
+int tenths; /* length of tape used per block written */
+static int caught; /* have we caught the signal to proceed? */
+static int ready; /* have we reached the lock point without having */
+ /* received the SIGUSR2 signal from the prev slave? */
+static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
+ /* SIGUSR2 arrives from the previous slave */
+
+int
+alloctape()
+{
+ int pgoff = getpagesize() - 1;
+ char *buf;
+ int i;
+
+ writesize = ntrec * TP_BSIZE;
+ reqsiz = (ntrec + 1) * sizeof(struct req);
+ /*
+ * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
+ * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
+ * repositioning after stopping, i.e, streaming mode, where the gap is
+ * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
+ */
+ if (blocksperfile == 0)
+ tenths = writesize / density +
+ (cartridge ? 16 : density == 625 ? 5 : 8);
+ /*
+ * Allocate tape buffer contiguous with the array of instruction
+ * packets, so flushtape() can write them together with one write().
+ * Align tape buffer on page boundary to speed up tape write().
+ */
+ for (i = 0; i <= SLAVES; i++) {
+ buf = (char *)
+ malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
+ if (buf == NULL)
+ return(0);
+ slaves[i].tblock = (char (*)[TP_BSIZE])
+ (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
+ slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
+ }
+ slp = &slaves[0];
+ slp->count = 1;
+ slp->tapea = 0;
+ slp->firstrec = 0;
+ nextblock = slp->tblock;
+ return(1);
+}
+
+void
+writerec(dp, isspcl)
+ char *dp;
+ int isspcl;
+{
+
+ slp->req[trecno].dblk = (daddr_t)0;
+ slp->req[trecno].count = 1;
+ *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
+ if (isspcl)
+ lastspclrec = spcl.c_tapea;
+ trecno++;
+ spcl.c_tapea++;
+ if (trecno >= ntrec)
+ flushtape();
+}
+
+void
+dumpblock(blkno, size)
+ daddr_t blkno;
+ int size;
+{
+ int avail, tpblks, dblkno;
+
+ dblkno = fsbtodb(sblock, blkno);
+ tpblks = size >> tp_bshift;
+ while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
+ slp->req[trecno].dblk = dblkno;
+ slp->req[trecno].count = avail;
+ trecno += avail;
+ spcl.c_tapea += avail;
+ if (trecno >= ntrec)
+ flushtape();
+ dblkno += avail << (tp_bshift - dev_bshift);
+ tpblks -= avail;
+ }
+}
+
+int nogripe = 0;
+
+void
+tperror(signo)
+ int signo;
+{
+
+ if (pipeout) {
+ msg("write error on %s\n", tape);
+ quit("Cannot recover\n");
+ /* NOTREACHED */
+ }
+ msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
+ broadcast("DUMP WRITE ERROR!\n");
+ if (!query("Do you want to restart?"))
+ dumpabort(0);
+ msg("Closing this volume. Prepare to restart with new media;\n");
+ msg("this dump volume will be rewritten.\n");
+ killall();
+ nogripe = 1;
+ close_rewind();
+ Exit(X_REWRITE);
+}
+
+void
+sigpipe(signo)
+ int signo;
+{
+
+ quit("Broken pipe\n");
+}
+
+static void
+flushtape()
+{
+ int i, blks, got;
+ long lastfirstrec;
+
+ int siz = (char *)nextblock - (char *)slp->req;
+
+ slp->req[trecno].count = 0; /* Sentinel */
+
+ if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
+ quit("error writing command pipe: %s\n", strerror(errno));
+ slp->sent = 1; /* we sent a request, read the response later */
+
+ lastfirstrec = slp->firstrec;
+
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ /* Read results back from next slave */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ /* Check for end of tape */
+ if (got < writesize) {
+ msg("End of tape detected\n");
+
+ /*
+ * Drain the results, don't care what the values were.
+ * If we read them here then trewind won't...
+ */
+ for (i = 0; i < SLAVES; i++) {
+ if (slaves[i].sent) {
+ if (atomic(read, slaves[i].fd,
+ (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[i].sent = 0;
+ }
+ }
+
+ close_rewind();
+ rollforward();
+ return;
+ }
+ }
+
+ blks = 0;
+ if (spcl.c_type != TS_END) {
+ for (i = 0; i < spcl.c_count; i++)
+ if (spcl.c_addr[i] != 0)
+ blks++;
+ }
+ slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
+ slp->tapea = spcl.c_tapea;
+ slp->firstrec = lastfirstrec + ntrec;
+ slp->inode = curino;
+ nextblock = slp->tblock;
+ trecno = 0;
+ asize += tenths;
+ blockswritten += ntrec;
+ blocksthisvol += ntrec;
+ if (!pipeout && (blocksperfile ?
+ (blocksthisvol >= blocksperfile) : (asize > tsize))) {
+ close_rewind();
+ startnewtape(0);
+ }
+ timeest();
+}
+
+void
+trewind()
+{
+ int f;
+ int got;
+
+ for (f = 0; f < SLAVES; f++) {
+ /*
+ * Drain the results, but unlike EOT we DO (or should) care
+ * what the return values were, since if we detect EOT after
+ * we think we've written the last blocks to the tape anyway,
+ * we have to replay those blocks with rollforward.
+ *
+ * fixme: punt for now.
+ */
+ if (slaves[f].sent) {
+ if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[f].sent = 0;
+ if (got != writesize) {
+ msg("EOT detected in last 2 tape records!\n");
+ msg("Use a longer tape, decrease the size estimate\n");
+ quit("or use no size estimate at all.\n");
+ }
+ }
+ (void) close(slaves[f].fd);
+ }
+ while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
+ /* void */;
+
+ if (pipeout)
+ return;
+
+ msg("Closing %s\n", tape);
+
+#ifdef RDUMP
+ if (host) {
+ rmtclose();
+ while (rmtopen(tape, 0) < 0)
+ sleep(10);
+ rmtclose();
+ return;
+ }
+#endif
+ (void) close(tapefd);
+ while ((f = open(tape, 0)) < 0)
+ sleep (10);
+ (void) close(f);
+}
+
+void
+close_rewind()
+{
+ trewind();
+ if (nexttape)
+ return;
+ if (!nogripe) {
+ msg("Change Volumes: Mount volume #%d\n", tapeno+1);
+ broadcast("CHANGE DUMP VOLUMES!\7\7\n");
+ }
+ while (!query("Is the new volume mounted and ready to go?"))
+ if (query("Do you want to abort?")) {
+ dumpabort(0);
+ /*NOTREACHED*/
+ }
+}
+
+void
+rollforward()
+{
+ register struct req *p, *q, *prev;
+ register struct slave *tslp;
+ int i, size, savedtapea, got;
+ union u_spcl *ntb, *otb;
+ tslp = &slaves[SLAVES];
+ ntb = (union u_spcl *)tslp->tblock[1];
+
+ /*
+ * Each of the N slaves should have requests that need to
+ * be replayed on the next tape. Use the extra slave buffers
+ * (slaves[SLAVES]) to construct request lists to be sent to
+ * each slave in turn.
+ */
+ for (i = 0; i < SLAVES; i++) {
+ q = &tslp->req[1];
+ otb = (union u_spcl *)slp->tblock;
+
+ /*
+ * For each request in the current slave, copy it to tslp.
+ */
+
+ prev = NULL;
+ for (p = slp->req; p->count > 0; p += p->count) {
+ *q = *p;
+ if (p->dblk == 0)
+ *ntb++ = *otb++; /* copy the datablock also */
+ prev = q;
+ q += q->count;
+ }
+ if (prev == NULL)
+ quit("rollforward: protocol botch");
+ if (prev->dblk != 0)
+ prev->count -= 1;
+ else
+ ntb--;
+ q -= 1;
+ q->count = 0;
+ q = &tslp->req[0];
+ if (i == 0) {
+ q->dblk = 0;
+ q->count = 1;
+ trecno = 0;
+ nextblock = tslp->tblock;
+ savedtapea = spcl.c_tapea;
+ spcl.c_tapea = slp->tapea;
+ startnewtape(0);
+ spcl.c_tapea = savedtapea;
+ lastspclrec = savedtapea - 1;
+ }
+ size = (char *)ntb - (char *)q;
+ if (atomic(write, slp->fd, (char *)q, size) != size) {
+ perror(" DUMP: error writing command pipe");
+ dumpabort(0);
+ }
+ slp->sent = 1;
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ q->count = 1;
+
+ if (prev->dblk != 0) {
+ /*
+ * If the last one was a disk block, make the
+ * first of this one be the last bit of that disk
+ * block...
+ */
+ q->dblk = prev->dblk +
+ prev->count * (TP_BSIZE / DEV_BSIZE);
+ ntb = (union u_spcl *)tslp->tblock;
+ } else {
+ /*
+ * It wasn't a disk block. Copy the data to its
+ * new location in the buffer.
+ */
+ q->dblk = 0;
+ *((union u_spcl *)tslp->tblock) = *ntb;
+ ntb = (union u_spcl *)tslp->tblock[1];
+ }
+ }
+ slp->req[0] = *q;
+ nextblock = slp->tblock;
+ if (q->dblk == 0)
+ nextblock++;
+ trecno = 1;
+
+ /*
+ * Clear the first slaves' response. One hopes that it
+ * worked ok, otherwise the tape is much too short!
+ */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ if (got != writesize) {
+ quit("EOT detected at start of the tape!\n");
+ }
+ }
+}
+
+/*
+ * We implement taking and restoring checkpoints on the tape level.
+ * When each tape is opened, a new process is created by forking; this
+ * saves all of the necessary context in the parent. The child
+ * continues the dump; the parent waits around, saving the context.
+ * If the child returns X_REWRITE, then it had problems writing that tape;
+ * this causes the parent to fork again, duplicating the context, and
+ * everything continues as if nothing had happened.
+ */
+void
+startnewtape(top)
+ int top;
+{
+ int parentpid;
+ int childpid;
+ int status;
+ int waitpid;
+ char *p;
+#ifdef sunos
+ void (*interrupt_save)();
+#else
+ sig_t interrupt_save;
+#endif
+
+ interrupt_save = signal(SIGINT, SIG_IGN);
+ parentpid = getpid();
+
+restore_check_point:
+ (void)signal(SIGINT, interrupt_save);
+ /*
+ * All signals are inherited...
+ */
+ childpid = fork();
+ if (childpid < 0) {
+ msg("Context save fork fails in parent %d\n", parentpid);
+ Exit(X_ABORT);
+ }
+ if (childpid != 0) {
+ /*
+ * PARENT:
+ * save the context by waiting
+ * until the child doing all of the work returns.
+ * don't catch the interrupt
+ */
+ signal(SIGINT, SIG_IGN);
+#ifdef TDEBUG
+ msg("Tape: %d; parent process: %d child process %d\n",
+ tapeno+1, parentpid, childpid);
+#endif /* TDEBUG */
+ while ((waitpid = wait(&status)) != childpid)
+ msg("Parent %d waiting for child %d has another child %d return\n",
+ parentpid, childpid, waitpid);
+ if (status & 0xFF) {
+ msg("Child %d returns LOB status %o\n",
+ childpid, status&0xFF);
+ }
+ status = (status >> 8) & 0xFF;
+#ifdef TDEBUG
+ switch(status) {
+ case X_FINOK:
+ msg("Child %d finishes X_FINOK\n", childpid);
+ break;
+ case X_ABORT:
+ msg("Child %d finishes X_ABORT\n", childpid);
+ break;
+ case X_REWRITE:
+ msg("Child %d finishes X_REWRITE\n", childpid);
+ break;
+ default:
+ msg("Child %d finishes unknown %d\n",
+ childpid, status);
+ break;
+ }
+#endif /* TDEBUG */
+ switch(status) {
+ case X_FINOK:
+ Exit(X_FINOK);
+ case X_ABORT:
+ Exit(X_ABORT);
+ case X_REWRITE:
+ goto restore_check_point;
+ default:
+ msg("Bad return code from dump: %d\n", status);
+ Exit(X_ABORT);
+ }
+ /*NOTREACHED*/
+ } else { /* we are the child; just continue */
+#ifdef TDEBUG
+ sleep(4); /* allow time for parent's message to get out */
+ msg("Child on Tape %d has parent %d, my pid = %d\n",
+ tapeno+1, parentpid, getpid());
+#endif /* TDEBUG */
+ /*
+ * If we have a name like "/dev/rmt0,/dev/rmt1",
+ * use the name before the comma first, and save
+ * the remaining names for subsequent volumes.
+ */
+ tapeno++; /* current tape sequence */
+ if (nexttape || index(tape, ',')) {
+ if (nexttape && *nexttape)
+ tape = nexttape;
+ if ((p = index(tape, ',')) != NULL) {
+ *p = '\0';
+ nexttape = p + 1;
+ } else
+ nexttape = NULL;
+ msg("Dumping volume %d on %s\n", tapeno, tape);
+ }
+#ifdef RDUMP
+ while ((tapefd = (host ? rmtopen(tape, 2) :
+ pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#else
+ while ((tapefd = (pipeout ? 1 :
+ open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#endif
+ {
+ msg("Cannot open output \"%s\".\n", tape);
+ if (!query("Do you want to retry the open?"))
+ dumpabort(0);
+ }
+
+ enslave(); /* Share open tape file descriptor with slaves */
+
+ asize = 0;
+ blocksthisvol = 0;
+ if (top)
+ newtape++; /* new tape signal */
+ spcl.c_count = slp->count;
+ /*
+ * measure firstrec in TP_BSIZE units since restore doesn't
+ * know the correct ntrec value...
+ */
+ spcl.c_firstrec = slp->firstrec;
+ spcl.c_volume++;
+ spcl.c_type = TS_TAPE;
+ spcl.c_flags |= DR_NEWHEADER;
+ writeheader((ino_t)slp->inode);
+ spcl.c_flags &=~ DR_NEWHEADER;
+ if (tapeno > 1)
+ msg("Volume %d begins with blocks from inode %d\n",
+ tapeno, slp->inode);
+ }
+}
+
+void
+dumpabort(signo)
+ int signo;
+{
+
+ if (master != 0 && master != getpid())
+ /* Signals master to call dumpabort */
+ (void) kill(master, SIGTERM);
+ else {
+ killall();
+ msg("The ENTIRE dump is aborted.\n");
+ }
+#ifdef RDUMP
+ rmtclose();
+#endif
+ Exit(X_ABORT);
+}
+
+__dead void
+Exit(status)
+ int status;
+{
+
+#ifdef TDEBUG
+ msg("pid = %d exits with status %d\n", getpid(), status);
+#endif /* TDEBUG */
+ exit(status);
+}
+
+/*
+ * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
+ */
+void
+proceed(signo)
+ int signo;
+{
+
+ if (ready)
+ longjmp(jmpbuf, 1);
+ caught++;
+}
+
+void
+enslave()
+{
+ int cmd[2];
+ register int i, j;
+
+ master = getpid();
+
+ signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+ signal(SIGPIPE, sigpipe);
+ signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
+ signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
+
+ for (i = 0; i < SLAVES; i++) {
+ if (i == slp - &slaves[0]) {
+ caught = 1;
+ } else {
+ caught = 0;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
+ (slaves[i].pid = fork()) < 0)
+ quit("too many slaves, %d (recompile smaller): %s\n",
+ i, strerror(errno));
+
+ slaves[i].fd = cmd[1];
+ slaves[i].sent = 0;
+ if (slaves[i].pid == 0) { /* Slave starts up here */
+ for (j = 0; j <= i; j++)
+ (void) close(slaves[j].fd);
+ signal(SIGINT, SIG_IGN); /* Master handles this */
+ doslave(cmd[0], i);
+ Exit(X_FINOK);
+ }
+ }
+
+ for (i = 0; i < SLAVES; i++)
+ (void) atomic(write, slaves[i].fd,
+ (char *) &slaves[(i + 1) % SLAVES].pid,
+ sizeof slaves[0].pid);
+
+ master = 0;
+}
+
+void
+killall()
+{
+ register int i;
+
+ for (i = 0; i < SLAVES; i++)
+ if (slaves[i].pid > 0)
+ (void) kill(slaves[i].pid, SIGKILL);
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile. When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+static void
+doslave(cmd, slave_number)
+ register int cmd;
+ int slave_number;
+{
+ register int nread;
+ int nextslave, size, wrote, eot_count;
+
+ /*
+ * Need our own seek pointer.
+ */
+ (void) close(diskfd);
+ if ((diskfd = open(disk, O_RDONLY)) < 0)
+ quit("slave couldn't reopen disk: %s\n", strerror(errno));
+
+ /*
+ * Need the pid of the next slave in the loop...
+ */
+ if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
+ != sizeof nextslave) {
+ quit("master/slave protocol botched - didn't get pid of next slave.\n");
+ }
+
+ /*
+ * Get list of blocks to dump, read the blocks into tape buffer
+ */
+ while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
+ register struct req *p = slp->req;
+
+ for (trecno = 0; trecno < ntrec;
+ trecno += p->count, p += p->count) {
+ if (p->dblk) {
+ bread(p->dblk, slp->tblock[trecno],
+ p->count * TP_BSIZE);
+ } else {
+ if (p->count != 1 || atomic(read, cmd,
+ (char *)slp->tblock[trecno],
+ TP_BSIZE) != TP_BSIZE)
+ quit("master/slave protocol botched.\n");
+ }
+ }
+ if (setjmp(jmpbuf) == 0) {
+ ready = 1;
+ if (!caught)
+ (void) pause();
+ }
+ ready = 0;
+ caught = 0;
+
+ /* Try to write the data... */
+ eot_count = 0;
+ size = 0;
+
+ while (eot_count < 10 && size < writesize) {
+#ifdef RDUMP
+ if (host)
+ wrote = rmtwrite(slp->tblock[0]+size,
+ writesize-size);
+ else
+#endif
+ wrote = write(tapefd, slp->tblock[0]+size,
+ writesize-size);
+#ifdef WRITEDEBUG
+ printf("slave %d wrote %d\n", slave_number, wrote);
+#endif
+ if (wrote < 0)
+ break;
+ if (wrote == 0)
+ eot_count++;
+ size += wrote;
+ }
+
+#ifdef WRITEDEBUG
+ if (size != writesize)
+ printf("slave %d only wrote %d out of %d bytes and gave up.\n",
+ slave_number, size, writesize);
+#endif
+
+ if (eot_count > 0)
+ size = 0;
+
+ /*
+ * fixme: Pyramids running OSx return ENOSPC
+ * at EOT on 1/2 inch drives.
+ */
+ if (size < 0) {
+ (void) kill(master, SIGUSR1);
+ for (;;)
+ (void) sigpause(0);
+ } else {
+ /*
+ * pass size of write back to master
+ * (for EOT handling)
+ */
+ (void) atomic(write, cmd, (char *)&size, sizeof size);
+ }
+
+ /*
+ * If partial write, don't want next slave to go.
+ * Also jolts him awake.
+ */
+ (void) kill(nextslave, SIGUSR2);
+ }
+ if (nread != 0)
+ quit("error reading command pipe: %s\n", strerror(errno));
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+static int
+atomic(func, fd, buf, count)
+ int (*func)(), fd;
+ char *buf;
+ int count;
+{
+ int got, need = count;
+
+ while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+ buf += got;
+ return (got < 0 ? got : count - need);
+}
diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c
new file mode 100644
index 000000000000..ce70050ce073
--- /dev/null
+++ b/sbin/dump/traverse.c
@@ -0,0 +1,613 @@
+/*-
+ * Copyright (c) 1980, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)traverse.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+
+#define HASDUMPEDFILE 0x1
+#define HASSUBDIRS 0x2
+
+#ifdef FS_44INODEFMT
+typedef quad_t fsizeT;
+#else
+typedef long fsizeT;
+#endif
+
+static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
+static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
+static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
+
+/*
+ * This is an estimation of the number of TP_BSIZE blocks in the file.
+ * It estimates the number of blocks in files with holes by assuming
+ * that all of the blocks accounted for by di_blocks are data blocks
+ * (when some of the blocks are usually used for indirect pointers);
+ * hence the estimate may be high.
+ */
+long
+blockest(dp)
+ register struct dinode *dp;
+{
+ long blkest, sizeest;
+
+ /*
+ * dp->di_size is the size of the file in bytes.
+ * dp->di_blocks stores the number of sectors actually in the file.
+ * If there are more sectors than the size would indicate, this just
+ * means that there are indirect blocks in the file or unused
+ * sectors in the last file block; we can safely ignore these
+ * (blkest = sizeest below).
+ * If the file is bigger than the number of sectors would indicate,
+ * then the file has holes in it. In this case we must use the
+ * block count to estimate the number of data blocks used, but
+ * we use the actual size for estimating the number of indirect
+ * dump blocks (sizeest vs. blkest in the indirect block
+ * calculation).
+ */
+ blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
+ sizeest = howmany(dp->di_size, TP_BSIZE);
+ if (blkest > sizeest)
+ blkest = sizeest;
+ if (dp->di_size > sblock->fs_bsize * NDADDR) {
+ /* calculate the number of indirect blocks on the dump tape */
+ blkest +=
+ howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
+ TP_NINDIR);
+ }
+ return (blkest + 1);
+}
+
+/* Auxiliary macro to pick up files changed since previous dump. */
+#ifdef FS_44INODEFMT
+#define CHANGEDSINCE(dp, t) \
+ ((dp)->di_mtime.ts_sec >= (t) || (dp)->di_ctime.ts_sec >= (t))
+#else
+#define CHANGEDSINCE(dp, t) \
+ ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
+#endif
+
+/* The WANTTODUMP macro decides whether a file should be dumped. */
+#ifdef UF_NODUMP
+#define WANTTODUMP(dp) \
+ (CHANGEDSINCE(dp, spcl.c_ddate) && \
+ (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP))
+#else
+#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
+#endif
+
+/*
+ * Dump pass 1.
+ *
+ * Walk the inode list for a filesystem to find all allocated inodes
+ * that have been modified since the previous dump time. Also, find all
+ * the directories in the filesystem.
+ */
+int
+mapfiles(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register int mode;
+ register ino_t ino;
+ register struct dinode *dp;
+ int anydirskipped = 0;
+
+ for (ino = ROOTINO; ino < maxino; ino++) {
+ dp = getino(ino);
+ if ((mode = (dp->di_mode & IFMT)) == 0)
+ continue;
+ SETINO(ino, usedinomap);
+ if (mode == IFDIR)
+ SETINO(ino, dumpdirmap);
+ if (WANTTODUMP(dp)) {
+ SETINO(ino, dumpinomap);
+ if (mode != IFREG && mode != IFDIR && mode != IFLNK)
+ *tapesize += 1;
+ else
+ *tapesize += blockest(dp);
+ continue;
+ }
+ if (mode == IFDIR)
+ anydirskipped = 1;
+ }
+ /*
+ * Restore gets very upset if the root is not dumped,
+ * so ensure that it always is dumped.
+ */
+ SETINO(ROOTINO, dumpinomap);
+ return (anydirskipped);
+}
+
+/*
+ * Dump pass 2.
+ *
+ * Scan each directory on the filesystem to see if it has any modified
+ * files in it. If it does, and has not already been added to the dump
+ * list (because it was itself modified), then add it. If a directory
+ * has not been modified itself, contains no modified files and has no
+ * subdirectories, then it can be deleted from the dump list and from
+ * the list of directories. By deleting it from the list of directories,
+ * its parent may now qualify for the same treatment on this or a later
+ * pass using this algorithm.
+ */
+int
+mapdirs(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register struct dinode *dp;
+ register int i, isdir;
+ register char *map;
+ register ino_t ino;
+ long filesize;
+ int ret, change = 0;
+
+ isdir = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ isdir = *map++;
+ else
+ isdir >>= 1;
+ if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap))
+ continue;
+ dp = getino(ino);
+ filesize = dp->di_size;
+ for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
+ if (dp->di_db[i] != 0)
+ ret |= searchdir(ino, dp->di_db[i],
+ (long)dblksize(sblock, dp, i),
+ filesize);
+ if (ret & HASDUMPEDFILE)
+ filesize = 0;
+ else
+ filesize -= sblock->fs_bsize;
+ }
+ for (i = 0; filesize > 0 && i < NIADDR; i++) {
+ if (dp->di_ib[i] == 0)
+ continue;
+ ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
+ }
+ if (ret & HASDUMPEDFILE) {
+ SETINO(ino, dumpinomap);
+ *tapesize += blockest(dp);
+ change = 1;
+ continue;
+ }
+ if ((ret & HASSUBDIRS) == 0) {
+ if (!TSTINO(ino, dumpinomap)) {
+ CLRINO(ino, dumpdirmap);
+ change = 1;
+ }
+ }
+ }
+ return (change);
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be searched
+ * as directories. Quit as soon as any entry is found that will
+ * require the directory to be dumped.
+ */
+static int
+dirindir(ino, blkno, ind_level, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ int ind_level;
+ long *filesize;
+{
+ int ret = 0;
+ register int i;
+ daddr_t idblk[MAXNINDIR];
+
+ bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= searchdir(ino, blkno, sblock->fs_bsize,
+ *filesize);
+ if (ret & HASDUMPEDFILE)
+ *filesize = 0;
+ else
+ *filesize -= sblock->fs_bsize;
+ }
+ return (ret);
+ }
+ ind_level--;
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= dirindir(ino, blkno, ind_level, filesize);
+ }
+ return (ret);
+}
+
+/*
+ * Scan a disk block containing directory information looking to see if
+ * any of the entries are on the dump list and to see if the directory
+ * contains any subdirectories.
+ */
+static int
+searchdir(ino, blkno, size, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ register long size;
+ long filesize;
+{
+ register struct direct *dp;
+ register long loc, ret = 0;
+ char dblk[MAXBSIZE];
+
+ bread(fsbtodb(sblock, blkno), dblk, (int)size);
+ if (filesize < size)
+ size = filesize;
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(dblk + loc);
+ if (dp->d_reclen == 0) {
+ msg("corrupted directory, inumber %d\n", ino);
+ break;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_name[0] == '.') {
+ if (dp->d_name[1] == '\0')
+ continue;
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
+ continue;
+ }
+ if (TSTINO(dp->d_ino, dumpinomap)) {
+ ret |= HASDUMPEDFILE;
+ if (ret & HASSUBDIRS)
+ break;
+ }
+ if (TSTINO(dp->d_ino, dumpdirmap)) {
+ ret |= HASSUBDIRS;
+ if (ret & HASDUMPEDFILE)
+ break;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(dp, ino)
+ register struct dinode *dp;
+ ino_t ino;
+{
+ int ind_level, cnt;
+ fsizeT size;
+ char buf[TP_BSIZE];
+
+ if (newtape) {
+ newtape = 0;
+ dumpmap(dumpinomap, TS_BITS, ino);
+ }
+ CLRINO(ino, dumpinomap);
+ spcl.c_dinode = *dp;
+ spcl.c_type = TS_INODE;
+ spcl.c_count = 0;
+ switch (dp->di_mode & S_IFMT) {
+
+ case 0:
+ /*
+ * Freed inode.
+ */
+ return;
+
+ case S_IFLNK:
+ /*
+ * Check for short symbolic link.
+ */
+#ifdef FS_44INODEFMT
+ if (dp->di_size > 0 &&
+ dp->di_size < sblock->fs_maxsymlinklen) {
+ spcl.c_addr[0] = 1;
+ spcl.c_count = 1;
+ writeheader(ino);
+ bcopy((caddr_t)dp->di_shortlink, buf,
+ (u_long)dp->di_size);
+ buf[dp->di_size] = '\0';
+ writerec(buf, 0);
+ return;
+ }
+#endif
+ /* fall through */
+
+ case S_IFDIR:
+ case S_IFREG:
+ if (dp->di_size > 0)
+ break;
+ /* fall through */
+
+ case S_IFIFO:
+ case S_IFSOCK:
+ case S_IFCHR:
+ case S_IFBLK:
+ writeheader(ino);
+ return;
+
+ default:
+ msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT);
+ return;
+ }
+ if (dp->di_size > NDADDR * sblock->fs_bsize)
+ cnt = NDADDR * sblock->fs_frag;
+ else
+ cnt = howmany(dp->di_size, sblock->fs_fsize);
+ blksout(&dp->di_db[0], cnt, ino);
+ if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0)
+ return;
+ for (ind_level = 0; ind_level < NIADDR; ind_level++) {
+ dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
+ if (size <= 0)
+ return;
+ }
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be dumped.
+ */
+static void
+dmpindir(ino, blk, ind_level, size)
+ ino_t ino;
+ daddr_t blk;
+ int ind_level;
+ fsizeT *size;
+{
+ int i, cnt;
+ daddr_t idblk[MAXNINDIR];
+
+ if (blk != 0)
+ bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize);
+ else
+ bzero((char *)idblk, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ if (*size < NINDIR(sblock) * sblock->fs_bsize)
+ cnt = howmany(*size, sblock->fs_fsize);
+ else
+ cnt = NINDIR(sblock) * sblock->fs_frag;
+ *size -= NINDIR(sblock) * sblock->fs_bsize;
+ blksout(&idblk[0], cnt, ino);
+ return;
+ }
+ ind_level--;
+ for (i = 0; i < NINDIR(sblock); i++) {
+ dmpindir(ino, idblk[i], ind_level, size);
+ if (*size <= 0)
+ return;
+ }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+blksout(blkp, frags, ino)
+ daddr_t *blkp;
+ int frags;
+ ino_t ino;
+{
+ register daddr_t *bp;
+ int i, j, count, blks, tbperdb;
+
+ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+ tbperdb = sblock->fs_bsize >> tp_bshift;
+ for (i = 0; i < blks; i += TP_NINDIR) {
+ if (i + TP_NINDIR > blks)
+ count = blks;
+ else
+ count = i + TP_NINDIR;
+ for (j = i; j < count; j++)
+ if (blkp[j / tbperdb] != 0)
+ spcl.c_addr[j - i] = 1;
+ else
+ spcl.c_addr[j - i] = 0;
+ spcl.c_count = count - i;
+ writeheader(ino);
+ bp = &blkp[i / tbperdb];
+ for (j = i; j < count; j += tbperdb, bp++)
+ if (*bp != 0)
+ if (j + tbperdb <= count)
+ dumpblock(*bp, (int)sblock->fs_bsize);
+ else
+ dumpblock(*bp, (count - j) * TP_BSIZE);
+ spcl.c_type = TS_ADDR;
+ }
+}
+
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(map, type, ino)
+ char *map;
+ int type;
+ ino_t ino;
+{
+ register int i;
+ char *cp;
+
+ spcl.c_type = type;
+ spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
+ writeheader(ino);
+ for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
+ writerec(cp, 0);
+}
+
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino)
+ ino_t ino;
+{
+ register long sum, cnt, *lp;
+
+ spcl.c_inumber = ino;
+ spcl.c_magic = NFS_MAGIC;
+ spcl.c_checksum = 0;
+ lp = (long *)&spcl;
+ sum = 0;
+ cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+ while (--cnt >= 0) {
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ }
+ spcl.c_checksum = CHECKSUM - sum;
+ writerec((char *)&spcl, 1);
+}
+
+struct dinode *
+getino(inum)
+ ino_t inum;
+{
+ static daddr_t minino, maxino;
+ static struct dinode inoblock[MAXINOPB];
+
+ curino = inum;
+ if (inum >= minino && inum < maxino)
+ return (&inoblock[inum - minino]);
+ bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock,
+ (int)sblock->fs_bsize);
+ minino = inum - (inum % INOPB(sblock));
+ maxino = minino + INOPB(sblock);
+ return (&inoblock[inum - minino]);
+}
+
+/*
+ * Read a chunk of data from the disk.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+int breaderrors = 0;
+#define BREADEMAX 32
+
+void
+bread(blkno, buf, size)
+ daddr_t blkno;
+ char *buf;
+ int size;
+{
+ int cnt, i;
+ extern int errno;
+
+loop:
+ if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
+ msg("bread: lseek fails\n");
+ if ((cnt = read(diskfd, buf, size)) == size)
+ return;
+ if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
+ /*
+ * Trying to read the final fragment.
+ *
+ * NB - dump only works in TP_BSIZE blocks, hence
+ * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
+ * It should be smarter about not actually trying to
+ * read more than it can get, but for the time being
+ * we punt and scale back the read only when it gets
+ * us into trouble. (mkm 9/25/83)
+ */
+ size -= dev_bsize;
+ goto loop;
+ }
+ if (cnt == -1)
+ msg("read error from %s: %s: [block %d]: count=%d\n",
+ disk, strerror(errno), blkno, size);
+ else
+ msg("short read error from %s: [block %d]: count=%d, got=%d\n",
+ disk, blkno, size, cnt);
+ if (++breaderrors > BREADEMAX) {
+ msg("More than %d block read errors from %d\n",
+ BREADEMAX, disk);
+ broadcast("DUMP IS AILING!\n");
+ msg("This is an unrecoverable error.\n");
+ if (!query("Do you want to attempt to continue?")){
+ dumpabort(0);
+ /*NOTREACHED*/
+ } else
+ breaderrors = 0;
+ }
+ /*
+ * Zero buffer, then try to read each sector of buffer separately.
+ */
+ bzero(buf, size);
+ for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+ if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
+ msg("bread: lseek2 fails!\n");
+ if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
+ continue;
+ if (cnt == -1) {
+ msg("read error from %s: %s: [sector %d]: count=%d\n",
+ disk, strerror(errno), blkno, dev_bsize);
+ continue;
+ }
+ msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
+ disk, blkno, dev_bsize, cnt);
+ }
+}
diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c
new file mode 100644
index 000000000000..7b3fd4ea7cbd
--- /dev/null
+++ b/sbin/dump/unctime.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)unctime.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <time.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#ifndef __P
+#include <sys/cdefs.h>
+#endif
+
+/*
+ * Convert a ctime(3) format string into a system format date.
+ * Return the date thus calculated.
+ *
+ * Return -1 if the string is not in ctime format.
+ */
+
+/*
+ * Offsets into the ctime string to various parts.
+ */
+
+#define E_MONTH 4
+#define E_DAY 8
+#define E_HOUR 11
+#define E_MINUTE 14
+#define E_SECOND 17
+#define E_YEAR 20
+
+static int dcmp __P((struct tm *, struct tm *));
+static time_t emitl __P((struct tm *));
+static int lookup __P((char *));
+
+
+time_t
+unctime(str)
+ char *str;
+{
+ struct tm then;
+ char dbuf[26];
+
+ (void) strncpy(dbuf, str, sizeof(dbuf) - 1);
+ dbuf[sizeof(dbuf) - 1] = '\0';
+ dbuf[E_MONTH+3] = '\0';
+ if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
+ return (-1);
+ then.tm_mday = atoi(&dbuf[E_DAY]);
+ then.tm_hour = atoi(&dbuf[E_HOUR]);
+ then.tm_min = atoi(&dbuf[E_MINUTE]);
+ then.tm_sec = atoi(&dbuf[E_SECOND]);
+ then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
+ return(emitl(&then));
+}
+
+static char months[] =
+ "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+static int
+lookup(str)
+ char *str;
+{
+ register char *cp, *cp2;
+
+ for (cp = months, cp2 = str; *cp != '\0'; cp += 3)
+ if (strncmp(cp, cp2, 3) == 0)
+ return((cp-months) / 3);
+ return(-1);
+}
+/*
+ * Routine to convert a localtime(3) format date back into
+ * a system format date.
+ *
+ * Use a binary search.
+ */
+
+static time_t
+emitl(dp)
+ struct tm *dp;
+{
+ time_t conv;
+ register int i, bit;
+ struct tm dcopy;
+
+ dcopy = *dp;
+ dp = &dcopy;
+ conv = 0;
+ for (i = 30; i >= 0; i--) {
+ bit = 1 << i;
+ conv |= bit;
+ if (dcmp(localtime(&conv), dp) > 0)
+ conv &= ~bit;
+ }
+ return(conv);
+}
+
+/*
+ * Compare two localtime dates, return result.
+ */
+
+#define DECIDE(a) \
+ if (dp->a > dp2->a) \
+ return(1); \
+ if (dp->a < dp2->a) \
+ return(-1)
+
+static int
+dcmp(dp, dp2)
+ register struct tm *dp, *dp2;
+{
+
+ DECIDE(tm_year);
+ DECIDE(tm_mon);
+ DECIDE(tm_mday);
+ DECIDE(tm_hour);
+ DECIDE(tm_min);
+ DECIDE(tm_sec);
+ return(0);
+}
diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile
new file mode 100644
index 000000000000..dd2a0c2830ca
--- /dev/null
+++ b/sbin/dumpfs/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= dumpfs
+MAN8= dumpfs.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8
new file mode 100644
index 000000000000..17263d0a96f0
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.8
@@ -0,0 +1,62 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dumpfs.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt DUMPFS 8
+.Os BSD 4.2
+.Sh NAME
+.Nm dumpfs
+.Nd dump file system information
+.Sh SYNOPSIS
+.Nm dumpfs
+.Op Ar filesys No \&| Ar device
+.Sh DESCRIPTION
+.Nm Dumpfs
+prints out the super block and cylinder group information
+for the file system or special device specified.
+The listing is very long and detailed. This
+command is useful mostly for finding out certain file system
+information such as the file system block size and minimum
+free space percentage.
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr tunefs 8 ,
+.Xr newfs 8 ,
+.Xr fsck 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c
new file mode 100644
index 000000000000..d453fab7512c
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumpfs.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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+union {
+ struct fs fs;
+ char pad[MAXBSIZE];
+} fsun;
+#define afs fsun.fs
+
+union {
+ struct cg cg;
+ char pad[MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+long dev_bsize = 1;
+
+int dumpfs __P((char *));
+int dumpcg __P((char *, int, int));
+void pbits __P((void *, int));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct fstab *fs;
+ int ch, eval;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ for (eval = 0; *argv; ++argv)
+ if ((fs = getfsfile(*argv)) == NULL)
+ eval |= dumpfs(*argv);
+ else
+ eval |= dumpfs(fs->fs_spec);
+ exit(eval);
+}
+
+int
+dumpfs(name)
+ char *name;
+{
+ int fd, c, i, j, k, size;
+
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ goto err;
+ if (lseek(fd, (off_t)SBOFF, SEEK_SET) == (off_t)-1)
+ goto err;
+ if (read(fd, &afs, SBSIZE) != SBSIZE)
+ goto err;
+
+ if (afs.fs_postblformat == FS_42POSTBLFMT)
+ afs.fs_nrpos = 8;
+ dev_bsize = afs.fs_fsize / fsbtodb(&afs, 1);
+ printf("magic\t%x\ttime\t%s", afs.fs_magic,
+ ctime(&afs.fs_time));
+ printf("cylgrp\t%s\tinodes\t%s\n",
+ afs.fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic",
+ afs.fs_inodefmt < FS_44INODEFMT ? "4.2/4.3BSD" : "4.4BSD");
+ printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
+ afs.fs_cstotal.cs_nbfree, afs.fs_cstotal.cs_ndir,
+ afs.fs_cstotal.cs_nifree, afs.fs_cstotal.cs_nffree);
+ printf("ncg\t%d\tncyl\t%d\tsize\t%d\tblocks\t%d\n",
+ afs.fs_ncg, afs.fs_ncyl, afs.fs_size, afs.fs_dsize);
+ printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n",
+ afs.fs_bsize, afs.fs_bshift, afs.fs_bmask);
+ printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n",
+ afs.fs_fsize, afs.fs_fshift, afs.fs_fmask);
+ printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n",
+ afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb);
+ printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n",
+ afs.fs_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg);
+ printf("minfree\t%d%%\toptim\t%s\tmaxcontig %d\tmaxbpg\t%d\n",
+ afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time",
+ afs.fs_maxcontig, afs.fs_maxbpg);
+ printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n",
+ afs.fs_rotdelay, afs.fs_headswitch, afs.fs_trkseek, afs.fs_rps);
+ printf("ntrak\t%d\tnsect\t%d\tnpsect\t%d\tspc\t%d\n",
+ afs.fs_ntrak, afs.fs_nsect, afs.fs_npsect, afs.fs_spc);
+ printf("symlinklen %d\ttrackskew %d\tinterleave %d\tcontigsumsize %d\n",
+ afs.fs_maxsymlinklen, afs.fs_trackskew, afs.fs_interleave,
+ afs.fs_contigsumsize);
+ printf("nindir\t%d\tinopb\t%d\tnspf\t%d\n",
+ afs.fs_nindir, afs.fs_inopb, afs.fs_nspf);
+ printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n",
+ afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno);
+ printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n",
+ afs.fs_sbsize, afs.fs_cgsize, afs.fs_cgoffset, afs.fs_cgmask);
+ printf("csaddr\t%d\tcssize\t%d\tshift\t%d\tmask\t0x%08x\n",
+ afs.fs_csaddr, afs.fs_cssize, afs.fs_csshift, afs.fs_csmask);
+ printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\n",
+ afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly);
+ if (afs.fs_cpc != 0)
+ printf("blocks available in each of %d rotational positions",
+ afs.fs_nrpos);
+ else
+ printf("insufficient space to maintain rotational tables\n");
+ for (c = 0; c < afs.fs_cpc; c++) {
+ printf("\ncylinder number %d:", c);
+ for (i = 0; i < afs.fs_nrpos; i++) {
+ if (fs_postbl(&afs, c)[i] == -1)
+ continue;
+ printf("\n position %d:\t", i);
+ for (j = fs_postbl(&afs, c)[i], k = 1; ;
+ j += fs_rotbl(&afs)[j], k++) {
+ printf("%5d", j);
+ if (k % 12 == 0)
+ printf("\n\t\t");
+ if (fs_rotbl(&afs)[j] == 0)
+ break;
+ }
+ }
+ }
+ printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t");
+ for (i = 0, j = 0; i < afs.fs_cssize; i += afs.fs_bsize, j++) {
+ size = afs.fs_cssize - i < afs.fs_bsize ?
+ afs.fs_cssize - i : afs.fs_bsize;
+ afs.fs_csp[j] = calloc(1, size);
+ if (lseek(fd,
+ (off_t)(fsbtodb(&afs, (afs.fs_csaddr + j * afs.fs_frag)) *
+ dev_bsize), SEEK_SET) == (off_t)-1)
+ goto err;
+ if (read(fd, afs.fs_csp[j], size) != size)
+ goto err;
+ }
+ for (i = 0; i < afs.fs_ncg; i++) {
+ struct csum *cs = &afs.fs_cs(&afs, i);
+ if (i && i % 4 == 0)
+ printf("\n\t");
+ printf("(%d,%d,%d,%d) ",
+ cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree);
+ }
+ printf("\n");
+ if (afs.fs_ncyl % afs.fs_cpg) {
+ printf("cylinders in last group %d\n",
+ i = afs.fs_ncyl % afs.fs_cpg);
+ printf("blocks in last group %d\n",
+ i * afs.fs_spc / NSPB(&afs));
+ }
+ printf("\n");
+ for (i = 0; i < afs.fs_ncg; i++)
+ if (dumpcg(name, fd, i))
+ goto err;
+ (void)close(fd);
+ return (0);
+
+err: if (fd != -1)
+ (void)close(fd);
+ (void)fprintf(stderr, "dumpfs: %s: %s\n", name, strerror(errno));
+ return (1);
+};
+
+int
+dumpcg(name, fd, c)
+ char *name;
+ int fd, c;
+{
+ off_t cur;
+ int i, j;
+
+ printf("\ncg %d:\n", c);
+ if ((cur = lseek(fd, (off_t)(fsbtodb(&afs, cgtod(&afs, c)) * dev_bsize),
+ SEEK_SET)) == (off_t)-1)
+ return (1);
+ if (read(fd, &acg, afs.fs_bsize) != afs.fs_bsize) {
+ (void)fprintf(stderr, "dumpfs: %s: error reading cg\n", name);
+ return (1);
+ }
+ printf("magic\t%x\ttell\t%qx\ttime\t%s",
+ afs.fs_postblformat == FS_42POSTBLFMT ?
+ ((struct ocg *)&acg)->cg_magic : acg.cg_magic,
+ cur, ctime(&acg.cg_time));
+ printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n",
+ acg.cg_cgx, acg.cg_ncyl, acg.cg_niblk, acg.cg_ndblk);
+ printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n",
+ acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir,
+ acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree);
+ printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum",
+ acg.cg_rotor, acg.cg_irotor, acg.cg_frotor);
+ for (i = 1, j = 0; i < afs.fs_frag; i++) {
+ printf("\t%d", acg.cg_frsum[i]);
+ j += i * acg.cg_frsum[i];
+ }
+ printf("\nsum of frsum: %d", j);
+ if (afs.fs_contigsumsize > 0) {
+ for (i = 1; i < afs.fs_contigsumsize; i++) {
+ if ((i - 1) % 8 == 0)
+ printf("\nclusters %d-%d:", i,
+ afs.fs_contigsumsize - 1 < i + 7 ?
+ afs.fs_contigsumsize - 1 : i + 7);
+ printf("\t%d", cg_clustersum(&acg)[i]);
+ }
+ printf("\nclusters size %d and over: %d\n",
+ afs.fs_contigsumsize,
+ cg_clustersum(&acg)[afs.fs_contigsumsize]);
+ printf("clusters free:\t");
+ pbits(cg_clustersfree(&acg), acg.cg_nclusterblks);
+ } else
+ printf("\n");
+ printf("iused:\t");
+ pbits(cg_inosused(&acg), afs.fs_ipg);
+ printf("free:\t");
+ pbits(cg_blksfree(&acg), afs.fs_fpg);
+ printf("b:\n");
+ for (i = 0; i < afs.fs_cpg; i++) {
+ if (cg_blktot(&acg)[i] == 0)
+ continue;
+ printf(" c%d:\t(%d)\t", i, cg_blktot(&acg)[i]);
+ for (j = 0; j < afs.fs_nrpos; j++) {
+ if (afs.fs_cpc > 0 &&
+ fs_postbl(&afs, i % afs.fs_cpc)[j] == -1)
+ continue;
+ printf(" %d", cg_blks(&afs, &acg, i)[j]);
+ }
+ printf("\n");
+ }
+ return (0);
+};
+
+void
+pbits(vp, max)
+ register void *vp;
+ int max;
+{
+ register int i;
+ register char *p;
+ int count, j;
+
+ for (count = i = 0, p = vp; i < max; i++)
+ if (isset(p, i)) {
+ if (count)
+ printf(",%s", count % 6 ? " " : "\n\t");
+ count++;
+ printf("%d", i);
+ j = i;
+ while ((i+1)<max && isset(p, i+1))
+ i++;
+ if (i != j)
+ printf("-%d", i);
+ }
+ printf("\n");
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: dumpfs filesys | device\n");
+ exit(1);
+}
diff --git a/sbin/dumplfs/Makefile b/sbin/dumplfs/Makefile
new file mode 100644
index 000000000000..25574924d0a7
--- /dev/null
+++ b/sbin/dumplfs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/18/93
+
+PROG= dumplfs
+CFLAGS+=-I/sys/ufs/lfs
+SRCS= dumplfs.c lfs_cksum.c misc.c
+.PATH: /sys/ufs/lfs
+MAN8= dumplfs.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumplfs/dumplfs.8 b/sbin/dumplfs/dumplfs.8
new file mode 100644
index 000000000000..de0c68055ac2
--- /dev/null
+++ b/sbin/dumplfs/dumplfs.8
@@ -0,0 +1,59 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dumplfs.8 8.1 (Berkeley) 6/18/93
+.\"
+.Dd June 18, 1993
+.Dt DUMPLFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm dumplfs
+.Nd dump file system information
+.Sh SYNOPSIS
+.Nm dumplfs
+.Op Ar filesys No \&| Ar device
+.Sh DESCRIPTION
+.Nm Dumplfs
+prints out the file system layout information for the
+LFS file system or special device specified.
+The listing is very long and detailed.
+This command is useful mostly for finding out certain file system
+information such as the file system block size.
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr newlfs 8 ,
+.Sh HISTORY
+The
+.Nm dumplfs
+command appeared in
+.Bx 4.4 .
diff --git a/sbin/dumplfs/dumplfs.c b/sbin/dumplfs/dumplfs.c
new file mode 100644
index 000000000000..943c6cd97701
--- /dev/null
+++ b/sbin/dumplfs/dumplfs.c
@@ -0,0 +1,612 @@
+/*-
+ * 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 copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumplfs.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <fcntl.h>
+#include <fstab.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+
+static void addseg __P((char *));
+static void dump_cleaner_info __P((struct lfs *, void *));
+static void dump_dinode __P((struct dinode *));
+static void dump_ifile __P((int, struct lfs *, int));
+static int dump_ipage_ifile __P((int, IFILE *, int));
+static int dump_ipage_segusage __P((struct lfs *, int, IFILE *, int));
+static void dump_segment __P((int, int, daddr_t, struct lfs *, int));
+static int dump_sum __P((int, struct lfs *, SEGSUM *, int, daddr_t));
+static void dump_super __P((struct lfs *));
+static void usage __P((void));
+
+typedef struct seglist SEGLIST;
+struct seglist {
+ SEGLIST *next;
+ int num;
+};
+SEGLIST *seglist;
+
+int daddr_shift;
+char *special;
+
+/* Segment Usage formats */
+#define print_suheader \
+ (void)printf("segnum\tflags\tnbytes\tninos\tnsums\tlastmod\n")
+
+#define print_suentry(i, sp) \
+ (void)printf("%d\t%c%c%c\t%d\t%d\t%d\t%s", i, \
+ (((sp)->su_flags & SEGUSE_ACTIVE) ? 'A' : ' '), \
+ (((sp)->su_flags & SEGUSE_DIRTY) ? 'D' : 'C'), \
+ (((sp)->su_flags & SEGUSE_SUPERBLOCK) ? 'S' : ' '), \
+ (sp)->su_nbytes, (sp)->su_ninos, (sp)->su_nsums, \
+ ctime((time_t *)&(sp)->su_lastmod))
+
+/* Ifile formats */
+#define print_iheader \
+ (void)printf("inum\tstatus\tversion\tdaddr\t\tfreeptr\n")
+#define print_ientry(i, ip) \
+ if (ip->if_daddr == LFS_UNUSED_DADDR) \
+ (void)printf("%d\tFREE\t%d\t \t\t%d\n", \
+ i, ip->if_version, ip->if_nextfree); \
+ else \
+ (void)printf("%d\tINUSE\t%d\t%8X \n", \
+ i, ip->if_version, ip->if_daddr)
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct lfs lfs_sb1, lfs_sb2, *lfs_master;
+ daddr_t seg_addr;
+ int ch, do_allsb, do_ientries, fd, segnum;
+
+ do_allsb = 0;
+ do_ientries = 0;
+ while ((ch = getopt(argc, argv, "ais:")) != EOF)
+ switch(ch) {
+ case 'a': /* Dump all superblocks */
+ do_allsb = 1;
+ break;
+ case 'i': /* Dump ifile entries */
+ do_ientries = 1;
+ break;
+ case 's': /* Dump out these segments */
+ addseg(optarg);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ special = argv[0];
+ if ((fd = open(special, O_RDONLY, 0)) < 0)
+ err("%s: %s", special, strerror(errno));
+
+ /* Read the first superblock */
+ get(fd, LFS_LABELPAD, &lfs_sb1, sizeof(struct lfs));
+ daddr_shift = lfs_sb1.lfs_bshift - lfs_sb1.lfs_fsbtodb;
+
+ /*
+ * Read the second superblock and figure out which check point is
+ * most up to date.
+ */
+ get(fd,
+ lfs_sb1.lfs_sboffs[1] << daddr_shift, &lfs_sb2, sizeof(struct lfs));
+
+ lfs_master = &lfs_sb1;
+ if (lfs_sb1.lfs_tstamp < lfs_sb2.lfs_tstamp)
+ lfs_master = &lfs_sb2;
+
+ (void)printf("Master Superblock:\n");
+ dump_super(lfs_master);
+
+ dump_ifile(fd, lfs_master, do_ientries);
+
+ if (seglist != NULL)
+ for (; seglist != NULL; seglist = seglist->next) {
+ seg_addr = lfs_master->lfs_sboffs[0] + seglist->num *
+ (lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb);
+ dump_segment(fd,
+ seglist->num, seg_addr, lfs_master, do_allsb);
+ }
+ else
+ for (segnum = 0, seg_addr = lfs_master->lfs_sboffs[0];
+ segnum < lfs_master->lfs_nseg; segnum++, seg_addr +=
+ lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb)
+ dump_segment(fd,
+ segnum, seg_addr, lfs_master, do_allsb);
+
+ (void)close(fd);
+ exit(0);
+}
+
+/*
+ * We are reading all the blocks of an inode and dumping out the ifile table.
+ * This code could be tighter, but this is a first pass at getting the stuff
+ * printed out rather than making this code incredibly efficient.
+ */
+static void
+dump_ifile(fd, lfsp, do_ientries)
+ int fd;
+ struct lfs *lfsp;
+ int do_ientries;
+{
+ IFILE *ipage;
+ struct dinode *dip, *dpage;
+ daddr_t addr, *addrp, *dindir, *iaddrp, *indir;
+ int block_limit, i, inum, j, nblocks, nsupb, psize;
+
+ psize = lfsp->lfs_bsize;
+ addr = lfsp->lfs_idaddr;
+
+ if (!(dpage = malloc(psize)))
+ err("%s", strerror(errno));
+ get(fd, addr << daddr_shift, dpage, psize);
+
+ for (dip = dpage + INOPB(lfsp) - 1; dip >= dpage; --dip)
+ if (dip->di_inumber == LFS_IFILE_INUM)
+ break;
+
+ if (dip < dpage)
+ err("unable to locate ifile inode");
+
+ (void)printf("\nIFILE inode\n");
+ dump_dinode(dip);
+
+ (void)printf("\nIFILE contents\n");
+ nblocks = dip->di_size >> lfsp->lfs_bshift;
+ block_limit = MIN(nblocks, NDADDR);
+
+ /* Get the direct block */
+ if ((ipage = malloc(psize)) == NULL)
+ err("%s", strerror(errno));
+ for (inum = 0, addrp = dip->di_db, i = 0; i < block_limit;
+ i++, addrp++) {
+ get(fd, *addrp << daddr_shift, ipage, psize);
+ if (i < lfsp->lfs_cleansz) {
+ dump_cleaner_info(lfsp, ipage);
+ print_suheader;
+ continue;
+ }
+
+ if (i < (lfsp->lfs_segtabsz + lfsp->lfs_cleansz)) {
+ inum = dump_ipage_segusage(lfsp, inum, ipage,
+ lfsp->lfs_sepb);
+ if (!inum)
+ if(!do_ientries)
+ goto e0;
+ else
+ print_iheader;
+ } else
+ inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb);
+
+ }
+
+ if (nblocks <= NDADDR)
+ goto e0;
+
+ /* Dump out blocks off of single indirect block */
+ if (!(indir = malloc(psize)))
+ err("%s", strerror(errno));
+ get(fd, dip->di_ib[0] << daddr_shift, indir, psize);
+ block_limit = MIN(i + lfsp->lfs_nindir, nblocks);
+ for (addrp = indir; i < block_limit; i++, addrp++) {
+ if (*addrp == LFS_UNUSED_DADDR)
+ break;
+ get(fd, *addrp << daddr_shift,ipage, psize);
+ if (i < lfsp->lfs_cleansz) {
+ dump_cleaner_info(lfsp, ipage);
+ continue;
+ } else
+ i -= lfsp->lfs_cleansz;
+
+ if (i < lfsp->lfs_segtabsz) {
+ inum = dump_ipage_segusage(lfsp, inum, ipage,
+ lfsp->lfs_sepb);
+ if (!inum)
+ if(!do_ientries)
+ goto e1;
+ else
+ print_iheader;
+ } else
+ inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb);
+ }
+
+ if (nblocks <= lfsp->lfs_nindir * lfsp->lfs_ifpb)
+ goto e1;
+
+ /* Get the double indirect block */
+ if (!(dindir = malloc(psize)))
+ err("%s", strerror(errno));
+ get(fd, dip->di_ib[1] << daddr_shift, dindir, psize);
+ for (iaddrp = dindir, j = 0; j < lfsp->lfs_nindir; j++, iaddrp++) {
+ if (*iaddrp == LFS_UNUSED_DADDR)
+ break;
+ get(fd, *iaddrp << daddr_shift, indir, psize);
+ block_limit = MIN(i + lfsp->lfs_nindir, nblocks);
+ for (addrp = indir; i < block_limit; i++, addrp++) {
+ if (*addrp == LFS_UNUSED_DADDR)
+ break;
+ get(fd, *addrp << daddr_shift, ipage, psize);
+ if (i < lfsp->lfs_cleansz) {
+ dump_cleaner_info(lfsp, ipage);
+ continue;
+ } else
+ i -= lfsp->lfs_cleansz;
+
+ if (i < lfsp->lfs_segtabsz) {
+ inum = dump_ipage_segusage(lfsp,
+ inum, ipage, lfsp->lfs_sepb);
+ if (!inum)
+ if(!do_ientries)
+ goto e2;
+ else
+ print_iheader;
+ } else
+ inum = dump_ipage_ifile(inum,
+ ipage, lfsp->lfs_ifpb);
+ }
+ }
+e2: free(dindir);
+e1: free(indir);
+e0: free(dpage);
+ free(ipage);
+}
+
+static int
+dump_ipage_ifile(i, pp, tot)
+ int i;
+ IFILE *pp;
+ int tot;
+{
+ IFILE *ip;
+ int cnt, max;
+
+ max = i + tot;
+
+ for (ip = pp, cnt = i; cnt < max; cnt++, ip++)
+ print_ientry(cnt, ip);
+ return (max);
+}
+
+static int
+dump_ipage_segusage(lfsp, i, pp, tot)
+ struct lfs *lfsp;
+ int i;
+ IFILE *pp;
+ int tot;
+{
+ SEGUSE *sp;
+ int cnt, max;
+
+ max = i + tot;
+ for (sp = (SEGUSE *)pp, cnt = i;
+ cnt < lfsp->lfs_nseg && cnt < max; cnt++, sp++)
+ print_suentry(cnt, sp);
+ if (max >= lfsp->lfs_nseg)
+ return (0);
+ else
+ return (max);
+}
+
+static void
+dump_dinode(dip)
+ struct dinode *dip;
+{
+ int i;
+
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\t%s%d\n",
+ "mode ", dip->di_mode,
+ "nlink ", dip->di_nlink,
+ "uid ", dip->di_uid,
+ "gid ", dip->di_gid,
+ "size ", dip->di_size);
+ (void)printf("%s%s%s%s%s%s",
+ "atime ", ctime(&dip->di_atime.ts_sec),
+ "mtime ", ctime(&dip->di_mtime.ts_sec),
+ "ctime ", ctime(&dip->di_ctime.ts_sec));
+ (void)printf("inum %d\n", dip->di_inumber);
+ (void)printf("Direct Addresses\n");
+ for (i = 0; i < NDADDR; i++) {
+ (void)printf("\t0x%X", dip->di_db[i]);
+ if ((i % 6) == 5)
+ (void)printf("\n");
+ }
+ for (i = 0; i < NIADDR; i++)
+ (void)printf("\t0x%X", dip->di_ib[i]);
+ (void)printf("\n");
+}
+
+static int
+dump_sum(fd, lfsp, sp, segnum, addr)
+ struct lfs *lfsp;
+ SEGSUM *sp;
+ int fd, segnum;
+ daddr_t addr;
+{
+ FINFO *fp;
+ long *dp;
+ int i, j;
+ int ck;
+ int numblocks;
+ struct dinode *inop;
+
+ if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum,
+ LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) {
+ (void)printf("dumplfs: %s %d address 0x%lx\n",
+ "corrupt summary block; segment", segnum, addr);
+ return(0);
+ }
+
+ (void)printf("Segment Summary Info at 0x%lx\n", addr);
+ (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X",
+ "next ", sp->ss_next,
+ "nfinfo ", sp->ss_nfinfo,
+ "ninos ", sp->ss_ninos,
+ "sumsum ", sp->ss_sumsum,
+ "datasum ", sp->ss_datasum );
+ (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create));
+
+ numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
+
+ /* Dump out inode disk addresses */
+ dp = (daddr_t *)sp;
+ dp += LFS_SUMMARY_SIZE / sizeof(daddr_t);
+ inop = malloc(1 << lfsp->lfs_bshift);
+ printf(" Inode addresses:");
+ for (dp--, i = 0; i < sp->ss_ninos; dp--) {
+ printf("\t0x%X {", *dp);
+ get(fd, *dp << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb), inop,
+ (1 << lfsp->lfs_bshift));
+ for (j = 0; i < sp->ss_ninos && j < INOPB(lfsp); j++, i++) {
+ if (j > 0)
+ (void)printf(", ");
+ (void)printf("%d", inop[j].di_inumber);
+ }
+ (void)printf("}");
+ if (((i/INOPB(lfsp)) % 4) == 3)
+ (void)printf("\n");
+ }
+ free(inop);
+
+ printf("\n");
+ for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; i++) {
+ numblocks += fp->fi_nblocks;
+ (void)printf(" FINFO for inode: %d version %d nblocks %d\n",
+ fp->fi_ino, fp->fi_version, fp->fi_nblocks);
+ dp = &(fp->fi_blocks[0]);
+ for (j = 0; j < fp->fi_nblocks; j++, dp++) {
+ (void)printf("\t%d", *dp);
+ if ((j % 8) == 7)
+ (void)printf("\n");
+ }
+ if ((j % 8) != 0)
+ (void)printf("\n");
+ fp = (FINFO *)dp;
+ }
+ return (numblocks);
+}
+
+static void
+dump_segment(fd, segnum, addr, lfsp, dump_sb)
+ int fd, segnum;
+ daddr_t addr;
+ struct lfs *lfsp;
+ int dump_sb;
+{
+ struct lfs lfs_sb, *sbp;
+ SEGSUM *sump;
+ char sumblock[LFS_SUMMARY_SIZE];
+ int did_one, nblocks, sb;
+ off_t sum_offset, super_off;
+
+ (void)printf("\nSEGMENT %d (Disk Address 0x%X)\n",
+ addr >> (lfsp->lfs_segshift - daddr_shift), addr);
+ sum_offset = (addr << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb));
+
+ sb = 0;
+ did_one = 0;
+ do {
+ get(fd, sum_offset, sumblock, LFS_SUMMARY_SIZE);
+ sump = (SEGSUM *)sumblock;
+ if (sump->ss_sumsum != cksum (&sump->ss_datasum,
+ LFS_SUMMARY_SIZE - sizeof(sump->ss_sumsum))) {
+ sbp = (struct lfs *)sump;
+ if (sb = (sbp->lfs_magic == LFS_MAGIC)) {
+ super_off = sum_offset;
+ sum_offset += LFS_SBPAD;
+ } else if (did_one)
+ break;
+ else {
+ printf("Segment at 0x%X corrupt\n", addr);
+ break;
+ }
+ } else {
+ nblocks = dump_sum(fd, lfsp, sump, segnum, sum_offset >>
+ (lfsp->lfs_bshift - lfsp->lfs_fsbtodb));
+ if (nblocks)
+ sum_offset += LFS_SUMMARY_SIZE +
+ (nblocks << lfsp->lfs_bshift);
+ else
+ sum_offset = 0;
+ did_one = 1;
+ }
+ } while (sum_offset);
+
+ if (dump_sb && sb) {
+ get(fd, super_off, &lfs_sb, sizeof(struct lfs));
+ dump_super(&lfs_sb);
+ }
+ return;
+}
+
+static void
+dump_super(lfsp)
+ struct lfs *lfsp;
+{
+ int i;
+
+ (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n",
+ "magic ", lfsp->lfs_magic,
+ "version ", lfsp->lfs_version,
+ "size ", lfsp->lfs_size,
+ "ssize ", lfsp->lfs_ssize);
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "dsize ", lfsp->lfs_dsize,
+ "bsize ", lfsp->lfs_bsize,
+ "fsize ", lfsp->lfs_fsize,
+ "frag ", lfsp->lfs_frag);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "minfree ", lfsp->lfs_minfree,
+ "inopb ", lfsp->lfs_inopb,
+ "ifpb ", lfsp->lfs_ifpb,
+ "nindir ", lfsp->lfs_nindir);
+
+ (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n",
+ "nseg ", lfsp->lfs_nseg,
+ "nspf ", lfsp->lfs_nspf,
+ "cleansz ", lfsp->lfs_cleansz,
+ "segtabsz ", lfsp->lfs_segtabsz);
+
+ (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n",
+ "ffmask ", lfsp->lfs_ffmask,
+ "ffshift ", lfsp->lfs_ffshift,
+ "fbmask ", lfsp->lfs_fbmask,
+ "fbshift ", lfsp->lfs_fbshift);
+
+ (void)printf("%s%d\t%s%d\t%s0x%X\t%s0x%qx\n",
+ "sushift ", lfsp->lfs_sushift,
+ "fsbtodb ", lfsp->lfs_fsbtodb,
+ "cksum ", lfsp->lfs_cksum,
+ "maxfilesize ", lfsp->lfs_maxfilesize);
+
+ (void)printf("Superblock disk addresses:\t");
+ for (i = 0; i < LFS_MAXNUMSB; i++) {
+ (void)printf(" 0x%X", lfsp->lfs_sboffs[i]);
+ if ( i == (LFS_MAXNUMSB >> 1))
+ (void)printf("\n\t\t\t\t");
+ }
+ (void)printf("\n");
+
+ (void)printf("Checkpoint Info\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d\n",
+ "free ", lfsp->lfs_free,
+ "idaddr ", lfsp->lfs_idaddr,
+ "ifile ", lfsp->lfs_ifile);
+ (void)printf("%s%d\t%s%d\t%s%d\n",
+ "bfree ", lfsp->lfs_bfree,
+ "avail ", lfsp->lfs_avail,
+ "uinodes ", lfsp->lfs_uinodes);
+ (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t",
+ "nfiles ", lfsp->lfs_nfiles,
+ "lastseg ", lfsp->lfs_lastseg,
+ "nextseg ", lfsp->lfs_nextseg,
+ "curseg ", lfsp->lfs_curseg,
+ "offset ", lfsp->lfs_offset);
+ (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp));
+ (void)printf("\nIn-Memory Information\n");
+ (void)printf("%s%d\t%s0x%X\t%s%d%s%d\t%s%d\n",
+ "seglock ", lfsp->lfs_seglock,
+ "iocount ", lfsp->lfs_iocount,
+ "writer ", lfsp->lfs_writer,
+ "dirops ", lfsp->lfs_dirops,
+ "doifile ", lfsp->lfs_doifile);
+ (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n",
+ "nactive ", lfsp->lfs_nactive,
+ "fmod ", lfsp->lfs_fmod,
+ "clean ", lfsp->lfs_clean,
+ "ronly ", lfsp->lfs_ronly);
+}
+
+static void
+addseg(arg)
+ char *arg;
+{
+ SEGLIST *p;
+
+ if ((p = malloc(sizeof(SEGLIST))) == NULL)
+ err("%s", strerror(errno));
+ p->next = seglist;
+ p->num = atoi(arg);
+ seglist = p;
+}
+
+static void
+dump_cleaner_info(lfsp, ipage)
+ struct lfs *lfsp;
+ void *ipage;
+{
+ CLEANERINFO *cip;
+
+ cip = (CLEANERINFO *)ipage;
+ (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n",
+ cip->clean, cip->dirty);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: dumplfs [-ai] [-s segnum] file\n");
+ exit(1);
+}
diff --git a/sbin/dumplfs/extern.h b/sbin/dumplfs/extern.h
new file mode 100644
index 000000000000..82e87b24e0a5
--- /dev/null
+++ b/sbin/dumplfs/extern.h
@@ -0,0 +1,39 @@
+/*-
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/5/93
+ */
+
+void err __P((const char *, ...));
+void get __P((int, off_t, void *, size_t));
+
+extern char *special;
diff --git a/sbin/dumplfs/misc.c b/sbin/dumplfs/misc.c
new file mode 100644
index 000000000000..861d0fd7f260
--- /dev/null
+++ b/sbin/dumplfs/misc.c
@@ -0,0 +1,90 @@
+/*-
+ * 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[] = "@(#)misc.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+
+void
+get(fd, off, p, len)
+ int fd;
+ off_t off;
+ void *p;
+ size_t len;
+{
+ int rbytes;
+
+ if (lseek(fd, off, SEEK_SET) < 0)
+ err("%s: %s", special, strerror(errno));
+ if ((rbytes = read(fd, p, len)) < 0)
+ err("%s: %s", special, strerror(errno));
+ if (rbytes != len)
+ err("%s: short read (%d, not %d)", special, rbytes, len);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "dumplfs: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/sbin/fastboot/Makefile b/sbin/fastboot/Makefile
new file mode 100644
index 000000000000..9504f792b9e0
--- /dev/null
+++ b/sbin/fastboot/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+MAN8= fastboot.0
+MLINKS= fastboot.8 fasthalt.8
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/fastboot.sh ${DESTDIR}${BINDIR}/fastboot
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/fasthalt.sh ${DESTDIR}${BINDIR}/fasthalt
+
+.include <bsd.prog.mk>
diff --git a/sbin/fastboot/fastboot.8 b/sbin/fastboot/fastboot.8
new file mode 100644
index 000000000000..2f6ac829da99
--- /dev/null
+++ b/sbin/fastboot/fastboot.8
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fastboot.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt FASTBOOT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm fastboot ,
+.Nm fasthalt
+.Nd "reboot/halt the system without checking the disks"
+.Sh SYNOPSIS
+.Nm fastboot
+.Op Ar boot-options
+.Nm fasthalt
+.Op Ar halt-options
+.Sh DESCRIPTION
+.Nm Fastboot
+and
+.Nm fasthalt
+are shell scripts which reboot and halt the system without
+checking the file systems. This is done by creating a
+file
+.Pa /fastboot ,
+then invoking the
+.Xr reboot
+program. The system startup script,
+.Pa /etc/rc ,
+looks for this file and, if present, skips the normal
+invocation of
+.Xr fsck 8 .
+.Sh SEE ALSO
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/fastboot/fastboot.sh b/sbin/fastboot/fastboot.sh
new file mode 100644
index 000000000000..852ec4a1b089
--- /dev/null
+++ b/sbin/fastboot/fastboot.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -
+#
+# Copyright (c) 1985, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)fastboot.sh 8.1 (Berkeley) 6/5/93
+#
+
+cp /dev/null /fastboot
+/sbin/reboot $*
diff --git a/sbin/fastboot/fasthalt.sh b/sbin/fastboot/fasthalt.sh
new file mode 100644
index 000000000000..c8e381f3e51e
--- /dev/null
+++ b/sbin/fastboot/fasthalt.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -
+#
+# Copyright (c) 1988, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)fasthalt.sh 8.1 (Berkeley) 6/5/93
+#
+
+cp /dev/null /fastboot
+/sbin/halt $*
diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile
new file mode 100644
index 000000000000..224f6b2200e7
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/0.t b/sbin/fsck/SMM.doc/0.t
new file mode 100644
index 000000000000..9de391eec1ef
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/1.t b/sbin/fsck/SMM.doc/1.t
new file mode 100644
index 000000000000..4d2f5357131b
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/2.t b/sbin/fsck/SMM.doc/2.t
new file mode 100644
index 000000000000..7d00ceaa4efd
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/3.t b/sbin/fsck/SMM.doc/3.t
new file mode 100644
index 000000000000..07b5431f3e59
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/4.t b/sbin/fsck/SMM.doc/4.t
new file mode 100644
index 000000000000..5ea8179fa904
--- /dev/null
+++ b/sbin/fsck/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/SMM.doc/Makefile b/sbin/fsck/SMM.doc/Makefile
new file mode 100644
index 000000000000..26823bcc94f2
--- /dev/null
+++ b/sbin/fsck/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/dir.c b/sbin/fsck/dir.c
new file mode 100644
index 000000000000..ee5d095e6c75
--- /dev/null
+++ b/sbin/fsck/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/fsck.8 b/sbin/fsck/fsck.8
new file mode 100644
index 000000000000..cf8c499a35e9
--- /dev/null
+++ b/sbin/fsck/fsck.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/fsck.h b/sbin/fsck/fsck.h
new file mode 100644
index 000000000000..7fa831f6c52e
--- /dev/null
+++ b/sbin/fsck/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/inode.c b/sbin/fsck/inode.c
new file mode 100644
index 000000000000..f1c1758f746a
--- /dev/null
+++ b/sbin/fsck/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/main.c b/sbin/fsck/main.c
new file mode 100644
index 000000000000..2e69715fecf0
--- /dev/null
+++ b/sbin/fsck/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/pass1.c b/sbin/fsck/pass1.c
new file mode 100644
index 000000000000..ca255fe78579
--- /dev/null
+++ b/sbin/fsck/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/pass1b.c b/sbin/fsck/pass1b.c
new file mode 100644
index 000000000000..27b2dfd6238c
--- /dev/null
+++ b/sbin/fsck/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/pass2.c b/sbin/fsck/pass2.c
new file mode 100644
index 000000000000..631475174372
--- /dev/null
+++ b/sbin/fsck/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/pass3.c b/sbin/fsck/pass3.c
new file mode 100644
index 000000000000..5c6f09d041de
--- /dev/null
+++ b/sbin/fsck/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/pass4.c b/sbin/fsck/pass4.c
new file mode 100644
index 000000000000..7450530e8a4e
--- /dev/null
+++ b/sbin/fsck/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/pass5.c b/sbin/fsck/pass5.c
new file mode 100644
index 000000000000..2e98f96b5ca8
--- /dev/null
+++ b/sbin/fsck/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/preen.c b/sbin/fsck/preen.c
new file mode 100644
index 000000000000..005a65d97989
--- /dev/null
+++ b/sbin/fsck/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/setup.c b/sbin/fsck/setup.c
new file mode 100644
index 000000000000..a32b23cc294d
--- /dev/null
+++ b/sbin/fsck/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/utilities.c b/sbin/fsck/utilities.c
new file mode 100644
index 000000000000..64a4cac19ab3
--- /dev/null
+++ b/sbin/fsck/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_ffs/Makefile b/sbin/fsck_ffs/Makefile
new file mode 100644
index 000000000000..224f6b2200e7
--- /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 000000000000..9de391eec1ef
--- /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 000000000000..4d2f5357131b
--- /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 000000000000..7d00ceaa4efd
--- /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 000000000000..07b5431f3e59
--- /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 000000000000..5ea8179fa904
--- /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 000000000000..26823bcc94f2
--- /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 000000000000..ee5d095e6c75
--- /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 000000000000..7fa831f6c52e
--- /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 000000000000..cf8c499a35e9
--- /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 000000000000..f1c1758f746a
--- /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 000000000000..2e69715fecf0
--- /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 000000000000..ca255fe78579
--- /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 000000000000..27b2dfd6238c
--- /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 000000000000..631475174372
--- /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 000000000000..5c6f09d041de
--- /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 000000000000..7450530e8a4e
--- /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 000000000000..2e98f96b5ca8
--- /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 000000000000..005a65d97989
--- /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 000000000000..a32b23cc294d
--- /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 000000000000..64a4cac19ab3
--- /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 000000000000..224f6b2200e7
--- /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 000000000000..ee5d095e6c75
--- /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 000000000000..7fa831f6c52e
--- /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 000000000000..cf8c499a35e9
--- /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 000000000000..f1c1758f746a
--- /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 000000000000..2e69715fecf0
--- /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 000000000000..ca255fe78579
--- /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 000000000000..27b2dfd6238c
--- /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 000000000000..631475174372
--- /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 000000000000..5c6f09d041de
--- /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 000000000000..7450530e8a4e
--- /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 000000000000..2e98f96b5ca8
--- /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 000000000000..005a65d97989
--- /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 000000000000..a32b23cc294d
--- /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 000000000000..64a4cac19ab3
--- /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/ifconfig/Makefile b/sbin/ifconfig/Makefile
new file mode 100644
index 000000000000..8be219395873
--- /dev/null
+++ b/sbin/ifconfig/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= ifconfig
+MAN8= ifconfig.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
new file mode 100644
index 000000000000..58a21638630e
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.8
@@ -0,0 +1,275 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
+.\"
+.Dd January 5, 1994
+.Dt IFCONFIG 8
+.Os BSD 4.2
+.Sh NAME
+.Nm ifconfig
+.Nd configure network interface parameters
+.Sh SYNOPSIS
+.Nm ifconfig
+.Ar interface address_family
+.Oo
+.Ar address
+.Op Ar dest_address
+.Oc
+.Op Ar parameters
+.Nm ifconfig
+.Ar interface
+.Op Ar protocol_family
+.Sh DESCRIPTION
+.Nm Ifconfig
+is used to assign an address
+to a network interface and/or configure
+network interface parameters.
+.Nm Ifconfig
+must be used at boot time to define the network address
+of each interface present on a machine; it may also be used at
+a later time to redefine an interface's address
+or other operating parameters.
+.Pp
+Available operands for
+.Nm ifconfig:
+.Bl -tag -width Ds
+.It Ar Address
+For the
+.Tn DARPA-Internet
+family,
+the address is either a host name present in the host name data
+base,
+.Xr hosts 5 ,
+or a
+.Tn DARPA
+Internet address expressed in the Internet standard
+.Dq dot notation .
+For the Xerox Network Systems(tm) family,
+addresses are
+.Ar net:a.b.c.d.e.f ,
+where
+.Ar net
+is the assigned network number (in decimal),
+and each of the six bytes of the host number,
+.Ar a
+through
+.Ar f ,
+are specified in hexadecimal.
+The host number may be omitted on 10Mb/s Ethernet interfaces,
+which use the hardware physical address,
+and on interfaces other than the first.
+For the
+.Tn ISO
+family, addresses are specified as a long hexadecimal string,
+as in the Xerox family. However, two consecutive dots imply a zero
+byte, and the dots are optional, if the user wishes to (carefully)
+count out long strings of digits in network byte order.
+.It Ar address_family
+Specifies the
+.Ar address family
+which affects interpretation of the remaining parameters.
+Since an interface can receive transmissions in differing protocols
+with different naming schemes, specifying the address family is recommeded.
+The address or protocol families currently
+supported are
+.Dq inet ,
+.Dq iso ,
+and
+.Dq ns .
+.It Ar Interface
+The
+.Ar interface
+parameter is a string of the form
+.Dq name unit ,
+for example,
+.Dq en0
+.El
+.Pp
+The following parameters may be set with
+.Nm ifconfig :
+.Bl -tag -width dest_addressxx
+.It Cm alias
+Establish an additional network address for this interface.
+This is sometimes useful when changing network numbers, and
+one wishes to accept packets addressed to the old interface.
+.It Cm arp
+Enable the use of the Address Resolution Protocol in mapping
+between network level addresses and link level addresses (default).
+This is currently implemented for mapping between
+.Tn DARPA
+Internet
+addresses and 10Mb/s Ethernet addresses.
+.It Fl arp
+Disable the use of the Address Resolution Protocol.
+.It Cm broadcast
+(Inet only)
+Specify the address to use to represent broadcasts to the
+network.
+The default broadcast address is the address with a host part of all 1's.
+.It Cm debug
+Enable driver dependent debugging code; usually, this turns on
+extra console error logging.
+.It Fl debug
+Disable driver dependent debugging code.
+.It Cm delete
+Remove the network address specified.
+This would be used if you incorrectly specified an alias, or it
+was no longer needed.
+If you have incorrectly set an NS address having the side effect
+of specifying the host portion, removing all NS addresses will
+allow you to respecify the host portion.
+.It Cm dest_address
+Specify the address of the correspondent on the other end
+of a point to point link.
+.It Cm down
+Mark an interface ``down''. When an interface is
+marked ``down'', the system will not attempt to
+transmit messages through that interface.
+If possible, the interface will be reset to disable reception as well.
+This action does not automatically disable routes using the interface.
+.It Cm ipdst
+This is used to specify an Internet host who is willing to receive
+ip packets encapsulating NS packets bound for a remote network.
+An apparent point to point link is constructed, and
+the address specified will be taken as the NS address and network
+of the destination.
+IP encapsulation of
+.Tn CLNP
+packets is done differently.
+.It Cm metric Ar n
+Set the routing metric of the interface to
+.Ar n ,
+default 0.
+The routing metric is used by the routing protocol
+.Pq Xr routed 8 .
+Higher metrics have the effect of making a route
+less favorable; metrics are counted as addition hops
+to the destination network or host.
+.It Cm netmask Ar mask
+(Inet and ISO)
+Specify how much of the address to reserve for subdividing
+networks into sub-networks.
+The mask includes the network part of the local address
+and the subnet part, which is taken from the host field of the address.
+The mask can be specified as a single hexadecimal number
+with a leading 0x, with a dot-notation Internet address,
+or with a pseudo-network name listed in the network table
+.Xr networks 5 .
+The mask contains 1's for the bit positions in the 32-bit address
+which are to be used for the network and subnet parts,
+and 0's for the host part.
+The mask should contain at least the standard network portion,
+and the subnet field should be contiguous with the network
+portion.
+.\" see
+.\" Xr eon 5 .
+.It Cm nsellength Ar n
+.Pf ( Tn ISO
+only)
+This specifies a trailing number of bytes for a received
+.Tn NSAP
+used for local identification, the remaining leading part of which is
+taken to be the
+.Tn NET
+(Network Entity Title).
+The default value is 1, which is conformant to US
+.Tn GOSIP .
+When an ISO address is set in an ifconfig command,
+it is really the
+.Tn NSAP
+which is being specified.
+For example, in
+.Tn US GOSIP ,
+20 hex digits should be
+specified in the
+.Tn ISO NSAP
+to be assigned to the interface.
+There is some evidence that a number different from 1 may be useful
+for
+.Tn AFI
+37 type addresses.
+.It Cm trailers
+Request the use of a ``trailer'' link level encapsulation when
+sending (default).
+If a network interface supports
+.Cm trailers ,
+the system will, when possible, encapsulate outgoing
+messages in a manner which minimizes the number of
+memory to memory copy operations performed by the receiver.
+On networks that support the Address Resolution Protocol (see
+.Xr arp 4 ;
+currently, only 10 Mb/s Ethernet),
+this flag indicates that the system should request that other
+systems use trailers when sending to this host.
+Similarly, trailer encapsulations will be sent to other
+hosts that have made such requests.
+Currently used by Internet protocols only.
+.It Fl trailers
+Disable the use of a ``trailer'' link level encapsulation.
+.It Cm link[0-2]
+Enable special processing of the link level of the interface.
+These three options are interface specific in actual effect, however,
+they are in general used to select special modes of operation. An example
+of this is to enable SLIP compression. Currently, only used by SLIP.
+.It Fl link[0-2]
+Disable special processing at the link level with the specified interface.
+.It Cm up
+Mark an interface ``up''.
+This may be used to enable an interface after an ``ifconfig down.''
+It happens automatically when setting the first address on an interface.
+If the interface was reset when previously marked down,
+the hardware will be re-initialized.
+.El
+.Pp
+.Pp
+.Nm Ifconfig
+displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+Ifconfig will report only the details specific to that protocol family.
+.Pp
+Only the super-user may modify the configuration of a network interface.
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exit, the
+requested address is unknown, or the user is not privileged and
+tried to alter an interface's configuration.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr netintro 4 ,
+.Xr rc 8 ,
+.Xr routed 8 ,
+.\" .Xr eon 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
new file mode 100644
index 000000000000..e9751b98e00b
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define NSIP
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netdb.h>
+
+#define EON
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <sys/protosw.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct ifreq ifr, ridreq;
+struct ifaliasreq addreq;
+struct iso_ifreq iso_ridreq;
+struct iso_aliasreq iso_addreq;
+struct sockaddr_in netmask;
+
+char name[30];
+int flags;
+int metric;
+int nsellength = 1;
+int setaddr;
+int setipdst;
+int doalias;
+int clearaddr;
+int newaddr = 1;
+int s;
+extern int errno;
+
+int setifflags(), setifaddr(), setifdstaddr(), setifnetmask();
+int setifmetric(), setifbroadaddr(), setifipdst();
+int notealias(), setsnpaoffset(), setnsellength(), notrailers();
+
+#define NEXTARG 0xffffff
+
+struct cmd {
+ char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ int (*c_func)();
+} cmds[] = {
+ { "up", IFF_UP, setifflags } ,
+ { "down", -IFF_UP, setifflags },
+ { "trailers", -1, notrailers },
+ { "-trailers", 1, notrailers },
+ { "arp", -IFF_NOARP, setifflags },
+ { "-arp", IFF_NOARP, setifflags },
+ { "debug", IFF_DEBUG, setifflags },
+ { "-debug", -IFF_DEBUG, setifflags },
+ { "alias", IFF_UP, notealias },
+ { "-alias", -IFF_UP, notealias },
+ { "delete", -IFF_UP, notealias },
+#ifdef notdef
+#define EN_SWABIPS 0x1000
+ { "swabips", EN_SWABIPS, setifflags },
+ { "-swabips", -EN_SWABIPS, setifflags },
+#endif
+ { "netmask", NEXTARG, setifnetmask },
+ { "metric", NEXTARG, setifmetric },
+ { "broadcast", NEXTARG, setifbroadaddr },
+ { "ipdst", NEXTARG, setifipdst },
+ { "snpaoffset", NEXTARG, setsnpaoffset },
+ { "nsellength", NEXTARG, setnsellength },
+ { "link0", IFF_LINK0, setifflags } ,
+ { "-link0", -IFF_LINK0, setifflags } ,
+ { "link1", IFF_LINK1, setifflags } ,
+ { "-link1", -IFF_LINK1, setifflags } ,
+ { "link2", IFF_LINK2, setifflags } ,
+ { "-link2", -IFF_LINK2, setifflags } ,
+ { 0, 0, setifaddr },
+ { 0, 0, setifdstaddr },
+};
+
+/*
+ * XNS support liberally adapted from code written at the University of
+ * Maryland principally by James O'Toole and Chris Torek.
+ */
+int in_status(), in_getaddr();
+int xns_status(), xns_getaddr();
+int iso_status(), iso_getaddr();
+
+/* Known address families */
+struct afswtch {
+ char *af_name;
+ short af_af;
+ int (*af_status)();
+ int (*af_getaddr)();
+ int af_difaddr;
+ int af_aifaddr;
+ caddr_t af_ridreq;
+ caddr_t af_addreq;
+} afs[] = {
+#define C(x) ((caddr_t) &x)
+ { "inet", AF_INET, in_status, in_getaddr,
+ SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
+ { "ns", AF_NS, xns_status, xns_getaddr,
+ SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
+ { "iso", AF_ISO, iso_status, iso_getaddr,
+ SIOCDIFADDR_ISO, SIOCAIFADDR_ISO, C(iso_ridreq), C(iso_addreq) },
+ { 0, 0, 0, 0 }
+};
+
+struct afswtch *afp; /*the address family being set or asked about*/
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int af = AF_INET;
+ register struct afswtch *rafp;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: ifconfig interface\n%s%s%s%s%s",
+ "\t[ af [ address [ dest_addr ] ] [ up ] [ down ]",
+ "[ netmask mask ] ]\n",
+ "\t[ metric n ]\n",
+ "\t[ arp | -arp ]\n",
+ "\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ] \n");
+ exit(1);
+ }
+ argc--, argv++;
+ strncpy(name, *argv, sizeof(name));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ argc--, argv++;
+ if (argc > 0) {
+ for (afp = rafp = afs; rafp->af_name; rafp++)
+ if (strcmp(rafp->af_name, *argv) == 0) {
+ afp = rafp; argc--; argv++;
+ break;
+ }
+ rafp = afp;
+ af = ifr.ifr_addr.sa_family = rafp->af_af;
+ }
+ s = socket(af, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("ifconfig: socket");
+ exit(1);
+ }
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ Perror("ioctl (SIOCGIFFLAGS)");
+ exit(1);
+ }
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ flags = ifr.ifr_flags;
+ if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMETRIC)");
+ else
+ metric = ifr.ifr_metric;
+ if (argc == 0) {
+ status();
+ exit(0);
+ }
+ while (argc > 0) {
+ register struct cmd *p;
+
+ for (p = cmds; p->c_name; p++)
+ if (strcmp(*argv, p->c_name) == 0)
+ break;
+ if (p->c_name == 0 && setaddr)
+ p++; /* got src, do dst */
+ if (p->c_func) {
+ if (p->c_parameter == NEXTARG) {
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ (*p->c_func)(argv[1]);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter);
+ }
+ argc--, argv++;
+ }
+ if (af == AF_ISO)
+ adjust_nsellength();
+ if (setipdst && af==AF_NS) {
+ struct nsip_req rq;
+ int size = sizeof(rq);
+
+ rq.rq_ns = addreq.ifra_addr;
+ rq.rq_ip = addreq.ifra_dstaddr;
+
+ if (setsockopt(s, 0, SO_NSIP_ROUTE, &rq, size) < 0)
+ Perror("Encapsulation Routing");
+ }
+ if (clearaddr) {
+ int ret;
+ strncpy(rafp->af_ridreq, name, sizeof ifr.ifr_name);
+ if ((ret = ioctl(s, rafp->af_difaddr, rafp->af_ridreq)) < 0) {
+ if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
+ /* means no previous address for interface */
+ } else
+ Perror("ioctl (SIOCDIFADDR)");
+ }
+ }
+ if (newaddr) {
+ strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, rafp->af_aifaddr, rafp->af_addreq) < 0)
+ Perror("ioctl (SIOCAIFADDR)");
+ }
+ exit(0);
+}
+#define RIDADDR 0
+#define ADDR 1
+#define MASK 2
+#define DSTADDR 3
+
+/*ARGSUSED*/
+setifaddr(addr, param)
+ char *addr;
+ short param;
+{
+ /*
+ * Delay the ioctl to set the interface addr until flags are all set.
+ * The address interpretation may depend on the flags,
+ * and the flags may change when the address is set.
+ */
+ setaddr++;
+ if (doalias == 0)
+ clearaddr = 1;
+ (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR));
+}
+
+setifnetmask(addr)
+ char *addr;
+{
+ (*afp->af_getaddr)(addr, MASK);
+}
+
+setifbroadaddr(addr)
+ char *addr;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+setifipdst(addr)
+ char *addr;
+{
+ in_getaddr(addr, DSTADDR);
+ setipdst++;
+ clearaddr = 0;
+ newaddr = 0;
+}
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
+/*ARGSUSED*/
+notealias(addr, param)
+ char *addr;
+{
+ if (setaddr && doalias == 0 && param < 0)
+ bcopy((caddr_t)rqtosa(af_addreq),
+ (caddr_t)rqtosa(af_ridreq),
+ rqtosa(af_addreq)->sa_len);
+ doalias = param;
+ if (param < 0) {
+ clearaddr = 1;
+ newaddr = 0;
+ } else
+ clearaddr = 0;
+}
+
+/*ARGSUSED*/
+notrailers(vname, value)
+ char *vname;
+ int value;
+{
+ printf("Note: trailers are no longer sent, but always received\n");
+}
+
+/*ARGSUSED*/
+setifdstaddr(addr, param)
+ char *addr;
+ int param;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+setifflags(vname, value)
+ char *vname;
+ short value;
+{
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ Perror("ioctl (SIOCGIFFLAGS)");
+ exit(1);
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ flags = ifr.ifr_flags;
+
+ if (value < 0) {
+ value = -value;
+ flags &= ~value;
+ } else
+ flags |= value;
+ ifr.ifr_flags = flags;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
+ Perror(vname);
+}
+
+setifmetric(val)
+ char *val;
+{
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ ifr.ifr_metric = atoi(val);
+ if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0)
+ perror("ioctl (set metric)");
+}
+
+setsnpaoffset(val)
+ char *val;
+{
+ iso_addreq.ifra_snpaoffset = atoi(val);
+}
+
+#define IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\
+\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST"
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+status()
+{
+ register struct afswtch *p = afp;
+ short af = ifr.ifr_addr.sa_family;
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (metric)
+ printf(" metric %d", metric);
+ putchar('\n');
+ if ((p = afp) != NULL) {
+ (*p->af_status)(1);
+ } else for (p = afs; p->af_name; p++) {
+ ifr.ifr_addr.sa_family = p->af_af;
+ (*p->af_status)(0);
+ }
+}
+
+in_status(force)
+ int force;
+{
+ struct sockaddr_in *sin;
+ char *inet_ntoa();
+
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
+ if (!force)
+ return;
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ } else
+ perror("ioctl (SIOCGIFADDR)");
+ }
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ perror("ioctl (SIOCGIFNETMASK)");
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ } else
+ netmask.sin_addr =
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+ if (flags & IFF_POINTOPOINT) {
+ if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ else
+ perror("ioctl (SIOCGIFDSTADDR)");
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_dstaddr;
+ printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+ printf("netmask 0x%x ", ntohl(netmask.sin_addr.s_addr));
+ if (flags & IFF_BROADCAST) {
+ if (ioctl(s, SIOCGIFBRDADDR, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ else
+ perror("ioctl (SIOCGIFADDR)");
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ if (sin->sin_addr.s_addr != 0)
+ printf("broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+
+xns_status(force)
+ int force;
+{
+ struct sockaddr_ns *sns;
+
+ close(s);
+ s = socket(AF_NS, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ perror("ifconfig: socket");
+ exit(1);
+ }
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
+ if (!force)
+ return;
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ } else
+ perror("ioctl (SIOCGIFADDR)");
+ }
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ sns = (struct sockaddr_ns *)&ifr.ifr_addr;
+ printf("\tns %s ", ns_ntoa(sns->sns_addr));
+ if (flags & IFF_POINTOPOINT) { /* by W. Nesheim@Cornell */
+ if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
+ else
+ Perror("ioctl (SIOCGIFDSTADDR)");
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ sns = (struct sockaddr_ns *)&ifr.ifr_dstaddr;
+ printf("--> %s ", ns_ntoa(sns->sns_addr));
+ }
+ putchar('\n');
+}
+
+iso_status(force)
+ int force;
+{
+ struct sockaddr_iso *siso;
+ struct iso_ifreq ifr;
+
+ close(s);
+ s = socket(AF_ISO, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ perror("ifconfig: socket");
+ exit(1);
+ }
+ bzero((caddr_t)&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFADDR_ISO, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
+ if (!force)
+ return;
+ bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr));
+ } else {
+ perror("ioctl (SIOCGIFADDR_ISO)");
+ exit(1);
+ }
+ }
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ siso = &ifr.ifr_Addr;
+ printf("\tiso %s ", iso_ntoa(&siso->siso_addr));
+ if (ioctl(s, SIOCGIFNETMASK_ISO, (caddr_t)&ifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ perror("ioctl (SIOCGIFNETMASK_ISO)");
+ } else {
+ printf(" netmask %s ", iso_ntoa(&siso->siso_addr));
+ }
+ if (flags & IFF_POINTOPOINT) {
+ if (ioctl(s, SIOCGIFDSTADDR_ISO, (caddr_t)&ifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr));
+ else
+ Perror("ioctl (SIOCGIFDSTADDR_ISO)");
+ }
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ siso = &ifr.ifr_Addr;
+ printf("--> %s ", iso_ntoa(&siso->siso_addr));
+ }
+ putchar('\n');
+}
+
+Perror(cmd)
+ char *cmd;
+{
+ extern int errno;
+
+ switch (errno) {
+
+ case ENXIO:
+ errx(1, "%s: no such interface", cmd);
+ break;
+
+ case EPERM:
+ errx(1, "%s: permission denied", cmd);
+ break;
+
+ default:
+ err(1, "%s", cmd);
+ }
+}
+
+struct in_addr inet_makeaddr();
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+struct sockaddr_in *sintab[] = {
+SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr),
+SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)};
+
+in_getaddr(s, which)
+ char *s;
+{
+ register struct sockaddr_in *sin = sintab[which];
+ struct hostent *hp;
+ struct netent *np;
+ int val;
+
+ sin->sin_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin_family = AF_INET;
+
+ if ((val = inet_addr(s)) != -1)
+ sin->sin_addr.s_addr = val;
+ else if (hp = gethostbyname(s))
+ bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
+ else if (np = getnetbyname(s))
+ sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+ else
+ errx(1, "%s: bad value", s);
+}
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+printb(s, v, bits)
+ char *s;
+ register char *bits;
+ register unsigned short v;
+{
+ register int i, any = 0;
+ register char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ bits++;
+ if (bits) {
+ putchar('<');
+ while (i = *bits++) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+#define SNS(x) ((struct sockaddr_ns *) &(x))
+struct sockaddr_ns *snstab[] = {
+SNS(ridreq.ifr_addr), SNS(addreq.ifra_addr),
+SNS(addreq.ifra_mask), SNS(addreq.ifra_broadaddr)};
+
+xns_getaddr(addr, which)
+char *addr;
+{
+ struct sockaddr_ns *sns = snstab[which];
+ struct ns_addr ns_addr();
+
+ sns->sns_family = AF_NS;
+ sns->sns_len = sizeof(*sns);
+ sns->sns_addr = ns_addr(addr);
+ if (which == MASK)
+ printf("Attempt to set XNS netmask will be ineffectual\n");
+}
+
+#define SISO(x) ((struct sockaddr_iso *) &(x))
+struct sockaddr_iso *sisotab[] = {
+SISO(iso_ridreq.ifr_Addr), SISO(iso_addreq.ifra_addr),
+SISO(iso_addreq.ifra_mask), SISO(iso_addreq.ifra_dstaddr)};
+
+iso_getaddr(addr, which)
+char *addr;
+{
+ register struct sockaddr_iso *siso = sisotab[which];
+ struct iso_addr *iso_addr();
+ siso->siso_addr = *iso_addr(addr);
+
+ if (which == MASK) {
+ siso->siso_len = TSEL(siso) - (caddr_t)(siso);
+ siso->siso_nlen = 0;
+ } else {
+ siso->siso_len = sizeof(*siso);
+ siso->siso_family = AF_ISO;
+ }
+}
+
+setnsellength(val)
+ char *val;
+{
+ nsellength = atoi(val);
+ if (nsellength < 0)
+ errx(1, "Negative NSEL length is absurd");
+ if (afp == 0 || afp->af_af != AF_ISO)
+ errx(1, "Setting NSEL length valid only for iso");
+}
+
+fixnsel(s)
+register struct sockaddr_iso *s;
+{
+ if (s->siso_family == 0)
+ return;
+ s->siso_tlen = nsellength;
+}
+
+adjust_nsellength()
+{
+ fixnsel(sisotab[RIDADDR]);
+ fixnsel(sisotab[ADDR]);
+ fixnsel(sisotab[DSTADDR]);
+}
diff --git a/sbin/init/Makefile b/sbin/init/Makefile
new file mode 100644
index 000000000000..1a796a1069f7
--- /dev/null
+++ b/sbin/init/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= init
+MAN8= init.0
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+BINMODE=500
+INSTALLFLAGS=-fschg
+CFLAGS+=-DDEBUGSHELL -DSECURE
+
+.include <bsd.prog.mk>
diff --git a/sbin/init/NOTES b/sbin/init/NOTES
new file mode 100644
index 000000000000..bf75101680ab
--- /dev/null
+++ b/sbin/init/NOTES
@@ -0,0 +1,112 @@
+POSIX and init:
+--------------
+
+POSIX.1 does not define 'init' but it mentions it in a few places.
+
+B.2.2.2, p205 line 873:
+
+ This is part of the extensive 'job control' glossary entry.
+ This specific reference says that 'init' must by default provide
+ protection from job control signals to jobs it starts --
+ it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN.
+
+B.2.2.2, p206 line 889:
+
+ Here is a reference to 'vhangup'. It says, 'POSIX.1 does
+ not specify how controlling terminal access is affected by
+ a user logging out (that is, by a controlling process
+ terminating).' vhangup() is recognized as one way to handle
+ the problem. I'm not clear what happens in Reno; I have
+ the impression that when the controlling process terminates,
+ references to the controlling terminal are converted to
+ references to a 'dead' vnode. I don't know whether vhangup()
+ is required.
+
+B.2.2.2, p206 line 921:
+
+ Orphaned process groups bear indirectly on this issue. A
+ session leader's process group is considered to be orphaned;
+ that is, it's immune to job control signals from the terminal.
+
+B.2.2.2, p233 line 2055:
+
+ 'Historically, the implementation-dependent process that
+ inherits children whose parents have terminated without
+ waiting on them is called "init" and has a process ID of 1.'
+
+ It goes on to note that it used to be the case that 'init'
+ was responsible for sending SIGHUP to the foreground process
+ group of a tty whose controlling process has exited, using
+ vhangup(). It is now the responsibility of the kernel to
+ do this when the controlling process calls _exit(). The
+ kernel is also responsible for sending SIGCONT to stopped
+ process groups that become orphaned. This is like old BSD
+ but entire process groups are signaled instead of individual
+ processes.
+
+ In general it appears that the kernel now automatically
+ takes care of orphans, relieving 'init' of any responsibility.
+ Specifics are listed on the _exit() page (p50).
+
+On setsid():
+-----------
+
+It appears that neither getty nor login call setsid(), so init must
+do this -- seems reasonable. B.4.3.2 p 248 implies that this is the
+way that 'init' should work; it says that setsid() should be called
+after forking.
+
+Process group leaders cannot call setsid() -- another reason to
+fork! Of course setsid() causes the current process to become a
+process group leader, so we can only call setsid() once. Note that
+the controlling terminal acquires the session leader's process
+group when opened.
+
+Controlling terminals:
+---------------------
+
+B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to
+allocate a controlling terminal. This is normally done by a system
+utility (such as 'getty') and is considered ... outside the scope
+of POSIX.1.' It goes on to say that historically the first open()
+of a tty in a session sets the controlling terminal. P130 has the
+full details; nothing particularly surprising.
+
+The glossary p12 describes a 'controlling process' as the first
+process in a session that acquires a controlling terminal. Access
+to the terminal from the session is revoked if the controlling
+process exits (see p50, in the discussion of process termination).
+
+Design notes:
+------------
+
+your generic finite state machine
+we are fascist about which signals we elect to receive,
+ even signals purportedly generated by hardware
+handle fatal errors gracefully if possible (we reboot if we goof!!)
+ if we get a segmentation fault etc., print a message on the console
+ and spin for a while before rebooting
+ (this at least decreases the amount of paper consumed :-)
+apply hysteresis to rapidly exiting gettys
+check wait status of children we reap
+ don't wait for stopped children
+don't use SIGCHILD, it's too expensive
+ but it may close windows and avoid races, sigh
+look for EINTR in case we need to change state
+init is responsible for utmp and wtmp maintenance (ick)
+ maybe now we can consider replacements? maintain them in parallel
+ init only removes utmp and closes out wtmp entries...
+
+necessary states and state transitions (gleaned from the man page):
+ 1: single user shell (with password checking?); on exit, go to 2
+ 2: rc script: on exit 0, go to 3; on exit N (error), go to 1
+ 3: read ttys file: on completion, go to 4
+ 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5;
+ on SIGTSTP, go to 6
+ 5: clean up mode (re-read ttys file, killing off controlling processes
+ on lines that are now 'off', starting them on lines newly 'on')
+ on completion, go to 4
+ 6: boring mode (no new sessions); signals as in 4
+ 7: death: send SIGHUP to all controlling processes, reap for 30 seconds,
+ then go to 1 (warn if not all processes died, i.e. wait blocks)
+Given the -s flag, we start at state 1; otherwise state 2
diff --git a/sbin/init/init.8 b/sbin/init/init.8
new file mode 100644
index 000000000000..71e318427d82
--- /dev/null
+++ b/sbin/init/init.8
@@ -0,0 +1,291 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Donn Seeley at Berkeley Software Design, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)init.8 8.3 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt INIT 8
+.Os BSD 4
+.Sh NAME
+.Nm init
+.Nd process control initialization
+.Sh SYNOPSIS
+.Nm init
+.Sh DESCRIPTION
+The
+.Nm init
+program
+is the last stage of the boot process.
+It normally runs the automatic reboot sequence as described in
+.Xr reboot 8 ,
+and if this succeeds, begins multi-user operation.
+If the reboot scripts fail,
+.Nm init
+commences single user operation by giving
+the super-user a shell on the console.
+The
+.Nm init
+program may be passed parameters
+from the boot program to
+prevent the system from going multi-user and to instead execute
+a single user shell without starting the normal daemons.
+The system is then quiescent for maintenance work and may
+later be made to go to multi-user by exiting the
+single-user shell (with ^D).
+This
+causes
+.Nm init
+to run the
+.Pa /etc/rc
+start up command file in fastboot mode (skipping disk checks).
+.Pp
+If the
+.Nm console
+entry in the
+.Xr ttys 5
+file is marked ``insecure'',
+then
+.Nm init
+will require that the superuser password be
+entered before the system will start a single-user shell.
+The password check is skipped if the
+.Nm console
+is marked as ``secure''.
+.Pp
+The kernel runs with four different levels of security.
+Any superuser process can raise the security level, but only
+.Nm init
+can lower it.
+Security levels are defined as follows:
+.Bl -tag -width flag
+.It Ic -1
+Permanently insecure mode \- always run system in level 0 mode.
+.It Ic 0
+Insecure mode \- immutable and append-only flags may be turned off.
+All devices may be read or written subject to their permissions.
+.It Ic 1
+Secure mode \- immutable and append-only flags may not be changed;
+disks for mounted filesystems,
+.Pa /dev/mem ,
+and
+.Pa /dev/kmem
+are read-only.
+.It Ic 2
+Highly secure mode \- same as secure mode, plus disks are always
+read-only whether mounted or not.
+This level precludes tampering with filesystems by unmounting them,
+but also inhibits running
+.Xr newfs 8
+while the system is multi-user.
+.El
+.Pp
+Normally, the system runs in level 0 mode while single user
+and in level 1 mode while multiuser.
+If the level 2 mode is desired while running multiuser,
+it can be set in the startup script
+.Pa /etc/rc
+using
+.Xr sysctl 8 .
+If it is desired to run the system in level 0 mode while multiuser,
+the administrator must build a kernel with the variable
+.Nm securelevel
+defined in the file
+.Pa /sys/compile/MACHINE/param.c
+and initialize it to -1.
+.Pp
+In multi-user operation,
+.Nm init
+maintains
+processes for the terminal ports found in the file
+.Xr ttys 5 .
+.Nm Init
+reads this file, and executes the command found in the second field.
+This command is usually
+.Xr getty 8 ;
+.Xr getty
+opens and initializes the tty line
+and
+executes the
+.Xr login
+program.
+The
+.Xr login
+program, when a valid user logs in,
+executes a shell for that user. When this shell
+dies, either because the user logged out
+or an abnormal termination occurred (a signal),
+the
+.Nm init
+program wakes up, deletes the user
+from the
+.Xr utmp 5
+file of current users and records the logout in the
+.Xr wtmp
+file.
+The cycle is
+then restarted by
+.Nm init
+executing a new
+.Xr getty
+for the line.
+.Pp
+Line status (on, off, secure, getty, or window information)
+may be changed in the
+.Xr ttys
+file without a reboot by sending the signal
+.Dv SIGHUP
+to
+.Nm init
+with the command
+.Dq Li "kill -HUP 1" .
+On receipt of this signal,
+.Nm init
+re-reads the
+.Xr ttys
+file.
+When a line is turned off in
+.Xr ttys ,
+.Nm init
+will send a SIGHUP signal to the controlling process
+for the session associated with the line.
+For any lines that were previously turned off in the
+.Xr ttys
+file and are now on,
+.Nm init
+executes a new
+.Xr getty
+to enable a new login.
+If the getty or window field for a line is changed,
+the change takes effect at the end of the current
+login session (e.g., the next time
+.Nm init
+starts a process on the line).
+If a line is commented out or deleted from
+.Xr ttys ,
+.Nm init
+will not do anything at all to that line.
+However, it will complain that the relationship between lines
+in the
+.Xr ttys
+file and records in the
+.Xr utmp
+file is out of sync,
+so this practice is not recommended.
+.Pp
+.Nm Init
+will terminate multi-user operations and resume single-user mode
+if sent a terminate
+.Pq Dv TERM
+signal, for example,
+.Dq Li "kill \-TERM 1" .
+If there are processes outstanding that are deadlocked (because of
+hardware or software failure),
+.Xr init
+will not wait for them all to die (which might take forever), but
+will time out after 30 seconds and print a warning message.
+.Pp
+.Nm Init
+will cease creating new
+.Xr getty Ns 's
+and allow the system to slowly die away, if it is sent a terminal stop
+.Pq Dv TSTP
+signal, i.e.
+.Dq Li "kill \-TSTP 1" .
+A later hangup will resume full
+multi-user operations, or a terminate will start a single user shell.
+This hook is used by
+.Xr reboot 8
+and
+.Xr halt 8 .
+.Pp
+The role of
+.Nm init
+is so critical that if it dies, the system will reboot itself
+automatically.
+If, at bootstrap time, the
+.Xr init
+process cannot be located, the system will panic with the message
+``panic: "init died (signal %d, exit %d)''.
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "getty repeating too quickly on port %s, sleeping"
+A process being started to service a line is exiting quickly
+each time it is started.
+This is often caused by a ringing or noisy terminal line.
+.Em "Init will sleep for 10 seconds" ,
+.Em "then continue trying to start the process" .
+.Pp
+.It "some processes would not die; ps axl advised."
+A process
+is hung and could not be killed when the system was shutting down.
+This condition is usually caused by a process
+that is stuck in a device driver because of
+a persistent device error condition.
+.El
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa /dev/console
+System console device.
+.It Pa /dev/tty*
+Terminal ports found in
+.Xr ttys .
+.It Pa /var/run/utmp
+Record of Current users on the system.
+.It Pa /var/log/wtmp
+Record of all logins and logouts.
+.It Pa /etc/ttys
+The terminal initialization information file.
+.It Pa /etc/rc
+System startup commands.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr kill 1 ,
+.Xr sh 1 ,
+.Xr ttys 5 ,
+.Xr crash 8 ,
+.Xr getty 8 ,
+.Xr rc 8 ,
+.Xr reboot 8 ,
+.Xr halt 8 ,
+.Xr shutdown 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
+.Sh BUGS
+Systems without
+.Xr sysctl
+behave as though they have security level \-1.
diff --git a/sbin/init/init.c b/sbin/init/init.c
new file mode 100644
index 000000000000..ff03598932a8
--- /dev/null
+++ b/sbin/init/init.c
@@ -0,0 +1,1297 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Donn Seeley at Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <ttyent.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef SECURE
+#include <pwd.h>
+#endif
+
+#include "pathnames.h"
+
+/*
+ * Until the mythical util.h arrives...
+ */
+extern int login_tty __P((int));
+extern int logout __P((const char *));
+extern void logwtmp __P((const char *, const char *, const char *));
+
+/*
+ * Sleep times; used to prevent thrashing.
+ */
+#define GETTY_SPACING 5 /* N secs minimum getty spacing */
+#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
+#define WINDOW_WAIT 3 /* wait N secs after starting window */
+#define STALL_TIMEOUT 30 /* wait N secs after warning */
+#define DEATH_WATCH 10 /* wait N secs for procs to die */
+
+void handle __P((sig_t, ...));
+void delset __P((sigset_t *, ...));
+
+void stall __P((char *, ...));
+void warning __P((char *, ...));
+void emergency __P((char *, ...));
+void disaster __P((int));
+void badsys __P((int));
+
+/*
+ * We really need a recursive typedef...
+ * The following at least guarantees that the return type of (*state_t)()
+ * is sufficiently wide to hold a function pointer.
+ */
+typedef long (*state_func_t) __P((void));
+typedef state_func_t (*state_t) __P((void));
+
+state_func_t single_user __P((void));
+state_func_t runcom __P((void));
+state_func_t read_ttys __P((void));
+state_func_t multi_user __P((void));
+state_func_t clean_ttys __P((void));
+state_func_t catatonia __P((void));
+state_func_t death __P((void));
+
+enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
+
+void transition __P((state_t));
+state_t requested_transition = runcom;
+
+void setctty __P((char *));
+
+typedef struct init_session {
+ int se_index; /* index of entry in ttys file */
+ pid_t se_process; /* controlling process */
+ time_t se_started; /* used to avoid thrashing */
+ int se_flags; /* status of session */
+#define SE_SHUTDOWN 0x1 /* session won't be restarted */
+ char *se_device; /* filename of port */
+ char *se_getty; /* what to run on that port */
+ char **se_getty_argv; /* pre-parsed argument array */
+ char *se_window; /* window system (started only once) */
+ char **se_window_argv; /* pre-parsed argument array */
+ struct init_session *se_prev;
+ struct init_session *se_next;
+} session_t;
+
+void free_session __P((session_t *));
+session_t *new_session __P((session_t *, int, struct ttyent *));
+session_t *sessions;
+
+char **construct_argv __P((char *));
+void start_window_system __P((session_t *));
+void collect_child __P((pid_t));
+pid_t start_getty __P((session_t *));
+void transition_handler __P((int));
+void alrm_handler __P((int));
+void setsecuritylevel __P((int));
+int getsecuritylevel __P((void));
+int setupargv __P((session_t *, struct ttyent *));
+int clang;
+
+void clear_session_logs __P((session_t *));
+
+int start_session_db __P((void));
+void add_session __P((session_t *));
+void del_session __P((session_t *));
+session_t *find_session __P((pid_t));
+DB *session_db;
+
+/*
+ * The mother of all processes.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ struct sigaction sa;
+ sigset_t mask;
+
+
+ /* Dispose of random users. */
+ if (getuid() != 0) {
+ (void)fprintf(stderr, "init: %s\n", strerror(EPERM));
+ exit (1);
+ }
+
+ /* System V users like to reexec init. */
+ if (getpid() != 1) {
+ (void)fprintf(stderr, "init: already running\n");
+ exit (1);
+ }
+
+ /*
+ * Note that this does NOT open a file...
+ * Does 'init' deserve its own facility number?
+ */
+ openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
+
+ /*
+ * Create an initial session.
+ */
+ if (setsid() < 0)
+ warning("initial setsid() failed: %m");
+
+ /*
+ * Establish an initial user so that programs running
+ * single user do not freak out and die (like passwd).
+ */
+ if (setlogin("root") < 0)
+ warning("setlogin() failed: %m");
+
+ /*
+ * This code assumes that we always get arguments through flags,
+ * never through bits set in some random machine register.
+ */
+ while ((c = getopt(argc, argv, "sf")) != -1)
+ switch (c) {
+ case 's':
+ requested_transition = single_user;
+ break;
+ case 'f':
+ runcom_mode = FASTBOOT;
+ break;
+ default:
+ warning("unrecognized flag '-%c'", c);
+ break;
+ }
+
+ if (optind != argc)
+ warning("ignoring excess arguments");
+
+ /*
+ * We catch or block signals rather than ignore them,
+ * so that they get reset on exec.
+ */
+ handle(badsys, SIGSYS, 0);
+ handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
+ SIGBUS, SIGXCPU, SIGXFSZ, 0);
+ handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0);
+ handle(alrm_handler, SIGALRM, 0);
+ sigfillset(&mask);
+ delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
+ SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
+
+ /*
+ * Paranoia.
+ */
+ close(0);
+ close(1);
+ close(2);
+
+ /*
+ * Start the state machine.
+ */
+ transition(requested_transition);
+
+ /*
+ * Should never reach here.
+ */
+ return 1;
+}
+
+/*
+ * Associate a function with a signal handler.
+ */
+void
+#ifdef __STDC__
+handle(sig_t handler, ...)
+#else
+handle(va_alist)
+ va_dcl
+#endif
+{
+ int sig;
+ struct sigaction sa;
+ int mask_everything;
+ va_list ap;
+#ifndef __STDC__
+ sig_t handler;
+
+ va_start(ap);
+ handler = va_arg(ap, sig_t);
+#else
+ va_start(ap, handler);
+#endif
+
+ sa.sa_handler = handler;
+ sigfillset(&mask_everything);
+
+ while (sig = va_arg(ap, int)) {
+ sa.sa_mask = mask_everything;
+ /* XXX SA_RESTART? */
+ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
+ sigaction(sig, &sa, (struct sigaction *) 0);
+ }
+ va_end(ap);
+}
+
+/*
+ * Delete a set of signals from a mask.
+ */
+void
+#ifdef __STDC__
+delset(sigset_t *maskp, ...)
+#else
+delset(va_alist)
+ va_dcl
+#endif
+{
+ int sig;
+ va_list ap;
+#ifndef __STDC__
+ sigset_t *maskp;
+
+ va_start(ap);
+ maskp = va_arg(ap, sigset_t *);
+#else
+ va_start(ap, maskp);
+#endif
+
+ while (sig = va_arg(ap, int))
+ sigdelset(maskp, sig);
+ va_end(ap);
+}
+
+/*
+ * Log a message and sleep for a while (to give someone an opportunity
+ * to read it and to save log or hardcopy output if the problem is chronic).
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+#ifdef __STDC__
+stall(char *message, ...)
+#else
+stall(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifndef __STDC__
+ char *message;
+
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+
+ vsyslog(LOG_ALERT, message, ap);
+ va_end(ap);
+ sleep(STALL_TIMEOUT);
+}
+
+/*
+ * Like stall(), but doesn't sleep.
+ * If cpp had variadic macros, the two functions could be #defines for another.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+#ifdef __STDC__
+warning(char *message, ...)
+#else
+warning(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifndef __STDC__
+ char *message;
+
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+
+ vsyslog(LOG_ALERT, message, ap);
+ va_end(ap);
+}
+
+/*
+ * Log an emergency message.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+#ifdef __STDC__
+emergency(char *message, ...)
+#else
+emergency(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifndef __STDC__
+ char *message;
+
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+
+ vsyslog(LOG_EMERG, message, ap);
+ va_end(ap);
+}
+
+/*
+ * Catch a SIGSYS signal.
+ *
+ * These may arise if a system does not support sysctl.
+ * We tolerate up to 25 of these, then throw in the towel.
+ */
+void
+badsys(sig)
+ int sig;
+{
+ static int badcount = 0;
+
+ if (badcount++ < 25)
+ return;
+ disaster(sig);
+}
+
+/*
+ * Catch an unexpected signal.
+ */
+void
+disaster(sig)
+ int sig;
+{
+ emergency("fatal signal: %s",
+ sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal");
+
+ sleep(STALL_TIMEOUT);
+ _exit(sig); /* reboot */
+}
+
+/*
+ * Get the security level of the kernel.
+ */
+int
+getsecuritylevel()
+{
+#ifdef KERN_SECURELVL
+ int name[2], curlevel;
+ size_t len;
+ extern int errno;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_SECURELVL;
+ len = sizeof curlevel;
+ if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
+ emergency("cannot get kernel security level: %s",
+ strerror(errno));
+ return (-1);
+ }
+ return (curlevel);
+#else
+ return (-1);
+#endif
+}
+
+/*
+ * Set the security level of the kernel.
+ */
+void
+setsecuritylevel(newlevel)
+ int newlevel;
+{
+#ifdef KERN_SECURELVL
+ int name[2], curlevel;
+ extern int errno;
+
+ curlevel = getsecuritylevel();
+ if (newlevel == curlevel)
+ return;
+ name[0] = CTL_KERN;
+ name[1] = KERN_SECURELVL;
+ if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
+ emergency(
+ "cannot change kernel security level from %d to %d: %s",
+ curlevel, newlevel, strerror(errno));
+ return;
+ }
+#ifdef SECURE
+ warning("kernel security level changed from %d to %d",
+ curlevel, newlevel);
+#endif
+#endif
+}
+
+/*
+ * Change states in the finite state machine.
+ * The initial state is passed as an argument.
+ */
+void
+transition(s)
+ state_t s;
+{
+ for (;;)
+ s = (state_t) (*s)();
+}
+
+/*
+ * Close out the accounting files for a login session.
+ * NB: should send a message to the session logger to avoid blocking.
+ */
+void
+clear_session_logs(sp)
+ session_t *sp;
+{
+ char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
+
+ if (logout(line))
+ logwtmp(line, "", "");
+}
+
+/*
+ * Start a session and allocate a controlling terminal.
+ * Only called by children of init after forking.
+ */
+void
+setctty(name)
+ char *name;
+{
+ int fd;
+
+ (void) revoke(name);
+ sleep (2); /* leave DTR low */
+ if ((fd = open(name, O_RDWR)) == -1) {
+ stall("can't open %s: %m", name);
+ _exit(1);
+ }
+ if (login_tty(fd) == -1) {
+ stall("can't get %s for controlling terminal: %m", name);
+ _exit(1);
+ }
+}
+
+/*
+ * Bring the system up single user.
+ */
+state_func_t
+single_user()
+{
+ pid_t pid, wpid;
+ int status;
+ sigset_t mask;
+ char *shell = _PATH_BSHELL;
+ char *argv[2];
+#ifdef SECURE
+ struct ttyent *typ;
+ struct passwd *pp;
+ static const char banner[] =
+ "Enter root password, or ^D to go multi-user\n";
+ char *clear, *password;
+#endif
+
+ /*
+ * If the kernel is in secure mode, downgrade it to insecure mode.
+ */
+ if (getsecuritylevel() > 0)
+ setsecuritylevel(0);
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Start the single user session.
+ */
+ setctty(_PATH_CONSOLE);
+
+#ifdef SECURE
+ /*
+ * Check the root password.
+ * We don't care if the console is 'on' by default;
+ * it's the only tty that can be 'off' and 'secure'.
+ */
+ typ = getttynam("console");
+ pp = getpwnam("root");
+ if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
+ write(2, banner, sizeof banner - 1);
+ for (;;) {
+ clear = getpass("Password:");
+ if (clear == 0 || *clear == '\0')
+ _exit(0);
+ password = crypt(clear, pp->pw_passwd);
+ bzero(clear, _PASSWORD_LEN);
+ if (strcmp(password, pp->pw_passwd) == 0)
+ break;
+ warning("single-user login failed\n");
+ }
+ }
+ endttyent();
+ endpwent();
+#endif /* SECURE */
+
+#ifdef DEBUGSHELL
+ {
+ char altshell[128], *cp = altshell;
+ int num;
+
+#define SHREQUEST \
+ "Enter pathname of shell or RETURN for sh: "
+ (void)write(STDERR_FILENO,
+ SHREQUEST, sizeof(SHREQUEST) - 1);
+ while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
+ num != 0 && *cp != '\n' && cp < &altshell[127])
+ cp++;
+ *cp = '\0';
+ if (altshell[0] != '\0')
+ shell = altshell;
+ }
+#endif /* DEBUGSHELL */
+
+ /*
+ * Unblock signals.
+ * We catch all the interesting ones,
+ * and those are reset to SIG_DFL on exec.
+ */
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ /*
+ * Fire off a shell.
+ * If the default one doesn't work, try the Bourne shell.
+ */
+ argv[0] = "-sh";
+ argv[1] = 0;
+ execv(shell, argv);
+ emergency("can't exec %s for single user: %m", shell);
+ execv(_PATH_BSHELL, argv);
+ emergency("can't exec %s for single user: %m", _PATH_BSHELL);
+ sleep(STALL_TIMEOUT);
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ /*
+ * We are seriously hosed. Do our best.
+ */
+ emergency("can't fork single-user shell, trying again");
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ return (state_func_t) single_user;
+ }
+
+ requested_transition = 0;
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for single-user shell failed: %m; restarting");
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: shell stopped, restarting\n");
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid && !requested_transition);
+
+ if (requested_transition)
+ return (state_func_t) requested_transition;
+
+ if (!WIFEXITED(status)) {
+ if (WTERMSIG(status) == SIGKILL) {
+ /*
+ * reboot(8) killed shell?
+ */
+ warning("single user shell terminated.");
+ sleep(STALL_TIMEOUT);
+ _exit(0);
+ } else {
+ warning("single user shell terminated, restarting");
+ return (state_func_t) single_user;
+ }
+ }
+
+ runcom_mode = FASTBOOT;
+ return (state_func_t) runcom;
+}
+
+/*
+ * Run the system startup script.
+ */
+state_func_t
+runcom()
+{
+ pid_t pid, wpid;
+ int status;
+ char *argv[4];
+ struct sigaction sa;
+
+ if ((pid = fork()) == 0) {
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ setctty(_PATH_CONSOLE);
+
+ argv[0] = "sh";
+ argv[1] = _PATH_RUNCOM;
+ argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
+ argv[3] = 0;
+
+ sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
+
+ execv(_PATH_BSHELL, argv);
+ stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
+ _exit(1); /* force single user mode */
+ }
+
+ if (pid == -1) {
+ emergency("can't fork for %s on %s: %m",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ while (waitpid(-1, (int *) 0, WNOHANG) > 0)
+ continue;
+ sleep(STALL_TIMEOUT);
+ return (state_func_t) single_user;
+ }
+
+ /*
+ * Copied from single_user(). This is a bit paranoid.
+ */
+ do {
+ if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
+ collect_child(wpid);
+ if (wpid == -1) {
+ if (errno == EINTR)
+ continue;
+ warning("wait for %s on %s failed: %m; going to single user mode",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ return (state_func_t) single_user;
+ }
+ if (wpid == pid && WIFSTOPPED(status)) {
+ warning("init: %s on %s stopped, restarting\n",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ kill(pid, SIGCONT);
+ wpid = -1;
+ }
+ } while (wpid != pid);
+
+ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
+ requested_transition == catatonia) {
+ /* /etc/rc executed /sbin/reboot; wait for the end quietly */
+ sigset_t s;
+
+ sigfillset(&s);
+ for (;;)
+ sigsuspend(&s);
+ }
+
+ if (!WIFEXITED(status)) {
+ warning("%s on %s terminated abnormally, going to single user mode",
+ _PATH_BSHELL, _PATH_RUNCOM);
+ return (state_func_t) single_user;
+ }
+
+ if (WEXITSTATUS(status))
+ return (state_func_t) single_user;
+
+ runcom_mode = AUTOBOOT; /* the default */
+ /* NB: should send a message to the session logger to avoid blocking. */
+ logwtmp("~", "reboot", "");
+ return (state_func_t) read_ttys;
+}
+
+/*
+ * Open the session database.
+ *
+ * NB: We could pass in the size here; is it necessary?
+ */
+int
+start_session_db()
+{
+ if (session_db && (*session_db->close)(session_db))
+ emergency("session database close: %s", strerror(errno));
+ if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
+ emergency("session database open: %s", strerror(errno));
+ return (1);
+ }
+ return (0);
+
+}
+
+/*
+ * Add a new login session.
+ */
+void
+add_session(sp)
+ session_t *sp;
+{
+ DBT key;
+ DBT data;
+
+ key.data = &sp->se_process;
+ key.size = sizeof sp->se_process;
+ data.data = &sp;
+ data.size = sizeof sp;
+
+ if ((*session_db->put)(session_db, &key, &data, 0))
+ emergency("insert %d: %s", sp->se_process, strerror(errno));
+}
+
+/*
+ * Delete an old login session.
+ */
+void
+del_session(sp)
+ session_t *sp;
+{
+ DBT key;
+
+ key.data = &sp->se_process;
+ key.size = sizeof sp->se_process;
+
+ if ((*session_db->del)(session_db, &key, 0))
+ emergency("delete %d: %s", sp->se_process, strerror(errno));
+}
+
+/*
+ * Look up a login session by pid.
+ */
+session_t *
+#ifdef __STDC__
+find_session(pid_t pid)
+#else
+find_session(pid)
+ pid_t pid;
+#endif
+{
+ DBT key;
+ DBT data;
+ session_t *ret;
+
+ key.data = &pid;
+ key.size = sizeof pid;
+ if ((*session_db->get)(session_db, &key, &data, 0) != 0)
+ return 0;
+ bcopy(data.data, (char *)&ret, sizeof(ret));
+ return ret;
+}
+
+/*
+ * Construct an argument vector from a command line.
+ */
+char **
+construct_argv(command)
+ char *command;
+{
+ register int argc = 0;
+ register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
+ * sizeof (char *));
+ static const char separators[] = " \t";
+
+ if ((argv[argc++] = strtok(command, separators)) == 0)
+ return 0;
+ while (argv[argc++] = strtok((char *) 0, separators))
+ continue;
+ return argv;
+}
+
+/*
+ * Deallocate a session descriptor.
+ */
+void
+free_session(sp)
+ register session_t *sp;
+{
+ free(sp->se_device);
+ if (sp->se_getty) {
+ free(sp->se_getty);
+ free(sp->se_getty_argv);
+ }
+ if (sp->se_window) {
+ free(sp->se_window);
+ free(sp->se_window_argv);
+ }
+ free(sp);
+}
+
+/*
+ * Allocate a new session descriptor.
+ */
+session_t *
+new_session(sprev, session_index, typ)
+ session_t *sprev;
+ int session_index;
+ register struct ttyent *typ;
+{
+ register session_t *sp;
+
+ if ((typ->ty_status & TTY_ON) == 0 ||
+ typ->ty_name == 0 ||
+ typ->ty_getty == 0)
+ return 0;
+
+ sp = (session_t *) malloc(sizeof (session_t));
+ bzero(sp, sizeof *sp);
+
+ sp->se_index = session_index;
+
+ sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
+ (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
+
+ if (setupargv(sp, typ) == 0) {
+ free_session(sp);
+ return (0);
+ }
+
+ sp->se_next = 0;
+ if (sprev == 0) {
+ sessions = sp;
+ sp->se_prev = 0;
+ } else {
+ sprev->se_next = sp;
+ sp->se_prev = sprev;
+ }
+
+ return sp;
+}
+
+/*
+ * Calculate getty and if useful window argv vectors.
+ */
+int
+setupargv(sp, typ)
+ session_t *sp;
+ struct ttyent *typ;
+{
+
+ if (sp->se_getty) {
+ free(sp->se_getty);
+ free(sp->se_getty_argv);
+ }
+ sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
+ (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
+ sp->se_getty_argv = construct_argv(sp->se_getty);
+ if (sp->se_getty_argv == 0) {
+ warning("can't parse getty for port %s", sp->se_device);
+ free(sp->se_getty);
+ sp->se_getty = 0;
+ return (0);
+ }
+ if (typ->ty_window) {
+ if (sp->se_window)
+ free(sp->se_window);
+ sp->se_window = strdup(typ->ty_window);
+ sp->se_window_argv = construct_argv(sp->se_window);
+ if (sp->se_window_argv == 0) {
+ warning("can't parse window for port %s",
+ sp->se_device);
+ free(sp->se_window);
+ sp->se_window = 0;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Walk the list of ttys and create sessions for each active line.
+ */
+state_func_t
+read_ttys()
+{
+ int session_index = 0;
+ register session_t *sp, *snext;
+ register struct ttyent *typ;
+
+ /*
+ * Destroy any previous session state.
+ * There shouldn't be any, but just in case...
+ */
+ for (sp = sessions; sp; sp = snext) {
+ if (sp->se_process)
+ clear_session_logs(sp);
+ snext = sp->se_next;
+ free_session(sp);
+ }
+ sessions = 0;
+ if (start_session_db())
+ return (state_func_t) single_user;
+
+ /*
+ * Allocate a session entry for each active port.
+ * Note that sp starts at 0.
+ */
+ while (typ = getttyent())
+ if (snext = new_session(sp, ++session_index, typ))
+ sp = snext;
+
+ endttyent();
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Start a window system running.
+ */
+void
+start_window_system(sp)
+ session_t *sp;
+{
+ pid_t pid;
+ sigset_t mask;
+
+ if ((pid = fork()) == -1) {
+ emergency("can't fork for window system on port %s: %m",
+ sp->se_device);
+ /* hope that getty fails and we can try again */
+ return;
+ }
+
+ if (pid)
+ return;
+
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ if (setsid() < 0)
+ emergency("setsid failed (window) %m");
+
+ execv(sp->se_window_argv[0], sp->se_window_argv);
+ stall("can't exec window system '%s' for port %s: %m",
+ sp->se_window_argv[0], sp->se_device);
+ _exit(1);
+}
+
+/*
+ * Start a login session running.
+ */
+pid_t
+start_getty(sp)
+ session_t *sp;
+{
+ pid_t pid;
+ sigset_t mask;
+ time_t current_time = time((time_t *) 0);
+
+ /*
+ * fork(), not vfork() -- we can't afford to block.
+ */
+ if ((pid = fork()) == -1) {
+ emergency("can't fork for getty on port %s: %m", sp->se_device);
+ return -1;
+ }
+
+ if (pid)
+ return pid;
+
+ if (current_time > sp->se_started &&
+ current_time - sp->se_started < GETTY_SPACING) {
+ warning("getty repeating too quickly on port %s, sleeping",
+ sp->se_device);
+ sleep((unsigned) GETTY_SLEEP);
+ }
+
+ if (sp->se_window) {
+ start_window_system(sp);
+ sleep(WINDOW_WAIT);
+ }
+
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ execv(sp->se_getty_argv[0], sp->se_getty_argv);
+ stall("can't exec getty '%s' for port %s: %m",
+ sp->se_getty_argv[0], sp->se_device);
+ _exit(1);
+}
+
+/*
+ * Collect exit status for a child.
+ * If an exiting login, start a new login running.
+ */
+void
+#ifdef __STDC__
+collect_child(pid_t pid)
+#else
+collect_child(pid)
+ pid_t pid;
+#endif
+{
+ register session_t *sp, *sprev, *snext;
+
+ if (! sessions)
+ return;
+
+ if (! (sp = find_session(pid)))
+ return;
+
+ clear_session_logs(sp);
+ del_session(sp);
+ sp->se_process = 0;
+
+ if (sp->se_flags & SE_SHUTDOWN) {
+ if (sprev = sp->se_prev)
+ sprev->se_next = sp->se_next;
+ else
+ sessions = sp->se_next;
+ if (snext = sp->se_next)
+ snext->se_prev = sp->se_prev;
+ free_session(sp);
+ return;
+ }
+
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ return;
+ }
+
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+}
+
+/*
+ * Catch a signal and request a state transition.
+ */
+void
+transition_handler(sig)
+ int sig;
+{
+
+ switch (sig) {
+ case SIGHUP:
+ requested_transition = clean_ttys;
+ break;
+ case SIGTERM:
+ requested_transition = death;
+ break;
+ case SIGTSTP:
+ requested_transition = catatonia;
+ break;
+ default:
+ requested_transition = 0;
+ break;
+ }
+}
+
+/*
+ * Take the system multiuser.
+ */
+state_func_t
+multi_user()
+{
+ pid_t pid;
+ register session_t *sp;
+
+ requested_transition = 0;
+
+ /*
+ * If the administrator has not set the security level to -1
+ * to indicate that the kernel should not run multiuser in secure
+ * mode, and the run script has not set a higher level of security
+ * than level 1, then put the kernel into secure mode.
+ */
+ if (getsecuritylevel() == 0)
+ setsecuritylevel(1);
+
+ for (sp = sessions; sp; sp = sp->se_next) {
+ if (sp->se_process)
+ continue;
+ if ((pid = start_getty(sp)) == -1) {
+ /* serious trouble */
+ requested_transition = clean_ttys;
+ break;
+ }
+ sp->se_process = pid;
+ sp->se_started = time((time_t *) 0);
+ add_session(sp);
+ }
+
+ while (!requested_transition)
+ if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
+ collect_child(pid);
+
+ return (state_func_t) requested_transition;
+}
+
+/*
+ * This is an n-squared algorithm. We hope it isn't run often...
+ */
+state_func_t
+clean_ttys()
+{
+ register session_t *sp, *sprev;
+ register struct ttyent *typ;
+ register int session_index = 0;
+ register int devlen;
+
+ if (! sessions)
+ return (state_func_t) multi_user;
+
+ devlen = sizeof(_PATH_DEV) - 1;
+ while (typ = getttyent()) {
+ ++session_index;
+
+ for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
+ if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
+ break;
+
+ if (sp) {
+ if (sp->se_index != session_index) {
+ warning("port %s changed utmp index from %d to %d",
+ sp->se_device, sp->se_index,
+ session_index);
+ sp->se_index = session_index;
+ }
+ if ((typ->ty_status & TTY_ON) == 0 ||
+ typ->ty_getty == 0) {
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ continue;
+ }
+ sp->se_flags &= ~SE_SHUTDOWN;
+ if (setupargv(sp, typ) == 0) {
+ warning("can't parse getty for port %s",
+ sp->se_device);
+ sp->se_flags |= SE_SHUTDOWN;
+ kill(sp->se_process, SIGHUP);
+ }
+ continue;
+ }
+
+ new_session(sprev, session_index, typ);
+ }
+
+ endttyent();
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Block further logins.
+ */
+state_func_t
+catatonia()
+{
+ register session_t *sp;
+
+ for (sp = sessions; sp; sp = sp->se_next)
+ sp->se_flags |= SE_SHUTDOWN;
+
+ return (state_func_t) multi_user;
+}
+
+/*
+ * Note SIGALRM.
+ */
+void
+alrm_handler(sig)
+ int sig;
+{
+ clang = 1;
+}
+
+/*
+ * Bring the system down to single user.
+ */
+state_func_t
+death()
+{
+ register session_t *sp;
+ register int i;
+ pid_t pid;
+ static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
+
+ for (sp = sessions; sp; sp = sp->se_next)
+ sp->se_flags |= SE_SHUTDOWN;
+
+ /* NB: should send a message to the session logger to avoid blocking. */
+ logwtmp("~", "shutdown", "");
+
+ for (i = 0; i < 3; ++i) {
+ if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
+ return (state_func_t) single_user;
+
+ clang = 0;
+ alarm(DEATH_WATCH);
+ do
+ if ((pid = waitpid(-1, (int *)0, 0)) != -1)
+ collect_child(pid);
+ while (clang == 0 && errno != ECHILD);
+
+ if (errno == ECHILD)
+ return (state_func_t) single_user;
+ }
+
+ warning("some processes would not die; ps axl advised");
+
+ return (state_func_t) single_user;
+}
diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h
new file mode 100644
index 000000000000..abb874a4b988
--- /dev/null
+++ b/sbin/init/pathnames.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Donn Seeley at Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+
+#include <paths.h>
+
+#define _PATH_SLOGGER "/sbin/session_logger"
+#define _PATH_RUNCOM "/etc/rc"
diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile
new file mode 100644
index 000000000000..272753faacea
--- /dev/null
+++ b/sbin/mknod/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= mknod
+MAN8= mknod.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8
new file mode 100644
index 000000000000..73f71d360ea1
--- /dev/null
+++ b/sbin/mknod/mknod.8
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt MKNOD 8
+.Os BSD 4
+.Sh NAME
+.Nm mknod
+.Nd build special file
+.Sh SYNOPSIS
+.Nm mknod
+.Ar name
+.Op Cm c | Cm b
+.Ar major minor
+.Sh DESCRIPTION
+The
+.Nm mknod
+command creates device special files.
+Normally the shell script
+.Pa /dev/MAKEDEV
+is used to create special files for commonly known devices; it executes
+.Nm mknod
+with the appropriate arguments and can make all the files required for the
+device.
+.Pp
+To make nodes manually, the four required arguments are:
+.Pp
+.Bl -tag -width majorx
+.It Ar name
+Device name, for example
+.Dq sd
+for a SCSI disk on an HP300 or a
+.Dq pty
+for pseudo-devices.
+.It Cm b | Cm c
+Type of device. If the
+device is a block type device such as a tape or disk drive which needs
+both cooked and raw special files,
+the type is
+.Cm b .
+All other devices are character type devices, such as terminal
+and pseudo devices, and are type
+.Cm c .
+.It Ar major
+The major device number is an integer number which tells the kernel
+which device driver entry point to use. To learn what
+major device number to use for a particular device, check the file
+.Pa /dev/MAKEDEV
+to see if the device is known, or check
+the system dependent device configuration file:
+.Bd -filled -offset indent
+.Dq Pa /usr/src/sys/conf/device. Ns Em architecture
+.Ed
+.Pp
+(for example
+.Pa device.hp300 ) .
+.It Ar minor
+The minor device number tells the kernel which subunit
+the node corresponds to on the device; for example,
+a subunit may be a filesystem partition
+or a tty line.
+.El
+.Sh SEE ALSO
+.Xr mknod 2 ,
+.Xr makedev 8
+.Sh HISTORY
+A
+.Nm
+command appeared in Version 6 AT&T UNIX.
diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c
new file mode 100644
index 000000000000..b70acf1e0919
--- /dev/null
+++ b/sbin/mknod/mknod.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * 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) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int errno;
+ u_short mode;
+ char *strerror();
+
+ if (argc != 5) {
+ (void)fprintf(stderr,
+ "usage: mknod name [b | c] major minor\n");
+ exit(1);
+ }
+
+ mode = 0666;
+ if (argv[2][0] == 'c')
+ mode |= S_IFCHR;
+ else if (argv[2][0] == 'b')
+ mode |= S_IFBLK;
+ else {
+ (void)fprintf(stderr,
+ "mknod: node must be type 'b' or 'c'.\n");
+ exit(1);
+ }
+
+ if (mknod(argv[1], mode, makedev(atoi(argv[3]), atoi(argv[4]))) < 0) {
+ (void)fprintf(stderr,
+ "mknod: %s: %s\n", argv[1], strerror(errno));
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile
new file mode 100644
index 000000000000..b3efd4ef63c4
--- /dev/null
+++ b/sbin/mount/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/getmntopts.3 b/sbin/mount/getmntopts.3
new file mode 100644
index 000000000000..642c57aa361d
--- /dev/null
+++ b/sbin/mount/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/getmntopts.c b/sbin/mount/getmntopts.c
new file mode 100644
index 000000000000..2c84ae56e3d5
--- /dev/null
+++ b/sbin/mount/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/mntopts.h b/sbin/mount/mntopts.h
new file mode 100644
index 000000000000..a89f63dc19ff
--- /dev/null
+++ b/sbin/mount/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/mount.8 b/sbin/mount/mount.8
new file mode 100644
index 000000000000..e2c53c8ffec8
--- /dev/null
+++ b/sbin/mount/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/mount.c b/sbin/mount/mount.c
new file mode 100644
index 000000000000..f0ff782f4e0e
--- /dev/null
+++ b/sbin/mount/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/mount_ufs.c b/sbin/mount/mount_ufs.c
new file mode 100644
index 000000000000..babb760a07d8
--- /dev/null
+++ b/sbin/mount/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/pathnames.h b/sbin/mount/pathnames.h
new file mode 100644
index 000000000000..45a4a010c1cf
--- /dev/null
+++ b/sbin/mount/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_cd9660/Makefile b/sbin/mount_cd9660/Makefile
new file mode 100644
index 000000000000..2491dff65559
--- /dev/null
+++ b/sbin/mount_cd9660/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_cd9660
+SRCS= mount_cd9660.c getmntopts.c
+MAN8= mount_cd9660.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_cd9660/mount_cd9660.8 b/sbin/mount_cd9660/mount_cd9660.8
new file mode 100644
index 000000000000..920dc6c87fd3
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.8
@@ -0,0 +1,99 @@
+.\" 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
+.\" Christopher G. Demetriou.
+.\"
+.\" 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_cd9660.8 8.3 (Berkeley) 3/27/94
+.Dd March 27, 1994
+.Dt MOUNT_CD9660 8
+.Os BSD 4
+.Sh NAME
+.Nm mount_cd9660
+.Nd mount an ISO-9660 filesystem
+.Sh SYNOPSIS
+.Nm mount_cd9660
+.Op Fl egr
+.Op Fl o Ar options
+.Ar special | node
+.Sh DESCRIPTION
+The
+.Nm mount_cd9660
+command attaches the ISO-9660 filesystem residing on the device
+.Pa special
+to the global filesystem namespace at the location indicated by
+.Pa node .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl e
+Enable the use of extended attributes.
+.It Fl g
+Do not strip version numbers on files.
+(By default, if there are files with different version numbers on the disk,
+only the last one will be listed.)
+In either case, files may be opened without explicitly stating a
+version number.
+.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
+Do not use any Rockridge extensions included in the filesystem.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh BUGS
+The cd9660 filesystem does not support the original "High Sierra"
+("CDROM001") format.
+.Pp
+POSIX device node mapping is currently not supported.
+.Pp
+Version numbers are not stripped if Rockridge extensions are in use.
+In this case, accessing files that don't have Rockridge names without
+version numbers gets the one with the lowest version number and not
+the one with the highest.
+.Pp
+There is no ECMA support.
+.Sh HISTORY
+The
+.Nm mount_cd9660
+utility first appeared 4.4BSD.
diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c
new file mode 100644
index 000000000000..9ef716df9d2f
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
+ * Support code is derived from software contributed to Berkeley
+ * by Atsushi Murai (amurai@spec.co.jp).
+ *
+ * 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_cd9660.c 8.4 (Berkeley) 3/27/94
+ */
+
+#ifndef lint
+static 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_cd9660.c 8.4 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#define CD9660
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct iso_args args;
+ int ch, mntflags, opts;
+ char *dev, *dir, *options;
+
+ options = NULL;
+ mntflags = opts = 0;
+ while ((ch = getopt(argc, argv, "ego:r")) != EOF)
+ switch (ch) {
+ case 'e':
+ opts |= ISOFSMNT_EXTATT;
+ break;
+ case 'g':
+ opts |= ISOFSMNT_GENS;
+ break;
+ case 'o':
+ getmntopts(options, mopts, &mntflags);
+ break;
+ case 'r':
+ opts |= ISOFSMNT_NORRIP;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+#define DEFAULT_ROOTUID -2
+ args.fspec = dev;
+ args.export.ex_root = DEFAULT_ROOTUID;
+
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ args.flags = opts;
+
+ if (mount(MOUNT_CD9660, dir, mntflags, &args) < 0)
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_cd9660 [-egrt] [-o options] special node\n");
+ exit(1);
+}
diff --git a/sbin/mount_fdesc/Makefile b/sbin/mount_fdesc/Makefile
new file mode 100644
index 000000000000..2f8f3cd7df10
--- /dev/null
+++ b/sbin/mount_fdesc/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_fdesc
+SRCS= mount_fdesc.c getmntopts.c
+MAN8= mount_fdesc.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_fdesc/mount_fdesc.8 b/sbin/mount_fdesc/mount_fdesc.8
new file mode 100644
index 000000000000..a61c952bb4a4
--- /dev/null
+++ b/sbin/mount_fdesc/mount_fdesc.8
@@ -0,0 +1,171 @@
+.\"
+.\" 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.
+.\"
+.\" 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_fdesc.8 8.2 (Berkeley) 3/27/94
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_FDESC 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_fdesc
+.Nd mount the file-descriptor file system
+.Sh SYNOPSIS
+.Nm mount_fdesc
+.Op Fl o Ar options
+.Ar fdesc
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_fdesc
+command attaches an instance of the per-process file descriptor
+namespace to the global filesystem namespace.
+The conventional mount point is
+.Pa /dev
+and the filesystem should be union mounted in order to augment,
+rather than replace, the existing entries in
+.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 contents of the mount point are
+.Pa fd ,
+.Pa stderr ,
+.Pa stdin ,
+.Pa stdout
+and
+.Pa tty .
+.Pp
+.Pa fd
+is a directory whose contents
+appear as a list of numbered files
+which correspond to the open files of the process reading the
+directory.
+The files
+.Pa /dev/fd/0
+through
+.Pa /dev/fd/#
+refer to file descriptors which can be accessed through the file
+system.
+If the file descriptor is open and the mode the file is being opened
+with is a subset of the mode of the existing descriptor, the call:
+.Bd -literal -offset indent
+fd = open("/dev/fd/0", mode);
+.Ed
+.Pp
+and the call:
+.Bd -literal -offset indent
+fd = fcntl(0, F_DUPFD, 0);
+.Ed
+.Pp
+are equivalent.
+.Pp
+The files
+.Pa /dev/stdin ,
+.Pa /dev/stdout
+and
+.Pa /dev/stderr
+appear as symlinks to the relevant entry in the
+.Pa /dev/fd
+sub-directory.
+Opening them is equivalent to the following calls:
+.Bd -literal -offset indent
+fd = fcntl(STDIN_FILENO, F_DUPFD, 0);
+fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
+fd = fcntl(STDERR_FILENO, F_DUPFD, 0);
+.Ed
+.Pp
+Flags to the
+.Xr open 2
+call other than
+.Dv O_RDONLY ,
+.Dv O_WRONLY
+and
+.Dv O_RDWR
+are ignored.
+.Pp
+The
+.Pa /dev/tty
+entry is an indirect reference to the current process's controlling terminal.
+It appears as a named pipe (FIFO) but behaves in exactly the same way as
+the real controlling terminal device.
+.Sh FILES
+.Bl -tag -width /dev/stderr -compact
+.It Pa /dev/fd/#
+.It Pa /dev/stdin
+.It Pa /dev/stdout
+.It Pa /dev/stderr
+.It Pa /dev/tty
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr tty 4 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh CAVEATS
+No
+.Pa .
+and
+.Pa ..
+entries appear when listing the contents of the
+.Pa /dev/fd
+directory.
+This makes sense in the context of this filesystem, but is inconsistent
+with usual filesystem conventions.
+However, it is still possible to refer to both
+.Pa .
+and
+.Pa ..
+in a pathname.
+.Pp
+This filesystem may not be NFS-exported.
+.Sh HISTORY
+The
+.Nm mount_fdesc
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_fdesc/mount_fdesc.c b/sbin/mount_fdesc/mount_fdesc.c
new file mode 100644
index 000000000000..55927d8efee1
--- /dev/null
+++ b/sbin/mount_fdesc/mount_fdesc.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1990, 1992 Jan-Simon Pendry
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed 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_fdesc.c 8.2 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, mntflags;
+
+ 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 (mount(MOUNT_FDESC, argv[1], mntflags, NULL))
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_fdesc [-o options] fdesc mount_point\n");
+ exit(1);
+}
diff --git a/sbin/mount_ifs/Makefile b/sbin/mount_ifs/Makefile
new file mode 100644
index 000000000000..b3efd4ef63c4
--- /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 000000000000..642c57aa361d
--- /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 000000000000..2c84ae56e3d5
--- /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 000000000000..a89f63dc19ff
--- /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 000000000000..e2c53c8ffec8
--- /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 000000000000..f0ff782f4e0e
--- /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 000000000000..babb760a07d8
--- /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 000000000000..45a4a010c1cf
--- /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_kernfs/Makefile b/sbin/mount_kernfs/Makefile
new file mode 100644
index 000000000000..6bd6d7c65e61
--- /dev/null
+++ b/sbin/mount_kernfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_kernfs
+SRCS= mount_kernfs.c getmntopts.c
+MAN8= mount_kernfs.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_kernfs/mount_kernfs.8 b/sbin/mount_kernfs/mount_kernfs.8
new file mode 100644
index 000000000000..787c174416b7
--- /dev/null
+++ b/sbin/mount_kernfs/mount_kernfs.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.
+.\"
+.\" 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_kernfs.8 8.2 (Berkeley) 3/27/94
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_KERNFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_kernfs
+.Nd mount the /kern file system
+.Sh SYNOPSIS
+.Nm mount_kernfs
+.Op Fl o Ar options
+.Ar /kern
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_kern
+command attaches an instance of the kernel parameter
+namespace to the global filesystem namespace.
+The conventional mount point is
+.Pa /kern .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The filesystem includes several regular files which can be read,
+some of which can also be written.
+The contents of the files is in a machine-independent format,
+either a string, or an integer in decimal ASCII.
+Where numbers are returned, a trailing newline character is also added.
+.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
+.Sh FILES
+.Bl -tag -width copyright -compact
+.It Pa boottime
+the time at which the system was last booted (decimal ASCII).
+.It Pa copyright
+kernel copyright message.
+.It Pa hostname
+the hostname, with a trailing newline.
+The hostname can be changed by writing to this file.
+A trailing newline will be stripped from the hostname being written.
+.It Pa hz
+the frequency of the system clock (decimal ASCII).
+.It Pa loadavg
+the 1, 5 and 15 minute load average in kernel fixed-point format.
+The final integer is the fix-point scaling factor.
+All numbers are in decimal ASCII.
+.It Pa pagesize
+the machine pagesize (decimal ASCII).
+.It Pa physmem
+the number of pages of physical memory in the machine (decimal ASCII).
+.It Pa root
+the system root directory.
+In a chroot'ed environment,
+.Nm
+can be used to create a new
+.Pa /kern
+mount point.
+.Pa /kern/root
+will then refer to the system global root, not the current process root.
+.It Pa rootdev
+the root device.
+.It Pa rrootdev
+the raw root device.
+.It Pa time
+the second and microsecond value of the system clock.
+Both numbers are in decimal ASCII.
+.It Pa version
+the kernel version string.
+The head line for
+.Pa /etc/motd
+can be generated by running:
+.Dq Ic "sed 1q /kern/version"
+.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_kernfs
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_kernfs/mount_kernfs.c b/sbin/mount_kernfs/mount_kernfs.c
new file mode 100644
index 000000000000..33a6b5e12dd6
--- /dev/null
+++ b/sbin/mount_kernfs/mount_kernfs.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1990, 1992 Jan-Simon Pendry
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed 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_kernfs.c 8.2 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, mntflags;
+
+ 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 (mount(MOUNT_KERNFS, argv[1], mntflags, NULL))
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_kernfs [-o options] /kern mount_point\n");
+ exit(1);
+}
diff --git a/sbin/mount_lfs/Makefile b/sbin/mount_lfs/Makefile
new file mode 100644
index 000000000000..d3fb12bae49d
--- /dev/null
+++ b/sbin/mount_lfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_lfs
+SRCS= mount_lfs.c getmntopts.c
+MAN8= mount_lfs.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_lfs/mount_lfs.8 b/sbin/mount_lfs/mount_lfs.8
new file mode 100644
index 000000000000..bd1bd8c19aaa
--- /dev/null
+++ b/sbin/mount_lfs/mount_lfs.8
@@ -0,0 +1,125 @@
+.\" 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.
+.\"
+.\" @(#)mount_lfs.8 8.5 (Berkeley) 3/30/94
+.\"
+.Dd "March 30, 1994"
+.Dt MOUNT_LFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_lfs
+.Nd mount a log-structured file system
+.Sh SYNOPSIS
+.Nm mount_lfs
+.Op Fl dns
+.Op Fl o Ar options
+.Ar special
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm mount_lfs
+command attaches a log-structured file system
+.Ar special
+device on to the file system tree at the point
+.Ar node .
+In addition, the
+.Xr lfs_cleanerd 8
+utility is invoked to clean the file system periodically.
+.Pp
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Run
+.Xr lfs_cleanerd 8
+in debug mode.
+.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 n
+Don't start
+.Xr lfs_cleanerd 8
+on the file system.
+.It Fl s
+Cause
+.Xr lfs_cleanerd 8
+to read data in small chunks when cleaning the file system.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr lfs_cleanerd 8 ,
+.Xr mount 8
+.sp
+.Rs
+.%A Ousterhout and Douglis
+.%D 1989
+.%T "Beating the I/O Bottleneck: A Case for Log-structured File Systems"
+.%J "Operating Systems Review"
+.%V Vol. 23
+.%N No. 1
+.%P pp. 11-27
+.%O "also available as Technical Report UCB/CSD 88/467"
+.Re
+.Rs
+.%A Rosenblum and Ousterhout
+.%D 1991
+.%T "The Design and Implementation of a Log-Structured File System"
+.%J "ACM SIGOPS Operating Systems Review"
+.%V Vol. 25
+.%N No. 5
+.Re
+.Rs
+.%A Seltzer
+.%D 1992
+.%T "File System Performance and Transaction Support"
+.%B "PhD Thesis, University of California, Berkeley"
+.%O "also available as Technical Report UCB/ERL M92"
+.Re
+.Rs
+.%A Seltzer, Bostic, McKusick and Staelin
+.%D 1993
+.%T "An Implementation of a Log-Structured File System for UNIX"
+.%J "Proc. of the Winter 1993 USENIX Conf."
+.%P pp. 315-331
+.Re
+.Sh HISTORY
+The
+.Nm mount_lfs
+function first appeared in 4.4BSD.
diff --git a/sbin/mount_lfs/mount_lfs.c b/sbin/mount_lfs/mount_lfs.c
new file mode 100644
index 000000000000..374c9304cca6
--- /dev/null
+++ b/sbin/mount_lfs/mount_lfs.c
@@ -0,0 +1,147 @@
+/*-
+ * 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_lfs.c 8.3 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+void usage __P((void));
+void invoke_cleaner __P((char *));
+
+int short_rds, cleaner_debug;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct ufs_args args;
+ int ch, mntflags, noclean;
+ char *fs_name, *options;
+
+ options = NULL;
+ mntflags = noclean = 0;
+ while ((ch = getopt(argc, argv, "dno:s")) != EOF)
+ switch (ch) {
+ case 'd':
+ cleaner_debug = 1;
+ break;
+ case 'n':
+ noclean = 1;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case 's':
+ short_rds = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ 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_LFS, fs_name, mntflags, &args))
+ err(1, NULL);
+
+ if (!noclean)
+ invoke_cleaner(fs_name);
+ /* NOTREACHED */
+
+ exit(0);
+}
+
+void
+invoke_cleaner(name)
+ char *name;
+{
+ char *args[6], **ap = args;
+
+ /* Build the argument list. */
+ *ap++ = _PATH_LFS_CLEANERD;
+ if (short_rds)
+ *ap++ = "-s";
+ if (cleaner_debug)
+ *ap++ = "-d";
+ *ap++ = name;
+ *ap = NULL;
+
+ execv(args[0], args);
+ err(1, "exec %s", _PATH_LFS_CLEANERD);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_lfs [-dns] [-o options] special node\n");
+ exit(1);
+}
diff --git a/sbin/mount_lfs/pathnames.h b/sbin/mount_lfs/pathnames.h
new file mode 100644
index 000000000000..fafbf714db52
--- /dev/null
+++ b/sbin/mount_lfs/pathnames.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/8/93
+ */
+
+#define _PATH_LFS_CLEANERD "/usr/libexec/lfs_cleanerd"
diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile
new file mode 100644
index 000000000000..f33b461560cf
--- /dev/null
+++ b/sbin/mount_nfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_nfs
+SRCS= mount_nfs.c getmntopts.c
+MAN8= mount_nfs.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -DNFS -I${MOUNT}
+.PATH: ${MOUNT}
+
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8
new file mode 100644
index 000000000000..decac6efefed
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.8
@@ -0,0 +1,222 @@
+.\" Copyright (c) 1992, 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.
+.\"
+.\" @(#)mount_nfs.8 8.2 (Berkeley) 3/27/94
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_NFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_nfs
+.Nd mount nfs file systems
+.Sh SYNOPSIS
+.Nm mount_nfs
+.Op Fl KMPTbcdiklqs
+.Op Fl D Ar deadthresh
+.Op Fl L Ar leaseterm
+.Op Fl R Ar retrycnt
+.Op Fl a Ar maxreadahead
+.Op Fl g Ar maxgroups
+.Op Fl m Ar realm
+.Op Fl o Ar options
+.Op Fl r Ar readsize
+.Op Fl t Ar timeout
+.Op Fl w Ar writesize
+.Op Fl x Ar retrans
+.Ar rhost:path node
+.Sh DESCRIPTION
+The
+.Nm mount_nfs
+command
+calls the
+.Xr mount 2
+system call to prepare and graft a remote nfs file system (rhost:path)
+on to the file system tree at the point
+.Ar node.
+This command is normally executed by
+.Xr mount 8 .
+It implements the mount protocol as described in RFC 1094, Appendix A.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl D
+Used with NQNFS to set the
+.Dq "dead server threshold"
+to the specified number of round trip timeout intervals.
+After a
+.Dq "dead server threshold"
+of retransmit timeouts,
+cached data for the unresponsive server is assumed to still be valid.
+Values may be set in the range of 1 - 9, with 9 referring to an
+.Dq "infinite dead threshold"
+(i.e. never assume cached data still valid).
+This option is not generally recommended and is really an experimental
+feature.
+.It Fl K
+Pass Kerberos authenticators to the server for client-to-server
+user-credential mapping.
+This may only be used over TCP mounts between 4.4BSD clients and servers.
+.It Fl L
+Used with NQNFS to set the lease term to the specified number of seconds.
+Only use this argument for mounts with a large round trip delay.
+Values are normally in the 10-30 second range.
+.It Fl M
+Assume that other clients are not writing a file concurrently with this client.
+This implements a slightly less strict consistency criteria than 4.3BSD-Reno
+did, that is more in line with most commercial client implementations.
+This is recommended for servers that do not support leasing.
+.It Fl P
+Use a reserved socket port number.
+This is useful for mounting servers that require clients to use a
+reserved port number.
+.It Fl R
+Set the retry count for doing the mount to the specified value.
+.It Fl T
+Use TCP transport instead of UDP.
+This is recommended for servers that are not on the same LAN cable as
+the client.
+(NB: This is NOT supported by most non-BSD servers.)
+.It Fl a
+Set the read-ahead count to the specified value.
+This may be in the range of 0 - 4, and determines how many blocks
+will be read ahead when a large file is being read sequentially.
+This is recommended for mounts with a large bandwidth * delay product.
+.It Fl b
+If an initial attempt to contact the server fails, fork off a child to keep
+trying the mount in the background.
+Useful for
+.Xr fstab 5 ,
+where the filesystem mount is not critical to multiuser operation.
+.It Fl c
+For UDP mount points, do not do a
+.Xr connect 2 .
+This must be used for servers that do not reply to requests from the
+standard port number.
+.It Fl d
+Do not estimate retransmit timeout dynamically.
+This may be useful for UDP mounts that exhibit high retry rates.
+.It Fl g
+Set the maximum size of the group list for the credentials to the
+specified value.
+This should be used for mounts on old servers that cannot handle a
+group list size of 16, as specified in RFC 1057.
+Try 8, if users in a lot of groups cannot get response from the mount
+point.
+.It Fl i
+Make the mount interruptible, which implies that file system calls that
+are delayed due to an unresponsive server will fail with EINTR when a
+termination signal is posted for the process.
+.It Fl k
+Used with NQNFS to specify
+.Dq get a lease
+for the file name being looked up.
+This is recommended unless the server is complaining about excessive
+lease load.
+.It Fl l
+Used with NQNFS to specify that the \fBReaddir_and_Lookup\fR RPC should
+be used.
+This option reduces RPC traffic for cases such as
+.Dq "ls -l" ,
+but increases the lease load on the server.
+This is recommended unless the server is complaining about excessive
+lease load.
+.It Fl m
+Set the Kerberos realm to the string argument.
+Used with the
+.Fl K
+option for mounts to other realms.
+.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 q
+Use the leasing extensions to the protocol to maintain cache consistency.
+This protocol, referred to as Not Quite Nfs (NQNFS),
+is only supported by 4.4BSD servers.
+.It Fl r
+Set the read data size to the specified value.
+It should be a power of 2 greater than or equal to 1024.
+This should be used for UDP mounts when the
+.Dq "fragments dropped due to timeout"
+value is getting large while actively using a mount point.
+(Use
+.Xr netstat 1
+with the
+.Fl s
+option to see what the
+.Dq "fragments dropped due to timeout"
+value is.)
+See the
+.Fl w
+option as well.
+.It Fl s
+A soft mount, which implies that file system calls will fail
+after \fBRetry\fR round trip timeout intervals.
+.It Fl t
+Set the initial retransmit timeout to the specified value.
+May be useful for fine tuning UDP mounts over internetworks
+with high packet loss rates or an overloaded server.
+Try increasing the interval if
+.Xr nfsstat 1
+shows high retransmit rates while the file system is active or reducing the
+value if there is a low retransmit rate but long response delay observed.
+.It Fl w
+Set the write data size to the specified value.
+Ditto the comments w.r.t. the
+.Fl r
+option, but using the
+.Dq "fragments dropped due to timeout"
+value on the server instead of the client.
+Note that both the
+.Fl r
+and
+.Fl w
+options should only be used as a last ditch effort at improving performance
+when mounting servers that do not support TCP mounts.
+.It Fl x
+Set the retransmit timeout count for soft mounts to the specified value.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh BUGS
+Due to the way that Sun RPC is implemented on top of UDP (unreliable datagram)
+transport, tuning such mounts is really a black art that can only be expected
+to have limited success.
+For clients mounting servers that are not on the same
+LAN cable or that tend to be overloaded,
+TCP transport is strongly recommended,
+but unfortunately this is restricted to mostly 4.4BSD servers.
diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c
new file mode 100644
index 000000000000..cbb3ddbc583e
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mount_nfs.c 8.3 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#define KERNEL
+#include <nfs/nfs.h>
+#undef KERNEL
+#include <nfs/nqnfs.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+struct nfs_args nfsdefargs = {
+ (struct sockaddr *)0,
+ sizeof (struct sockaddr_in),
+ SOCK_DGRAM,
+ 0,
+ (nfsv2fh_t *)0,
+ 0,
+ NFS_WSIZE,
+ NFS_RSIZE,
+ NFS_TIMEO,
+ NFS_RETRANS,
+ NFS_MAXGRPS,
+ NFS_DEFRAHEAD,
+ NQ_DEFLEASE,
+ NQ_DEADTHRESH,
+ (char *)0,
+};
+
+struct nfhret {
+ u_long stat;
+ nfsv2fh_t nfh;
+};
+#define DEF_RETRY 10000
+#define BGRND 1
+#define ISBGRND 2
+int retrycnt = DEF_RETRY;
+int opflags = 0;
+
+#ifdef KERBEROS
+char inst[INST_SZ];
+char realm[REALM_SZ];
+KTEXT_ST kt;
+#endif
+
+int getnfsargs __P((char *, struct nfs_args *));
+#ifdef ISO
+struct iso_addr *iso_addr __P((const char *));
+#endif
+void set_rpc_maxgrouplist __P((int));
+__dead void usage __P((void));
+int xdr_dir __P((XDR *, char *));
+int xdr_fh __P((XDR *, struct nfhret *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c;
+ register struct nfs_args *nfsargsp;
+ struct nfs_args nfsargs;
+ struct nfsd_cargs ncd;
+ int mntflags, i, nfssvc_flag, num;
+ char *name, *p, *spec;
+ int error = 0;
+#ifdef KERBEROS
+ uid_t last_ruid;
+#endif
+
+#ifdef KERBEROS
+ last_ruid = -1;
+ (void)strcpy(realm, KRB_REALM);
+#endif
+ retrycnt = DEF_RETRY;
+
+ mntflags = 0;
+ nfsargs = nfsdefargs;
+ nfsargsp = &nfsargs;
+ while ((c = getopt(argc, argv,
+ "a:bcdD:g:iKklL:Mm:o:PpqR:r:sTt:w:x:")) != EOF)
+ switch (c) {
+ case 'a':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 0)
+ errx(1, "illegal -a value -- %s", optarg);
+ nfsargsp->readahead = num;
+ nfsargsp->flags |= NFSMNT_READAHEAD;
+ break;
+ case 'b':
+ opflags |= BGRND;
+ break;
+ case 'c':
+ nfsargsp->flags |= NFSMNT_NOCONN;
+ break;
+ case 'D':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -D value -- %s", optarg);
+ nfsargsp->deadthresh = num;
+ nfsargsp->flags |= NFSMNT_DEADTHRESH;
+ break;
+ case 'd':
+ nfsargsp->flags |= NFSMNT_DUMBTIMR;
+ break;
+ case 'g':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -g value -- %s", optarg);
+ set_rpc_maxgrouplist(num);
+ nfsargsp->maxgrouplist = num;
+ nfsargsp->flags |= NFSMNT_MAXGRPS;
+ break;
+ case 'i':
+ nfsargsp->flags |= NFSMNT_INT;
+ break;
+#ifdef KERBEROS
+ case 'K':
+ nfsargsp->flags |= NFSMNT_KERB;
+ break;
+#endif
+ case 'k':
+ nfsargsp->flags |= NFSMNT_NQLOOKLEASE;
+ break;
+ case 'L':
+ num = strtol(optarg, &p, 10);
+ if (*p || num < 2)
+ errx(1, "illegal -L value -- %s", optarg);
+ nfsargsp->leaseterm = num;
+ nfsargsp->flags |= NFSMNT_LEASETERM;
+ break;
+ case 'l':
+ nfsargsp->flags |= NFSMNT_RDIRALOOK;
+ break;
+ case 'M':
+ nfsargsp->flags |= NFSMNT_MYWRITE;
+ break;
+#ifdef KERBEROS
+ case 'm':
+ (void)strncpy(realm, optarg, REALM_SZ - 1);
+ realm[REALM_SZ - 1] = '\0';
+ break;
+#endif
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags);
+ break;
+ case 'P':
+ nfsargsp->flags |= NFSMNT_RESVPORT;
+ break;
+#ifdef ISO
+ case 'p':
+ nfsargsp->sotype = SOCK_SEQPACKET;
+ break;
+#endif
+ case 'q':
+ nfsargsp->flags |= NFSMNT_NQNFS;
+ break;
+ case 'R':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -R value -- %s", optarg);
+ retrycnt = num;
+ break;
+ case 'r':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -r value -- %s", optarg);
+ nfsargsp->rsize = num;
+ nfsargsp->flags |= NFSMNT_RSIZE;
+ break;
+ case 's':
+ nfsargsp->flags |= NFSMNT_SOFT;
+ break;
+ case 'T':
+ nfsargsp->sotype = SOCK_STREAM;
+ break;
+ case 't':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -t value -- %s", optarg);
+ nfsargsp->timeo = num;
+ nfsargsp->flags |= NFSMNT_TIMEO;
+ break;
+ case 'w':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -w value -- %s", optarg);
+ nfsargsp->wsize = num;
+ nfsargsp->flags |= NFSMNT_WSIZE;
+ break;
+ case 'x':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -x value -- %s", optarg);
+ nfsargsp->retrans = num;
+ nfsargsp->flags |= NFSMNT_RETRANS;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ error = 1;
+
+ spec = *argv++;
+ name = *argv;
+
+ if (!getnfsargs(spec, nfsargsp))
+ exit(1);
+ if (mount(MOUNT_NFS, name, mntflags, nfsargsp))
+ err(1, "%s", name);
+ if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) {
+ if ((opflags & ISBGRND) == 0) {
+ if (i = fork()) {
+ if (i == -1)
+ err(1, "nqnfs 1");
+ exit(0);
+ }
+ (void) setsid();
+ (void) close(STDIN_FILENO);
+ (void) close(STDOUT_FILENO);
+ (void) close(STDERR_FILENO);
+ (void) chdir("/");
+ }
+ openlog("mount_nfs:", LOG_PID, LOG_DAEMON);
+ nfssvc_flag = NFSSVC_MNTD;
+ ncd.ncd_dirp = name;
+ while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc err %m");
+ continue;
+ }
+ nfssvc_flag =
+ NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL;
+#ifdef KERBEROS
+ /*
+ * Set up as ncd_authuid for the kerberos call.
+ * Must set ruid to ncd_authuid and reset the
+ * ticket name iff ncd_authuid is not the same
+ * as last time, so that the right ticket file
+ * is found.
+ */
+ if (ncd.ncd_authuid != last_ruid) {
+ krb_set_tkt_string("");
+ last_ruid = ncd.ncd_authuid;
+ }
+ setreuid(ncd.ncd_authuid, 0);
+ if (krb_mk_req(&kt, "rcmd", inst, realm, 0) ==
+ KSUCCESS &&
+ kt.length <= (RPCAUTH_MAXSIZ - 2 * NFSX_UNSIGNED)) {
+ ncd.ncd_authtype = RPCAUTH_NQNFS;
+ ncd.ncd_authlen = kt.length;
+ ncd.ncd_authstr = (char *)kt.dat;
+ nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH;
+ }
+ setreuid(0, 0);
+#endif /* KERBEROS */
+ }
+ }
+ exit(0);
+}
+
+int
+getnfsargs(spec, nfsargsp)
+ char *spec;
+ struct nfs_args *nfsargsp;
+{
+ register CLIENT *clp;
+ struct hostent *hp;
+ static struct sockaddr_in saddr;
+#ifdef ISO
+ static struct sockaddr_iso isoaddr;
+ struct iso_addr *isop;
+ int isoflag = 0;
+#endif
+ struct timeval pertry, try;
+ enum clnt_stat clnt_stat;
+ int so = RPC_ANYSOCK, i;
+ char *hostp, *delimp;
+#ifdef KERBEROS
+ char *cp;
+#endif
+ u_short tport;
+ static struct nfhret nfhret;
+ static char nam[MNAMELEN + 1];
+
+ strncpy(nam, spec, MNAMELEN);
+ nam[MNAMELEN] = '\0';
+ if ((delimp = strchr(spec, '@')) != NULL) {
+ hostp = delimp + 1;
+ } else if ((delimp = strchr(spec, ':')) != NULL) {
+ hostp = spec;
+ spec = delimp + 1;
+ } else {
+ warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
+ return (0);
+ }
+ *delimp = '\0';
+ /*
+ * DUMB!! Until the mount protocol works on iso transport, we must
+ * supply both an iso and an inet address for the host.
+ */
+#ifdef ISO
+ if (!strncmp(hostp, "iso=", 4)) {
+ u_short isoport;
+
+ hostp += 4;
+ isoflag++;
+ if ((delimp = strchr(hostp, '+')) == NULL) {
+ warnx("no iso+inet address");
+ return (0);
+ }
+ *delimp = '\0';
+ if ((isop = iso_addr(hostp)) == NULL) {
+ warnx("bad ISO address");
+ return (0);
+ }
+ bzero((caddr_t)&isoaddr, sizeof (isoaddr));
+ bcopy((caddr_t)isop, (caddr_t)&isoaddr.siso_addr,
+ sizeof (struct iso_addr));
+ isoaddr.siso_len = sizeof (isoaddr);
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ isoport = htons(NFS_PORT);
+ bcopy((caddr_t)&isoport, TSEL(&isoaddr), isoaddr.siso_tlen);
+ hostp = delimp + 1;
+ }
+#endif /* ISO */
+
+ /*
+ * Handle an internet host address and reverse resolve it if
+ * doing Kerberos.
+ */
+ if (isdigit(*hostp)) {
+ if ((saddr.sin_addr.s_addr = inet_addr(hostp)) == -1) {
+ warnx("bad net address %s", hostp);
+ return (0);
+ }
+ if ((nfsargsp->flags & NFSMNT_KERB) &&
+ (hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr,
+ sizeof (u_long), AF_INET)) == (struct hostent *)0) {
+ warnx("can't reverse resolve net address");
+ return (0);
+ }
+ } else if ((hp = gethostbyname(hostp)) == NULL) {
+ warnx("can't get net id for host");
+ return (0);
+ }
+#ifdef KERBEROS
+ if (nfsargsp->flags & NFSMNT_KERB) {
+ strncpy(inst, hp->h_name, INST_SZ);
+ inst[INST_SZ - 1] = '\0';
+ if (cp = strchr(inst, '.'))
+ *cp = '\0';
+ }
+#endif /* KERBEROS */
+
+ bcopy(hp->h_addr, (caddr_t)&saddr.sin_addr, hp->h_length);
+ nfhret.stat = EACCES; /* Mark not yet successful */
+ while (retrycnt > 0) {
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(PMAPPORT);
+ if ((tport = pmap_getport(&saddr, RPCPROG_NFS,
+ NFS_VER2, IPPROTO_UDP)) == 0) {
+ if ((opflags & ISBGRND) == 0)
+ clnt_pcreateerror("NFS Portmap");
+ } else {
+ saddr.sin_port = 0;
+ pertry.tv_sec = 10;
+ pertry.tv_usec = 0;
+ if ((clp = clntudp_create(&saddr, RPCPROG_MNT,
+ RPCMNT_VER1, pertry, &so)) == NULL) {
+ if ((opflags & ISBGRND) == 0)
+ clnt_pcreateerror("Cannot MNT PRC");
+ } else {
+ clp->cl_auth = authunix_create_default();
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
+ xdr_dir, spec, xdr_fh, &nfhret, try);
+ if (clnt_stat != RPC_SUCCESS) {
+ if ((opflags & ISBGRND) == 0)
+ warnx("%s", clnt_sperror(clp,
+ "bad MNT RPC"));
+ } else {
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ retrycnt = 0;
+ }
+ }
+ }
+ if (--retrycnt > 0) {
+ if (opflags & BGRND) {
+ opflags &= ~BGRND;
+ if (i = fork()) {
+ if (i == -1)
+ err(1, "nqnfs 2");
+ exit(0);
+ }
+ (void) setsid();
+ (void) close(STDIN_FILENO);
+ (void) close(STDOUT_FILENO);
+ (void) close(STDERR_FILENO);
+ (void) chdir("/");
+ opflags |= ISBGRND;
+ }
+ sleep(60);
+ }
+ }
+ if (nfhret.stat) {
+ if (opflags & ISBGRND)
+ exit(1);
+ warn("can't access %s", spec);
+ return (0);
+ }
+ saddr.sin_port = htons(tport);
+#ifdef ISO
+ if (isoflag) {
+ nfsargsp->addr = (struct sockaddr *) &isoaddr;
+ nfsargsp->addrlen = sizeof (isoaddr);
+ } else
+#endif /* ISO */
+ {
+ nfsargsp->addr = (struct sockaddr *) &saddr;
+ nfsargsp->addrlen = sizeof (saddr);
+ }
+ nfsargsp->fh = &nfhret.nfh;
+ nfsargsp->hostname = nam;
+ return (1);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+int
+xdr_fh(xdrsp, np)
+ XDR *xdrsp;
+ struct nfhret *np;
+{
+ if (!xdr_u_long(xdrsp, &(np->stat)))
+ return (0);
+ if (np->stat)
+ return (1);
+ return (xdr_opaque(xdrsp, (caddr_t)&(np->nfh), NFSX_FH));
+}
+
+__dead void
+usage()
+{
+ (void)fprintf(stderr, "usage: mount_nfs %s\n%s\n%s\n%s\n",
+"[-bcdiKklMPqsT] [-a maxreadahead] [-D deadthresh]",
+"\t[-g maxgroups] [-L leaseterm] [-m realm] [-o options] [-R retrycnt]",
+"\t[-r readsize] [-t timeout] [-w writesize] [-x retrans]",
+"\trhost:path node");
+ exit(1);
+}
diff --git a/sbin/mount_null/Makefile b/sbin/mount_null/Makefile
new file mode 100644
index 000000000000..aae87e11b2db
--- /dev/null
+++ b/sbin/mount_null/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_null/mount_null.8 b/sbin/mount_null/mount_null.8
new file mode 100644
index 000000000000..6c8106fd1cfa
--- /dev/null
+++ b/sbin/mount_null/mount_null.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_null/mount_null.c b/sbin/mount_null/mount_null.c
new file mode 100644
index 000000000000..e8c26df1b8f9
--- /dev/null
+++ b/sbin/mount_null/mount_null.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_nullfs/Makefile b/sbin/mount_nullfs/Makefile
new file mode 100644
index 000000000000..aae87e11b2db
--- /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 000000000000..6c8106fd1cfa
--- /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 000000000000..e8c26df1b8f9
--- /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_portal/Makefile b/sbin/mount_portal/Makefile
new file mode 100644
index 000000000000..1f17d245ed20
--- /dev/null
+++ b/sbin/mount_portal/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_portal/activate.c b/sbin/mount_portal/activate.c
new file mode 100644
index 000000000000..33617988f7df
--- /dev/null
+++ b/sbin/mount_portal/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_portal/conf.c b/sbin/mount_portal/conf.c
new file mode 100644
index 000000000000..18833b608093
--- /dev/null
+++ b/sbin/mount_portal/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_portal/mount_portal.8 b/sbin/mount_portal/mount_portal.8
new file mode 100644
index 000000000000..0df65316c2ad
--- /dev/null
+++ b/sbin/mount_portal/mount_portal.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_portal/mount_portal.c b/sbin/mount_portal/mount_portal.c
new file mode 100644
index 000000000000..ae5345d3049f
--- /dev/null
+++ b/sbin/mount_portal/mount_portal.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_portal/pathnames.h b/sbin/mount_portal/pathnames.h
new file mode 100644
index 000000000000..25321145d990
--- /dev/null
+++ b/sbin/mount_portal/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_portal/portal.conf b/sbin/mount_portal/portal.conf
new file mode 100644
index 000000000000..5b5a773eaa3c
--- /dev/null
+++ b/sbin/mount_portal/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_portal/portald.h b/sbin/mount_portal/portald.h
new file mode 100644
index 000000000000..fbe111b1a7ad
--- /dev/null
+++ b/sbin/mount_portal/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_portal/pt_conf.c b/sbin/mount_portal/pt_conf.c
new file mode 100644
index 000000000000..d1eba94ea3fe
--- /dev/null
+++ b/sbin/mount_portal/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_portal/pt_exec.c b/sbin/mount_portal/pt_exec.c
new file mode 100644
index 000000000000..06e3382da85b
--- /dev/null
+++ b/sbin/mount_portal/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_portal/pt_file.c b/sbin/mount_portal/pt_file.c
new file mode 100644
index 000000000000..ace35c04fe59
--- /dev/null
+++ b/sbin/mount_portal/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_portal/pt_tcp.c b/sbin/mount_portal/pt_tcp.c
new file mode 100644
index 000000000000..18a53ce26aba
--- /dev/null
+++ b/sbin/mount_portal/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_portalfs/Makefile b/sbin/mount_portalfs/Makefile
new file mode 100644
index 000000000000..1f17d245ed20
--- /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 000000000000..33617988f7df
--- /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 000000000000..18833b608093
--- /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 000000000000..0df65316c2ad
--- /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 000000000000..ae5345d3049f
--- /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 000000000000..25321145d990
--- /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 000000000000..5b5a773eaa3c
--- /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 000000000000..fbe111b1a7ad
--- /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 000000000000..d1eba94ea3fe
--- /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 000000000000..06e3382da85b
--- /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 000000000000..ace35c04fe59
--- /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 000000000000..18a53ce26aba
--- /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_procfs/Makefile b/sbin/mount_procfs/Makefile
new file mode 100644
index 000000000000..ca0c872520b0
--- /dev/null
+++ b/sbin/mount_procfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.4 (Berkeley) 3/27/94
+
+PROG= mount_procfs
+SRCS= mount_procfs.c getmntopts.c
+MAN8= mount_procfs.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_procfs/mount_procfs.8 b/sbin/mount_procfs/mount_procfs.8
new file mode 100644
index 000000000000..3aa20e0a4c2e
--- /dev/null
+++ b/sbin/mount_procfs/mount_procfs.8
@@ -0,0 +1,253 @@
+.\"
+.\" 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.
+.\"
+.\" @(#)mount_procfs.8 8.2 (Berkeley) 3/27/94
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_PROCFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_procfs
+.Nd mount the process file system
+.Sh SYNOPSIS
+.Nm mount_procfs
+.Op Fl o Ar options
+.Pa /proc
+.Pa mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_procfs
+command attaches an instance of the process
+namespace to the global filesystem namespace.
+The conventional mount point is
+.Pa /proc .
+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 root of the process filesystem
+contains an entry for each active process.
+These processes are visible as a directory whose
+name is the process' pid.
+In addition, the special entry
+.Pa curproc
+references the current process.
+.Pp
+Each directory contains several files.
+.Bl -tag -width status
+.It Pa ctl
+a writeonly file which supports a variety
+of control operations.
+Control commands are written as strings to the
+.Pa ctl
+file.
+The control commands are:
+.Bl -tag -width detach -compact
+.It attach
+stops the target process and arranges for the sending
+process to become the debug control process.
+.It detach
+continue execution of the target process and
+remove it from control by the debug process (which
+need not be the sending process).
+.It run
+continue running the target process until
+a signal is delivered, a breakpoint is hit, or the
+target process exits.
+.It step
+single step the target process, with no signal delivery.
+.It wait
+wait for the target process to come to a steady
+state ready for debugging.
+The target process must be in this state before
+any of the other commands are allowed.
+.El
+.Pp
+The string can also be the name of a signal, lower case
+and without the
+.Dv SIG
+prefix,
+in which case that signal is delivered to the process
+(see
+.Xr sigaction 2 ).
+.It Pa file
+A reference to the vnode from which the process text was read.
+This can be used to gain access to the process' symbol table,
+or to start another copy of the process.
+.It Pa mem
+The complete virtual memory image of the process.
+Only those address which exist in the process can be accessed.
+Reads and writes to this file modify the process.
+Writes to the text segment remain private to the process.
+.It Pa note
+Not implemented.
+.It Pa notepg
+Not implemented.
+.It Pa regs
+Allows read and write access to the process' register set.
+This file contains a binary data structure
+.Dv "struct regs"
+defined in
+.Pa <machine/reg.h> .
+.Pa regs
+can only be written when the process is stopped.
+.It Pa fpregs
+The floating point registers as defined by
+.Dv "struct fpregs"
+in
+.Pa <machine/reg.h> .
+.Pa fpregs
+is only implemented on machines which have distinct general
+purpose and floating point register sets.
+.It Pa status
+The process status.
+This file is readonly and returns a single line containing
+multiple space-separated fields as follows:
+.Pp
+.Bl -bullet -compact
+.It
+command name
+.It
+process id
+.It
+parent process id
+.It
+process group id
+.It
+session id
+.It
+.Ar major,minor
+of the controlling terminal, or
+.Dv -1,-1
+if there is no controlling terminal.
+.It
+a list of process flags:
+.Dv ctty
+if there is a controlling terminal,
+.Dv sldr
+if the process is a session leader,
+.Dv noflags
+if neither of the other two flags are set.
+.It
+the process start time in seconds and microseconds,
+comma separated.
+.It
+the user time in seconds and microseconds,
+comma separated.
+.It
+the system time in seconds and microseconds,
+comma separated.
+.It
+the wait channel message
+.It
+the process credentials consisting of
+the effective user id
+and the list of groups (whose first member
+is the effective group id)
+all comma separated.
+.El
+.El
+.Pp
+In a normal debugging environment,
+where the target is fork/exec'd by the debugger,
+the debugger should fork and the child should stop
+itself (with a self-inflicted
+.Dv SIGSTOP
+for example).
+The parent should issue a
+.Dv wait
+and then an
+.Dv attach
+command via the appropriate
+.Pa ctl
+file.
+The child process will receive a
+.Dv SIGTRAP
+immediately after the call to exec (see
+.Xr execve 2 ).
+.Sh FILES
+.Bl -tag -width /proc/curproc -compact
+.It Pa /proc/#
+.It Pa /proc/curproc
+.It Pa /proc/curproc/ctl
+.It Pa /proc/curproc/file
+.It Pa /proc/curproc/mem
+.It Pa /proc/curproc/note
+.It Pa /proc/curproc/notepg
+.It Pa /proc/curproc/regs
+.It Pa /proc/curproc/fpregs
+.It Pa /proc/curproc/status
+.El
+.Sh SEE ALSO
+.Xr sigaction 2 ,
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Sh CAVEATS
+No
+.Pa .
+and
+.Pa ..
+entries appear when listing the contents of the
+.Pa /proc
+directory.
+This makes sense in the context of this filesystem, but is inconsistent
+with usual filesystem conventions.
+However, it is still possible to refer to both
+.Pa .
+and
+.Pa ..
+in a pathname.
+.Pp
+This filesystem may not be NFS-exported
+since most of the functionality of
+.Dv procfs
+requires that state be maintained.
+.Sh HISTORY
+The
+.Nm mount_procfs
+utility first appeared in 4.4BSD.
diff --git a/sbin/mount_procfs/mount_procfs.c b/sbin/mount_procfs/mount_procfs.c
new file mode 100644
index 000000000000..79016c8377c1
--- /dev/null
+++ b/sbin/mount_procfs/mount_procfs.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1990, 1992, 1993 Jan-Simon Pendry
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed 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_procfs.c 8.3 (Berkeley) 3/27/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, mntflags;
+
+ 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 (mount(MOUNT_PROCFS, argv[1], mntflags, NULL))
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_procfs [-o options] /proc mount_point\n");
+ exit(1);
+}
diff --git a/sbin/mount_umap/Makefile b/sbin/mount_umap/Makefile
new file mode 100644
index 000000000000..af1bf2f0457d
--- /dev/null
+++ b/sbin/mount_umap/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_umap/mount_umap.8 b/sbin/mount_umap/mount_umap.8
new file mode 100644
index 000000000000..e90ce265b5c6
--- /dev/null
+++ b/sbin/mount_umap/mount_umap.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_umap/mount_umap.c b/sbin/mount_umap/mount_umap.c
new file mode 100644
index 000000000000..a069fe5f02b9
--- /dev/null
+++ b/sbin/mount_umap/mount_umap.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_umap/sample.group.mapfile b/sbin/mount_umap/sample.group.mapfile
new file mode 100644
index 000000000000..69d3d77c6363
--- /dev/null
+++ b/sbin/mount_umap/sample.group.mapfile
@@ -0,0 +1,2 @@
+1
+1200 1200
diff --git a/sbin/mount_umap/sample.user.mapfile b/sbin/mount_umap/sample.user.mapfile
new file mode 100644
index 000000000000..03c59e9e7569
--- /dev/null
+++ b/sbin/mount_umap/sample.user.mapfile
@@ -0,0 +1,3 @@
+2
+5217 5217
+3 3
diff --git a/sbin/mount_umap/umap_manual b/sbin/mount_umap/umap_manual
new file mode 100644
index 000000000000..059939f0f51a
--- /dev/null
+++ b/sbin/mount_umap/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_umapfs/Makefile b/sbin/mount_umapfs/Makefile
new file mode 100644
index 000000000000..af1bf2f0457d
--- /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 000000000000..e90ce265b5c6
--- /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 000000000000..a069fe5f02b9
--- /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/sample.group.mapfile b/sbin/mount_umapfs/sample.group.mapfile
new file mode 100644
index 000000000000..69d3d77c6363
--- /dev/null
+++ b/sbin/mount_umapfs/sample.group.mapfile
@@ -0,0 +1,2 @@
+1
+1200 1200
diff --git a/sbin/mount_umapfs/sample.user.mapfile b/sbin/mount_umapfs/sample.user.mapfile
new file mode 100644
index 000000000000..03c59e9e7569
--- /dev/null
+++ b/sbin/mount_umapfs/sample.user.mapfile
@@ -0,0 +1,3 @@
+2
+5217 5217
+3 3
diff --git a/sbin/mount_umapfs/umap_manual b/sbin/mount_umapfs/umap_manual
new file mode 100644
index 000000000000..059939f0f51a
--- /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_union/Makefile b/sbin/mount_union/Makefile
new file mode 100644
index 000000000000..a6b33af5ebf8
--- /dev/null
+++ b/sbin/mount_union/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_union/mount_union.8 b/sbin/mount_union/mount_union.8
new file mode 100644
index 000000000000..65a649722579
--- /dev/null
+++ b/sbin/mount_union/mount_union.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_union/mount_union.c b/sbin/mount_union/mount_union.c
new file mode 100644
index 000000000000..90d075b1dec5
--- /dev/null
+++ b/sbin/mount_union/mount_union.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/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile
new file mode 100644
index 000000000000..a6b33af5ebf8
--- /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 000000000000..65a649722579
--- /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 000000000000..90d075b1dec5
--- /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/mountd/Makefile b/sbin/mountd/Makefile
new file mode 100644
index 000000000000..36e2a0572406
--- /dev/null
+++ b/sbin/mountd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.3 (Berkeley) 1/25/94
+
+PROG= mountd
+CFLAGS+=-DNFS -DMFS -DCD9660
+MAN5= exports.0 netgroup.0
+MAN8= mountd.0
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/sbin/mountd/exports.5 b/sbin/mountd/exports.5
new file mode 100644
index 000000000000..d32527f71766
--- /dev/null
+++ b/sbin/mountd/exports.5
@@ -0,0 +1,250 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)exports.5 8.2 (Berkeley) 1/28/94
+.\"
+.Dd January 28, 1994
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd define remote mount points for
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm exports
+.Sh DESCRIPTION
+The
+.Nm exports
+file specifies remote mount points for the
+.Tn NFS
+mount protocol per the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification \\*(tNRFC\\*(sP 1094, Appendix A" .
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the mount point(s) and export flags within one local server
+filesystem for one or more hosts.
+A host may be specified only once for each local filesystem on the
+server and there may be only one default entry for each server
+filesystem that applies to all other hosts.
+The latter exports the filesystem to the ``world'' and should
+be used only when the filesystem contains public information.
+.Pp
+In a mount entry,
+the first field(s) specify the directory path(s) within a server filesystem
+that can be mounted on by the corresponding client(s).
+There are two forms of this specification.
+The first is to list all mount points as absolute
+directory paths separated by whitespace.
+The second is to specify the pathname of the root of the filesystem
+followed by the
+.Fl alldirs
+flag;
+this form allows the host(s) to mount any directory within the filesystem.
+The pathnames must not have any symbolic links in them and should not have
+any "." or ".." components.
+Mount points for a filesystem may appear on multiple lines each with
+different sets of hosts and export options.
+.Pp
+The second component of a line specifies how the filesystem is to be
+exported to the host set.
+The option flags specify whether the filesystem
+is exported read-only or read-write and how the client uid is mapped to
+user credentials on the server.
+.Pp
+Export options are specified as follows:
+.Pp
+.Sm off
+.Fl maproot No = Sy user
+.Sm on
+The credential of the specified user is used for remote access by root.
+The credential includes all the groups to which the user is a member
+on the local machine (see
+.Xr id 1 ).
+The user may be specified by name or number.
+.Pp
+.Sm off
+.Fl maproot No = Sy user:group1:group2:...
+.Sm on
+The colon separated list is used to specify the precise credential
+to be used for remote access by root.
+The elements of the list may be either names or numbers.
+Note that user: should be used to distinguish a credential containing
+no groups from a complete credential for that user.
+.Pp
+.Sm off
+.Fl mapall No = Sy user
+.Sm on
+or
+.Sm off
+.Fl mapall No = Sy user:group1:group2:...
+.Sm on
+specifies a mapping for all client uids (including root)
+using the same semantics as
+.Fl maproot .
+.Pp
+The option
+.Fl r
+is a synonym for
+.Fl maproot
+in an effort to be backward compatible with older export file formats.
+.Pp
+In the absence of
+.Fl maproot
+and
+.Fl mapall
+options, remote accesses by root will result in using a credential of -2:-2.
+All other users will be mapped to their remote credential.
+If a
+.Fl maproot
+option is given,
+remote access by root will be mapped to that credential instead of -2:-2.
+If a
+.Fl mapall
+option is given,
+all users (including root) will be mapped to that credential in
+place of their own.
+.Pp
+The
+.Fl kerb
+option specifies that the Kerberos authentication server should be
+used to authenticate and map client credentials.
+(Note that this is NOT Sun NFS compatible and
+is supported for TCP transport only.)
+.Pp
+The
+.Fl ro
+option specifies that the filesystem should be exported read-only
+(default read/write).
+The option
+.Fl o
+is a synonym for
+.Fl ro
+in an effort to be backward compatible with older export file formats.
+.Pp
+The third component of a line specifies the host set to which the line applies.
+The set may be specified in three ways.
+The first way is to list the host name(s) separated by white space.
+(Standard internet ``dot'' addresses may be used in place of names.)
+The second way is to specify a ``netgroup'' as defined in the netgroup file (see
+.Xr netgroup 5 ).
+The third way is to specify an internet subnetwork using a network and
+network mask that is defined as the set of all hosts with addresses within
+the subnetwork.
+This latter approach requires less overhead within the
+kernel and is recommended for cases where the export line refers to a
+large number of clients within an administrative subnet.
+.Pp
+The first two cases are specified by simply listing the name(s) separated
+by whitespace.
+All names are checked to see if they are ``netgroup'' names
+first and are assumed to be hostnames otherwise.
+Using the full domain specification for a hostname can normally
+circumvent the problem of a host that has the same name as a netgroup.
+The third case is specified by the flag
+.Sm off
+.Fl network No = Sy netname
+.Sm on
+and optionally
+.Sm off
+.Fl mask No = Sy netmask .
+.Sm on
+If the mask is not specified, it will default to the mask for that network
+class (A, B or C; see
+.Xr inet 5 ).
+.Pp
+For example:
+.Bd -literal -offset indent
+/usr /usr/local -maproot=0:10 friends
+/usr -maproot=daemon grumpy.cis.uoguelph.ca 131.104.48.16
+/usr -ro -mapall=nobody
+/u -maproot=bin: -network 131.104.48 -mask 255.255.255.0
+/u2 -maproot=root friends
+/u2 -alldirs -kerb -network cis-net -mask cis-mask
+.Ed
+.Pp
+Given that
+.Sy /usr ,
+.Sy /u
+and
+.Sy /u2
+are
+local filesystem mount points, the above example specifies the following:
+.Sy /usr
+is exported to hosts
+.Em friends
+where friends is specified in the netgroup file
+with users mapped to their remote credentials and
+root mapped to uid 0 and group 10.
+It is exported read-write and the hosts in ``friends'' can mount either /usr
+or /usr/local.
+It is exported to
+.Em 131.104.48.16
+and
+.Em grumpy.cis.uoguelph.ca
+with users mapped to their remote credentials and
+root mapped to the user and groups associated with ``daemon'';
+it is exported to the rest of the world as read-only with
+all users mapped to the user and groups associated with ``nobody''.
+.Pp
+.Sy /u
+is exported to all hosts on the subnetwork
+.Em 131.104.48
+with root mapped to the uid for ``bin'' and with no group access.
+.Pp
+.Sy /u2
+is exported to the hosts in ``friends'' with root mapped to uid and groups
+associated with ``root'';
+it is exported to all hosts on network ``cis-net'' allowing mounts at any
+directory within /u2 and mapping all uids to credentials for the principal
+that is authenticated by a Kerberos ticket.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+The default remote mount-point file.
+.El
+.Sh SEE ALSO
+.Xr netgroup 5 ,
+.Xr mountd 8 ,
+.Xr nfsd 8 ,
+.Xr showmount 8
+.Sh BUGS
+The export options are tied to the local mount points in the kernel and
+must be non-contradictory for any exported subdirectory of the local
+server mount point.
+It is recommended that all exported directories within the same server
+filesystem be specified on adjacent lines going down the tree.
+You cannot specify a hostname that is also the name of a netgroup.
+Specifying the full domain specification for a hostname can normally
+circumvent the problem.
diff --git a/sbin/mountd/mountd.8 b/sbin/mountd/mountd.8
new file mode 100644
index 000000000000..47a6cfc5ebea
--- /dev/null
+++ b/sbin/mountd/mountd.8
@@ -0,0 +1,100 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)mountd.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm /sbin/mountd
+.Op Fl n
+.Op Ar exportsfile
+.Sh DESCRIPTION
+.Xr Mountd
+is the server for
+.Tn NFS
+mount requests from other client machines.
+.Xr Mountd
+listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094.
+.Pp
+Options and operands available for
+.Nm mountd :
+.Bl -tag -width Ds
+.It Fl n
+The
+.Fl n
+option allows non-root mount requests to be served.
+This should only be specified if there are clients such as PC's,
+that require it.
+.It Ar exportsfile
+The
+.Ar exportsfile
+argument specifies an alternate location
+for the exports file.
+.El
+.Pp
+When mountd is started,
+it loads the export host addresses and options into the kernel
+using the mount(2) system call.
+After changing the exports file,
+a hangup signal should be sent to the mountd daemon
+to get it to reload the export information.
+After sending the SIGHUP
+(kill -HUP `cat /var/run/mountd.pid`),
+check the syslog output to see if mountd logged any parsing
+errors in the exports file.
+.Sh FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+the list of exported filesystems
+.It Pa /var/run/mountd.pid
+the pid of the currently running mountd
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr exports 5 ,
+.Xr nfsd 8 ,
+.Xr portmap 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm mountd
+utility first appeared in 4.4BSD.
diff --git a/sbin/mountd/mountd.c b/sbin/mountd/mountd.c
new file mode 100644
index 000000000000..d7c31dfb4ea1
--- /dev/null
+++ b/sbin/mountd/mountd.c
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * 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) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)mountd.c 8.8 (Berkeley) 2/20/94";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+/*
+ * Structures for keeping the mount list and export list
+ */
+struct mountlist {
+ struct mountlist *ml_next;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct dirlist {
+ struct dirlist *dp_left;
+ struct dirlist *dp_right;
+ int dp_flag;
+ struct hostlist *dp_hosts; /* List of hosts this dir exported to */
+ char dp_dirp[1]; /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define DP_DEFSET 0x1
+
+struct exportlist {
+ struct exportlist *ex_next;
+ struct dirlist *ex_dirl;
+ struct dirlist *ex_defdir;
+ int ex_flag;
+ fsid_t ex_fs;
+ char *ex_fsdir;
+};
+/* ex_flag bits */
+#define EX_LINKED 0x1
+
+struct netmsk {
+ u_long nt_net;
+ u_long nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct hostent *gt_hostent;
+ struct netmsk gt_net;
+#ifdef ISO
+ struct sockaddr_iso *gt_isoaddr;
+#endif
+};
+
+struct grouplist {
+ int gr_type;
+ union grouptypes gr_ptr;
+ struct grouplist *gr_next;
+};
+/* Group types */
+#define GT_NULL 0x0
+#define GT_HOST 0x1
+#define GT_NET 0x2
+#define GT_ISO 0x4
+
+struct hostlist {
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+/* Global defs */
+char *add_expdir __P((struct dirlist **, char *, int));
+void add_dlist __P((struct dirlist **, struct dirlist *,
+ struct grouplist *));
+void add_mlist __P((char *, char *));
+int check_dirpath __P((char *));
+int check_options __P((struct dirlist *));
+int chk_host __P((struct dirlist *, u_long, int *));
+void del_mlist __P((char *, char *));
+struct dirlist *dirp_search __P((struct dirlist *, char *));
+int do_mount __P((struct exportlist *, struct grouplist *, int,
+ struct ucred *, char *, int, struct statfs *));
+int do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct ucred *));
+struct exportlist *ex_search __P((fsid_t *));
+struct exportlist *get_exp __P((void));
+void free_dir __P((struct dirlist *));
+void free_exp __P((struct exportlist *));
+void free_grp __P((struct grouplist *));
+void free_host __P((struct hostlist *));
+void get_exportlist __P((void));
+int get_host __P((char *, struct grouplist *));
+struct hostlist *get_ht __P((void));
+int get_line __P((void));
+void get_mountlist __P((void));
+int get_net __P((char *, struct netmsk *, int));
+void getexp_err __P((struct exportlist *, struct grouplist *));
+struct grouplist *get_grp __P((void));
+void hang_dirp __P((struct dirlist *, struct grouplist *,
+ struct exportlist *, int));
+void mntsrv __P((struct svc_req *, SVCXPRT *));
+void nextfield __P((char **, char **));
+void out_of_mem __P((void));
+void parsecred __P((char *, struct ucred *));
+int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
+int scan_tree __P((struct dirlist *, u_long));
+void send_umntall __P((void));
+int umntall_each __P((caddr_t, struct sockaddr_in *));
+int xdr_dir __P((XDR *, char *));
+int xdr_explist __P((XDR *, caddr_t));
+int xdr_fhs __P((XDR *, nfsv2fh_t *));
+int xdr_mlist __P((XDR *, caddr_t));
+
+/* C library */
+int getnetgrent();
+void endnetgrent();
+void setnetgrent();
+
+#ifdef ISO
+struct iso_addr *iso_addr();
+#endif
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char exname[MAXPATHLEN];
+struct ucred def_anon = {
+ 1,
+ (uid_t) -2,
+ 1,
+ { (gid_t) -2 }
+};
+int root_only = 1;
+int opt_flags;
+/* Bits for above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+#define OP_KERB 0x04
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ISO 0x20
+#define OP_ALLDIRS 0x40
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG __P((int, const char *, ...));
+#define syslog SYSLOG
+#else
+int debug = 0;
+#endif
+
+/*
+ * Mountd server for NFS mount protocol as described in:
+ * NFS: Network File System Protocol Specification, RFC1094, Appendix A
+ * The optional arguments are the exports file name
+ * default: _PATH_EXPORTS
+ * and "-n" to allow nonroot mount.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *transp;
+ int c;
+
+ while ((c = getopt(argc, argv, "n")) != EOF)
+ switch (c) {
+ case 'n':
+ root_only = 0;
+ break;
+ default:
+ fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
+ exit(1);
+ };
+ argc -= optind;
+ argv += optind;
+ grphead = (struct grouplist *)NULL;
+ exphead = (struct exportlist *)NULL;
+ mlhead = (struct mountlist *)NULL;
+ if (argc == 1) {
+ strncpy(exname, *argv, MAXPATHLEN-1);
+ exname[MAXPATHLEN-1] = '\0';
+ } else
+ strcpy(exname, _PATH_EXPORTS);
+ openlog("mountd", LOG_PID, LOG_DAEMON);
+ if (debug)
+ fprintf(stderr,"Getting export list.\n");
+ get_exportlist();
+ if (debug)
+ fprintf(stderr,"Getting mount list.\n");
+ get_mountlist();
+ if (debug)
+ fprintf(stderr,"Here we go.\n");
+ if (debug == 0) {
+ daemon(0, 0);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ }
+ signal(SIGHUP, (void (*) __P((int))) get_exportlist);
+ signal(SIGTERM, (void (*) __P((int))) send_umntall);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+ if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
+ syslog(LOG_ERR, "Can't create socket");
+ exit(1);
+ }
+ pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
+ if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,
+ IPPROTO_UDP)) {
+ syslog(LOG_ERR, "Can't register mount");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "Mountd died");
+ exit(1);
+}
+
+/*
+ * The mount rpc service
+ */
+void
+mntsrv(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct exportlist *ep;
+ struct dirlist *dp;
+ nfsv2fh_t nfh;
+ struct authunix_parms *ucr;
+ struct stat stb;
+ struct statfs fsb;
+ struct hostent *hp;
+ u_long saddr;
+ char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];
+ int bad = ENOENT, omask, defset;
+ uid_t uid = -2;
+
+ /* Get authorization */
+ switch (rqstp->rq_cred.oa_flavor) {
+ case AUTH_UNIX:
+ ucr = (struct authunix_parms *)rqstp->rq_clntcred;
+ uid = ucr->aup_uid;
+ break;
+ case AUTH_NULL:
+ default:
+ break;
+ }
+
+ saddr = transp->xp_raddr.sin_addr.s_addr;
+ hp = (struct hostent *)NULL;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ case RPCMNT_MOUNT:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+
+ /*
+ * Get the real pathname and make sure it is a directory
+ * that exists.
+ */
+ if (realpath(rpcpath, dirpath) == 0 ||
+ stat(dirpath, &stb) < 0 ||
+ (stb.st_mode & S_IFMT) != S_IFDIR ||
+ statfs(dirpath, &fsb) < 0) {
+ chdir("/"); /* Just in case realpath doesn't */
+ if (debug)
+ fprintf(stderr, "stat failed on %s\n", dirpath);
+ if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ }
+
+ /* Check in the exports list */
+ omask = sigblock(sigmask(SIGHUP));
+ ep = ex_search(&fsb.f_fsid);
+ defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ /* Get the file handle */
+ bzero((caddr_t)&nfh, sizeof(nfh));
+ if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
+ bad = errno;
+ syslog(LOG_ERR, "Can't get fh for %s", dirpath);
+ if (!svc_sendreply(transp, xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ sigsetmask(omask);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
+ syslog(LOG_ERR, "Can't send reply");
+ if (hp == NULL)
+ hp = gethostbyaddr((caddr_t)&saddr,
+ sizeof(saddr), AF_INET);
+ if (hp)
+ add_mlist(hp->h_name, dirpath);
+ else
+ add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
+ dirpath);
+ if (debug)
+ fprintf(stderr,"Mount successfull.\n");
+ } else {
+ bad = EACCES;
+ if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ }
+ sigsetmask(omask);
+ return;
+ case RPCMNT_DUMP:
+ if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ case RPCMNT_UMOUNT:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, dirpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, dirpath);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, (char *)NULL);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
+ return;
+ case RPCMNT_EXPORT:
+ if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+}
+
+/*
+ * Xdr conversion for a dirpath string
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+/*
+ * Xdr routine to generate fhstatus
+ */
+int
+xdr_fhs(xdrsp, nfh)
+ XDR *xdrsp;
+ nfsv2fh_t *nfh;
+{
+ int ok = 0;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
+}
+
+int
+xdr_mlist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct mountlist *mlp;
+ int true = 1;
+ int false = 0;
+ char *strp;
+
+ mlp = mlhead;
+ while (mlp) {
+ if (!xdr_bool(xdrsp, &true))
+ return (0);
+ strp = &mlp->ml_host[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = &mlp->ml_dirp[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ mlp = mlp->ml_next;
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+}
+
+/*
+ * Xdr conversion for export list
+ */
+int
+xdr_explist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct exportlist *ep;
+ int false = 0;
+ int omask, putdef;
+
+ omask = sigblock(sigmask(SIGHUP));
+ ep = exphead;
+ while (ep) {
+ putdef = 0;
+ if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
+ goto errout;
+ if (ep->ex_defdir && putdef == 0 &&
+ put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+ &putdef))
+ goto errout;
+ ep = ep->ex_next;
+ }
+ sigsetmask(omask);
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+errout:
+ sigsetmask(omask);
+ return (0);
+}
+
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+int
+put_exlist(dp, xdrsp, adp, putdefp)
+ struct dirlist *dp;
+ XDR *xdrsp;
+ struct dirlist *adp;
+ int *putdefp;
+{
+ struct grouplist *grp;
+ struct hostlist *hp;
+ int true = 1;
+ int false = 0;
+ int gotalldir = 0;
+ char *strp;
+
+ if (dp) {
+ if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
+ return (1);
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = dp->dp_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (1);
+ if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
+ gotalldir = 1;
+ *putdefp = 1;
+ }
+ if ((dp->dp_flag & DP_DEFSET) == 0 &&
+ (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ if (grp->gr_type == GT_HOST) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_hostent->h_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ } else if (grp->gr_type == GT_NET) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_net.nt_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ }
+ hp = hp->ht_next;
+ if (gotalldir && hp == (struct hostlist *)NULL) {
+ hp = adp->dp_hosts;
+ gotalldir = 0;
+ }
+ }
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (1);
+ if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
+ return (1);
+ }
+ return (0);
+}
+
+#define LINESIZ 10240
+char line[LINESIZ];
+FILE *exp_file;
+
+/*
+ * Get the export list
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb, *fsp;
+ struct hostent *hpe;
+ struct ucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ /*
+ * First, get rid of the old list
+ */
+ ep = exphead;
+ while (ep) {
+ ep2 = ep;
+ ep = ep->ex_next;
+ free_exp(ep2);
+ }
+ exphead = (struct exportlist *)NULL;
+
+ grp = grphead;
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+ grphead = (struct grouplist *)NULL;
+
+ /*
+ * And delete exports that are in the kernel for all local
+ * file systems.
+ * XXX: Should know how to handle all local exportable file systems
+ * instead of just MOUNT_UFS.
+ */
+ num = getmntinfo(&fsp, MNT_NOWAIT);
+ for (i = 0; i < num; i++) {
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+ } targs;
+
+ switch (fsp->f_type) {
+ case MOUNT_MFS:
+ case MOUNT_UFS:
+ case MOUNT_CD9660:
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_type, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE,
+ (caddr_t)&targs) < 0)
+ syslog(LOG_ERR, "Can't delete exports for %s",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", exname);
+ exit(2);
+ }
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ fprintf(stderr,"Got line %s\n",line);
+ cp = line;
+ nextfield(&cp, &endcp);
+ if (*cp == '#')
+ goto nextline;
+
+ /*
+ * Set defaults.
+ */
+ has_host = FALSE;
+ anon = def_anon;
+ exflags = MNT_EXPORTED;
+ got_nondir = 0;
+ opt_flags = 0;
+ ep = (struct exportlist *)NULL;
+
+ /*
+ * Create new exports list entry
+ */
+ len = endcp-cp;
+ tgrp = grp = get_grp();
+ while (len > 0) {
+ if (len > RPCMNT_NAMELEN) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (*cp == '-') {
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (debug)
+ fprintf(stderr, "doing opt %s\n", cp);
+ got_nondir = 1;
+ if (do_opt(&cp, &endcp, ep, grp, &has_host,
+ &exflags, &anon)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (*cp == '/') {
+ savedc = *endcp;
+ *endcp = '\0';
+ if (check_dirpath(cp) &&
+ statfs(cp, &fsb) >= 0) {
+ if (got_nondir) {
+ syslog(LOG_ERR, "Dirs must be first");
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (ep) {
+ if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+ ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else {
+ /*
+ * See if this directory is already
+ * in the list.
+ */
+ ep = ex_search(&fsb.f_fsid);
+ if (ep == (struct exportlist *)NULL) {
+ ep = get_exp();
+ ep->ex_fs = fsb.f_fsid;
+ ep->ex_fsdir = (char *)
+ malloc(strlen(fsb.f_mntonname) + 1);
+ if (ep->ex_fsdir)
+ strcpy(ep->ex_fsdir,
+ fsb.f_mntonname);
+ else
+ out_of_mem();
+ if (debug)
+ fprintf(stderr,
+ "Making new ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ fprintf(stderr,
+ "Found ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ }
+
+ /*
+ * Add dirpath to export mount point.
+ */
+ dirp = add_expdir(&dirhead, cp, len);
+ dirplen = len;
+ } else {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ *endcp = savedc;
+ } else {
+ savedc = *endcp;
+ *endcp = '\0';
+ got_nondir = 1;
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Get the host or netgroup.
+ */
+ setnetgrent(cp);
+ netgrp = getnetgrent(&hst, &usr, &dom);
+ do {
+ if (has_host) {
+ grp->gr_next = get_grp();
+ grp = grp->gr_next;
+ }
+ if (netgrp) {
+ if (get_host(hst, grp)) {
+ syslog(LOG_ERR, "Bad netgroup %s", cp);
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (get_host(cp, grp)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ has_host = TRUE;
+ } while (netgrp && getnetgrent(&hst, &usr, &dom));
+ endnetgrent();
+ *endcp = savedc;
+ }
+ cp = endcp;
+ nextfield(&cp, &endcp);
+ len = endcp - cp;
+ }
+ if (check_options(dirhead)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (!has_host) {
+ grp->gr_type = GT_HOST;
+ if (debug)
+ fprintf(stderr,"Adding a default entry\n");
+ /* add a default group and make the grp list NULL */
+ hpe = (struct hostent *)malloc(sizeof(struct hostent));
+ if (hpe == (struct hostent *)NULL)
+ out_of_mem();
+ hpe->h_name = "Default";
+ hpe->h_addrtype = AF_INET;
+ hpe->h_length = sizeof (u_long);
+ hpe->h_addr_list = (char **)NULL;
+ grp->gr_ptr.gt_hostent = hpe;
+
+ /*
+ * Don't allow a network export coincide with a list of
+ * host(s) on the same line.
+ */
+ } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Loop through hosts, pushing the exports into the kernel.
+ * After loop, tgrp points to the start of the list and
+ * grp points to the last entry in the list.
+ */
+ grp = tgrp;
+ do {
+ if (do_mount(ep, grp, exflags, &anon, dirp,
+ dirplen, &fsb)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } while (grp->gr_next && (grp = grp->gr_next));
+
+ /*
+ * Success. Update the data structures.
+ */
+ if (has_host) {
+ hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS));
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ (opt_flags & OP_ALLDIRS));
+ free_grp(grp);
+ }
+ dirhead = (struct dirlist *)NULL;
+ if ((ep->ex_flag & EX_LINKED) == 0) {
+ ep2 = exphead;
+ epp = &exphead;
+
+ /*
+ * Insert in the list in alphabetical order.
+ */
+ while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+ epp = &ep2->ex_next;
+ ep2 = ep2->ex_next;
+ }
+ if (ep2)
+ ep->ex_next = ep2;
+ *epp = ep;
+ ep->ex_flag |= EX_LINKED;
+ }
+nextline:
+ if (dirhead) {
+ free_dir(dirhead);
+ dirhead = (struct dirlist *)NULL;
+ }
+ }
+ fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+ struct exportlist *ep;
+
+ ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+ if (ep == (struct exportlist *)NULL)
+ out_of_mem();
+ bzero((caddr_t)ep, sizeof (struct exportlist));
+ return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+ struct grouplist *gp;
+
+ gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+ if (gp == (struct grouplist *)NULL)
+ out_of_mem();
+ bzero((caddr_t)gp, sizeof (struct grouplist));
+ return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+ struct exportlist *ep;
+ struct grouplist *grp;
+{
+ struct grouplist *tgrp;
+
+ syslog(LOG_ERR, "Bad exports list line %s", line);
+ if (ep && (ep->ex_flag & EX_LINKED) == 0)
+ free_exp(ep);
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+ fsid_t *fsid;
+{
+ struct exportlist *ep;
+
+ ep = exphead;
+ while (ep) {
+ if (ep->ex_fs.val[0] == fsid->val[0] &&
+ ep->ex_fs.val[1] == fsid->val[1])
+ return (ep);
+ ep = ep->ex_next;
+ }
+ return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+ struct dirlist **dpp;
+ char *cp;
+ int len;
+{
+ struct dirlist *dp;
+
+ dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+ dp->dp_left = *dpp;
+ dp->dp_right = (struct dirlist *)NULL;
+ dp->dp_flag = 0;
+ dp->dp_hosts = (struct hostlist *)NULL;
+ strcpy(dp->dp_dirp, cp);
+ *dpp = dp;
+ return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, alldirs)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int alldirs;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (alldirs) {
+ if (ep->ex_defdir)
+ free((caddr_t)dp);
+ else
+ ep->ex_defdir = dp;
+ if (grp == (struct grouplist *)NULL)
+ ep->ex_defdir->dp_flag |= DP_DEFSET;
+ else while (grp) {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = ep->ex_defdir->dp_hosts;
+ ep->ex_defdir->dp_hosts = hp;
+ grp = grp->gr_next;
+ }
+ } else {
+
+ /*
+ * Loop throught the directories adding them to the tree.
+ */
+ while (dp) {
+ dp2 = dp->dp_left;
+ add_dlist(&ep->ex_dirl, dp, grp);
+ dp = dp2;
+ }
+ }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+{
+ struct dirlist *dp;
+ struct hostlist *hp;
+ int cmp;
+
+ dp = *dpp;
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+ if (cmp > 0) {
+ add_dlist(&dp->dp_left, newdp, grp);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp);
+ return;
+ } else
+ free((caddr_t)newdp);
+ } else {
+ dp = newdp;
+ dp->dp_left = (struct dirlist *)NULL;
+ *dpp = dp;
+ }
+ if (grp) {
+
+ /*
+ * Hang all of the host(s) off of the directory point.
+ */
+ do {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = dp->dp_hosts;
+ dp->dp_hosts = hp;
+ grp = grp->gr_next;
+ } while (grp);
+ } else
+ dp->dp_flag |= DP_DEFSET;
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirpath)
+ struct dirlist *dp;
+ char *dirpath;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirpath);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirpath));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirpath));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp)
+ struct dirlist *dp;
+ u_long saddr;
+ int *defsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ u_long **addrp;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = 1;
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ switch (grp->gr_type) {
+ case GT_HOST:
+ addrp = (u_long **)
+ grp->gr_ptr.gt_hostent->h_addr_list;
+ while (*addrp) {
+ if (**addrp == saddr)
+ return (1);
+ addrp++;
+ }
+ break;
+ case GT_NET:
+ if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+ grp->gr_ptr.gt_net.nt_net)
+ return (1);
+ break;
+ };
+ hp = hp->ht_next;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+ struct dirlist *dp;
+ u_long saddr;
+{
+ int defset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset))
+ return (1);
+ if (scan_tree(dp->dp_right, saddr))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+ struct dirlist *dp;
+{
+
+ if (dp) {
+ free_dir(dp->dp_left);
+ free_dir(dp->dp_right);
+ free_host(dp->dp_hosts);
+ free((caddr_t)dp);
+ }
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+ char **cpp, **endcpp;
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int *has_hostp;
+ int *exflagsp;
+ struct ucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ cpopt = *cpp;
+ cpopt++;
+ cp = *endcpp;
+ savedc = *cp;
+ *cp = '\0';
+ while (cpopt && *cpopt) {
+ allflag = 1;
+ usedarg = -2;
+ if (cpoptend = index(cpopt, ',')) {
+ *cpoptend++ = '\0';
+ if (cpoptarg = index(cpopt, '='))
+ *cpoptarg++ = '\0';
+ } else {
+ if (cpoptarg = index(cpopt, '='))
+ *cpoptarg++ = '\0';
+ else {
+ *cp = savedc;
+ nextfield(&cp, &endcp);
+ **endcpp = '\0';
+ if (endcp > cp && *cp != '-') {
+ cpoptarg = cp;
+ savedc2 = *endcp;
+ *endcp = '\0';
+ usedarg = 0;
+ }
+ }
+ }
+ if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ *exflagsp |= MNT_EXRDONLY;
+ } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+ !(allflag = strcmp(cpopt, "mapall")) ||
+ !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+ usedarg++;
+ parsecred(cpoptarg, cr);
+ if (allflag == 0) {
+ *exflagsp |= MNT_EXPORTANON;
+ opt_flags |= OP_MAPALL;
+ } else
+ opt_flags |= OP_MAPROOT;
+ } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
+ *exflagsp |= MNT_EXKERB;
+ opt_flags |= OP_KERB;
+ } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+ !strcmp(cpopt, "m"))) {
+ if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+ syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
+ return (1);
+ }
+ usedarg++;
+ opt_flags |= OP_MASK;
+ } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+ !strcmp(cpopt, "n"))) {
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "Network/host conflict");
+ return (1);
+ } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+ syslog(LOG_ERR, "Bad net: %s", cpoptarg);
+ return (1);
+ }
+ grp->gr_type = GT_NET;
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_NET;
+ } else if (!strcmp(cpopt, "alldirs")) {
+ opt_flags |= OP_ALLDIRS;
+#ifdef ISO
+ } else if (cpoptarg && !strcmp(cpopt, "iso")) {
+ if (get_isoaddr(cpoptarg, grp)) {
+ syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
+ return (1);
+ }
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_ISO;
+#endif /* ISO */
+ } else {
+ syslog(LOG_ERR, "Bad opt %s", cpopt);
+ return (1);
+ }
+ if (usedarg >= 0) {
+ *endcp = savedc2;
+ **endcpp = savedc;
+ if (usedarg > 0) {
+ *cpp = cp;
+ *endcpp = endcp;
+ }
+ return (0);
+ }
+ cpopt = cpoptend;
+ }
+ **endcpp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct hostent *hp, *nhp;
+ char **addrp, **naddrp;
+ struct hostent t_host;
+ int i;
+ u_long saddr;
+ char *aptr[2];
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((hp = gethostbyname(cp)) == NULL) {
+ if (isdigit(*cp)) {
+ saddr = inet_addr(cp);
+ if (saddr == -1) {
+ syslog(LOG_ERR, "Inet_addr failed");
+ return (1);
+ }
+ if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
+ AF_INET)) == NULL) {
+ hp = &t_host;
+ hp->h_name = cp;
+ hp->h_addrtype = AF_INET;
+ hp->h_length = sizeof (u_long);
+ hp->h_addr_list = aptr;
+ aptr[0] = (char *)&saddr;
+ aptr[1] = (char *)NULL;
+ }
+ } else {
+ syslog(LOG_ERR, "Gethostbyname failed");
+ return (1);
+ }
+ }
+ grp->gr_type = GT_HOST;
+ nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+ malloc(sizeof(struct hostent));
+ if (nhp == (struct hostent *)NULL)
+ out_of_mem();
+ bcopy((caddr_t)hp, (caddr_t)nhp,
+ sizeof(struct hostent));
+ i = strlen(hp->h_name)+1;
+ nhp->h_name = (char *)malloc(i);
+ if (nhp->h_name == (char *)NULL)
+ out_of_mem();
+ bcopy(hp->h_name, nhp->h_name, i);
+ addrp = hp->h_addr_list;
+ i = 1;
+ while (*addrp++)
+ i++;
+ naddrp = nhp->h_addr_list = (char **)
+ malloc(i*sizeof(char *));
+ if (naddrp == (char **)NULL)
+ out_of_mem();
+ addrp = hp->h_addr_list;
+ while (*addrp) {
+ *naddrp = (char *)
+ malloc(hp->h_length);
+ if (*naddrp == (char *)NULL)
+ out_of_mem();
+ bcopy(*addrp, *naddrp,
+ hp->h_length);
+ addrp++;
+ naddrp++;
+ }
+ *naddrp = (char *)NULL;
+ if (debug)
+ fprintf(stderr, "got host %s\n", hp->h_name);
+ return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+ struct exportlist *ep;
+{
+
+ if (ep->ex_defdir) {
+ free_host(ep->ex_defdir->dp_hosts);
+ free((caddr_t)ep->ex_defdir);
+ }
+ if (ep->ex_fsdir)
+ free(ep->ex_fsdir);
+ free_dir(ep->ex_dirl);
+ free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+ struct hostlist *hp;
+{
+ struct hostlist *hp2;
+
+ while (hp) {
+ hp2 = hp;
+ hp = hp->ht_next;
+ free((caddr_t)hp2);
+ }
+}
+
+struct hostlist *
+get_ht()
+{
+ struct hostlist *hp;
+
+ hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+ if (hp == (struct hostlist *)NULL)
+ out_of_mem();
+ hp->ht_next = (struct hostlist *)NULL;
+ return (hp);
+}
+
+#ifdef ISO
+/*
+ * Translate an iso address.
+ */
+get_isoaddr(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct iso_addr *isop;
+ struct sockaddr_iso *isoaddr;
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((isop = iso_addr(cp)) == NULL) {
+ syslog(LOG_ERR,
+ "iso_addr failed, ignored");
+ return (1);
+ }
+ isoaddr = (struct sockaddr_iso *)
+ malloc(sizeof (struct sockaddr_iso));
+ if (isoaddr == (struct sockaddr_iso *)NULL)
+ out_of_mem();
+ bzero((caddr_t)isoaddr, sizeof (struct sockaddr_iso));
+ bcopy((caddr_t)isop, (caddr_t)&isoaddr->siso_addr,
+ sizeof (struct iso_addr));
+ isoaddr->siso_len = sizeof (struct sockaddr_iso);
+ isoaddr->siso_family = AF_ISO;
+ grp->gr_type = GT_ISO;
+ grp->gr_ptr.gt_isoaddr = isoaddr;
+ return (0);
+}
+#endif /* ISO */
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "Out of memory");
+ exit(2);
+}
+
+/*
+ * Do the mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int exflags;
+ struct ucred *anoncrp;
+ char *dirp;
+ int dirplen;
+ struct statfs *fsb;
+{
+ char *cp = (char *)NULL;
+ u_long **addrp;
+ int done;
+ char savedc = '\0';
+ struct sockaddr_in sin, imask;
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+ } args;
+ u_long net;
+
+ args.ua.fspec = 0;
+ args.ua.export.ex_flags = exflags;
+ args.ua.export.ex_anon = *anoncrp;
+ bzero((char *)&sin, sizeof(sin));
+ bzero((char *)&imask, sizeof(imask));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ imask.sin_family = AF_INET;
+ imask.sin_len = sizeof(sin);
+ if (grp->gr_type == GT_HOST)
+ addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
+ else
+ addrp = (u_long **)NULL;
+ done = FALSE;
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (addrp) {
+ sin.sin_addr.s_addr = **addrp;
+ args.ua.export.ex_addrlen = sizeof(sin);
+ } else
+ args.ua.export.ex_addrlen = 0;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_mask)
+ imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
+ else {
+ net = ntohl(grp->gr_ptr.gt_net.nt_net);
+ if (IN_CLASSA(net))
+ imask.sin_addr.s_addr = inet_addr("255.0.0.0");
+ else if (IN_CLASSB(net))
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.0.0");
+ else
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.255.0");
+ grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
+ }
+ sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_addrlen = sizeof (sin);
+ args.ua.export.ex_mask = (struct sockaddr *)&imask;
+ args.ua.export.ex_masklen = sizeof (imask);
+ break;
+#ifdef ISO
+ case GT_ISO:
+ args.ua.export.ex_addr =
+ (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+ args.ua.export.ex_addrlen =
+ sizeof(struct sockaddr_iso);
+ args.ua.export.ex_masklen = 0;
+ break;
+#endif /* ISO */
+ default:
+ syslog(LOG_ERR, "Bad grouptype");
+ if (cp)
+ *cp = savedc;
+ return (1);
+ };
+
+ /*
+ * XXX:
+ * Maybe I should just use the fsb->f_mntonname path instead
+ * of looping back up the dirp to the mount point??
+ * Also, needs to know how to export all types of local
+ * exportable file systems and not just MOUNT_UFS.
+ */
+ while (mount(fsb->f_type, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (errno == EPERM) {
+ syslog(LOG_ERR,
+ "Can't change attributes for %s.\n", dirp);
+ return (1);
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ syslog(LOG_ERR, "Not root dir");
+ return (1);
+ }
+ /* back up over the last component */
+ while (*cp == '/' && cp > dirp)
+ cp--;
+ while (*(cp - 1) != '/' && cp > dirp)
+ cp--;
+ if (cp == dirp) {
+ if (debug)
+ fprintf(stderr,"mnt unsucc\n");
+ syslog(LOG_ERR, "Can't export %s", dirp);
+ return (1);
+ }
+ savedc = *cp;
+ *cp = '\0';
+ }
+ if (addrp) {
+ ++addrp;
+ if (*addrp == (u_long *)NULL)
+ done = TRUE;
+ } else
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np;
+ long netaddr;
+ struct in_addr inetaddr, inetaddr2;
+ char *name;
+
+ if (np = getnetbyname(cp))
+ inetaddr = inet_makeaddr(np->n_net, 0);
+ else if (isdigit(*cp)) {
+ if ((netaddr = inet_network(cp)) == -1)
+ return (1);
+ inetaddr = inet_makeaddr(netaddr, 0);
+ /*
+ * Due to arbritrary subnet masks, you don't know how many
+ * bits to shift the address to make it into a network,
+ * however you do know how to make a network address into
+ * a host with host == 0 and then compare them.
+ * (What a pest)
+ */
+ if (!maskflg) {
+ setnetent(0);
+ while (np = getnetent()) {
+ inetaddr2 = inet_makeaddr(np->n_net, 0);
+ if (inetaddr2.s_addr == inetaddr.s_addr)
+ break;
+ }
+ endnetent();
+ }
+ } else
+ return (1);
+ if (maskflg)
+ net->nt_mask = inetaddr.s_addr;
+ else {
+ if (np)
+ name = np->n_name;
+ else
+ name = inet_ntoa(inetaddr);
+ net->nt_name = (char *)malloc(strlen(name) + 1);
+ if (net->nt_name == (char *)NULL)
+ out_of_mem();
+ strcpy(net->nt_name, name);
+ net->nt_net = inetaddr.s_addr;
+ }
+ return (0);
+}
+
+/*
+ * Parse out the next white space separated field
+ */
+void
+nextfield(cp, endcp)
+ char **cp;
+ char **endcp;
+{
+ char *p;
+
+ p = *cp;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ *cp = *endcp = p;
+ else {
+ *cp = p++;
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ p++;
+ *endcp = p;
+ }
+}
+
+/*
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
+ */
+int
+get_line()
+{
+ char *p, *cp;
+ int len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
+ return (0);
+ len = strlen(p);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ *++cp = '\0';
+ if (len > 0) {
+ totlen += len;
+ if (totlen >= LINESIZ) {
+ syslog(LOG_ERR, "Exports line too long");
+ exit(2);
+ }
+ p = cp;
+ }
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct ucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ int ngroups, groups[NGROUPS + 1];
+
+ /*
+ * Set up the unpriviledged user.
+ */
+ cr->cr_ref = 1;
+ cr->cr_uid = -2;
+ cr->cr_groups[0] = -2;
+ cr->cr_ngroups = 1;
+ /*
+ * Get the user's password table entry.
+ */
+ names = strsep(&namelist, " \t\n");
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-')
+ pw = getpwuid(atoi(name));
+ else
+ pw = getpwnam(name);
+ /*
+ * Credentials specified as those of a user.
+ */
+ if (names == NULL) {
+ if (pw == NULL) {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_uid = pw->pw_uid;
+ ngroups = NGROUPS + 1;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+ syslog(LOG_ERR, "Too many groups");
+ /*
+ * Convert from int's to gid_t's and compress out duplicate
+ */
+ cr->cr_ngroups = ngroups - 1;
+ cr->cr_groups[0] = groups[0];
+ for (cnt = 2; cnt < ngroups; cnt++)
+ cr->cr_groups[cnt - 1] = groups[cnt];
+ return;
+ }
+ /*
+ * Explicit credential specified as a colon separated list:
+ * uid:gid:gid:...
+ */
+ if (pw != NULL)
+ cr->cr_uid = pw->pw_uid;
+ else if (isdigit(*name) || *name == '-')
+ cr->cr_uid = atoi(name);
+ else {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_ngroups = 0;
+ while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-') {
+ cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+ } else {
+ if ((gr = getgrnam(name)) == NULL) {
+ syslog(LOG_ERR, "Unknown group: %s", name);
+ continue;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+ }
+ }
+ if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+ syslog(LOG_ERR, "Too many groups");
+}
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
+/*
+ * Routines that maintain the remote mounttab
+ */
+void
+get_mountlist()
+{
+ struct mountlist *mlp, **mlpp;
+ char *eos, *dirp;
+ int len;
+ char str[STRSIZ];
+ FILE *mlfile;
+
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlpp = &mlhead;
+ while (fgets(str, STRSIZ, mlfile) != NULL) {
+ if ((dirp = index(str, '\t')) == NULL &&
+ (dirp = index(str, ' ')) == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ len = dirp-str;
+ if (len > RPCMNT_NAMELEN)
+ len = RPCMNT_NAMELEN;
+ bcopy(str, mlp->ml_host, len);
+ mlp->ml_host[len] = '\0';
+ while (*dirp == '\t' || *dirp == ' ')
+ dirp++;
+ if ((eos = index(dirp, '\t')) == NULL &&
+ (eos = index(dirp, ' ')) == NULL &&
+ (eos = index(dirp, '\n')) == NULL)
+ len = strlen(dirp);
+ else
+ len = eos-dirp;
+ if (len > RPCMNT_PATHLEN)
+ len = RPCMNT_PATHLEN;
+ bcopy(dirp, mlp->ml_dirp, len);
+ mlp->ml_dirp[len] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ mlpp = &mlp->ml_next;
+ }
+ fclose(mlfile);
+}
+
+void
+del_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ struct mountlist *mlp2;
+ FILE *mlfile;
+ int fnd = 0;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) &&
+ (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
+ fnd = 1;
+ mlp2 = mlp;
+ *mlpp = mlp = mlp->ml_next;
+ free((caddr_t)mlp2);
+ } else {
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ }
+ if (fnd) {
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
+ syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlp = mlhead;
+ while (mlp) {
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ mlp = mlp->ml_next;
+ }
+ fclose(mlfile);
+ }
+}
+
+void
+add_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ FILE *mlfile;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
+ return;
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
+ syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ fclose(mlfile);
+}
+
+/*
+ * This function is called via. SIGTERM when the system is going down.
+ * It sends a broadcast RPCMNT_UMNTALL.
+ */
+void
+send_umntall()
+{
+ (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
+ xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
+ exit(0);
+}
+
+int
+umntall_each(resultsp, raddr)
+ caddr_t resultsp;
+ struct sockaddr_in *raddr;
+{
+ return (1);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ char **addrp;
+
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_hostent->h_name) {
+ addrp = grp->gr_ptr.gt_hostent->h_addr_list;
+ while (addrp && *addrp)
+ free(*addrp++);
+ free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
+ free(grp->gr_ptr.gt_hostent->h_name);
+ }
+ free((caddr_t)grp->gr_ptr.gt_hostent);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+#ifdef ISO
+ else if (grp->gr_type == GT_ISO)
+ free((caddr_t)grp->gr_ptr.gt_isoaddr);
+#endif
+ free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+ struct dirlist *dp;
+{
+
+ if (dp == (struct dirlist *)NULL)
+ return (1);
+ if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
+ (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
+ (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
+ syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -net");
+ return (1);
+ }
+ if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
+ syslog(LOG_ERR, "-net and -iso mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldir has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ * if no symbolic links are found.
+ */
+int
+check_dirpath(dirp)
+ char *dirp;
+{
+ char *cp;
+ int ret = 1;
+ struct stat sb;
+
+ cp = dirp + 1;
+ while (*cp && ret) {
+ if (*cp == '/') {
+ *cp = '\0';
+ if (lstat(dirp, &sb) < 0 ||
+ (sb.st_mode & S_IFMT) != S_IFDIR)
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 ||
+ (sb.st_mode & S_IFMT) != S_IFDIR)
+ ret = 0;
+ return (ret);
+}
diff --git a/sbin/mountd/netgroup.5 b/sbin/mountd/netgroup.5
new file mode 100644
index 000000000000..9ad8c48258a7
--- /dev/null
+++ b/sbin/mountd/netgroup.5
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm netgroup
+.Sh DESCRIPTION
+The
+.Nm netgroup
+file
+specifies ``netgroups'', which are sets of
+.Sy (host, user, domain)
+tuples that are to be given similar network access.
+.Pp
+Each line in the file
+consists of a netgroup name followed by a list of the members of the
+netgroup.
+Each member can be either the name of another netgroup or a specification
+of a tuple as follows:
+.Bd -literal -offset indent
+(host, user, domain)
+.Ed
+where the
+.Sy host ,
+.Sy user ,
+and
+.Sy domain
+are character string names for the corresponding component.
+Any of the comma separated fields may be empty to specify a ``wildcard'' value
+or may consist of the string ``-'' to specify ``no valid value''.
+The members of the list may be separated by whitespace and/or commas;
+the ``\e'' character may be used at the end of a line to specify
+line continuation.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm netgroup
+database.
+.Pp
+Lines that begin with a # are treated as comments.
+.Sh FILES
+.Bl -tag -width /etc/netgroup -compact
+.It Pa /etc/netgroup
+the netgroup database.
+.El
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr exports 5
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.Sh BUGS
+The interpretation of access restrictions based on the member tuples of a
+netgroup is left up to the various network applications.
+Also, it is not obvious how the domain specification
+applies to the BSD environment.
diff --git a/sbin/mountd/pathnames.h b/sbin/mountd/pathnames.h
new file mode 100644
index 000000000000..aa1c55523103
--- /dev/null
+++ b/sbin/mountd/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * 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_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/sbin/newfs/Makefile b/sbin/newfs/Makefile
new file mode 100644
index 000000000000..23942725acf7
--- /dev/null
+++ b/sbin/newfs/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= newfs
+SRCS= dkcksum.c getmntopts.c newfs.c mkfs.c
+MAN8= newfs.0
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+=-DMFS -I${MOUNT}
+.PATH: ${MOUNT} ${.CURDIR}/../disklabel
+
+LINKS= ${BINDIR}/newfs ${BINDIR}/mount_mfs
+MLINKS= newfs.8 mount_mfs.8 newfs.8 mfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
new file mode 100644
index 000000000000..80cb7340c00b
--- /dev/null
+++ b/sbin/newfs/mkfs.c
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (c) 1980, 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkfs.c 8.3 (Berkeley) 2/3/94";
+#endif /* not lint */
+
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <sys/disklabel.h>
+
+#ifndef STANDALONE
+#include <a.out.h>
+#include <stdio.h>
+#endif
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+
+/*
+ * We limit the size of the inode map to be no more than a
+ * third of the cylinder group space, since we must leave at
+ * least an equal amount of space for the block map.
+ *
+ * N.B.: MAXIPG must be a multiple of INOPB(fs).
+ */
+#define MAXIPG(fs) roundup((fs)->fs_bsize * NBBY / 3, INOPB(fs))
+
+#define UMASK 0755
+#define MAXINOPB (MAXBSIZE / sizeof(struct dinode))
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+/*
+ * variables set up by front end.
+ */
+extern int mfs; /* run as the memory based filesystem */
+extern int Nflag; /* run mkfs without writing file system */
+extern int Oflag; /* format as an 4.3BSD file system */
+extern int fssize; /* file system size */
+extern int ntracks; /* # tracks/cylinder */
+extern int nsectors; /* # sectors/track */
+extern int nphyssectors; /* # sectors/track including spares */
+extern int secpercyl; /* sectors per cylinder */
+extern int sectorsize; /* bytes/sector */
+extern int rpm; /* revolutions/minute of drive */
+extern int interleave; /* hardware sector interleave */
+extern int trackskew; /* sector 0 skew, per track */
+extern int headswitch; /* head switch time, usec */
+extern int trackseek; /* track-to-track seek, usec */
+extern int fsize; /* fragment size */
+extern int bsize; /* block size */
+extern int cpg; /* cylinders/cylinder group */
+extern int cpgflg; /* cylinders/cylinder group flag was given */
+extern int minfree; /* free space threshold */
+extern int opt; /* optimization preference (space or time) */
+extern int density; /* number of bytes per inode */
+extern int maxcontig; /* max contiguous blocks to allocate */
+extern int rotdelay; /* rotational delay between blocks */
+extern int maxbpg; /* maximum blocks per file in a cyl group */
+extern int nrpos; /* # of distinguished rotational positions */
+extern int bbsize; /* boot block size */
+extern int sbsize; /* superblock size */
+extern u_long memleft; /* virtual memory available */
+extern caddr_t membase; /* start address of memory based filesystem */
+extern caddr_t malloc(), calloc();
+
+union {
+ struct fs fs;
+ char pad[SBSIZE];
+} fsun;
+#define sblock fsun.fs
+struct csum *fscs;
+
+union {
+ struct cg cg;
+ char pad[MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+struct dinode zino[MAXBSIZE / sizeof(struct dinode)];
+
+int fsi, fso;
+daddr_t alloc();
+
+mkfs(pp, fsys, fi, fo)
+ struct partition *pp;
+ char *fsys;
+ int fi, fo;
+{
+ register long i, mincpc, mincpg, inospercg;
+ long cylno, rpos, blk, j, warn = 0;
+ long used, mincpgcnt, bpcg;
+ long mapcramped, inodecramped;
+ long postblsize, rotblsize, totalsbsize;
+ int ppid, status;
+ time_t utime;
+ quad_t sizepb;
+ void started();
+
+#ifndef STANDALONE
+ time(&utime);
+#endif
+ if (mfs) {
+ ppid = getpid();
+ (void) signal(SIGUSR1, started);
+ if (i = fork()) {
+ if (i == -1) {
+ perror("mfs");
+ exit(10);
+ }
+ if (waitpid(i, &status, 0) != -1 && WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+ exit(11);
+ /* NOTREACHED */
+ }
+ (void)malloc(0);
+ if (fssize * sectorsize > memleft)
+ fssize = (memleft - 16384) / sectorsize;
+ if ((membase = malloc(fssize * sectorsize)) == 0)
+ exit(12);
+ }
+ fsi = fi;
+ fso = fo;
+ if (Oflag) {
+ sblock.fs_inodefmt = FS_42INODEFMT;
+ sblock.fs_maxsymlinklen = 0;
+ } else {
+ sblock.fs_inodefmt = FS_44INODEFMT;
+ sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
+ }
+ /*
+ * Validate the given file system size.
+ * Verify that its last block can actually be accessed.
+ */
+ if (fssize <= 0)
+ printf("preposterous size %d\n", fssize), exit(13);
+ wtfs(fssize - 1, sectorsize, (char *)&sblock);
+ /*
+ * collect and verify the sector and track info
+ */
+ sblock.fs_nsect = nsectors;
+ sblock.fs_ntrak = ntracks;
+ if (sblock.fs_ntrak <= 0)
+ printf("preposterous ntrak %d\n", sblock.fs_ntrak), exit(14);
+ if (sblock.fs_nsect <= 0)
+ printf("preposterous nsect %d\n", sblock.fs_nsect), exit(15);
+ /*
+ * collect and verify the block and fragment sizes
+ */
+ sblock.fs_bsize = bsize;
+ sblock.fs_fsize = fsize;
+ if (!POWEROF2(sblock.fs_bsize)) {
+ printf("block size must be a power of 2, not %d\n",
+ sblock.fs_bsize);
+ exit(16);
+ }
+ if (!POWEROF2(sblock.fs_fsize)) {
+ printf("fragment size must be a power of 2, not %d\n",
+ sblock.fs_fsize);
+ exit(17);
+ }
+ if (sblock.fs_fsize < sectorsize) {
+ printf("fragment size %d is too small, minimum is %d\n",
+ sblock.fs_fsize, sectorsize);
+ exit(18);
+ }
+ if (sblock.fs_bsize < MINBSIZE) {
+ printf("block size %d is too small, minimum is %d\n",
+ sblock.fs_bsize, MINBSIZE);
+ exit(19);
+ }
+ if (sblock.fs_bsize < sblock.fs_fsize) {
+ printf("block size (%d) cannot be smaller than fragment size (%d)\n",
+ sblock.fs_bsize, sblock.fs_fsize);
+ exit(20);
+ }
+ sblock.fs_bmask = ~(sblock.fs_bsize - 1);
+ sblock.fs_fmask = ~(sblock.fs_fsize - 1);
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
+ sblock.fs_bshift++;
+ for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
+ sblock.fs_fshift++;
+ sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
+ for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
+ sblock.fs_fragshift++;
+ if (sblock.fs_frag > MAXFRAG) {
+ printf("fragment size %d is too small, minimum with block size %d is %d\n",
+ sblock.fs_fsize, sblock.fs_bsize,
+ sblock.fs_bsize / MAXFRAG);
+ exit(21);
+ }
+ sblock.fs_nrpos = nrpos;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(daddr_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct dinode);
+ sblock.fs_nspf = sblock.fs_fsize / sectorsize;
+ for (sblock.fs_fsbtodb = 0, i = NSPF(&sblock); i > 1; i >>= 1)
+ sblock.fs_fsbtodb++;
+ sblock.fs_sblkno =
+ roundup(howmany(bbsize + sbsize, sblock.fs_fsize), sblock.fs_frag);
+ sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+ roundup(howmany(sbsize, sblock.fs_fsize), sblock.fs_frag));
+ sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+ sblock.fs_cgoffset = roundup(
+ howmany(sblock.fs_nsect, NSPF(&sblock)), sblock.fs_frag);
+ for (sblock.fs_cgmask = 0xffffffff, i = sblock.fs_ntrak; i > 1; i >>= 1)
+ sblock.fs_cgmask <<= 1;
+ if (!POWEROF2(sblock.fs_ntrak))
+ sblock.fs_cgmask <<= 1;
+ sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+ for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+ /*
+ * Validate specified/determined secpercyl
+ * and calculate minimum cylinders per group.
+ */
+ sblock.fs_spc = secpercyl;
+ for (sblock.fs_cpc = NSPB(&sblock), i = sblock.fs_spc;
+ sblock.fs_cpc > 1 && (i & 1) == 0;
+ sblock.fs_cpc >>= 1, i >>= 1)
+ /* void */;
+ mincpc = sblock.fs_cpc;
+ bpcg = sblock.fs_spc * sectorsize;
+ inospercg = roundup(bpcg / sizeof(struct dinode), INOPB(&sblock));
+ if (inospercg > MAXIPG(&sblock))
+ inospercg = MAXIPG(&sblock);
+ used = (sblock.fs_iblkno + inospercg / INOPF(&sblock)) * NSPF(&sblock);
+ mincpgcnt = howmany(sblock.fs_cgoffset * (~sblock.fs_cgmask) + used,
+ sblock.fs_spc);
+ mincpg = roundup(mincpgcnt, mincpc);
+ /*
+ * Ensure that cylinder group with mincpg has enough space
+ * for block maps.
+ */
+ sblock.fs_cpg = mincpg;
+ sblock.fs_ipg = inospercg;
+ if (maxcontig > 1)
+ sblock.fs_contigsumsize = MIN(maxcontig, FS_MAXCONTIG);
+ mapcramped = 0;
+ while (CGSIZE(&sblock) > sblock.fs_bsize) {
+ mapcramped = 1;
+ if (sblock.fs_bsize < MAXBSIZE) {
+ sblock.fs_bsize <<= 1;
+ if ((i & 1) == 0) {
+ i >>= 1;
+ } else {
+ sblock.fs_cpc <<= 1;
+ mincpc <<= 1;
+ mincpg = roundup(mincpgcnt, mincpc);
+ sblock.fs_cpg = mincpg;
+ }
+ sblock.fs_frag <<= 1;
+ sblock.fs_fragshift += 1;
+ if (sblock.fs_frag <= MAXFRAG)
+ continue;
+ }
+ if (sblock.fs_fsize == sblock.fs_bsize) {
+ printf("There is no block size that");
+ printf(" can support this disk\n");
+ exit(22);
+ }
+ sblock.fs_frag >>= 1;
+ sblock.fs_fragshift -= 1;
+ sblock.fs_fsize <<= 1;
+ sblock.fs_nspf <<= 1;
+ }
+ /*
+ * Ensure that cylinder group with mincpg has enough space for inodes.
+ */
+ inodecramped = 0;
+ used *= sectorsize;
+ inospercg = roundup((mincpg * bpcg - used) / density, INOPB(&sblock));
+ sblock.fs_ipg = inospercg;
+ while (inospercg > MAXIPG(&sblock)) {
+ inodecramped = 1;
+ if (mincpc == 1 || sblock.fs_frag == 1 ||
+ sblock.fs_bsize == MINBSIZE)
+ break;
+ printf("With a block size of %d %s %d\n", sblock.fs_bsize,
+ "minimum bytes per inode is",
+ (mincpg * bpcg - used) / MAXIPG(&sblock) + 1);
+ sblock.fs_bsize >>= 1;
+ sblock.fs_frag >>= 1;
+ sblock.fs_fragshift -= 1;
+ mincpc >>= 1;
+ sblock.fs_cpg = roundup(mincpgcnt, mincpc);
+ if (CGSIZE(&sblock) > sblock.fs_bsize) {
+ sblock.fs_bsize <<= 1;
+ break;
+ }
+ mincpg = sblock.fs_cpg;
+ inospercg =
+ roundup((mincpg * bpcg - used) / density, INOPB(&sblock));
+ sblock.fs_ipg = inospercg;
+ }
+ if (inodecramped) {
+ if (inospercg > MAXIPG(&sblock)) {
+ printf("Minimum bytes per inode is %d\n",
+ (mincpg * bpcg - used) / MAXIPG(&sblock) + 1);
+ } else if (!mapcramped) {
+ printf("With %d bytes per inode, ", density);
+ printf("minimum cylinders per group is %d\n", mincpg);
+ }
+ }
+ if (mapcramped) {
+ printf("With %d sectors per cylinder, ", sblock.fs_spc);
+ printf("minimum cylinders per group is %d\n", mincpg);
+ }
+ if (inodecramped || mapcramped) {
+ if (sblock.fs_bsize != bsize)
+ printf("%s to be changed from %d to %d\n",
+ "This requires the block size",
+ bsize, sblock.fs_bsize);
+ if (sblock.fs_fsize != fsize)
+ printf("\t%s to be changed from %d to %d\n",
+ "and the fragment size",
+ fsize, sblock.fs_fsize);
+ exit(23);
+ }
+ /*
+ * Calculate the number of cylinders per group
+ */
+ sblock.fs_cpg = cpg;
+ if (sblock.fs_cpg % mincpc != 0) {
+ printf("%s groups must have a multiple of %d cylinders\n",
+ cpgflg ? "Cylinder" : "Warning: cylinder", mincpc);
+ sblock.fs_cpg = roundup(sblock.fs_cpg, mincpc);
+ if (!cpgflg)
+ cpg = sblock.fs_cpg;
+ }
+ /*
+ * Must ensure there is enough space for inodes.
+ */
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ while (sblock.fs_ipg > MAXIPG(&sblock)) {
+ inodecramped = 1;
+ sblock.fs_cpg -= mincpc;
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ }
+ /*
+ * Must ensure there is enough space to hold block map.
+ */
+ while (CGSIZE(&sblock) > sblock.fs_bsize) {
+ mapcramped = 1;
+ sblock.fs_cpg -= mincpc;
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ }
+ sblock.fs_fpg = (sblock.fs_cpg * sblock.fs_spc) / NSPF(&sblock);
+ if ((sblock.fs_cpg * sblock.fs_spc) % NSPB(&sblock) != 0) {
+ printf("panic (fs_cpg * fs_spc) % NSPF != 0");
+ exit(24);
+ }
+ if (sblock.fs_cpg < mincpg) {
+ printf("cylinder groups must have at least %d cylinders\n",
+ mincpg);
+ exit(25);
+ } else if (sblock.fs_cpg != cpg) {
+ if (!cpgflg)
+ printf("Warning: ");
+ else if (!mapcramped && !inodecramped)
+ exit(26);
+ if (mapcramped && inodecramped)
+ printf("Block size and bytes per inode restrict");
+ else if (mapcramped)
+ printf("Block size restricts");
+ else
+ printf("Bytes per inode restrict");
+ printf(" cylinders per group to %d.\n", sblock.fs_cpg);
+ if (cpgflg)
+ exit(27);
+ }
+ sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
+ /*
+ * Now have size for file system and nsect and ntrak.
+ * Determine number of cylinders and blocks in the file system.
+ */
+ sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+ sblock.fs_ncyl = fssize * NSPF(&sblock) / sblock.fs_spc;
+ if (fssize * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc) {
+ sblock.fs_ncyl++;
+ warn = 1;
+ }
+ if (sblock.fs_ncyl < 1) {
+ printf("file systems must have at least one cylinder\n");
+ exit(28);
+ }
+ /*
+ * Determine feasability/values of rotational layout tables.
+ *
+ * The size of the rotational layout tables is limited by the
+ * size of the superblock, SBSIZE. The amount of space available
+ * for tables is calculated as (SBSIZE - sizeof (struct fs)).
+ * The size of these tables is inversely proportional to the block
+ * size of the file system. The size increases if sectors per track
+ * are not powers of two, because more cylinders must be described
+ * by the tables before the rotational pattern repeats (fs_cpc).
+ */
+ sblock.fs_interleave = interleave;
+ sblock.fs_trackskew = trackskew;
+ sblock.fs_npsect = nphyssectors;
+ sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+ sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
+ if (sblock.fs_ntrak == 1) {
+ sblock.fs_cpc = 0;
+ goto next;
+ }
+ postblsize = sblock.fs_nrpos * sblock.fs_cpc * sizeof(short);
+ rotblsize = sblock.fs_cpc * sblock.fs_spc / NSPB(&sblock);
+ totalsbsize = sizeof(struct fs) + rotblsize;
+ if (sblock.fs_nrpos == 8 && sblock.fs_cpc <= 16) {
+ /* use old static table space */
+ 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);
+ } else {
+ /* use dynamic table space */
+ sblock.fs_postbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = sblock.fs_postbloff + postblsize;
+ totalsbsize += postblsize;
+ }
+ if (totalsbsize > SBSIZE ||
+ sblock.fs_nsect > (1 << NBBY) * NSPB(&sblock)) {
+ printf("%s %s %d %s %d.%s",
+ "Warning: insufficient space in super block for\n",
+ "rotational layout tables with nsect", sblock.fs_nsect,
+ "and ntrak", sblock.fs_ntrak,
+ "\nFile system performance may be impaired.\n");
+ sblock.fs_cpc = 0;
+ goto next;
+ }
+ sblock.fs_sbsize = fragroundup(&sblock, totalsbsize);
+ /*
+ * calculate the available blocks for each rotational position
+ */
+ for (cylno = 0; cylno < sblock.fs_cpc; cylno++)
+ for (rpos = 0; rpos < sblock.fs_nrpos; rpos++)
+ fs_postbl(&sblock, cylno)[rpos] = -1;
+ for (i = (rotblsize - 1) * sblock.fs_frag;
+ i >= 0; i -= sblock.fs_frag) {
+ cylno = cbtocylno(&sblock, i);
+ rpos = cbtorpos(&sblock, i);
+ blk = fragstoblks(&sblock, i);
+ if (fs_postbl(&sblock, cylno)[rpos] == -1)
+ fs_rotbl(&sblock)[blk] = 0;
+ else
+ fs_rotbl(&sblock)[blk] =
+ fs_postbl(&sblock, cylno)[rpos] - blk;
+ fs_postbl(&sblock, cylno)[rpos] = blk;
+ }
+next:
+ /*
+ * Compute/validate number of cylinder groups.
+ */
+ sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg;
+ if (sblock.fs_ncyl % sblock.fs_cpg)
+ sblock.fs_ncg++;
+ sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
+ i = MIN(~sblock.fs_cgmask, sblock.fs_ncg - 1);
+ if (cgdmin(&sblock, i) - cgbase(&sblock, i) >= sblock.fs_fpg) {
+ printf("inode blocks/cyl group (%d) >= data blocks (%d)\n",
+ cgdmin(&sblock, i) - cgbase(&sblock, i) / sblock.fs_frag,
+ sblock.fs_fpg / sblock.fs_frag);
+ printf("number of cylinders per cylinder group (%d) %s.\n",
+ sblock.fs_cpg, "must be increased");
+ exit(29);
+ }
+ j = sblock.fs_ncg - 1;
+ if ((i = fssize - j * sblock.fs_fpg) < sblock.fs_fpg &&
+ cgdmin(&sblock, j) - cgbase(&sblock, j) > i) {
+ if (j == 0) {
+ printf("Filesystem must have at least %d sectors\n",
+ NSPF(&sblock) *
+ (cgdmin(&sblock, 0) + 3 * sblock.fs_frag));
+ exit(30);
+ }
+ printf("Warning: inode blocks/cyl group (%d) >= data blocks (%d) in last\n",
+ (cgdmin(&sblock, j) - cgbase(&sblock, j)) / sblock.fs_frag,
+ i / sblock.fs_frag);
+ printf(" cylinder group. This implies %d sector(s) cannot be allocated.\n",
+ i * NSPF(&sblock));
+ sblock.fs_ncg--;
+ sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg;
+ sblock.fs_size = fssize = sblock.fs_ncyl * sblock.fs_spc /
+ NSPF(&sblock);
+ warn = 0;
+ }
+ if (warn && !mfs) {
+ printf("Warning: %d sector(s) in last cylinder unallocated\n",
+ sblock.fs_spc -
+ (fssize * NSPF(&sblock) - (sblock.fs_ncyl - 1)
+ * sblock.fs_spc));
+ }
+ /*
+ * fill in remaining fields of the super block
+ */
+ sblock.fs_csaddr = cgdmin(&sblock, 0);
+ sblock.fs_cssize =
+ fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+ i = sblock.fs_bsize / sizeof(struct csum);
+ sblock.fs_csmask = ~(i - 1);
+ for (sblock.fs_csshift = 0; i > 1; i >>= 1)
+ sblock.fs_csshift++;
+ fscs = (struct csum *)calloc(1, sblock.fs_cssize);
+ sblock.fs_magic = FS_MAGIC;
+ sblock.fs_rotdelay = rotdelay;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxcontig = maxcontig;
+ sblock.fs_headswitch = headswitch;
+ sblock.fs_trkseek = trackseek;
+ sblock.fs_maxbpg = maxbpg;
+ sblock.fs_rps = rpm / 60;
+ sblock.fs_optim = opt;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_cstotal.cs_ndir = 0;
+ sblock.fs_cstotal.cs_nbfree = 0;
+ sblock.fs_cstotal.cs_nifree = 0;
+ sblock.fs_cstotal.cs_nffree = 0;
+ sblock.fs_fmod = 0;
+ sblock.fs_ronly = 0;
+ /*
+ * Dump out summary information about file system.
+ */
+ if (!mfs) {
+ printf("%s:\t%d sectors in %d %s of %d tracks, %d sectors\n",
+ fsys, sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl,
+ "cylinders", sblock.fs_ntrak, sblock.fs_nsect);
+#define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("\t%.1fMB in %d cyl groups (%d c/g, %.2fMB/g, %d i/g)\n",
+ (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ncg, sblock.fs_cpg,
+ (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ipg);
+#undef B2MBFACTOR
+ }
+ /*
+ * Now build the cylinders group blocks and
+ * then print out indices of cylinder groups.
+ */
+ if (!mfs)
+ printf("super-block backups (for fsck -b #) at:");
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime);
+ if (mfs)
+ continue;
+ if (cylno % 9 == 0)
+ printf("\n");
+ printf(" %d,", fsbtodb(&sblock, cgsblock(&sblock, cylno)));
+ }
+ if (!mfs)
+ printf("\n");
+ if (Nflag && !mfs)
+ exit(0);
+ /*
+ * Now construct the initial file system,
+ * then write out the super-block.
+ */
+ fsinit(utime);
+ sblock.fs_time = utime;
+ wtfs((int)SBOFF / sectorsize, sbsize, (char *)&sblock);
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize)
+ wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize,
+ ((char *)fscs) + i);
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ sbsize, (char *)&sblock);
+ /*
+ * Update information about this partion in pack
+ * label, to that it may be updated on disk.
+ */
+ pp->p_fstype = FS_BSDFFS;
+ pp->p_fsize = sblock.fs_fsize;
+ pp->p_frag = sblock.fs_frag;
+ pp->p_cpg = sblock.fs_cpg;
+ /*
+ * Notify parent process of success.
+ * Dissociate from session and tty.
+ */
+ if (mfs) {
+ kill(ppid, SIGUSR1);
+ (void) setsid();
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) chdir("/");
+ }
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+initcg(cylno, utime)
+ int cylno;
+ time_t utime;
+{
+ daddr_t cbase, d, dlower, dupper, dmax, blkno;
+ long i, j, s;
+ register struct csum *cs;
+
+ /*
+ * Determine block bounds for cylinder group.
+ * Allow space for super block summary information in first
+ * cylinder group.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > sblock.fs_size)
+ dmax = sblock.fs_size;
+ dlower = cgsblock(&sblock, cylno) - cbase;
+ dupper = cgdmin(&sblock, cylno) - cbase;
+ if (cylno == 0)
+ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ cs = fscs + cylno;
+ bzero(&acg, sblock.fs_cgsize);
+ acg.cg_time = utime;
+ acg.cg_magic = CG_MAGIC;
+ acg.cg_cgx = cylno;
+ if (cylno == sblock.fs_ncg - 1)
+ acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg;
+ else
+ acg.cg_ncyl = sblock.fs_cpg;
+ acg.cg_niblk = sblock.fs_ipg;
+ acg.cg_ndblk = dmax - cbase;
+ if (sblock.fs_contigsumsize > 0)
+ acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
+ acg.cg_btotoff = &acg.cg_space[0] - (u_char *)(&acg.cg_link);
+ acg.cg_boff = acg.cg_btotoff + sblock.fs_cpg * sizeof(long);
+ acg.cg_iusedoff = acg.cg_boff +
+ sblock.fs_cpg * sblock.fs_nrpos * sizeof(short);
+ acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY);
+ if (sblock.fs_contigsumsize <= 0) {
+ acg.cg_nextfreeoff = acg.cg_freeoff +
+ howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
+ } else {
+ acg.cg_clustersumoff = acg.cg_freeoff + howmany
+ (sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY) -
+ sizeof(long);
+ acg.cg_clustersumoff =
+ roundup(acg.cg_clustersumoff, sizeof(long));
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(long);
+ acg.cg_nextfreeoff = acg.cg_clusteroff + howmany
+ (sblock.fs_cpg * sblock.fs_spc / NSPB(&sblock), NBBY);
+ }
+ if (acg.cg_nextfreeoff - (long)(&acg.cg_link) > sblock.fs_cgsize) {
+ printf("Panic: cylinder group too big\n");
+ exit(37);
+ }
+ acg.cg_cs.cs_nifree += sblock.fs_ipg;
+ if (cylno == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(&acg), i);
+ acg.cg_cs.cs_nifree--;
+ }
+ for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag)
+ wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, (char *)zino);
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0; d < dlower; d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]++;
+ }
+ sblock.fs_dsize += dlower;
+ }
+ sblock.fs_dsize += acg.cg_ndblk - dupper;
+ if (i = dupper % sblock.fs_frag) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree(&acg), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper; d + sblock.fs_frag <= dmax - cbase; ) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]++;
+ d += sblock.fs_frag;
+ }
+ if (d < dmax - cbase) {
+ acg.cg_frsum[dmax - cbase - d]++;
+ for (; d < dmax - cbase; d++) {
+ setbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ long *sump = cg_clustersum(&acg);
+ u_char *mapp = cg_clustersfree(&acg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < acg.cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
+ sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
+ sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
+ sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
+ *cs = acg.cg_cs;
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
+ sblock.fs_bsize, (char *)&acg);
+}
+
+/*
+ * initialize the file system
+ */
+struct dinode node;
+
+#ifdef LOSTDIR
+#define PREDEFDIR 3
+#else
+#define PREDEFDIR 2
+#endif
+
+struct direct root_dir[] = {
+ { ROOTINO, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+#ifdef LOSTDIR
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found" },
+#endif
+};
+struct odirect {
+ u_long d_ino;
+ u_short d_reclen;
+ u_short d_namlen;
+ u_char d_name[MAXNAMLEN + 1];
+} oroot_dir[] = {
+ { ROOTINO, sizeof(struct direct), 1, "." },
+ { ROOTINO, sizeof(struct direct), 2, ".." },
+#ifdef LOSTDIR
+ { LOSTFOUNDINO, sizeof(struct direct), 10, "lost+found" },
+#endif
+};
+#ifdef LOSTDIR
+struct direct lost_found_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+ { 0, DIRBLKSIZ, 0, 0, 0 },
+};
+struct odirect olost_found_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct direct), 1, "." },
+ { ROOTINO, sizeof(struct direct), 2, ".." },
+ { 0, DIRBLKSIZ, 0, 0 },
+};
+#endif
+char buf[MAXBSIZE];
+
+fsinit(utime)
+ time_t utime;
+{
+ int i;
+
+ /*
+ * initialize the node
+ */
+ node.di_atime.ts_sec = utime;
+ node.di_mtime.ts_sec = utime;
+ node.di_ctime.ts_sec = utime;
+#ifdef LOSTDIR
+ /*
+ * create the lost+found directory
+ */
+ if (Oflag) {
+ (void)makedir((struct direct *)olost_found_dir, 2);
+ for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
+ bcopy(&olost_found_dir[2], &buf[i],
+ DIRSIZ(0, &olost_found_dir[2]));
+ } else {
+ (void)makedir(lost_found_dir, 2);
+ for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
+ bcopy(&lost_found_dir[2], &buf[i],
+ DIRSIZ(0, &lost_found_dir[2]));
+ }
+ node.di_mode = IFDIR | UMASK;
+ node.di_nlink = 2;
+ node.di_size = sblock.fs_bsize;
+ node.di_db[0] = alloc(node.di_size, node.di_mode);
+ node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
+ wtfs(fsbtodb(&sblock, node.di_db[0]), node.di_size, buf);
+ iput(&node, LOSTFOUNDINO);
+#endif
+ /*
+ * create the root directory
+ */
+ if (mfs)
+ node.di_mode = IFDIR | 01777;
+ else
+ node.di_mode = IFDIR | UMASK;
+ node.di_nlink = PREDEFDIR;
+ if (Oflag)
+ node.di_size = makedir((struct direct *)oroot_dir, PREDEFDIR);
+ else
+ node.di_size = makedir(root_dir, PREDEFDIR);
+ node.di_db[0] = alloc(sblock.fs_fsize, node.di_mode);
+ node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
+ wtfs(fsbtodb(&sblock, node.di_db[0]), sblock.fs_fsize, buf);
+ iput(&node, ROOTINO);
+}
+
+/*
+ * construct a set of directory entries in "buf".
+ * return size of directory.
+ */
+makedir(protodir, entries)
+ register struct direct *protodir;
+ int entries;
+{
+ char *cp;
+ int i, spcleft;
+
+ spcleft = DIRBLKSIZ;
+ for (cp = buf, i = 0; i < entries - 1; i++) {
+ protodir[i].d_reclen = DIRSIZ(0, &protodir[i]);
+ bcopy(&protodir[i], cp, protodir[i].d_reclen);
+ cp += protodir[i].d_reclen;
+ spcleft -= protodir[i].d_reclen;
+ }
+ protodir[i].d_reclen = spcleft;
+ bcopy(&protodir[i], cp, DIRSIZ(0, &protodir[i]));
+ return (DIRBLKSIZ);
+}
+
+/*
+ * allocate a block or frag
+ */
+daddr_t
+alloc(size, mode)
+ int size;
+ int mode;
+{
+ int i, frag;
+ daddr_t d, blkno;
+
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ if (acg.cg_magic != CG_MAGIC) {
+ printf("cg 0: bad magic number\n");
+ return (0);
+ }
+ if (acg.cg_cs.cs_nbfree == 0) {
+ printf("first cylinder group ran out of space\n");
+ return (0);
+ }
+ for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
+ if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag))
+ goto goth;
+ printf("internal error: can't find block in cyl 0\n");
+ return (0);
+goth:
+ blkno = fragstoblks(&sblock, d);
+ clrblock(&sblock, cg_blksfree(&acg), blkno);
+ clrbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ fscs[0].cs_nbfree--;
+ if (mode & IFDIR) {
+ acg.cg_cs.cs_ndir++;
+ sblock.fs_cstotal.cs_ndir++;
+ fscs[0].cs_ndir++;
+ }
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
+ if (size != sblock.fs_bsize) {
+ frag = howmany(size, sblock.fs_fsize);
+ fscs[0].cs_nffree += sblock.fs_frag - frag;
+ sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_frsum[sblock.fs_frag - frag]++;
+ for (i = frag; i < sblock.fs_frag; i++)
+ setbit(cg_blksfree(&acg), d + i);
+ }
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ return (d);
+}
+
+/*
+ * Allocate an inode on the disk
+ */
+iput(ip, ino)
+ register struct dinode *ip;
+ register ino_t ino;
+{
+ struct dinode buf[MAXINOPB];
+ daddr_t d;
+ int c;
+
+ c = ino_to_cg(&sblock, ino);
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ if (acg.cg_magic != CG_MAGIC) {
+ printf("cg 0: bad magic number\n");
+ exit(31);
+ }
+ acg.cg_cs.cs_nifree--;
+ setbit(cg_inosused(&acg), ino);
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ sblock.fs_cstotal.cs_nifree--;
+ fscs[0].cs_nifree--;
+ if (ino >= sblock.fs_ipg * sblock.fs_ncg) {
+ printf("fsinit: inode value out of range (%d).\n", ino);
+ exit(32);
+ }
+ d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
+ rdfs(d, sblock.fs_bsize, buf);
+ buf[ino_to_fsbo(&sblock, ino)] = *ip;
+ wtfs(d, sblock.fs_bsize, buf);
+}
+
+/*
+ * Notify parent process that the filesystem has created itself successfully.
+ */
+void
+started()
+{
+
+ exit(0);
+}
+
+/*
+ * Replace libc function with one suited to our needs.
+ */
+caddr_t
+malloc(size)
+ register u_long size;
+{
+ char *base, *i;
+ static u_long pgsz;
+ struct rlimit rlp;
+
+ if (pgsz == 0) {
+ base = sbrk(0);
+ pgsz = getpagesize() - 1;
+ i = (char *)((u_long)(base + pgsz) &~ pgsz);
+ base = sbrk(i - base);
+ if (getrlimit(RLIMIT_DATA, &rlp) < 0)
+ perror("getrlimit");
+ rlp.rlim_cur = rlp.rlim_max;
+ if (setrlimit(RLIMIT_DATA, &rlp) < 0)
+ perror("setrlimit");
+ memleft = rlp.rlim_max - (u_long)base;
+ }
+ size = (size + pgsz) &~ pgsz;
+ if (size > memleft)
+ size = memleft;
+ memleft -= size;
+ if (size == 0)
+ return (0);
+ return ((caddr_t)sbrk(size));
+}
+
+/*
+ * Replace libc function with one suited to our needs.
+ */
+caddr_t
+realloc(ptr, size)
+ char *ptr;
+ u_long size;
+{
+ void *p;
+
+ if ((p = malloc(size)) == NULL)
+ return (NULL);
+ bcopy(ptr, p, size);
+ free(ptr);
+ return (p);
+}
+
+/*
+ * Replace libc function with one suited to our needs.
+ */
+char *
+calloc(size, numelm)
+ u_long size, numelm;
+{
+ caddr_t base;
+
+ size *= numelm;
+ base = malloc(size);
+ bzero(base, size);
+ return (base);
+}
+
+/*
+ * Replace libc function with one suited to our needs.
+ */
+free(ptr)
+ char *ptr;
+{
+
+ /* do not worry about it for now */
+}
+
+/*
+ * read a block from the file system
+ */
+rdfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+
+ if (mfs) {
+ bcopy(membase + bno * sectorsize, bf, size);
+ return;
+ }
+ if (lseek(fsi, (off_t)bno * sectorsize, 0) < 0) {
+ printf("seek error: %ld\n", bno);
+ perror("rdfs");
+ exit(33);
+ }
+ n = read(fsi, bf, size);
+ if (n != size) {
+ printf("read error: %ld\n", bno);
+ perror("rdfs");
+ exit(34);
+ }
+}
+
+/*
+ * write a block to the file system
+ */
+wtfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+
+ if (mfs) {
+ bcopy(bf, membase + bno * sectorsize, size);
+ return;
+ }
+ if (Nflag)
+ return;
+ if (lseek(fso, (off_t)bno * sectorsize, SEEK_SET) < 0) {
+ printf("seek error: %ld\n", bno);
+ perror("wtfs");
+ exit(35);
+ }
+ n = write(fso, bf, size);
+ if (n != size) {
+ printf("write error: %ld\n", bno);
+ perror("wtfs");
+ exit(36);
+ }
+}
+
+/*
+ * check if a block is available
+ */
+isblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ unsigned char mask;
+
+ switch (fs->fs_frag) {
+ case 8:
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+#ifdef STANDALONE
+ printf("isblock bad fs_frag %d\n", fs->fs_frag);
+#else
+ fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
+#endif
+ return (0);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+clrblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ switch ((fs)->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ return;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+#ifdef STANDALONE
+ printf("clrblock bad fs_frag %d\n", fs->fs_frag);
+#else
+ fprintf(stderr, "clrblock bad fs_frag %d\n", fs->fs_frag);
+#endif
+ return;
+ }
+}
+
+/*
+ * put a block into the map
+ */
+setblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ switch (fs->fs_frag) {
+ case 8:
+ cp[h] = 0xff;
+ return;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+#ifdef STANDALONE
+ printf("setblock bad fs_frag %d\n", fs->fs_frag);
+#else
+ fprintf(stderr, "setblock bad fs_frag %d\n", fs->fs_frag);
+#endif
+ return;
+ }
+}
diff --git a/sbin/newfs/newfs.8 b/sbin/newfs/newfs.8
new file mode 100644
index 000000000000..0ffcfdfafa73
--- /dev/null
+++ b/sbin/newfs/newfs.8
@@ -0,0 +1,282 @@
+.\" Copyright (c) 1983, 1987, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)newfs.8 8.3 (Berkeley) 3/27/94
+.\"
+.Dd March 27, 1994
+.Dt NEWFS 8
+.Os BSD 4.2
+.Sh NAME
+.Nm newfs ,
+.Nm mfs
+.Nd construct a new file system
+.Sh SYNOPSIS
+.Nm newfs
+.Op Fl NO
+.Op Fl S Ar sector-size
+.Op Fl a maxcontig
+.Op Fl b Ar block-size
+.Op Fl c Ar cylinders
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl f Ar frag-size
+.Op Fl i Ar bytes
+.Op Fl k Ar skew
+.Op Fl l Ar interleave
+.Op Fl m Ar free space
+.Op Fl o Ar optimization
+.Op Fl p Ar sectors
+.Op Fl r Ar revolutions
+.Op Fl s Ar size
+.Op Fl t Ar tracks
+.Op Fl u Ar sectors
+.Op Fl x Ar sectors
+.Ar special
+.Nm mount_mfs
+.Op Fl N
+.Op Fl a maxcontig
+.Op Fl b Ar block-size
+.Op Fl c Ar cylinders
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl f Ar frag-size
+.Op Fl i Ar bytes
+.Op Fl m Ar free space
+.Op Fl o Ar options
+.Op Fl s Ar size
+.Ar special node
+.Sh DESCRIPTION
+.Nm Newfs
+replaces the more obtuse
+.Xr mkfs 8
+program.
+Before running
+.Nm newfs
+or
+.Nm mount_mfs ,
+the disk must be labeled using
+.Xr disklabel 8 .
+.Nm Newfs
+builds a file system on the specified special device
+basing its defaults on the information in the disk label.
+Typically the defaults are reasonable, however
+.Nm newfs
+has numerous options to allow the defaults to be selectively overridden.
+.Pp
+.Nm Mount_mfs
+is used to build a file system in virtual memory and then mount it
+on a specified node.
+.Nm Mount_mfs
+exits and the contents of the file system are lost
+when the file system is unmounted.
+If
+.Nm mount_mfs
+is sent a signal while running,
+for example during system shutdown,
+it will attempt to unmount its
+corresponding file system.
+The parameters to
+.Nm mount_mfs
+are the same as those to
+.Nm newfs .
+The special file is only used to read the disk label which provides
+a set of configuration parameters for the memory based file system.
+The special file is typically that of the primary swap area,
+since that is where the file system will be backed up when
+free memory gets low and the memory supporting
+the file system has to be paged.
+.Pp
+The following options define the general layout policies.
+.Bl -tag -width Fl
+.It Fl N
+Causes the file system parameters to be printed out
+without really creating the file system.
+.It Fl O
+Creates a 4.3BSD format filesystem.
+This options is primarily used to build root filesystems
+that can be understood by older boot ROMs.
+.It Fl a Ar maxcontig
+This specifies the maximum number of contiguous blocks that will be
+laid out before forcing a rotational delay (see the
+.Fl d
+option).
+The default value is one.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl b Ar block-size
+The block size of the file system, in bytes.
+.It Fl c Ar #cylinders/group
+The number of cylinders per cylinder group in a file system.
+The default value is 16.
+.It Fl d Ar rotdelay
+This specifies the expected time (in milliseconds) to service a transfer
+completion interrupt and initiate a new transfer on the same disk.
+The default is 4 milliseconds.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl e Ar maxbpg
+This indicates the maximum number of blocks any single file can
+allocate out of a cylinder group before it is forced to begin
+allocating blocks from another cylinder group.
+The default is about one quarter of the total blocks in a cylinder group.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl f Ar frag-size
+The fragment size of the file system in bytes.
+.It Fl i Ar number of bytes per inode
+This specifies the density of inodes in the file system.
+The default is to create an inode for each 2048 bytes of data space.
+If fewer inodes are desired, a larger number should be used;
+to create more inodes a smaller number should be given.
+.It Fl m Ar free space \&%
+The percentage of space reserved from normal users; the minimum free
+space threshold.
+The default value used is 10%.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl o Ar optimization\ preference
+.Pq ``space'' or ``time''
+The file system can either be instructed to try to minimize the time spent
+allocating blocks, or to try to minimize the space fragmentation on the disk.
+If the value of minfree (see above) is less than 10%,
+the default is to optimize for space;
+if the value of minfree is greater than or equal to 10%,
+the default is to optimize for time.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl s Ar size
+The size of the file system in sectors.
+.El
+.Pp
+The following options override the standard sizes for the disk geometry.
+Their default values are taken from the disk label.
+Changing these defaults is useful only when using
+.Nm newfs
+to build a file system whose raw image will eventually be used on a
+different type of disk than the one on which it is initially created
+(for example on a write-once disk).
+Note that changing any of these values from their defaults will make
+it impossible for
+.Xr fsck
+to find the alternate superblocks if the standard superblock is lost.
+.Bl -tag -width Fl
+.It Fl S Ar sector-size
+The size of a sector in bytes (almost never anything but 512).
+.It Fl k Ar sector \&0 skew , per track
+Used to describe perturbations in the media format to compensate for
+a slow controller.
+Track skew is the offset of sector 0 on track N relative to sector 0
+on track N-1 on the same cylinder.
+.It Fl l Ar hardware sector interleave
+Used to describe perturbations in the media format to compensate for
+a slow controller.
+Interleave is physical sector interleave on each track,
+specified as the denominator of the ratio:
+.Dl sectors read/sectors passed over
+Thus an interleave of 1/1 implies contiguous layout, while 1/2 implies
+logical sector 0 is separated by one sector from logical sector 1.
+.It Fl p Ar spare sectors per track
+Spare sectors (bad sector replacements) are physical sectors that occupy
+space at the end of each track.
+They are not counted as part of the sectors/track
+.Pq Fl u
+since they are not available to the file system for data allocation.
+.It Fl r Ar revolutions/minute
+The speed of the disk in revolutions per minute.
+.It Fl t Ar #tracks/cylinder
+The number of tracks/cylinder available for data allocation by the file
+system.
+.It Fl u Ar sectors/track
+The number of sectors per track available for data allocation by the file
+system.
+This does not include sectors reserved at the end of each track for bad
+block replacement (see the
+.Fl p
+option.)
+.It Fl x Ar spare sectors per cylinder
+Spare sectors (bad sector replacements) are physical sectors that occupy
+space at the end of the last track in the cylinder.
+They are deducted from the sectors/track
+.Pq Fl u
+of the last track of each cylinder since they are not available to the file
+system for data allocation.
+.El
+.Pp
+The options to the
+.Nm mount_mfs
+command are as described for the
+.Nm newfs
+command, except for the
+.Fl o
+option.
+.Pp
+That option is 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
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr dumpfs 8 ,
+.Xr disklabel 8 ,
+.Xr diskpart 8 ,
+.Xr fsck 8 ,
+.Xr format 8 ,
+.Xr mount 8 ,
+.Xr tunefs 8
+.Rs
+.%A M. McKusick
+.%A W. Joy
+.%A S. Leffler
+.%A R. Fabry
+.%T A Fast File System for UNIX ,
+.%J ACM Transactions on Computer Systems 2
+.%V 3
+.%P pp 181-197
+.%D August 1984
+.%O (reprinted in the BSD System Manager's Manual)
+.Re
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c
new file mode 100644
index 000000000000..c7748e8e59d0
--- /dev/null
+++ b/sbin/newfs/newfs.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 1983, 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 sccsid[] = "@(#)newfs.c 8.8 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * newfs: friendly front end to mkfs
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ { NULL },
+};
+
+#if __STDC__
+void fatal(const char *fmt, ...);
+#else
+void fatal();
+#endif
+
+#define COMPAT /* allow non-labeled disks */
+
+/*
+ * The following two constants set the default block and fragment sizes.
+ * Both constants must be a power of 2 and meet the following constraints:
+ * MINBSIZE <= DESBLKSIZE <= MAXBSIZE
+ * sectorsize <= DESFRAGSIZE <= DESBLKSIZE
+ * DESBLKSIZE / DESFRAGSIZE <= 8
+ */
+#define DFL_FRAGSIZE 1024
+#define DFL_BLKSIZE 8192
+
+/*
+ * Cylinder groups may have up to many cylinders. The actual
+ * number used depends upon how much information can be stored
+ * on a single cylinder. The default is to use 16 cylinders
+ * per group.
+ */
+#define DESCPG 16 /* desired fs_cpg */
+
+/*
+ * ROTDELAY gives the minimum number of milliseconds to initiate
+ * another disk transfer on the same cylinder. It is used in
+ * determining the rotationally optimal layout for disk blocks
+ * within a file; the default of fs_rotdelay is 4ms.
+ */
+#define ROTDELAY 4
+
+/*
+ * MAXBLKPG determines the maximum number of data blocks which are
+ * placed in a single cylinder group. The default is one indirect
+ * block worth of data blocks.
+ */
+#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t))
+
+/*
+ * Each file system has a number of inodes statically allocated.
+ * We allocate one inode slot per NFPI fragments, expecting this
+ * to be far more than we will ever need.
+ */
+#define NFPI 4
+
+/*
+ * For each cylinder we keep track of the availability of blocks at different
+ * rotational positions, so that we can lay out the data to be picked
+ * up with minimum rotational latency. NRPOS is the default number of
+ * rotational positions that we distinguish. With NRPOS of 8 the resolution
+ * of our summary information is 2ms for a typical 3600 rpm drive.
+ */
+#define NRPOS 8 /* number distinct rotational positions */
+
+
+int mfs; /* run as the memory based filesystem */
+int Nflag; /* run without writing file system */
+int Oflag; /* format as an 4.3BSD file system */
+int fssize; /* file system size */
+int ntracks; /* # tracks/cylinder */
+int nsectors; /* # sectors/track */
+int nphyssectors; /* # sectors/track including spares */
+int secpercyl; /* sectors per cylinder */
+int trackspares = -1; /* spare sectors per track */
+int cylspares = -1; /* spare sectors per cylinder */
+int sectorsize; /* bytes/sector */
+#ifdef tahoe
+int realsectorsize; /* bytes/sector in hardware */
+#endif
+int rpm; /* revolutions/minute of drive */
+int interleave; /* hardware sector interleave */
+int trackskew = -1; /* sector 0 skew, per track */
+int headswitch; /* head switch time, usec */
+int trackseek; /* track-to-track seek, usec */
+int fsize = 0; /* fragment size */
+int bsize = 0; /* block size */
+int cpg = DESCPG; /* cylinders/cylinder group */
+int cpgflg; /* cylinders/cylinder group flag was given */
+int minfree = MINFREE; /* free space threshold */
+int opt = DEFAULTOPT; /* optimization preference (space or time) */
+int density; /* number of bytes per inode */
+int maxcontig = 0; /* max contiguous blocks to allocate */
+int rotdelay = ROTDELAY; /* rotational delay between blocks */
+int maxbpg; /* maximum blocks per file in a cyl group */
+int nrpos = NRPOS; /* # of distinguished rotational positions */
+int bbsize = BBSIZE; /* boot block size */
+int sbsize = SBSIZE; /* superblock size */
+int mntflags = MNT_ASYNC; /* flags to be passed to mount */
+u_long memleft; /* virtual memory available */
+caddr_t membase; /* start address of memory based filesystem */
+#ifdef COMPAT
+char *disktype;
+int unlabeled;
+#endif
+
+char device[MAXPATHLEN];
+char *progname;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ register int ch;
+ register struct partition *pp;
+ register struct disklabel *lp;
+ struct disklabel *getdisklabel();
+ struct partition oldpartition;
+ struct stat st;
+ struct statfs *mp;
+ int fsi, fso, len, n;
+ char *cp, *s1, *s2, *special, *opstring, buf[BUFSIZ];
+
+ if (progname = rindex(*argv, '/'))
+ ++progname;
+ else
+ progname = *argv;
+
+ if (strstr(progname, "mfs")) {
+ mfs = 1;
+ Nflag++;
+ }
+
+ opstring = mfs ?
+ "NT:a:b:c:d:e:f:i:m:o:s:" :
+ "NOS:T:a:b:c:d:e:f:i:k:l:m:n:o:p:r:s:t:u:x:";
+ while ((ch = getopt(argc, argv, opstring)) != EOF)
+ switch (ch) {
+ case 'N':
+ Nflag = 1;
+ break;
+ case 'O':
+ Oflag = 1;
+ break;
+ case 'S':
+ if ((sectorsize = atoi(optarg)) <= 0)
+ fatal("%s: bad sector size", optarg);
+ break;
+#ifdef COMPAT
+ case 'T':
+ disktype = optarg;
+ break;
+#endif
+ case 'a':
+ if ((maxcontig = atoi(optarg)) <= 0)
+ fatal("%s: bad maximum contiguous blocks\n",
+ optarg);
+ break;
+ case 'b':
+ if ((bsize = atoi(optarg)) < MINBSIZE)
+ fatal("%s: bad block size", optarg);
+ break;
+ case 'c':
+ if ((cpg = atoi(optarg)) <= 0)
+ fatal("%s: bad cylinders/group", optarg);
+ cpgflg++;
+ break;
+ case 'd':
+ if ((rotdelay = atoi(optarg)) < 0)
+ fatal("%s: bad rotational delay\n", optarg);
+ break;
+ case 'e':
+ if ((maxbpg = atoi(optarg)) <= 0)
+ fatal("%s: bad blocks per file in a cylinder group\n",
+ optarg);
+ break;
+ case 'f':
+ if ((fsize = atoi(optarg)) <= 0)
+ fatal("%s: bad fragment size", optarg);
+ break;
+ case 'i':
+ if ((density = atoi(optarg)) <= 0)
+ fatal("%s: bad bytes per inode\n", optarg);
+ break;
+ case 'k':
+ if ((trackskew = atoi(optarg)) < 0)
+ fatal("%s: bad track skew", optarg);
+ break;
+ case 'l':
+ if ((interleave = atoi(optarg)) <= 0)
+ fatal("%s: bad interleave", optarg);
+ break;
+ case 'm':
+ if ((minfree = atoi(optarg)) < 0 || minfree > 99)
+ fatal("%s: bad free space %%\n", optarg);
+ break;
+ case 'n':
+ if ((nrpos = atoi(optarg)) <= 0)
+ fatal("%s: bad rotational layout count\n",
+ optarg);
+ break;
+ case 'o':
+ if (mfs)
+ getmntopts(optarg, mopts, &mntflags);
+ else {
+ if (strcmp(optarg, "space") == 0)
+ opt = FS_OPTSPACE;
+ else if (strcmp(optarg, "time") == 0)
+ opt = FS_OPTTIME;
+ else
+ fatal("%s: unknown optimization preference: use `space' or `time'.");
+ }
+ break;
+ case 'p':
+ if ((trackspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per track",
+ optarg);
+ break;
+ case 'r':
+ if ((rpm = atoi(optarg)) <= 0)
+ fatal("%s: bad revolutions/minute\n", optarg);
+ break;
+ case 's':
+ if ((fssize = atoi(optarg)) <= 0)
+ fatal("%s: bad file system size", optarg);
+ break;
+ case 't':
+ if ((ntracks = atoi(optarg)) <= 0)
+ fatal("%s: bad total tracks", optarg);
+ break;
+ case 'u':
+ if ((nsectors = atoi(optarg)) <= 0)
+ fatal("%s: bad sectors/track", optarg);
+ break;
+ case 'x':
+ if ((cylspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per cylinder",
+ optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 && (mfs || argc != 1))
+ usage();
+
+ special = argv[0];
+ cp = rindex(special, '/');
+ if (cp == 0) {
+ /*
+ * No path prefix; try /dev/r%s then /dev/%s.
+ */
+ (void)sprintf(device, "%sr%s", _PATH_DEV, special);
+ if (stat(device, &st) == -1)
+ (void)sprintf(device, "%s%s", _PATH_DEV, special);
+ special = device;
+ }
+ if (Nflag) {
+ fso = -1;
+ } else {
+ fso = open(special, O_WRONLY);
+ if (fso < 0)
+ fatal("%s: %s", special, strerror(errno));
+
+ /* Bail if target special is mounted */
+ n = getmntinfo(&mp, MNT_NOWAIT);
+ if (n == 0)
+ fatal("%s: getmntinfo: %s", special, strerror(errno));
+
+ len = sizeof(_PATH_DEV) - 1;
+ s1 = special;
+ if (strncmp(_PATH_DEV, s1, len) == 0)
+ s1 += len;
+
+ while (--n >= 0) {
+ s2 = mp->f_mntfromname;
+ if (strncmp(_PATH_DEV, s2, len) == 0) {
+ s2 += len - 1;
+ *s2 = 'r';
+ }
+ if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0)
+ fatal("%s is mounted on %s",
+ special, mp->f_mntonname);
+ ++mp;
+ }
+ }
+ if (mfs && disktype != NULL) {
+ lp = (struct disklabel *)getdiskbyname(disktype);
+ if (lp == NULL)
+ fatal("%s: unknown disk type", disktype);
+ pp = &lp->d_partitions[1];
+ } else {
+ fsi = open(special, O_RDONLY);
+ if (fsi < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if (fstat(fsi, &st) < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if ((st.st_mode & S_IFMT) != S_IFCHR && !mfs)
+ printf("%s: %s: not a character-special device\n",
+ progname, special);
+ cp = index(argv[0], '\0') - 1;
+ if (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp))
+ fatal("%s: can't figure out file system partition",
+ argv[0]);
+#ifdef COMPAT
+ if (!mfs && disktype == NULL)
+ disktype = argv[1];
+#endif
+ lp = getdisklabel(special, fsi);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_size == 0)
+ fatal("%s: `%c' partition is unavailable",
+ argv[0], *cp);
+ if (pp->p_fstype == FS_BOOT)
+ fatal("%s: `%c' partition overlaps boot program",
+ argv[0], *cp);
+ }
+ if (fssize == 0)
+ fssize = pp->p_size;
+ if (fssize > pp->p_size && !mfs)
+ fatal("%s: maximum file system size on the `%c' partition is %d",
+ argv[0], *cp, pp->p_size);
+ if (rpm == 0) {
+ rpm = lp->d_rpm;
+ if (rpm <= 0)
+ rpm = 3600;
+ }
+ if (ntracks == 0) {
+ ntracks = lp->d_ntracks;
+ if (ntracks <= 0)
+ fatal("%s: no default #tracks", argv[0]);
+ }
+ if (nsectors == 0) {
+ nsectors = lp->d_nsectors;
+ if (nsectors <= 0)
+ fatal("%s: no default #sectors/track", argv[0]);
+ }
+ if (sectorsize == 0) {
+ sectorsize = lp->d_secsize;
+ if (sectorsize <= 0)
+ fatal("%s: no default sector size", argv[0]);
+ }
+ if (trackskew == -1) {
+ trackskew = lp->d_trackskew;
+ if (trackskew < 0)
+ trackskew = 0;
+ }
+ if (interleave == 0) {
+ interleave = lp->d_interleave;
+ if (interleave <= 0)
+ interleave = 1;
+ }
+ if (fsize == 0) {
+ fsize = pp->p_fsize;
+ if (fsize <= 0)
+ fsize = MAX(DFL_FRAGSIZE, lp->d_secsize);
+ }
+ if (bsize == 0) {
+ bsize = pp->p_frag * pp->p_fsize;
+ if (bsize <= 0)
+ bsize = MIN(DFL_BLKSIZE, 8 * fsize);
+ }
+ /*
+ * Maxcontig sets the default for the maximum number of blocks
+ * that may be allocated sequentially. With filesystem clustering
+ * it is possible to allocate contiguous blocks up to the maximum
+ * transfer size permitted by the controller or buffering.
+ */
+ if (maxcontig == 0)
+ maxcontig = MAX(1, MIN(MAXPHYS, MAXBSIZE) / bsize - 1);
+ if (density == 0)
+ density = NFPI * fsize;
+ if (minfree < MINFREE && opt != FS_OPTSPACE) {
+ fprintf(stderr, "Warning: changing optimization to space ");
+ fprintf(stderr, "because minfree is less than %d%%\n", MINFREE);
+ opt = FS_OPTSPACE;
+ }
+ if (trackspares == -1) {
+ trackspares = lp->d_sparespertrack;
+ if (trackspares < 0)
+ trackspares = 0;
+ }
+ nphyssectors = nsectors + trackspares;
+ if (cylspares == -1) {
+ cylspares = lp->d_sparespercyl;
+ if (cylspares < 0)
+ cylspares = 0;
+ }
+ secpercyl = nsectors * ntracks - cylspares;
+ if (secpercyl != lp->d_secpercyl)
+ fprintf(stderr, "%s (%d) %s (%lu)\n",
+ "Warning: calculated sectors per cylinder", secpercyl,
+ "disagrees with disk label", lp->d_secpercyl);
+ if (maxbpg == 0)
+ maxbpg = MAXBLKPG(bsize);
+ headswitch = lp->d_headswitch;
+ trackseek = lp->d_trkseek;
+#ifdef notdef /* label may be 0 if faked up by kernel */
+ bbsize = lp->d_bbsize;
+ sbsize = lp->d_sbsize;
+#endif
+ oldpartition = *pp;
+#ifdef tahoe
+ realsectorsize = sectorsize;
+ if (sectorsize != DEV_BSIZE) { /* XXX */
+ int secperblk = DEV_BSIZE / sectorsize;
+
+ sectorsize = DEV_BSIZE;
+ nsectors /= secperblk;
+ nphyssectors /= secperblk;
+ secpercyl /= secperblk;
+ fssize /= secperblk;
+ pp->p_size /= secperblk;
+ }
+#endif
+ mkfs(pp, special, fsi, fso);
+#ifdef tahoe
+ if (realsectorsize != DEV_BSIZE)
+ pp->p_size *= DEV_BSIZE / realsectorsize;
+#endif
+ if (!Nflag && bcmp(pp, &oldpartition, sizeof(oldpartition)))
+ rewritelabel(special, fso, lp);
+ if (!Nflag)
+ close(fso);
+ close(fsi);
+#ifdef MFS
+ if (mfs) {
+ struct mfs_args args;
+
+ sprintf(buf, "mfs:%d", getpid());
+ args.fspec = buf;
+ args.export.ex_root = -2;
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ args.base = membase;
+ args.size = fssize * sectorsize;
+ if (mount(MOUNT_MFS, argv[1], mntflags, &args) < 0)
+ fatal("%s: %s", argv[1], strerror(errno));
+ }
+#endif
+ exit(0);
+}
+
+#ifdef COMPAT
+char lmsg[] = "%s: can't read disk label; disk type must be specified";
+#else
+char lmsg[] = "%s: can't read disk label";
+#endif
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+#ifdef COMPAT
+ if (disktype) {
+ struct disklabel *lp, *getdiskbyname();
+
+ unlabeled++;
+ lp = getdiskbyname(disktype);
+ if (lp == NULL)
+ fatal("%s: unknown disk type", disktype);
+ return (lp);
+ }
+#endif
+ warn("ioctl (GDINFO)");
+ fatal(lmsg, s);
+ }
+ return (&lab);
+}
+
+rewritelabel(s, fd, lp)
+ char *s;
+ int fd;
+ register struct disklabel *lp;
+{
+#ifdef COMPAT
+ if (unlabeled)
+ return;
+#endif
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) {
+ warn("ioctl (WDINFO)");
+ fatal("%s: can't rewrite disk label", s);
+ }
+#if vax
+ if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
+ register i;
+ int cfd;
+ daddr_t alt;
+ char specname[64];
+ char blk[1024];
+ char *cp;
+
+ /*
+ * Make name for 'c' partition.
+ */
+ strcpy(specname, s);
+ cp = specname + strlen(specname) - 1;
+ if (!isdigit(*cp))
+ *cp = 'c';
+ cfd = open(specname, O_WRONLY);
+ if (cfd < 0)
+ fatal("%s: %s", specname, strerror(errno));
+ bzero(blk, sizeof(blk));
+ *(struct disklabel *)(blk + LABELOFFSET) = *lp;
+ alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
+ for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
+ if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize,
+ L_SET) == -1)
+ fatal("lseek to badsector area: %s",
+ strerror(errno));
+ if (write(cfd, blk, lp->d_secsize) < lp->d_secsize)
+ warn("alternate label %d write", i/2);
+ }
+ close(cfd);
+ }
+#endif
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (fcntl(STDERR_FILENO, F_GETFL) < 0) {
+ openlog(progname, LOG_CONS, LOG_DAEMON);
+ vsyslog(LOG_ERR, fmt, ap);
+ closelog();
+ } else {
+ vwarnx(fmt, ap);
+ }
+ va_end(ap);
+ exit(1);
+ /*NOTREACHED*/
+}
+
+usage()
+{
+ if (mfs) {
+ fprintf(stderr,
+ "usage: %s [ -fsoptions ] special-device mount-point\n",
+ progname);
+ } else
+ fprintf(stderr,
+ "usage: %s [ -fsoptions ] special-device%s\n",
+ progname,
+#ifdef COMPAT
+ " [device-type]");
+#else
+ "");
+#endif
+ fprintf(stderr, "where fsoptions are:\n");
+ fprintf(stderr,
+ "\t-N do not create file system, just print out parameters\n");
+ fprintf(stderr, "\t-O create a 4.3BSD format filesystem\n");
+ fprintf(stderr, "\t-S sector size\n");
+#ifdef COMPAT
+ fprintf(stderr, "\t-T disktype\n");
+#endif
+ fprintf(stderr, "\t-a maximum contiguous blocks\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c cylinders/group\n");
+ fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
+ fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
+ fprintf(stderr, "\t-f frag size\n");
+ fprintf(stderr, "\t-i number of bytes per inode\n");
+ fprintf(stderr, "\t-k sector 0 skew, per track\n");
+ fprintf(stderr, "\t-l hardware sector interleave\n");
+ fprintf(stderr, "\t-m minimum free space %%\n");
+ fprintf(stderr, "\t-n number of distinguished rotational positions\n");
+ fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
+ fprintf(stderr, "\t-p spare sectors per track\n");
+ fprintf(stderr, "\t-s file system size (sectors)\n");
+ fprintf(stderr, "\t-r revolutions/minute\n");
+ fprintf(stderr, "\t-t tracks/cylinder\n");
+ fprintf(stderr, "\t-u sectors/track\n");
+ fprintf(stderr, "\t-x spare sectors per cylinder\n");
+ exit(1);
+}
diff --git a/sbin/newlfs/Makefile b/sbin/newlfs/Makefile
new file mode 100644
index 000000000000..c79c83e2116c
--- /dev/null
+++ b/sbin/newlfs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/18/93
+
+PROG= newlfs
+CFLAGS+=-I/sys/ufs/lfs
+SRCS= dkcksum.c lfs.c lfs_cksum.c misc.c newfs.c
+MAN8= newlfs.0
+.PATH: /sys/ufs/lfs ${.CURDIR}/../disklabel
+
+.include <bsd.prog.mk>
diff --git a/sbin/newlfs/config.h b/sbin/newlfs/config.h
new file mode 100644
index 000000000000..9c8b394a9d5b
--- /dev/null
+++ b/sbin/newlfs/config.h
@@ -0,0 +1,134 @@
+/*-
+ * 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.
+ *
+ * @(#)config.h 8.2 (Berkeley) 4/22/94
+ */
+
+/*
+ * The first boot and super blocks are given in absolute disk addresses.
+ * The byte-offset forms are preferred, as they don't imply a sector size.
+ */
+#define BBSIZE 8192
+#define SBSIZE 8192
+
+/*
+ * The following two constants set the default block and fragment sizes.
+ * Both constants must be a power of 2 and meet the following constraints:
+ * MINBSIZE <= DESBLKSIZE <= MAXBSIZE
+ * sectorsize <= DESFRAGSIZE <= DESBLKSIZE
+ * DESBLKSIZE / DESFRAGSIZE <= 8
+ */
+#define DFL_FRAGSIZE 1024
+#define DFL_BLKSIZE 8192
+
+/*
+ * Cylinder groups may have up to many cylinders. The actual
+ * number used depends upon how much information can be stored
+ * on a single cylinder. The default is to use 16 cylinders
+ * per group.
+ */
+#define DESCPG 16 /* desired fs_cpg */
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 90% and 100% full; thus the default value of
+ * fs_minfree is 10%. With 10% free space, fragmentation is not a
+ * problem, so we choose to optimize for time.
+ */
+#define MINFREE 10
+#define DEFAULTOPT FS_OPTTIME
+
+/*
+ * Preference for optimization.
+ */
+#define FS_OPTTIME 0 /* minimize allocation time */
+#define FS_OPTSPACE 1 /* minimize disk fragmentation */
+
+
+/*
+ * ROTDELAY gives the minimum number of milliseconds to initiate
+ * another disk transfer on the same cylinder. It is used in
+ * determining the rotationally optimal layout for disk blocks
+ * within a file; the default of fs_rotdelay is 4ms.
+ */
+#define ROTDELAY 4
+
+/*
+ * MAXCONTIG sets the default for the maximum number of blocks
+ * that may be allocated sequentially. Since UNIX drivers are
+ * not capable of scheduling multi-block transfers, this defaults
+ * to 1 (ie no contiguous blocks are allocated).
+ */
+#define MAXCONTIG 1
+
+/*
+ * MAXBLKPG determines the maximum number of data blocks which are
+ * placed in a single cylinder group. The default is one indirect
+ * block worth of data blocks.
+ */
+#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t))
+
+/*
+ * Each file system has a number of inodes statically allocated.
+ * We allocate one inode slot per NFPI fragments, expecting this
+ * to be far more than we will ever need.
+ */
+#define NFPI 4
+
+/*
+ * For each cylinder we keep track of the availability of blocks at different
+ * rotational positions, so that we can lay out the data to be picked
+ * up with minimum rotational latency. NRPOS is the default number of
+ * rotational positions that we distinguish. With NRPOS of 8 the resolution
+ * of our summary information is 2ms for a typical 3600 rpm drive.
+ */
+#define NRPOS 8 /* number distinct rotational positions */
+
+/*
+ * The following constants set the default block and segment size for a log
+ * structured file system. Both must be powers of two and the segment size
+ * must be a multiple of the block size. We also set minimum block and segment
+ * sizes.
+ */
+#define LFS_MINSEGSIZE (64*1024)
+#define DFL_LFSSEG (1024 * 1024)
+#define DFL_LFSSEG_SHIFT 20
+#define DFL_LFSSEG_MASK 0xFFFFF
+
+#define LFS_MINBLOCKSIZE 1024
+#define DFL_LFSBLOCK 4096
+#define DFL_LFSBLOCK_SHIFT 12
+#define DFL_LFSBLOCK_MASK 0xFFF
diff --git a/sbin/newlfs/extern.h b/sbin/newlfs/extern.h
new file mode 100644
index 000000000000..c65cdba30589
--- /dev/null
+++ b/sbin/newlfs/extern.h
@@ -0,0 +1,45 @@
+/*-
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/5/93
+ */
+
+u_long cksum __P((void *, size_t));
+u_short dkcksum __P((struct disklabel *));
+void fatal __P((const char *fmt, ...));
+u_int log2 __P((u_int));
+int make_lfs
+ __P((int, struct disklabel *, struct partition *, int, int, int));
+int mkfs __P((struct partition *, char *, int, int));
+
+extern char *progname;
+extern char *special;
diff --git a/sbin/newlfs/lfs.c b/sbin/newlfs/lfs.c
new file mode 100644
index 000000000000..c79a0dcb8349
--- /dev/null
+++ b/sbin/newlfs/lfs.c
@@ -0,0 +1,673 @@
+/*-
+ * 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[] = "@(#)lfs.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/lfs/lfs.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "extern.h"
+
+/*
+ * This table is indexed by the log base 2 of the block size.
+ * It returns the maximum file size allowed in a file system
+ * with the specified block size. For block sizes smaller than
+ * 8K, the size is limited by tha maximum number of blocks that
+ * can be reached by triply indirect blocks:
+ * NDADDR + INOPB(bsize) + INOPB(bsize)^2 + INOPB(bsize)^3
+ * For block size of 8K or larger, the file size is limited by the
+ * number of blocks that can be represented in the file system. Since
+ * we use negative block numbers to represent indirect blocks, we can
+ * have a maximum of 2^31 blocks.
+ */
+
+u_quad_t maxtable[] = {
+ /* 1 */ -1,
+ /* 2 */ -1,
+ /* 4 */ -1,
+ /* 8 */ -1,
+ /* 16 */ -1,
+ /* 32 */ -1,
+ /* 64 */ -1,
+ /* 128 */ -1,
+ /* 256 */ -1,
+ /* 512 */ NDADDR + 128 + 128 * 128 + 128 * 128 * 128,
+ /* 1024 */ NDADDR + 256 + 256 * 256 + 256 * 256 * 256,
+ /* 2048 */ NDADDR + 512 + 512 * 512 + 512 * 512 * 512,
+ /* 4096 */ NDADDR + 1024 + 1024 * 1024 + 1024 * 1024 * 1024,
+ /* 8192 */ 1 << 31,
+ /* 16 K */ 1 << 31,
+ /* 32 K */ 1 << 31,
+};
+
+static struct lfs lfs_default = {
+ /* lfs_magic */ LFS_MAGIC,
+ /* lfs_version */ LFS_VERSION,
+ /* lfs_size */ 0,
+ /* lfs_ssize */ DFL_LFSSEG/DFL_LFSBLOCK,
+ /* lfs_dsize */ 0,
+ /* lfs_bsize */ DFL_LFSBLOCK,
+ /* lfs_fsize */ DFL_LFSBLOCK,
+ /* lfs_frag */ 1,
+ /* lfs_free */ LFS_FIRST_INUM,
+ /* lfs_bfree */ 0,
+ /* lfs_nfiles */ 0,
+ /* lfs_avail */ 0,
+ /* lfs_uinodes */ 0,
+ /* lfs_idaddr */ 0,
+ /* lfs_ifile */ LFS_IFILE_INUM,
+ /* lfs_lastseg */ 0,
+ /* lfs_nextseg */ 0,
+ /* lfs_curseg */ 0,
+ /* lfs_offset */ 0,
+ /* lfs_lastpseg */ 0,
+ /* lfs_tstamp */ 0,
+ /* lfs_minfree */ MINFREE,
+ /* lfs_maxfilesize */ 0,
+ /* lfs_dbpseg */ DFL_LFSSEG/DEV_BSIZE,
+ /* lfs_inopb */ DFL_LFSBLOCK/sizeof(struct dinode),
+ /* lfs_ifpb */ DFL_LFSBLOCK/sizeof(IFILE),
+ /* lfs_sepb */ DFL_LFSBLOCK/sizeof(SEGUSE),
+ /* lfs_nindir */ DFL_LFSBLOCK/sizeof(daddr_t),
+ /* lfs_nseg */ 0,
+ /* lfs_nspf */ 0,
+ /* lfs_cleansz */ 0,
+ /* lfs_segtabsz */ 0,
+ /* lfs_segmask */ DFL_LFSSEG_MASK,
+ /* lfs_segshift */ DFL_LFSSEG_SHIFT,
+ /* lfs_bmask */ DFL_LFSBLOCK_MASK,
+ /* lfs_bshift */ DFL_LFSBLOCK_SHIFT,
+ /* lfs_ffmask */ 0,
+ /* lfs_ffshift */ 0,
+ /* lfs_fbmask */ 0,
+ /* lfs_fbshift */ 0,
+ /* lfs_fsbtodb */ 0,
+ /* lfs_sushift */ 0,
+ /* lfs_sboffs */ { 0 },
+ /* lfs_sp */ NULL,
+ /* lfs_ivnode */ NULL,
+ /* lfs_seglock */ 0,
+ /* lfs_lockpid */ 0,
+ /* lfs_iocount */ 0,
+ /* lfs_writer */ 0,
+ /* lfs_dirops */ 0,
+ /* lfs_doifile */ 0,
+ /* lfs_nactive */ 0,
+ /* lfs_fmod */ 0,
+ /* lfs_clean */ 0,
+ /* lfs_ronly */ 0,
+ /* lfs_flags */ 0,
+ /* lfs_fsmnt */ { 0 },
+ /* lfs_pad */ { 0 },
+ /* lfs_cksum */ 0
+};
+
+
+struct direct lfs_root_dir[] = {
+ { ROOTINO, sizeof(struct direct), DT_DIR, 1, "."},
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".."},
+ { LFS_IFILE_INUM, sizeof(struct direct), DT_REG, 5, "ifile"},
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found"},
+};
+
+struct direct lfs_lf_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+};
+
+static daddr_t make_dinode
+ __P((ino_t, struct dinode *, int, daddr_t, struct lfs *));
+static void make_dir __P(( void *, struct direct *, int));
+static void put __P((int, off_t, void *, size_t));
+
+int
+make_lfs(fd, lp, partp, minfree, block_size, seg_size)
+ int fd;
+ struct disklabel *lp;
+ struct partition *partp;
+ int minfree;
+ int block_size;
+ int seg_size;
+{
+ struct dinode *dip; /* Pointer to a disk inode */
+ struct dinode *dpagep; /* Pointer to page of disk inodes */
+ CLEANERINFO *cleaninfo; /* Segment cleaner information table */
+ FINFO file_info; /* File info structure in summary blocks */
+ IFILE *ifile; /* Pointer to array of ifile structures */
+ IFILE *ip; /* Pointer to array of ifile structures */
+ struct lfs *lfsp; /* Superblock */
+ SEGUSE *segp; /* Segment usage table */
+ SEGUSE *segtable; /* Segment usage table */
+ SEGSUM summary; /* Segment summary structure */
+ SEGSUM *sp; /* Segment summary pointer */
+ daddr_t last_sb_addr; /* Address of superblocks */
+ daddr_t last_addr; /* Previous segment address */
+ daddr_t sb_addr; /* Address of superblocks */
+ daddr_t seg_addr; /* Address of current segment */
+ void *ipagep; /* Pointer to the page we use to write stuff */
+ void *sump; /* Used to copy stuff into segment buffer */
+ u_long *block_array; /* Array of logical block nos to put in sum */
+ u_long blocks_used; /* Number of blocks in first segment */
+ u_long *dp; /* Used to computed checksum on data */
+ u_long *datasump; /* Used to computed checksum on data */
+ int block_array_size; /* How many entries in block array */
+ int bsize; /* Block size */
+ int db_per_fb; /* Disk blocks per file block */
+ int i, j;
+ int off; /* Offset at which to write */
+ int sb_interval; /* number of segs between super blocks */
+ int seg_seek; /* Seek offset for a segment */
+ int ssize; /* Segment size */
+ int sum_size; /* Size of the summary block */
+
+ lfsp = &lfs_default;
+
+ if (!(bsize = block_size))
+ bsize = DFL_LFSBLOCK;
+ if (!(ssize = seg_size))
+ ssize = DFL_LFSSEG;
+
+ /* Modify parts of superblock overridden by command line arguments */
+ if (bsize != DFL_LFSBLOCK) {
+ lfsp->lfs_bshift = log2(bsize);
+ if (1 << lfsp->lfs_bshift != bsize)
+ fatal("%d: block size not a power of 2", bsize);
+ lfsp->lfs_bsize = bsize;
+ lfsp->lfs_fsize = bsize;
+ lfsp->lfs_bmask = bsize - 1;
+ lfsp->lfs_inopb = bsize / sizeof(struct dinode);
+/* MIS -- should I round to power of 2 */
+ lfsp->lfs_ifpb = bsize / sizeof(IFILE);
+ lfsp->lfs_sepb = bsize / sizeof(SEGUSE);
+ lfsp->lfs_nindir = bsize / sizeof(daddr_t);
+ }
+
+ if (ssize != DFL_LFSSEG) {
+ lfsp->lfs_segshift = log2(ssize);
+ if (1 << lfsp->lfs_segshift != ssize)
+ fatal("%d: segment size not power of 2", ssize);
+ lfsp->lfs_ssize = ssize;
+ lfsp->lfs_segmask = ssize - 1;
+ lfsp->lfs_dbpseg = ssize / DEV_BSIZE;
+ }
+ lfsp->lfs_ssize = ssize >> lfsp->lfs_bshift;
+
+ if (minfree)
+ lfsp->lfs_minfree = minfree;
+
+ /*
+ * Fill in parts of superblock that can be computed from file system
+ * size, disk geometry and current time.
+ */
+ db_per_fb = bsize/lp->d_secsize;
+ lfsp->lfs_fsbtodb = log2(db_per_fb);
+ lfsp->lfs_sushift = log2(lfsp->lfs_sepb);
+ lfsp->lfs_size = partp->p_size >> lfsp->lfs_fsbtodb;
+ lfsp->lfs_dsize = lfsp->lfs_size - (LFS_LABELPAD >> lfsp->lfs_bshift);
+ lfsp->lfs_nseg = lfsp->lfs_dsize / lfsp->lfs_ssize;
+ lfsp->lfs_maxfilesize = maxtable[lfsp->lfs_bshift] << lfsp->lfs_bshift;
+
+ /*
+ * The number of free blocks is set from the number of segments times
+ * the segment size - 2 (that we never write because we need to make
+ * sure the cleaner can run). Then we'll subtract off the room for the
+ * superblocks ifile entries and segment usage table.
+ */
+ lfsp->lfs_dsize = fsbtodb(lfsp, (lfsp->lfs_nseg - 2) * lfsp->lfs_ssize);
+ lfsp->lfs_bfree = lfsp->lfs_dsize;
+ lfsp->lfs_segtabsz = SEGTABSIZE_SU(lfsp);
+ lfsp->lfs_cleansz = CLEANSIZE_SU(lfsp);
+ if ((lfsp->lfs_tstamp = time(NULL)) == -1)
+ fatal("time: %s", strerror(errno));
+ if ((sb_interval = lfsp->lfs_nseg / LFS_MAXNUMSB) < LFS_MIN_SBINTERVAL)
+ sb_interval = LFS_MIN_SBINTERVAL;
+
+ /*
+ * Now, lay out the file system. We need to figure out where
+ * the superblocks go, initialize the checkpoint information
+ * for the first two superblocks, initialize the segment usage
+ * information, put the segusage information in the ifile, create
+ * the first block of IFILE structures, and link all the IFILE
+ * structures into a free list.
+ */
+
+ /* Figure out where the superblocks are going to live */
+ lfsp->lfs_sboffs[0] = LFS_LABELPAD/lp->d_secsize;
+ for (i = 1; i < LFS_MAXNUMSB; i++) {
+ sb_addr = ((i * sb_interval) <<
+ (lfsp->lfs_segshift - lfsp->lfs_bshift + lfsp->lfs_fsbtodb))
+ + lfsp->lfs_sboffs[0];
+ if (sb_addr > partp->p_size)
+ break;
+ lfsp->lfs_sboffs[i] = sb_addr;
+ }
+ last_sb_addr = lfsp->lfs_sboffs[i - 1];
+ lfsp->lfs_lastseg = lfsp->lfs_sboffs[0];
+ lfsp->lfs_nextseg =
+ lfsp->lfs_sboffs[1] ? lfsp->lfs_sboffs[1] : lfsp->lfs_sboffs[0];
+ lfsp->lfs_curseg = lfsp->lfs_lastseg;
+
+ /*
+ * Initialize the segment usage table. The first segment will
+ * contain the superblock, the cleanerinfo (cleansz), the segusage
+ * table * (segtabsz), 1 block's worth of IFILE entries, the root
+ * directory, the lost+found directory and one block's worth of
+ * inodes (containing the ifile, root, and l+f inodes).
+ */
+ if (!(cleaninfo = malloc(lfsp->lfs_cleansz << lfsp->lfs_bshift)))
+ fatal("%s", strerror(errno));
+ cleaninfo->clean = lfsp->lfs_nseg - 1;
+ cleaninfo->dirty = 1;
+
+ if (!(segtable = malloc(lfsp->lfs_segtabsz << lfsp->lfs_bshift)))
+ fatal("%s", strerror(errno));
+ segp = segtable;
+ blocks_used = lfsp->lfs_segtabsz + lfsp->lfs_cleansz + 4;
+ segp->su_nbytes = ((blocks_used - 1) << lfsp->lfs_bshift) +
+ 3 * sizeof(struct dinode) + LFS_SUMMARY_SIZE;
+ segp->su_lastmod = lfsp->lfs_tstamp;
+ segp->su_nsums = 1; /* 1 summary blocks */
+ segp->su_ninos = 1; /* 1 inode block */
+ segp->su_flags = SEGUSE_SUPERBLOCK | SEGUSE_DIRTY;
+ lfsp->lfs_bfree -= LFS_SUMMARY_SIZE / lp->d_secsize;
+ lfsp->lfs_bfree -=
+ fsbtodb(lfsp, lfsp->lfs_cleansz + lfsp->lfs_segtabsz + 4);
+
+ /*
+ * Now figure out the address of the ifile inode. The inode block
+ * appears immediately after the segment summary.
+ */
+ lfsp->lfs_idaddr = (LFS_LABELPAD + LFS_SBPAD + LFS_SUMMARY_SIZE) /
+ lp->d_secsize;
+
+ for (segp = segtable + 1, i = 1; i < lfsp->lfs_nseg; i++, segp++) {
+ if ((i % sb_interval) == 0) {
+ segp->su_flags = SEGUSE_SUPERBLOCK;
+ lfsp->lfs_bfree -= (LFS_SBPAD / lp->d_secsize);
+ } else
+ segp->su_flags = 0;
+ segp->su_lastmod = 0;
+ segp->su_nbytes = 0;
+ segp->su_ninos = 0;
+ segp->su_nsums = 0;
+ }
+
+ /*
+ * Initialize dynamic accounting. The blocks available for
+ * writing are the bfree blocks minus 1 segment summary for
+ * each segment since you can't write any new data without
+ * creating a segment summary - 2 segments that the cleaner
+ * needs.
+ */
+ lfsp->lfs_avail = lfsp->lfs_bfree - lfsp->lfs_nseg -
+ fsbtodb(lfsp, 2 * lfsp->lfs_ssize);
+ lfsp->lfs_uinodes = 0;
+ /*
+ * Ready to start writing segments. The first segment is different
+ * because it contains the segment usage table and the ifile inode
+ * as well as a superblock. For the rest of the segments, set the
+ * time stamp to be 0 so that the first segment is the most recent.
+ * For each segment that is supposed to contain a copy of the super
+ * block, initialize its first few blocks and its segment summary
+ * to indicate this.
+ */
+ lfsp->lfs_nfiles = LFS_FIRST_INUM - 1;
+ lfsp->lfs_cksum =
+ cksum(lfsp, sizeof(struct lfs) - sizeof(lfsp->lfs_cksum));
+
+ /* Now create a block of disk inodes */
+ if (!(dpagep = malloc(lfsp->lfs_bsize)))
+ fatal("%s", strerror(errno));
+ dip = (struct dinode *)dpagep;
+ bzero(dip, lfsp->lfs_bsize);
+
+ /* Create a block of IFILE structures. */
+ if (!(ipagep = malloc(lfsp->lfs_bsize)))
+ fatal("%s", strerror(errno));
+ ifile = (IFILE *)ipagep;
+
+ /*
+ * Initialize IFILE. It is the next block following the
+ * block of inodes (whose address has been calculated in
+ * lfsp->lfs_idaddr;
+ */
+ sb_addr = lfsp->lfs_idaddr + lfsp->lfs_bsize / lp->d_secsize;
+ sb_addr = make_dinode(LFS_IFILE_INUM, dip,
+ lfsp->lfs_cleansz + lfsp->lfs_segtabsz+1, sb_addr, lfsp);
+ dip->di_mode = IFREG|IREAD|IWRITE;
+ ip = &ifile[LFS_IFILE_INUM];
+ ip->if_version = 1;
+ ip->if_daddr = lfsp->lfs_idaddr;
+
+ /* Initialize the ROOT Directory */
+ sb_addr = make_dinode(ROOTINO, ++dip, 1, sb_addr, lfsp);
+ dip->di_mode = IFDIR|IREAD|IWRITE|IEXEC;
+ dip->di_size = DIRBLKSIZ;
+ dip->di_nlink = 3;
+ ip = &ifile[ROOTINO];
+ ip->if_version = 1;
+ ip->if_daddr = lfsp->lfs_idaddr;
+
+ /* Initialize the lost+found Directory */
+ sb_addr = make_dinode(LOSTFOUNDINO, ++dip, 1, sb_addr, lfsp);
+ dip->di_mode = IFDIR|IREAD|IWRITE|IEXEC;
+ dip->di_size = DIRBLKSIZ;
+ dip->di_nlink = 2;
+ ip = &ifile[LOSTFOUNDINO];
+ ip->if_version = 1;
+ ip->if_daddr = lfsp->lfs_idaddr;
+
+ /* Make all the other dinodes invalid */
+ for (i = INOPB(lfsp)-3, dip++; i; i--, dip++)
+ dip->di_inumber = LFS_UNUSED_INUM;
+
+
+ /* Link remaining IFILE entries in free list */
+ for (ip = &ifile[LFS_FIRST_INUM], i = LFS_FIRST_INUM;
+ i < lfsp->lfs_ifpb; ++ip) {
+ ip->if_version = 1;
+ ip->if_daddr = LFS_UNUSED_DADDR;
+ ip->if_nextfree = ++i;
+ }
+ ifile[lfsp->lfs_ifpb - 1].if_nextfree = LFS_UNUSED_INUM;
+
+ /* Now, write the segment */
+
+ /* Compute a checksum across all the data you're writing */
+ dp = datasump = malloc (blocks_used * sizeof(u_long));
+ *dp++ = ((u_long *)dpagep)[0]; /* inode block */
+ for (i = 0; i < lfsp->lfs_cleansz; i++)
+ *dp++ = ((u_long *)cleaninfo)[(i << lfsp->lfs_bshift) /
+ sizeof(u_long)]; /* Cleaner info */
+ for (i = 0; i < lfsp->lfs_segtabsz; i++)
+ *dp++ = ((u_long *)segtable)[(i << lfsp->lfs_bshift) /
+ sizeof(u_long)]; /* Segusage table */
+ *dp++ = ((u_long *)ifile)[0]; /* Ifile */
+
+ /* Still need the root and l+f bytes; get them later */
+
+ /* Write out the inode block */
+ off = LFS_LABELPAD + LFS_SBPAD + LFS_SUMMARY_SIZE;
+ put(fd, off, dpagep, lfsp->lfs_bsize);
+ free(dpagep);
+ off += lfsp->lfs_bsize;
+
+ /* Write out the ifile */
+
+ put(fd, off, cleaninfo, lfsp->lfs_cleansz << lfsp->lfs_bshift);
+ off += (lfsp->lfs_cleansz << lfsp->lfs_bshift);
+ (void)free(cleaninfo);
+
+ put(fd, off, segtable, lfsp->lfs_segtabsz << lfsp->lfs_bshift);
+ off += (lfsp->lfs_segtabsz << lfsp->lfs_bshift);
+ (void)free(segtable);
+
+ put(fd, off, ifile, lfsp->lfs_bsize);
+ off += lfsp->lfs_bsize;
+
+ /*
+ * use ipagep for space for writing out other stuff. It used to
+ * contain the ifile, but we're done with it.
+ */
+
+ /* Write out the root and lost and found directories */
+ bzero(ipagep, lfsp->lfs_bsize);
+ make_dir(ipagep, lfs_root_dir,
+ sizeof(lfs_root_dir) / sizeof(struct direct));
+ *dp++ = ((u_long *)ipagep)[0];
+ put(fd, off, ipagep, lfsp->lfs_bsize);
+ off += lfsp->lfs_bsize;
+
+ bzero(ipagep, lfsp->lfs_bsize);
+ make_dir(ipagep, lfs_lf_dir,
+ sizeof(lfs_lf_dir) / sizeof(struct direct));
+ *dp++ = ((u_long *)ipagep)[0];
+ put(fd, off, ipagep, lfsp->lfs_bsize);
+
+ /* Write Supberblock */
+ lfsp->lfs_offset = (off + lfsp->lfs_bsize) / lp->d_secsize;
+ put(fd, LFS_LABELPAD, lfsp, sizeof(struct lfs));
+
+ /*
+ * Finally, calculate all the fields for the summary structure
+ * and write it.
+ */
+
+ summary.ss_next = lfsp->lfs_nextseg;
+ summary.ss_create = lfsp->lfs_tstamp;
+ summary.ss_nfinfo = 3;
+ summary.ss_ninos = 3;
+ summary.ss_datasum = cksum(datasump, sizeof(u_long) * blocks_used);
+
+ /*
+ * Make sure that we don't overflow a summary block. We have to
+ * record: FINFO structures for ifile, root, and l+f. The number
+ * of blocks recorded for the ifile is determined by the size of
+ * the cleaner info and the segments usage table. There is room
+ * for one block included in sizeof(FINFO) so we don't need to add
+ * any extra space for the ROOT and L+F, and one block of the ifile
+ * is already counted. Finally, we leave room for 1 inode block
+ * address.
+ */
+ sum_size = 3*sizeof(FINFO) + sizeof(SEGSUM) + sizeof(daddr_t) +
+ (lfsp->lfs_cleansz + lfsp->lfs_segtabsz) * sizeof(u_long);
+#define SUMERR \
+"Multiple summary blocks in segment 1 not yet implemented\nsummary is %d bytes."
+ if (sum_size > LFS_SUMMARY_SIZE)
+ fatal(SUMERR, sum_size);
+
+ block_array_size = lfsp->lfs_cleansz + lfsp->lfs_segtabsz + 1;
+
+ if (!(block_array = malloc(block_array_size *sizeof(int))))
+ fatal("%s: %s", special, strerror(errno));
+
+ /* fill in the array */
+ for (i = 0; i < block_array_size; i++)
+ block_array[i] = i;
+
+ /* copy into segment */
+ sump = ipagep;
+ bcopy(&summary, sump, sizeof(SEGSUM));
+ sump += sizeof(SEGSUM);
+
+ /* Now, add the ifile */
+ file_info.fi_nblocks = block_array_size;
+ file_info.fi_version = 1;
+ file_info.fi_ino = LFS_IFILE_INUM;
+
+ bcopy(&file_info, sump, sizeof(FINFO) - sizeof(u_long));
+ sump += sizeof(FINFO) - sizeof(u_long);
+ bcopy(block_array, sump, sizeof(u_long) * file_info.fi_nblocks);
+ sump += sizeof(u_long) * file_info.fi_nblocks;
+
+ /* Now, add the root directory */
+ file_info.fi_nblocks = 1;
+ file_info.fi_version = 1;
+ file_info.fi_ino = ROOTINO;
+ file_info.fi_blocks[0] = 0;
+ bcopy(&file_info, sump, sizeof(FINFO));
+ sump += sizeof(FINFO);
+
+ /* Now, add the lost and found */
+ file_info.fi_ino = LOSTFOUNDINO;
+ bcopy(&file_info, sump, sizeof(FINFO));
+
+ ((daddr_t *)ipagep)[LFS_SUMMARY_SIZE / sizeof(daddr_t) - 1] =
+ lfsp->lfs_idaddr;
+ ((SEGSUM *)ipagep)->ss_sumsum = cksum(ipagep+sizeof(summary.ss_sumsum),
+ LFS_SUMMARY_SIZE - sizeof(summary.ss_sumsum));
+ put(fd, LFS_LABELPAD + LFS_SBPAD, ipagep, LFS_SUMMARY_SIZE);
+
+ sp = (SEGSUM *)ipagep;
+ sp->ss_create = 0;
+ sp->ss_nfinfo = 0;
+ sp->ss_ninos = 0;
+ sp->ss_datasum = 0;
+
+ /* Now write the summary block for the next partial so it's invalid */
+ lfsp->lfs_tstamp = 0;
+ off += lfsp->lfs_bsize;
+ sp->ss_sumsum =
+ cksum(&sp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum));
+ put(fd, off, sp, LFS_SUMMARY_SIZE);
+
+ /* Now, write rest of segments containing superblocks */
+ lfsp->lfs_cksum =
+ cksum(lfsp, sizeof(struct lfs) - sizeof(lfsp->lfs_cksum));
+ for (seg_addr = last_addr = lfsp->lfs_sboffs[0], j = 1, i = 1;
+ i < lfsp->lfs_nseg; i++) {
+
+ seg_addr += lfsp->lfs_ssize << lfsp->lfs_fsbtodb;
+ sp->ss_next = last_addr;
+ last_addr = seg_addr;
+ seg_seek = seg_addr * lp->d_secsize;
+
+ if (seg_addr == lfsp->lfs_sboffs[j]) {
+ if (j < (LFS_MAXNUMSB - 2))
+ j++;
+ put(fd, seg_seek, lfsp, sizeof(struct lfs));
+ seg_seek += LFS_SBPAD;
+ }
+
+ /* Summary */
+ sp->ss_sumsum = cksum(&sp->ss_datasum,
+ LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum));
+ put(fd, seg_seek, sp, LFS_SUMMARY_SIZE);
+ }
+ free(ipagep);
+ close(fd);
+ return (0);
+}
+
+static void
+put(fd, off, p, len)
+ int fd;
+ off_t off;
+ void *p;
+ size_t len;
+{
+ int wbytes;
+
+ if (lseek(fd, off, SEEK_SET) < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if ((wbytes = write(fd, p, len)) < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if (wbytes != len)
+ fatal("%s: short write (%d, not %d)", special, wbytes, len);
+}
+
+/*
+ * Create the root directory for this file system and the lost+found
+ * directory.
+ */
+
+ u_long d_ino; /* inode number of entry */
+ u_short d_reclen; /* length of this record */
+ u_short d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMLEN + 1]; /* name with length <= MAXNAMLEN */
+void
+lfsinit()
+{}
+
+static daddr_t
+make_dinode(ino, dip, nblocks, saddr, lfsp)
+ ino_t ino; /* inode we're creating */
+ struct dinode *dip; /* disk inode */
+ int nblocks; /* number of blocks in file */
+ daddr_t saddr; /* starting block address */
+ struct lfs *lfsp; /* superblock */
+{
+ int db_per_fb, i;
+
+ dip->di_nlink = 1;
+ dip->di_blocks = nblocks << lfsp->lfs_fsbtodb;
+
+ dip->di_size = (nblocks << lfsp->lfs_bshift);
+ dip->di_atime.ts_sec = dip->di_mtime.ts_sec =
+ dip->di_ctime.ts_sec = lfsp->lfs_tstamp;
+ dip->di_atime.ts_nsec = dip->di_mtime.ts_nsec =
+ dip->di_ctime.ts_nsec = 0;
+ dip->di_inumber = ino;
+
+#define SEGERR \
+"File requires more than the number of direct blocks; increase block or segment size."
+ if (NDADDR < nblocks)
+ fatal("%s", SEGERR);
+
+ /* Assign the block addresses for the ifile */
+ db_per_fb = 1 << lfsp->lfs_fsbtodb;
+ for (i = 0; i < nblocks; i++, saddr += db_per_fb)
+ dip->di_db[i] = saddr;
+
+ return (saddr);
+}
+
+
+/*
+ * Construct a set of directory entries in "bufp". We assume that all the
+ * entries in protodir fir in the first DIRBLKSIZ.
+ */
+static void
+make_dir(bufp, protodir, entries)
+ void *bufp;
+ register struct direct *protodir;
+ int entries;
+{
+ char *cp;
+ int i, spcleft;
+
+ spcleft = DIRBLKSIZ;
+ for (cp = bufp, i = 0; i < entries - 1; i++) {
+ protodir[i].d_reclen = DIRSIZ(NEWDIRFMT, &protodir[i]);
+ bcopy(&protodir[i], cp, protodir[i].d_reclen);
+ cp += protodir[i].d_reclen;
+ if ((spcleft -= protodir[i].d_reclen) < 0)
+ fatal("%s: %s", special, "directory too big");
+ }
+ protodir[i].d_reclen = spcleft;
+ bcopy(&protodir[i], cp, DIRSIZ(NEWDIRFMT, &protodir[i]));
+}
diff --git a/sbin/newlfs/misc.c b/sbin/newlfs/misc.c
new file mode 100644
index 000000000000..074fb71e9246
--- /dev/null
+++ b/sbin/newlfs/misc.c
@@ -0,0 +1,83 @@
+/*-
+ * 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[] = "@(#)misc.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "extern.h"
+
+u_int
+log2(num)
+ u_int num;
+{
+ register u_int i, limit;
+
+ limit = 1;
+ for (i = 0; limit < num; limit = limit << 1, i++);
+ return (i);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "%s: ", progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/sbin/newlfs/newfs.c b/sbin/newlfs/newfs.c
new file mode 100644
index 000000000000..25ac7f05f176
--- /dev/null
+++ b/sbin/newlfs/newfs.c
@@ -0,0 +1,466 @@
+/*-
+ * Copyright (c) 1989, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)newfs.c 8.3 (Berkeley) 4/22/94";
+#endif /* not lint */
+
+/*
+ * newfs: friendly front end to mkfs
+ */
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <paths.h>
+#include "config.h"
+#include "extern.h"
+
+#define COMPAT /* allow non-labeled disks */
+
+int mfs; /* run as the memory based filesystem */
+int Nflag; /* run without writing file system */
+int fssize; /* file system size */
+int ntracks; /* # tracks/cylinder */
+int nsectors; /* # sectors/track */
+int nphyssectors; /* # sectors/track including spares */
+int secpercyl; /* sectors per cylinder */
+int trackspares = -1; /* spare sectors per track */
+int cylspares = -1; /* spare sectors per cylinder */
+int sectorsize; /* bytes/sector */
+#ifdef tahoe
+int realsectorsize; /* bytes/sector in hardware */
+#endif
+int rpm; /* revolutions/minute of drive */
+int interleave; /* hardware sector interleave */
+int trackskew = -1; /* sector 0 skew, per track */
+int headswitch; /* head switch time, usec */
+int trackseek; /* track-to-track seek, usec */
+int fsize = 0; /* fragment size */
+int bsize = 0; /* block size */
+int cpg = DESCPG; /* cylinders/cylinder group */
+int cpgflg; /* cylinders/cylinder group flag was given */
+int minfree = MINFREE; /* free space threshold */
+int opt = DEFAULTOPT; /* optimization preference (space or time) */
+int density; /* number of bytes per inode */
+int maxcontig = MAXCONTIG; /* max contiguous blocks to allocate */
+int rotdelay = ROTDELAY; /* rotational delay between blocks */
+int maxbpg; /* maximum blocks per file in a cyl group */
+int nrpos = NRPOS; /* # of distinguished rotational positions */
+int bbsize = BBSIZE; /* boot block size */
+int sbsize = SBSIZE; /* superblock size */
+int mntflags; /* flags to be passed to mount */
+u_long memleft; /* virtual memory available */
+caddr_t membase; /* start address of memory based filesystem */
+#ifdef COMPAT
+char *disktype;
+int unlabeled;
+#endif
+
+char device[MAXPATHLEN];
+char *progname, *special;
+
+static struct disklabel *getdisklabel __P((char *, int));
+static struct disklabel *debug_readlabel __P((int));
+static void rewritelabel __P((char *, int, struct disklabel *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch;
+ register struct partition *pp;
+ register struct disklabel *lp;
+ struct partition oldpartition;
+ struct stat st;
+ int debug, lfs, fsi, fso, segsize;
+ char *cp, *opstring;
+
+ if (progname = rindex(*argv, '/'))
+ ++progname;
+ else
+ progname = *argv;
+
+ if (strstr(progname, "mfs")) {
+ mfs = 1;
+ Nflag++;
+ }
+
+ /* -F is mfs only and MUST come first! */
+ opstring = "F:B:DLNS:T:a:b:c:d:e:f:i:k:l:m:n:o:p:r:s:t:u:x:";
+ if (!mfs)
+ opstring += 2;
+
+ debug = lfs = segsize = 0;
+ while ((ch = getopt(argc, argv, opstring)) != EOF)
+ switch(ch) {
+ case 'B': /* LFS segment size */
+ if ((segsize = atoi(optarg)) < LFS_MINSEGSIZE)
+ fatal("%s: bad segment size", optarg);
+ break;
+ case 'D':
+ debug = 1;
+ break;
+ case 'F':
+ if ((mntflags = atoi(optarg)) == 0)
+ fatal("%s: bad mount flags", optarg);
+ break;
+ case 'L': /* Create lfs */
+ lfs = 1;
+ break;
+ case 'N':
+ Nflag++;
+ break;
+ case 'S':
+ if ((sectorsize = atoi(optarg)) <= 0)
+ fatal("%s: bad sector size", optarg);
+ break;
+#ifdef COMPAT
+ case 'T':
+ disktype = optarg;
+ break;
+#endif
+ case 'a':
+ if ((maxcontig = atoi(optarg)) <= 0)
+ fatal("%s: bad max contiguous blocks\n",
+ optarg);
+ break;
+ case 'b': /* used for LFS */
+ if ((bsize = atoi(optarg)) < LFS_MINBLOCKSIZE)
+ fatal("%s: bad block size", optarg);
+ break;
+ case 'c':
+ if ((cpg = atoi(optarg)) <= 0)
+ fatal("%s: bad cylinders/group", optarg);
+ cpgflg++;
+ break;
+ case 'd':
+ if ((rotdelay = atoi(optarg)) < 0)
+ fatal("%s: bad rotational delay\n", optarg);
+ break;
+ case 'e':
+ if ((maxbpg = atoi(optarg)) <= 0)
+ fatal("%s: bad blocks per file in a cyl group\n",
+ optarg);
+ break;
+ case 'f':
+ if ((fsize = atoi(optarg)) <= 0)
+ fatal("%s: bad frag size", optarg);
+ break;
+ case 'i':
+ if ((density = atoi(optarg)) <= 0)
+ fatal("%s: bad bytes per inode\n", optarg);
+ break;
+ case 'k':
+ if ((trackskew = atoi(optarg)) < 0)
+ fatal("%s: bad track skew", optarg);
+ break;
+ case 'l':
+ if ((interleave = atoi(optarg)) <= 0)
+ fatal("%s: bad interleave", optarg);
+ break;
+ case 'm': /* used for LFS */
+ if ((minfree = atoi(optarg)) < 0 || minfree > 99)
+ fatal("%s: bad free space %%\n", optarg);
+ break;
+ case 'n':
+ if ((nrpos = atoi(optarg)) <= 0)
+ fatal("%s: bad rotational layout count\n",
+ optarg);
+ break;
+ case 'o':
+ if (strcmp(optarg, "space") == 0)
+ opt = FS_OPTSPACE;
+ else if (strcmp(optarg, "time") == 0)
+ opt = FS_OPTTIME;
+ else
+ fatal("%s: bad optimization preference %s",
+ optarg, "(options are `space' or `time')");
+ break;
+ case 'p':
+ if ((trackspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per track",
+ optarg);
+ break;
+ case 'r':
+ if ((rpm = atoi(optarg)) <= 0)
+ fatal("%s: bad revs/minute\n", optarg);
+ break;
+ case 's': /* used for LFS */
+ if ((fssize = atoi(optarg)) <= 0)
+ fatal("%s: bad file system size", optarg);
+ break;
+ case 't':
+ if ((ntracks = atoi(optarg)) <= 0)
+ fatal("%s: bad total tracks", optarg);
+ break;
+ case 'u':
+ if ((nsectors = atoi(optarg)) <= 0)
+ fatal("%s: bad sectors/track", optarg);
+ break;
+ case 'x':
+ if ((cylspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per cylinder",
+ optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 && (mfs || argc != 1))
+ usage();
+
+ /*
+ * If the -N flag isn't specified, open the output file. If no path
+ * prefix, try /dev/r%s and then /dev/%s.
+ */
+ special = argv[0];
+ if (index(special, '/') == NULL) {
+ (void)sprintf(device, "%sr%s", _PATH_DEV, special);
+ if (stat(device, &st) == -1)
+ (void)sprintf(device, "%s%s", _PATH_DEV, special);
+ special = device;
+ }
+ if (!Nflag) {
+ fso = open(special,
+ (debug ? O_CREAT : 0) | O_WRONLY, DEFFILEMODE);
+ if (fso < 0)
+ fatal("%s: %s", special, strerror(errno));
+ } else
+ fso = -1;
+
+ /* Open the input file. */
+ fsi = open(special, O_RDONLY);
+ if (fsi < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if (fstat(fsi, &st) < 0)
+ fatal("%s: %s", special, strerror(errno));
+
+ if (!debug && !mfs && !S_ISCHR(st.st_mode))
+ (void)printf("%s: %s: not a character-special device\n",
+ progname, special);
+ cp = index(argv[0], '\0') - 1;
+ if (!debug && (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)))
+ fatal("%s: can't figure out file system partition", argv[0]);
+
+#ifdef COMPAT
+ if (!mfs && disktype == NULL)
+ disktype = argv[1];
+#endif
+ if (debug)
+ lp = debug_readlabel(fsi);
+ else
+ lp = getdisklabel(special, fsi);
+
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_size == 0)
+ fatal("%s: `%c' partition is unavailable", argv[0], *cp);
+
+ /* If we're making a LFS, we break out here */
+ exit(make_lfs(fso, lp, pp, minfree, bsize, segsize));
+}
+
+#ifdef COMPAT
+char lmsg[] = "%s: can't read disk label; disk type must be specified";
+#else
+char lmsg[] = "%s: can't read disk label";
+#endif
+
+static struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+#ifdef COMPAT
+ if (disktype) {
+ struct disklabel *lp, *getdiskbyname();
+
+ unlabeled++;
+ lp = getdiskbyname(disktype);
+ if (lp == NULL)
+ fatal("%s: unknown disk type", disktype);
+ return (lp);
+ }
+#endif
+ (void)fprintf(stderr,
+ "%s: ioctl (GDINFO): %s\n", progname, strerror(errno));
+ fatal(lmsg, s);
+ }
+ return (&lab);
+}
+
+
+static struct disklabel *
+debug_readlabel(fd)
+ int fd;
+{
+ static struct disklabel lab;
+ int n;
+
+ if ((n = read(fd, &lab, sizeof(struct disklabel))) < 0)
+ fatal("unable to read disk label: %s", strerror(errno));
+ else if (n < sizeof(struct disklabel))
+ fatal("short read of disklabel: %d of %d bytes", n,
+ sizeof(struct disklabel));
+ return(&lab);
+}
+
+static void
+rewritelabel(s, fd, lp)
+ char *s;
+ int fd;
+ register struct disklabel *lp;
+{
+#ifdef COMPAT
+ if (unlabeled)
+ return;
+#endif
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) {
+ (void)fprintf(stderr,
+ "%s: ioctl (WDINFO): %s\n", progname, strerror(errno));
+ fatal("%s: can't rewrite disk label", s);
+ }
+#if vax
+ if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
+ register i;
+ int cfd;
+ daddr_t alt;
+ char specname[64];
+ char blk[1024];
+ char *cp;
+
+ /*
+ * Make name for 'c' partition.
+ */
+ strcpy(specname, s);
+ cp = specname + strlen(specname) - 1;
+ if (!isdigit(*cp))
+ *cp = 'c';
+ cfd = open(specname, O_WRONLY);
+ if (cfd < 0)
+ fatal("%s: %s", specname, strerror(errno));
+ bzero(blk, sizeof(blk));
+ *(struct disklabel *)(blk + LABELOFFSET) = *lp;
+ alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
+ for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
+ if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize,
+ L_SET) == -1)
+ fatal("lseek to badsector area: %s",
+ strerror(errno));
+ if (write(cfd, blk, lp->d_secsize) < lp->d_secsize)
+ fprintf(stderr,
+ "%s: alternate label %d write: %s\n",
+ progname, i/2, strerror(errno));
+ }
+ close(cfd);
+ }
+#endif
+}
+
+void
+usage()
+{
+ if (mfs) {
+ fprintf(stderr,
+ "usage: mfs [ -fsoptions ] special-device mount-point\n");
+ } else
+ fprintf(stderr,
+ "usage: newlfs [ -fsoptions ] special-device%s\n",
+#ifdef COMPAT
+ " [device-type]");
+#else
+ "");
+#endif
+ fprintf(stderr, "where fsoptions are:\n");
+ fprintf(stderr, "\t-B LFS segment size\n");
+ fprintf(stderr, "\t-D debug\n");
+ fprintf(stderr, "\t-F mount flags\n");
+ fprintf(stderr, "\t-L create LFS file system\n");
+ fprintf(stderr,
+ "\t-N do not create file system, just print out parameters\n");
+ fprintf(stderr, "\t-S sector size\n");
+#ifdef COMPAT
+ fprintf(stderr, "\t-T disktype\n");
+#endif
+ fprintf(stderr, "\t-a maximum contiguous blocks\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c cylinders/group\n");
+ fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
+ fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
+ fprintf(stderr, "\t-f frag size\n");
+ fprintf(stderr, "\t-i number of bytes per inode\n");
+ fprintf(stderr, "\t-k sector 0 skew, per track\n");
+ fprintf(stderr, "\t-l hardware sector interleave\n");
+ fprintf(stderr, "\t-m minimum free space %%\n");
+ fprintf(stderr, "\t-n number of distinguished rotational positions\n");
+ fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
+ fprintf(stderr, "\t-p spare sectors per track\n");
+ fprintf(stderr, "\t-r revolutions/minute\n");
+ fprintf(stderr, "\t-s file system size (sectors)\n");
+ fprintf(stderr, "\t-t tracks/cylinder\n");
+ fprintf(stderr, "\t-u sectors/track\n");
+ fprintf(stderr, "\t-x spare sectors per cylinder\n");
+ exit(1);
+}
diff --git a/sbin/newlfs/newlfs.8 b/sbin/newlfs/newlfs.8
new file mode 100644
index 000000000000..92474e54609b
--- /dev/null
+++ b/sbin/newlfs/newlfs.8
@@ -0,0 +1,95 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)newlfs.8 8.1 (Berkeley) 6/19/93
+.\"
+.Dd June 19, 1993
+.Dt NEWLFS 8
+.Os BSD 4.4
+.Sh NAME
+.Nm newlfs
+.Nd construct a new LFS file system
+.Sh SYNOPSIS
+.Nm newlfs
+.Fl L
+.Op Ar newlfs-options
+.Ar special
+.Sh DESCRIPTION
+.Nm Newlfs
+builds a log-structured file system on the specified special
+device basing its defaults on the information in the disk label.
+(Before running
+.Nm newlfs
+the disk must be labeled using
+.Xr disklabel 8 .)
+.Pp
+The following options define the general layout policies.
+.Bl -tag -width Fl
+.It Fl B
+The logical segment size of the file system in bytes.
+.It Fl b Ar block-size
+The block size of the file system in bytes.
+.It Fl L
+Create a log-structured file system (LFS).
+This flag is currently required.
+.It Fl m Ar free space \&%
+The percentage of space reserved from normal users; the minimum
+free space threshold. The default value used is 10%.
+See
+.Xr tunefs 8
+for more details on how to set this option.
+.It Fl s Ar size
+The size of the file system in sectors.
+.El
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr dumplfs 8 ,
+.Xr disklabel 8 ,
+.Xr diskpart 8 ,
+.Xr tunefs 8
+.Rs
+.%A M. McKusick
+.%A W. Joy
+.%A S. Leffler
+.%A R. Fabry
+.%T A Fast File System for UNIX ,
+.%J ACM Transactions on Computer Systems 2
+.%V 3
+.%P pp 181-197
+.%D August 1984
+.%O (reprinted in the BSD System Manager's Manual)
+.Re
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/sbin/nfsd/Makefile b/sbin/nfsd/Makefile
new file mode 100644
index 000000000000..35d0d0e21413
--- /dev/null
+++ b/sbin/nfsd/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+=-DNFS
+MAN8= nfsd.0
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/sbin/nfsd/nfsd.8 b/sbin/nfsd/nfsd.8
new file mode 100644
index 000000000000..4ee6c4b21277
--- /dev/null
+++ b/sbin/nfsd/nfsd.8
@@ -0,0 +1,114 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)nfsd.8 8.3 (Berkeley) 2/22/94
+.\"
+.Dd February 22, 1994
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm nfsd
+.Op Fl rut
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+.Nm Nfsd
+runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm nfsd
+must be running for a machine to operate as a server.
+.Pp
+Unless otherwise specified, four servers for
+.Tn UDP
+transport are started.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl r
+Register the
+.Tn NFS
+service with
+.Xr portmap 8
+without creating any servers.
+This option can be used along with the
+.Fl u
+or
+.Fl t
+options to re-register NFS if the portmap server is restarted.
+.It Fl n
+Specifies how many servers to create.
+.It Fl t
+Serve
+.Tn TCP NFS
+clients.
+.It Fl u
+Serve
+.Tn UDP NFS
+clients.
+.El
+.Pp
+For example,
+.Dq Li "nfsd -u -t 6"
+serves
+.Tn UDP
+and
+.Tn TCP
+transports using six daemons.
+.Pp
+A server should run enough daemons to handle
+the maximum level of concurrency from its clients,
+typically four to six.
+.Pp
+.Nm Nfsd
+listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094.
+.Pp
+The
+.Nm nfsd
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsd
+utility first appeared in 4.4BSD.
diff --git a/sbin/nfsd/nfsd.c b/sbin/nfsd/nfsd.c
new file mode 100644
index 000000000000..63f4f005c2fb
--- /dev/null
+++ b/sbin/nfsd/nfsd.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)nfsd.c 8.7 (Berkeley) 2/22/94";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s) fprintf(stderr,(s))
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArg = NULL; /* end of argv */
+
+#ifdef KERBEROS
+char lnam[ANAME_SZ];
+KTEXT_ST kt;
+AUTH_DAT auth;
+char inst[INST_SZ];
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+void setproctitle __P((char *));
+void usage __P((void));
+
+/*
+ * Nfs server daemon mostly just a user context for nfssvc()
+ *
+ * 1 - do file descriptor and signal cleanup
+ * 2 - fork the nfsd(s)
+ * 3 - create server socket(s)
+ * 4 - register socket with portmap
+ *
+ * For connectionless protocols, just pass the socket into the kernel via.
+ * nfssvc().
+ * For connection based sockets, loop doing accepts. When you get a new
+ * socket from accept, pass the msgsock into the kernel via. nfssvc().
+ * The arguments are:
+ * -c - support iso cltp clients
+ * -r - reregister with portmapper
+ * -t - support tcp nfs clients
+ * -u - support udp nfs clients
+ * followed by "n" which is the number of nfsds' to fork off
+ */
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ extern int optind;
+ struct group *grp;
+ struct nfsd_args nfsdargs;
+ struct passwd *pwd;
+ struct ucred *cr;
+ struct sockaddr_in inetaddr, inetpeer;
+#ifdef ISO
+ struct sockaddr_iso isoaddr, isopeer;
+#endif
+ fd_set ready, sockbits;
+ int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock;
+ int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock;
+ int tp4cnt, tp4flag, tp4sock, tpipcnt, tpipflag, tpipsock, udpflag;
+ char *cp, **cpp;
+
+ /* Save start and extent of argv for setproctitle. */
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 4
+ nfsdcnt = DEFNFSDCNT;
+ cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0;
+ tpipflag = udpflag = 0;
+#ifdef ISO
+#define GETOPT "cn:rtu"
+#define USAGE "[-crtu] [-n num_servers]"
+#else
+#define GETOPT "n:rtu"
+#define USAGE "[-rtu] [-n num_servers]"
+#endif
+ while ((ch = getopt(argc, argv, GETOPT)) != EOF)
+ switch (ch) {
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+#ifdef ISO
+ case 'c':
+ cltpflag = 1;
+ break;
+#ifdef notyet
+ case 'i':
+ tp4cnt = 1;
+ break;
+ case 'p':
+ tpipcnt = 1;
+ break;
+#endif /* notyet */
+#endif /* ISO */
+ default:
+ case '?':
+ usage();
+ };
+ argv += optind;
+ argc -= optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ nfsdcnt = atoi(argv[0]);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ }
+
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGTERM, SIG_IGN);
+ }
+ (void)signal(SIGCHLD, reapchild);
+
+ if (reregister) {
+ if (udpflag &&
+ !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT))
+ err(1, "can't register with portmap for UDP.");
+ if (tcpflag &&
+ !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT))
+ err(1, "can't register with portmap for TCP.");
+ exit(0);
+ }
+ openlog("nfsd:", LOG_PID, LOG_DAEMON);
+
+ for (i = 0; i < nfsdcnt; i++) {
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit (1);
+ case 0:
+ break;
+ default:
+ continue;
+ }
+
+ setproctitle("nfsd-srv");
+ nfssvc_flag = NFSSVC_NFSD;
+ nsd.nsd_nfsd = NULL;
+#ifdef KERBEROS
+ nsd.nsd_authstr = (char *)kt.dat;
+#endif
+ while (nfssvc(nfssvc_flag, &nsd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit(1);
+ }
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
+#ifdef KERBEROS
+ kt.length = nsd.nsd_authlen;
+ kt.mbz = 0;
+ (void)strcpy(inst, "*");
+ if (krb_rd_req(&kt, "rcmd",
+ inst, nsd.nsd_haddr, &auth, "") == RD_AP_OK &&
+ krb_kntoln(&auth, lnam) == KSUCCESS &&
+ (pwd = getpwnam(lnam)) != NULL) {
+ cr = &nsd.nsd_cr;
+ cr->cr_uid = pwd->pw_uid;
+ cr->cr_groups[0] = pwd->pw_gid;
+ cr->cr_ngroups = 1;
+ setgrent();
+ while ((grp = getgrent()) != NULL) {
+ if (grp->gr_gid == cr->cr_groups[0])
+ continue;
+ for (cpp = grp->gr_mem;
+ *cpp != NULL; ++cpp)
+ if (!strcmp(*cpp, lnam))
+ break;
+ if (*cpp == NULL)
+ continue;
+ cr->cr_groups[cr->cr_ngroups++]
+ = grp->gr_gid;
+ if (cr->cr_ngroups == NGROUPS)
+ break;
+ }
+ endgrent();
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
+ }
+#endif /* KERBEROS */
+ }
+ exit(0);
+ }
+
+ /* If we are serving udp, set up the socket. */
+ if (udpflag) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create udp socket");
+ exit(1);
+ }
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(sock,
+ (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind udp addr");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ exit(1);
+ }
+ (void)close(sock);
+ }
+
+#ifdef ISO
+ /* If we are serving cltp, set up the socket. */
+ if (cltpflag) {
+ if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create cltp socket");
+ exit(1);
+ }
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(sock,
+ (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind cltp addr");
+ exit(1);
+ }
+#ifdef notyet
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+#endif /* notyet */
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't add UDP socket");
+ exit(1);
+ }
+ close(sock);
+ }
+#endif /* ISO */
+
+ /* Now set up the master server socket waiting for tcp connections. */
+ on = 1;
+ FD_ZERO(&sockbits);
+ connect_type_cnt = 0;
+ if (tcpflag) {
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tcp socket");
+ exit(1);
+ }
+ if (setsockopt(tcpsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tcpsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tcpsock, &sockbits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+
+#ifdef notyet
+ /* Now set up the master server socket waiting for tp4 connections. */
+ if (tp4flag) {
+ if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tp4 socket");
+ exit(1);
+ }
+ if (setsockopt(tp4sock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(tp4sock,
+ (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tp4 addr");
+ exit(1);
+ }
+ if (listen(tp4sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tp4sock, &sockbits);
+ maxsock = tp4sock;
+ connect_type_cnt++;
+ }
+
+ /* Now set up the master server socket waiting for tpip connections. */
+ if (tpipflag) {
+ if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tpip socket");
+ exit(1);
+ }
+ if (setsockopt(tpipsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tpipsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tpipsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tpipsock, &sockbits);
+ maxsock = tpipsock;
+ connect_type_cnt++;
+ }
+#endif /* notyet */
+
+ if (connect_type_cnt == 0)
+ exit(0);
+
+ setproctitle("nfsd-master");
+
+ /*
+ * Loop forever accepting connections and passing the sockets
+ * into the kernel for the mounts.
+ */
+ for (;;) {
+ ready = sockbits;
+ if (connect_type_cnt > 1) {
+ if (select(maxsock + 1,
+ &ready, NULL, NULL, NULL) < 1) {
+ syslog(LOG_ERR, "select failed: %m");
+ exit(1);
+ }
+ }
+ if (tcpflag && FD_ISSET(tcpsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero));
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = sizeof(inetpeer);
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#ifdef notyet
+ if (tp4flag && FD_ISSET(tp4sock, &ready)) {
+ len = sizeof(isopeer);
+ if ((msgsock = accept(tp4sock,
+ (struct sockaddr *)&isopeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&isopeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+ if (tpipflag && FD_ISSET(tpipsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tpipsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "Accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#endif /* notyet */
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "nfsd %s\n", USAGE);
+ exit(1);
+}
+
+void
+nonfs(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available.");
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+
+ while (wait3(NULL, WNOHANG, NULL));
+}
+
+void
+setproctitle(a)
+ char *a;
+{
+ register char *cp;
+ char buf[80];
+
+ cp = Argv[0];
+ (void)snprintf(buf, sizeof(buf), "%s", a);
+ (void)strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile
new file mode 100644
index 000000000000..72db42bac6b5
--- /dev/null
+++ b/sbin/nfsiod/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsiod
+CFLAGS+=-DNFS
+MAN8= nfsiod.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/nfsiod/nfsiod.8 b/sbin/nfsiod/nfsiod.8
new file mode 100644
index 000000000000..2acd3651ca18
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.8
@@ -0,0 +1,74 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)nfsiod.8 8.2 (Berkeley) 2/22/94
+.\"
+.Dd February 22, 1994
+.Dt NFSIOD 8
+.Os
+.Sh NAME
+.Nm nfsiod
+.Nd local
+.Tn NFS
+asynchronous I/O server
+.Sh SYNOPSIS
+.Nm nfsiod
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+.Nm Nfsiod
+runs on an
+.Tn NFS
+client machine to service asynchronous I/O requests to its server.
+It improves performance but is not required for correct operation.
+.Pp
+Unless otherwise specified, a single server is started.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl n
+Specify how many servers are to be started.
+.El
+.Pp
+A client should run enough daemons to handle its maximum
+level of concurrency, typically four to six.
+.Pp
+The
+.Nm nfsiod
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsiod
+utility first appeared in 4.4BSD.
diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c
new file mode 100644
index 000000000000..b742fdc114f6
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)nfsiod.c 8.3 (Berkeley) 2/22/94";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+#include <sys/wait.h>
+
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Global defs */
+#ifdef DEBUG
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+void usage __P((void));
+
+/*
+ * Nfsiod does asynchronous buffered I/O on behalf of the NFS client.
+ * It does not have to be running for correct operation, but will
+ * improve throughput.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, num_servers;
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 1
+ num_servers = DEFNFSDCNT;
+ while ((ch = getopt(argc, argv, "n:")) != EOF)
+ switch (ch) {
+ case 'n':
+ num_servers = atoi(optarg);
+ if (num_servers < 1 || num_servers > MAXNFSDCNT) {
+ warnx("nfsiod count %d; reset to %d",
+ DEFNFSDCNT);
+ num_servers = DEFNFSDCNT;
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ num_servers = atoi(argv[0]);
+ if (num_servers < 1 || num_servers > MAXNFSDCNT) {
+ warnx("nfsiod count %d; reset to %d", DEFNFSDCNT);
+ num_servers = DEFNFSDCNT;
+ }
+ }
+
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGSYS, nonfs);
+ }
+ (void)signal(SIGCHLD, reapchild);
+
+ openlog("nfsiod:", LOG_PID, LOG_DAEMON);
+
+ while (num_servers--)
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit (1);
+ case 0:
+ if (nfssvc(NFSSVC_BIOD, NULL) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit (1);
+ }
+ exit(0);
+ }
+ exit (0);
+}
+
+void
+nonfs(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available.");
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+
+ while (wait3(NULL, WNOHANG, NULL));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: nfsiod [-n num_servers]\n");
+ exit(1);
+}
diff --git a/sbin/nologin/Makefile b/sbin/nologin/Makefile
new file mode 100644
index 000000000000..84e9f0c3b152
--- /dev/null
+++ b/sbin/nologin/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+MAN8= nologin.0
+
+nologin clean depend lint tags:
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/nologin.sh ${DESTDIR}/sbin/nologin
+
+cleandir:
+ rm -f nologin.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/nologin/nologin.8 b/sbin/nologin/nologin.8
new file mode 100644
index 000000000000..32a7e73070b8
--- /dev/null
+++ b/sbin/nologin/nologin.8
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os BSD 4.4
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm nologin
+.Sh DESCRIPTION
+.Nm Nologin
+displays a message that an account is not available and
+exits non-zero.
+It is intended as a replacement shell field for accounts that
+have been disabled.
+.Sh SEE ALSO
+.Xr login 1
+.Sh HISTORY
+The
+.Nm nologin
+command appeared in
+.Bx 4.4 .
diff --git a/sbin/nologin/nologin.sh b/sbin/nologin/nologin.sh
new file mode 100644
index 000000000000..8ad87fb8d872
--- /dev/null
+++ b/sbin/nologin/nologin.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)nologin.sh 8.1 (Berkeley) 6/5/93
+#
+
+echo 'This account is currently not available.'
+exit 1
diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile
new file mode 100644
index 000000000000..d08f74dddbc8
--- /dev/null
+++ b/sbin/ping/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= ping
+MAN8= ping.0
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8
new file mode 100644
index 000000000000..e3de68ad266e
--- /dev/null
+++ b/sbin/ping/ping.8
@@ -0,0 +1,327 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ping.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt PING 8
+.Os BSD 4.3
+.Sh NAME
+.Nm ping
+.Nd send
+.Tn ICMP ECHO_REQUEST
+packets to network hosts
+.Sh SYNOPSIS
+.Nm ping
+.Op Fl dfnqrvR
+.Op Fl c Ar count
+.Op Fl i Ar wait
+.Op Fl l Ar preload
+.Op Fl p Ar pattern
+.Op Fl s Ar packetsize
+.Sh DESCRIPTION
+.Nm Ping
+uses the
+.Tn ICMP
+protocol's mandatory
+.Tn ECHO_REQUEST
+datagram to elicit an
+.Tn ICMP ECHO_RESPONSE
+from a host or gateway.
+.Tn ECHO_REQUEST
+datagrams (``pings'') have an IP and
+.Tn ICMP
+header,
+followed by a
+.Dq struct timeval
+and then an arbitrary number of ``pad'' bytes used to fill out the
+packet.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar count
+Stop after sending (and receiving)
+.Ar count
+.Tn ECHO_RESPONSE
+packets.
+.It Fl d
+Set the
+.Dv SO_DEBUG
+option on the socket being used.
+.It Fl f
+Flood ping.
+Outputs packets as fast as they come back or one hundred times per second,
+whichever is more.
+For every
+.Tn ECHO_REQUEST
+sent a period ``.'' is printed, while for every
+.Tn ECHO_REPLY
+received a backspace is printed.
+This provides a rapid display of how many packets are being dropped.
+Only the super-user may use this option.
+.Bf -emphasis
+This can be very hard on a network and should be used with caution.
+.Ef
+.It Fl i Ar wait
+Wait
+.Ar wait
+seconds
+.Em between sending each packet .
+The default is to wait for one second between each packet.
+This option is incompatible with the
+.Fl f
+option.
+.It Fl l Ar preload
+If
+.Ar preload
+is specified,
+.Nm ping
+sends that many packets as fast as possible before falling into its normal
+mode of behavior.
+.It Fl n
+Numeric output only.
+No attempt will be made to lookup symbolic names for host addresses.
+.It Fl p Ar pattern
+You may specify up to 16 ``pad'' bytes to fill out the packet you send.
+This is useful for diagnosing data-dependent problems in a network.
+For example,
+.Dq Li \-p ff
+will cause the sent packet to be filled with all
+ones.
+.It Fl q
+Quiet output.
+Nothing is displayed except the summary lines at startup time and
+when finished.
+.It Fl R
+Record route.
+Includes the
+.Tn RECORD_ROUTE
+option in the
+.Tn ECHO_REQUEST
+packet and displays
+the route buffer on returned packets.
+Note that the IP header is only large enough for nine such routes.
+Many hosts ignore or discard this option.
+.It Fl r
+Bypass the normal routing tables and send directly to a host on an attached
+network.
+If the host is not on a directly-attached network, an error is returned.
+This option can be used to ping a local host through an interface
+that has no route through it (e.g., after the interface was dropped by
+.Xr routed 8 ) .
+.It Fl s Ar packetsize
+Specifies the number of data bytes to be sent.
+The default is 56, which translates into 64
+.Tn ICMP
+data bytes when combined
+with the 8 bytes of
+.Tn ICMP
+header data.
+.It Fl v
+Verbose output.
+.Tn ICMP
+packets other than
+.Tn ECHO_RESPONSE
+that are received are listed.
+.El
+.Pp
+When using
+.Nm ping
+for fault isolation, it should first be run on the local host, to verify
+that the local network interface is up and running.
+Then, hosts and gateways further and further away should be ``pinged''.
+Round-trip times and packet loss statistics are computed.
+If duplicate packets are received, they are not included in the packet
+loss calculation, although the round trip time of these packets is used
+in calculating the minimum/average/maximum round-trip time numbers.
+When the specified number of packets have been sent (and received) or
+if the program is terminated with a
+.Dv SIGINT ,
+a brief summary is displayed.
+.Pp
+This program is intended for use in network testing, measurement and
+management.
+Because of the load it can impose on the network, it is unwise to use
+.Nm ping
+during normal operations or from automated scripts.
+.Sh ICMP PACKET DETAILS
+An IP header without options is 20 bytes.
+An
+.Tn ICMP
+.Tn ECHO_REQUEST
+packet contains an additional 8 bytes worth
+of
+.Tn ICMP
+header followed by an arbitrary amount of data.
+When a
+.Ar packetsize
+is given, this indicated the size of this extra piece of data (the
+default is 56).
+Thus the amount of data received inside of an IP packet of type
+.Tn ICMP
+.Tn ECHO_REPLY
+will always be 8 bytes more than the requested data space
+(the
+.Tn ICMP
+header).
+.Pp
+If the data space is at least eight bytes large,
+.Nm ping
+uses the first eight bytes of this space to include a timestamp which
+it uses in the computation of round trip times.
+If less than eight bytes of pad are specified, no round trip times are
+given.
+.Sh DUPLICATE AND DAMAGED PACKETS
+.Nm Ping
+will report duplicate and damaged packets.
+Duplicate packets should never occur, and seem to be caused by
+inappropriate link-level retransmissions.
+Duplicates may occur in many situations and are rarely (if ever) a
+good sign, although the presence of low levels of duplicates may not
+always be cause for alarm.
+.Pp
+Damaged packets are obviously serious cause for alarm and often
+indicate broken hardware somewhere in the
+.Nm ping
+packet's path (in the network or in the hosts).
+.Sh TRYING DIFFERENT DATA PATTERNS
+The (inter)network layer should never treat packets differently depending
+on the data contained in the data portion.
+Unfortunately, data-dependent problems have been known to sneak into
+networks and remain undetected for long periods of time.
+In many cases the particular pattern that will have problems is something
+that doesn't have sufficient ``transitions'', such as all ones or all
+zeros, or a pattern right at the edge, such as almost all zeros.
+It isn't necessarily enough to specify a data pattern of all zeros (for
+example) on the command line because the pattern that is of interest is
+at the data link level, and the relationship between what you type and
+what the controllers transmit can be complicated.
+.Pp
+This means that if you have a data-dependent problem you will probably
+have to do a lot of testing to find it.
+If you are lucky, you may manage to find a file that either can't be sent
+across your network or that takes much longer to transfer than other
+similar length files.
+You can then examine this file for repeated patterns that you can test
+using the
+.Fl p
+option of
+.Nm ping .
+.Sh TTL DETAILS
+The
+.Tn TTL
+value of an IP packet represents the maximum number of IP routers
+that the packet can go through before being thrown away.
+In current practice you can expect each router in the Internet to decrement
+the
+.Tn TTL
+field by exactly one.
+.Pp
+The
+.Tn TCP/IP
+specification states that the
+.Tn TTL
+field for
+.Tn TCP
+packets should
+be set to 60, but many systems use smaller values (4.3
+.Tn BSD
+uses 30, 4.2 used
+15).
+.Pp
+The maximum possible value of this field is 255, and most Unix systems set
+the
+.Tn TTL
+field of
+.Tn ICMP ECHO_REQUEST
+packets to 255.
+This is why you will find you can ``ping'' some hosts, but not reach them
+with
+.Xr telnet 1
+or
+.Xr ftp 1 .
+.Pp
+In normal operation ping prints the ttl value from the packet it receives.
+When a remote system receives a ping packet, it can do one of three things
+with the
+.Tn TTL
+field in its response:
+.Bl -bullet
+.It
+Not change it; this is what Berkeley Unix systems did before the
+.Bx 4.3 tahoe
+release.
+In this case the
+.Tn TTL
+value in the received packet will be 255 minus the
+number of routers in the round-trip path.
+.It
+Set it to 255; this is what current Berkeley Unix systems do.
+In this case the
+.Tn TTL
+value in the received packet will be 255 minus the
+number of routers in the path
+.Xr from
+the remote system
+.Em to
+the
+.Nm ping Ns Em ing
+host.
+.It
+Set it to some other value.
+Some machines use the same value for
+.Tn ICMP
+packets that they use for
+.Tn TCP
+packets, for example either 30 or 60.
+Others may use completely wild values.
+.El
+.Sh BUGS
+Many Hosts and Gateways ignore the
+.Tn RECORD_ROUTE
+option.
+.Pp
+The maximum IP header length is too small for options like
+.Tn RECORD_ROUTE
+to
+be completely useful.
+There's not much that that can be done about this, however.
+.Pp
+Flood pinging is not recommended in general, and flood pinging the
+broadcast address should only be done under very controlled conditions.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr routed 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
new file mode 100644
index 000000000000..46db1ca1b6af
--- /dev/null
+++ b/sbin/ping/ping.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * 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) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * P I N G . C
+ *
+ * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
+ * measure round-trip-delays and packet loss across network paths.
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ *
+ * Status -
+ * Public Domain. Distribution Unlimited.
+ * Bugs -
+ * More statistics could always be gathered.
+ * This program has to run SUID to ROOT to access the ICMP socket.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#define DEFDATALEN (64 - 8) /* default data length */
+#define MAXIPLEN 60
+#define MAXICMPLEN 76
+#define MAXPACKET (65536 - 60 - 8)/* max packet size */
+#define MAXWAIT 10 /* max seconds to wait for response */
+#define NROUTES 9 /* number of record route slots */
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+/* various options */
+int options;
+#define F_FLOOD 0x001
+#define F_INTERVAL 0x002
+#define F_NUMERIC 0x004
+#define F_PINGFILLED 0x008
+#define F_QUIET 0x010
+#define F_RROUTE 0x020
+#define F_SO_DEBUG 0x040
+#define F_SO_DONTROUTE 0x080
+#define F_VERBOSE 0x100
+
+/*
+ * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
+ * number of received sequence numbers we can keep track of. Change 128
+ * to 8192 for complete accuracy...
+ */
+#define MAX_DUP_CHK (8 * 128)
+int mx_dup_ck = MAX_DUP_CHK;
+char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct sockaddr whereto; /* who to ping */
+int datalen = DEFDATALEN;
+int s; /* socket file descriptor */
+u_char outpack[MAXPACKET];
+char BSPACE = '\b'; /* characters written for flood */
+char DOT = '.';
+char *hostname;
+int ident; /* process id to identify our packets */
+
+/* counters */
+long npackets; /* max packets to transmit */
+long nreceived; /* # of packets we got back */
+long nrepeats; /* number of duplicates */
+long ntransmitted; /* sequence # for outbound packets = #sent */
+int interval = 1; /* interval between packets */
+
+/* timing */
+int timing; /* flag to do timing */
+double tmin = 999999999.0; /* minimum round trip time */
+double tmax = 0.0; /* maximum round trip time */
+double tsum = 0.0; /* sum of all times, for doing average */
+
+char *pr_addr();
+void catcher(), finish();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int errno, optind;
+ extern char *optarg;
+ struct timeval timeout;
+ struct hostent *hp;
+ struct sockaddr_in *to;
+ struct protoent *proto;
+ register int i;
+ int ch, fdmask, hold, packlen, preload;
+ u_char *datap, *packet;
+ char *target, hnamebuf[MAXHOSTNAMELEN], *malloc();
+#ifdef IP_OPTIONS
+ char rspace[3 + 4 * NROUTES + 1]; /* record route space */
+#endif
+
+ preload = 0;
+ datap = &outpack[8 + sizeof(struct timeval)];
+ while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF)
+ switch(ch) {
+ case 'c':
+ npackets = atoi(optarg);
+ if (npackets <= 0) {
+ (void)fprintf(stderr,
+ "ping: bad number of packets to transmit.\n");
+ exit(1);
+ }
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (getuid()) {
+ (void)fprintf(stderr,
+ "ping: %s\n", strerror(EPERM));
+ exit(1);
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ break;
+ case 'i': /* wait between sending packets */
+ interval = atoi(optarg);
+ if (interval <= 0) {
+ (void)fprintf(stderr,
+ "ping: bad timing interval.\n");
+ exit(1);
+ }
+ options |= F_INTERVAL;
+ break;
+ case 'l':
+ preload = atoi(optarg);
+ if (preload < 0) {
+ (void)fprintf(stderr,
+ "ping: bad preload value.\n");
+ exit(1);
+ }
+ break;
+ case 'n':
+ options |= F_NUMERIC;
+ break;
+ case 'p': /* fill buffer with user pattern */
+ options |= F_PINGFILLED;
+ fill((char *)datap, optarg);
+ break;
+ case 'q':
+ options |= F_QUIET;
+ break;
+ case 'R':
+ options |= F_RROUTE;
+ break;
+ case 'r':
+ options |= F_SO_DONTROUTE;
+ break;
+ case 's': /* size of packet to send */
+ datalen = atoi(optarg);
+ if (datalen > MAXPACKET) {
+ (void)fprintf(stderr,
+ "ping: packet size too large.\n");
+ exit(1);
+ }
+ if (datalen <= 0) {
+ (void)fprintf(stderr,
+ "ping: illegal packet size.\n");
+ exit(1);
+ }
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+ target = *argv;
+
+ bzero((char *)&whereto, sizeof(struct sockaddr));
+ to = (struct sockaddr_in *)&whereto;
+ to->sin_family = AF_INET;
+ to->sin_addr.s_addr = inet_addr(target);
+ if (to->sin_addr.s_addr != (u_int)-1)
+ hostname = target;
+ else {
+ hp = gethostbyname(target);
+ if (!hp) {
+ (void)fprintf(stderr,
+ "ping: unknown host %s\n", target);
+ exit(1);
+ }
+ to->sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
+ (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
+ hostname = hnamebuf;
+ }
+
+ if (options & F_FLOOD && options & F_INTERVAL) {
+ (void)fprintf(stderr,
+ "ping: -f and -i incompatible options.\n");
+ exit(1);
+ }
+
+ if (datalen >= sizeof(struct timeval)) /* can we time transfer */
+ timing = 1;
+ packlen = datalen + MAXIPLEN + MAXICMPLEN;
+ if (!(packet = (u_char *)malloc((u_int)packlen))) {
+ (void)fprintf(stderr, "ping: out of memory.\n");
+ exit(1);
+ }
+ if (!(options & F_PINGFILLED))
+ for (i = 8; i < datalen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+
+ if (!(proto = getprotobyname("icmp"))) {
+ (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
+ exit(1);
+ }
+ if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
+ perror("ping: socket");
+ exit(1);
+ }
+ hold = 1;
+ if (options & F_SO_DEBUG)
+ (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
+ sizeof(hold));
+ if (options & F_SO_DONTROUTE)
+ (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
+ sizeof(hold));
+
+ /* record route option */
+ if (options & F_RROUTE) {
+#ifdef IP_OPTIONS
+ rspace[IPOPT_OPTVAL] = IPOPT_RR;
+ rspace[IPOPT_OLEN] = sizeof(rspace)-1;
+ rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
+ if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
+ sizeof(rspace)) < 0) {
+ perror("ping: record route");
+ exit(1);
+ }
+#else
+ (void)fprintf(stderr,
+ "ping: record route not available in this implementation.\n");
+ exit(1);
+#endif /* IP_OPTIONS */
+ }
+
+ /*
+ * When pinging the broadcast address, you can get a lot of answers.
+ * Doing something so evil is useful if you are trying to stress the
+ * ethernet, or just want to fill the arp cache to get some stuff for
+ * /etc/ethers.
+ */
+ hold = 48 * 1024;
+ (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
+ sizeof(hold));
+
+ if (to->sin_family == AF_INET)
+ (void)printf("PING %s (%s): %d data bytes\n", hostname,
+ inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
+ datalen);
+ else
+ (void)printf("PING %s: %d data bytes\n", hostname, datalen);
+
+ (void)signal(SIGINT, finish);
+ (void)signal(SIGALRM, catcher);
+
+ while (preload--) /* fire off them quickies */
+ pinger();
+
+ if ((options & F_FLOOD) == 0)
+ catcher(); /* start things going */
+
+ for (;;) {
+ struct sockaddr_in from;
+ register int cc;
+ int fromlen;
+
+ if (options & F_FLOOD) {
+ pinger();
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ fdmask = 1 << s;
+ if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout) < 1)
+ continue;
+ }
+ fromlen = sizeof(from);
+ if ((cc = recvfrom(s, (char *)packet, packlen, 0,
+ (struct sockaddr *)&from, &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("ping: recvfrom");
+ continue;
+ }
+ pr_pack((char *)packet, cc, &from);
+ if (npackets && nreceived >= npackets)
+ break;
+ }
+ finish();
+ /* NOTREACHED */
+}
+
+/*
+ * catcher --
+ * This routine causes another PING to be transmitted, and then
+ * schedules another SIGALRM for 1 second from now.
+ *
+ * bug --
+ * Our sense of time will slowly skew (i.e., packets will not be
+ * launched exactly at 1-second intervals). This does not affect the
+ * quality of the delay and loss statistics.
+ */
+void
+catcher()
+{
+ int waittime;
+
+ pinger();
+ (void)signal(SIGALRM, catcher);
+ if (!npackets || ntransmitted < npackets)
+ alarm((u_int)interval);
+ else {
+ if (nreceived) {
+ waittime = 2 * tmax / 1000;
+ if (!waittime)
+ waittime = 1;
+ } else
+ waittime = MAXWAIT;
+ (void)signal(SIGALRM, finish);
+ (void)alarm((u_int)waittime);
+ }
+}
+
+/*
+ * pinger --
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+pinger()
+{
+ register struct icmp *icp;
+ register int cc;
+ int i;
+
+ icp = (struct icmp *)outpack;
+ icp->icmp_type = ICMP_ECHO;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = ntransmitted++;
+ icp->icmp_id = ident; /* ID */
+
+ CLR(icp->icmp_seq % mx_dup_ck);
+
+ if (timing)
+ (void)gettimeofday((struct timeval *)&outpack[8],
+ (struct timezone *)NULL);
+
+ cc = datalen + 8; /* skips ICMP portion */
+
+ /* compute ICMP checksum here */
+ icp->icmp_cksum = in_cksum((u_short *)icp, cc);
+
+ i = sendto(s, (char *)outpack, cc, 0, &whereto,
+ sizeof(struct sockaddr));
+
+ if (i < 0 || i != cc) {
+ if (i < 0)
+ perror("ping: sendto");
+ (void)printf("ping: wrote %s %d chars, ret=%d\n",
+ hostname, cc, i);
+ }
+ if (!(options & F_QUIET) && options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &DOT, 1);
+}
+
+/*
+ * pr_pack --
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+pr_pack(buf, cc, from)
+ char *buf;
+ int cc;
+ struct sockaddr_in *from;
+{
+ register struct icmp *icp;
+ register u_long l;
+ register int i, j;
+ register u_char *cp,*dp;
+ static int old_rrlen;
+ static char old_rr[MAX_IPOPTLEN];
+ struct ip *ip;
+ struct timeval tv, *tp;
+ double triptime;
+ int hlen, dupflag;
+
+ (void)gettimeofday(&tv, (struct timezone *)NULL);
+
+ /* Check the IP header */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (options & F_VERBOSE)
+ (void)fprintf(stderr,
+ "ping: packet too short (%d bytes) from %s\n", cc,
+ inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
+ return;
+ }
+
+ /* Now the ICMP part */
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+ if (icp->icmp_type == ICMP_ECHOREPLY) {
+ if (icp->icmp_id != ident)
+ return; /* 'Twas not our ECHO */
+ ++nreceived;
+ if (timing) {
+#ifndef icmp_data
+ tp = (struct timeval *)&icp->icmp_ip;
+#else
+ tp = (struct timeval *)icp->icmp_data;
+#endif
+ tvsub(&tv, tp);
+ triptime = ((double)tv.tv_sec) * 1000.0 +
+ ((double)tv.tv_usec) / 1000.0;
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+
+ if (TST(icp->icmp_seq % mx_dup_ck)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(icp->icmp_seq % mx_dup_ck);
+ dupflag = 0;
+ }
+
+ if (options & F_QUIET)
+ return;
+
+ if (options & F_FLOOD)
+ (void)write(STDOUT_FILENO, &BSPACE, 1);
+ else {
+ (void)printf("%d bytes from %s: icmp_seq=%u", cc,
+ inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
+ icp->icmp_seq);
+ (void)printf(" ttl=%d", ip->ip_ttl);
+ if (timing)
+ (void)printf(" time=%g ms", triptime);
+ if (dupflag)
+ (void)printf(" (DUP!)");
+ /* check the data */
+ cp = (u_char*)&icp->icmp_data[8];
+ dp = &outpack[8 + sizeof(struct timeval)];
+ for (i = 8; i < datalen; ++i, ++cp, ++dp) {
+ if (*cp != *dp) {
+ (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
+ i, *dp, *cp);
+ cp = (u_char*)&icp->icmp_data[0];
+ for (i = 8; i < datalen; ++i, ++cp) {
+ if ((i % 32) == 8)
+ (void)printf("\n\t");
+ (void)printf("%x ", *cp);
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ /* We've got something other than an ECHOREPLY */
+ if (!(options & F_VERBOSE))
+ return;
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from->sin_addr.s_addr));
+ pr_icmph(icp);
+ }
+
+ /* Display any IP options */
+ cp = (u_char *)buf + sizeof(struct ip);
+
+ for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
+ switch (*cp) {
+ case IPOPT_EOL:
+ hlen = 0;
+ break;
+ case IPOPT_LSRR:
+ (void)printf("\nLSRR: ");
+ hlen -= 2;
+ j = *++cp;
+ ++cp;
+ if (j > IPOPT_MINOFF)
+ for (;;) {
+ l = *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ if (l == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s", pr_addr(ntohl(l)));
+ hlen -= 4;
+ j -= 4;
+ if (j <= IPOPT_MINOFF)
+ break;
+ (void)putchar('\n');
+ }
+ break;
+ case IPOPT_RR:
+ j = *++cp; /* get length */
+ i = *++cp; /* and pointer */
+ hlen -= 2;
+ if (i > j)
+ i = j;
+ i -= IPOPT_MINOFF;
+ if (i <= 0)
+ continue;
+ if (i == old_rrlen
+ && cp == (u_char *)buf + sizeof(struct ip) + 2
+ && !bcmp((char *)cp, old_rr, i)
+ && !(options & F_FLOOD)) {
+ (void)printf("\t(same route)");
+ i = ((i + 3) / 4) * 4;
+ hlen -= i;
+ cp += i;
+ break;
+ }
+ old_rrlen = i;
+ bcopy((char *)cp, old_rr, i);
+ (void)printf("\nRR: ");
+ for (;;) {
+ l = *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ l = (l<<8) + *++cp;
+ if (l == 0)
+ (void)printf("\t0.0.0.0");
+ else
+ (void)printf("\t%s", pr_addr(ntohl(l)));
+ hlen -= 4;
+ i -= 4;
+ if (i <= 0)
+ break;
+ (void)putchar('\n');
+ }
+ break;
+ case IPOPT_NOP:
+ (void)printf("\nNOP");
+ break;
+ default:
+ (void)printf("\nunknown option %x", *cp);
+ break;
+ }
+ if (!(options & F_FLOOD)) {
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ }
+}
+
+/*
+ * in_cksum --
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+in_cksum(addr, len)
+ u_short *addr;
+ int len;
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register int sum = 0;
+ u_short answer = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *)(&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
+
+/*
+ * tvsub --
+ * Subtract 2 timeval structs: out = out - in. Out is assumed to
+ * be >= in.
+ */
+tvsub(out, in)
+ register struct timeval *out, *in;
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * finish --
+ * Print out statistics, and give up.
+ */
+void
+finish()
+{
+ register int i;
+
+ (void)signal(SIGINT, SIG_IGN);
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ (void)printf("--- %s ping statistics ---\n", hostname);
+ (void)printf("%ld packets transmitted, ", ntransmitted);
+ (void)printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ (void)printf("+%ld duplicates, ", nrepeats);
+ if (ntransmitted)
+ if (nreceived > ntransmitted)
+ (void)printf("-- somebody's printing up packets!");
+ else
+ (void)printf("%d%% packet loss",
+ (int) (((ntransmitted - nreceived) * 100) /
+ ntransmitted));
+ (void)putchar('\n');
+ if (nreceived && timing) {
+ /* Only display average to microseconds */
+ i = 1000.0 * tsum / (nreceived + nrepeats);
+ (void)printf("round-trip min/avg/max = %g/%g/%g ms\n",
+ tmin, ((double)i) / 1000.0, tmax);
+ }
+ exit(0);
+}
+
+#ifdef notdef
+static char *ttab[] = {
+ "Echo Reply", /* ip + seq + udata */
+ "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
+ "Source Quench", /* IP */
+ "Redirect", /* redirect type, gateway, + IP */
+ "Echo",
+ "Time Exceeded", /* transit, frag reassem + IP */
+ "Parameter Problem", /* pointer + IP */
+ "Timestamp", /* id + seq + three timestamps */
+ "Timestamp Reply", /* " */
+ "Info Request", /* id + sq */
+ "Info Reply" /* " */
+};
+#endif
+
+/*
+ * pr_icmph --
+ * Print a descriptive string about an ICMP header.
+ */
+pr_icmph(icp)
+ struct icmp *icp;
+{
+ switch(icp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ (void)printf("Echo Reply\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_UNREACH:
+ switch(icp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ (void)printf("Destination Net Unreachable\n");
+ break;
+ case ICMP_UNREACH_HOST:
+ (void)printf("Destination Host Unreachable\n");
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ (void)printf("Destination Protocol Unreachable\n");
+ break;
+ case ICMP_UNREACH_PORT:
+ (void)printf("Destination Port Unreachable\n");
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ (void)printf("frag needed and DF set\n");
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ (void)printf("Source Route Failed\n");
+ break;
+ default:
+ (void)printf("Dest Unreachable, Bad Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+ /* Print returned IP header information */
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_SOURCEQUENCH:
+ (void)printf("Source Quench\n");
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_REDIRECT:
+ switch(icp->icmp_code) {
+ case ICMP_REDIRECT_NET:
+ (void)printf("Redirect Network");
+ break;
+ case ICMP_REDIRECT_HOST:
+ (void)printf("Redirect Host");
+ break;
+ case ICMP_REDIRECT_TOSNET:
+ (void)printf("Redirect Type of Service and Network");
+ break;
+ case ICMP_REDIRECT_TOSHOST:
+ (void)printf("Redirect Type of Service and Host");
+ break;
+ default:
+ (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
+ break;
+ }
+ (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr);
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_ECHO:
+ (void)printf("Echo Request\n");
+ /* XXX ID + Seq + Data */
+ break;
+ case ICMP_TIMXCEED:
+ switch(icp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ (void)printf("Time to live exceeded\n");
+ break;
+ case ICMP_TIMXCEED_REASS:
+ (void)printf("Frag reassembly time exceeded\n");
+ break;
+ default:
+ (void)printf("Time exceeded, Bad Code: %d\n",
+ icp->icmp_code);
+ break;
+ }
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_PARAMPROB:
+ (void)printf("Parameter problem: pointer = 0x%02x\n",
+ icp->icmp_hun.ih_pptr);
+#ifndef icmp_data
+ pr_retip(&icp->icmp_ip);
+#else
+ pr_retip((struct ip *)icp->icmp_data);
+#endif
+ break;
+ case ICMP_TSTAMP:
+ (void)printf("Timestamp\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_TSTAMPREPLY:
+ (void)printf("Timestamp Reply\n");
+ /* XXX ID + Seq + 3 timestamps */
+ break;
+ case ICMP_IREQ:
+ (void)printf("Information Request\n");
+ /* XXX ID + Seq */
+ break;
+ case ICMP_IREQREPLY:
+ (void)printf("Information Reply\n");
+ /* XXX ID + Seq */
+ break;
+#ifdef ICMP_MASKREQ
+ case ICMP_MASKREQ:
+ (void)printf("Address Mask Request\n");
+ break;
+#endif
+#ifdef ICMP_MASKREPLY
+ case ICMP_MASKREPLY:
+ (void)printf("Address Mask Reply\n");
+ break;
+#endif
+ default:
+ (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
+ }
+}
+
+/*
+ * pr_iph --
+ * Print an IP header with options.
+ */
+pr_iph(ip)
+ struct ip *ip;
+{
+ int hlen;
+ u_char *cp;
+
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + 20; /* point to options */
+
+ (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
+ (void)printf(" %1x %1x %02x %04x %04x",
+ ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
+ (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
+ (ip->ip_off) & 0x1fff);
+ (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
+ (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
+ (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
+ /* dump and option bytes */
+ while (hlen-- > 20) {
+ (void)printf("%02x", *cp++);
+ }
+ (void)putchar('\n');
+}
+
+/*
+ * pr_addr --
+ * Return an ascii host address as a dotted quad and optionally with
+ * a hostname.
+ */
+char *
+pr_addr(l)
+ u_long l;
+{
+ struct hostent *hp;
+ static char buf[80];
+
+ if ((options & F_NUMERIC) ||
+ !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
+ (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l));
+ else
+ (void)sprintf(buf, "%s (%s)", hp->h_name,
+ inet_ntoa(*(struct in_addr *)&l));
+ return(buf);
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMP) IP packet.
+ */
+pr_retip(ip)
+ struct ip *ip;
+{
+ int hlen;
+ u_char *cp;
+
+ pr_iph(ip);
+ hlen = ip->ip_hl << 2;
+ cp = (u_char *)ip + hlen;
+
+ if (ip->ip_p == 6)
+ (void)printf("TCP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+ else if (ip->ip_p == 17)
+ (void)printf("UDP: from port %u, to port %u (decimal)\n",
+ (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
+}
+
+fill(bp, patp)
+ char *bp, *patp;
+{
+ register int ii, jj, kk;
+ int pat[16];
+ char *cp;
+
+ for (cp = patp; *cp; cp++)
+ if (!isxdigit(*cp)) {
+ (void)fprintf(stderr,
+ "ping: patterns must be specified as hex digits.\n");
+ exit(1);
+ }
+ ii = sscanf(patp,
+ "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
+ &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
+ &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
+ &pat[13], &pat[14], &pat[15]);
+
+ if (ii > 0)
+ for (kk = 0;
+ kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii);
+ kk += ii)
+ for (jj = 0; jj < ii; ++jj)
+ bp[jj + kk] = pat[jj];
+ if (!(options & F_QUIET)) {
+ (void)printf("PATTERN: 0x");
+ for (jj = 0; jj < ii; ++jj)
+ (void)printf("%02x", bp[jj] & 0xFF);
+ (void)printf("\n");
+ }
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n");
+ exit(1);
+}
diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile
new file mode 100644
index 000000000000..c6b8e3eee1ea
--- /dev/null
+++ b/sbin/quotacheck/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= quotacheck
+SRCS= quotacheck.c preen.c
+MAN8= quotacheck.0
+.PATH: ${.CURDIR}/../fsck
+
+.include <bsd.prog.mk>
diff --git a/sbin/quotacheck/preen.c b/sbin/quotacheck/preen.c
new file mode 100644
index 000000000000..005a65d97989
--- /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);
+}
diff --git a/sbin/quotacheck/quotacheck.8 b/sbin/quotacheck/quotacheck.8
new file mode 100644
index 000000000000..41d9fc4b4018
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.8
@@ -0,0 +1,157 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" 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.
+.\"
+.\" @(#)quotacheck.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt QUOTACHECK 8
+.Os BSD 4.2
+.Sh NAME
+.Nm quotacheck
+.Nd filesystem quota consistency checker
+.Sh SYNOPSIS
+.Nm quotacheck
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotacheck
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Quotacheck
+examines each filesystem,
+builds a table of current disk usage,
+and compares this table against that recorded
+in the disk quota file for the filesystem.
+If any inconsistencies are detected, both the
+quota file and the current system copy of the
+incorrect quotas are updated (the latter only
+occurs if an active filesystem is checked).
+By default both user and group quotas are checked.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl a
+If the
+.Fl a
+flag is supplied in place of any filesystem names,
+.Nm quotacheck
+will check all the filesystems indicated in
+.Pa /etc/fstab
+to be read-write with disk quotas.
+By default only the types of quotas listed in
+.Pa /etc/fstab
+are checked.
+.It Fl g
+Only group quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl u
+Only user quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl v
+.Nm quotacheck
+reports discrepancies between the
+calculated and recorded disk quotas.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+is equivalent to the default.
+Parallel passes are run on the filesystems required,
+using the pass numbers in
+.Pa /etc/fstab
+in an identical fashion to
+.Xr fsck 8 .
+.Pp
+Normally
+.Nm quotacheck
+operates silently.
+.Pp
+.Nm Quotacheck
+expects each filesystem to be checked to have a
+quota files named
+.Pa quota.user
+and
+.Pa quota.group
+which are located at the root of the associated file system.
+These defaults may be overridden in
+.Pa /etc/fstab .
+If a file is not present,
+.Nm quotacheck
+will create it.
+.Pp
+.Nm Quotacheck
+is normally run at boot time from the
+.Pa /etc/rc.local
+file, see
+.Xr rc 8 ,
+before enabling disk quotas with
+.Xr quotaon 8 .
+.Pp
+.Nm Quotacheck
+accesses the raw device in calculating the actual
+disk usage for each user.
+Thus, the filesystems
+checked should be quiescent while
+.Nm quotacheck
+is running.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+default filesystems
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr fsck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c
new file mode 100644
index 000000000000..1ee9e87ec231
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * 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, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94";
+#endif /* not lint */
+
+/*
+ * Fix up / report on disk quotas & usage
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ffs/fs.h>
+
+#include <fcntl.h>
+#include <fstab.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+char *quotagroup = QUOTAGROUP;
+
+union {
+ struct fs sblk;
+ char dummy[MAXBSIZE];
+} un;
+#define sblock un.sblk
+long dev_bsize = 1;
+long maxino;
+
+struct quotaname {
+ long flags;
+ char grpqfname[MAXPATHLEN + 1];
+ char usrqfname[MAXPATHLEN + 1];
+};
+#define HASUSR 1
+#define HASGRP 2
+
+struct fileusage {
+ struct fileusage *fu_next;
+ u_long fu_curinodes;
+ u_long fu_curblocks;
+ u_long fu_id;
+ char fu_name[1];
+ /* actually bigger */
+};
+#define FUHASH 1024 /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+
+int aflag; /* all file systems */
+int gflag; /* check group quotas */
+int uflag; /* check user quotas */
+int vflag; /* verbose */
+int fi; /* open disk file descriptor */
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+struct fileusage *
+ addid __P((u_long, int, char *));
+char *blockcheck __P((char *));
+void bread __P((daddr_t, char *, long));
+int chkquota __P((char *, char *, struct quotaname *));
+void err __P((const char *, ...));
+void freeinodebuf __P((void));
+struct dinode *
+ getnextinode __P((ino_t));
+int getquotagid __P((void));
+int hasquota __P((struct fstab *, int, char **));
+struct fileusage *
+ lookup __P((u_long, int));
+void *needchk __P((struct fstab *));
+int oneof __P((char *, char*[], int));
+void resetinodebuf __P((void));
+int update __P((char *, char *, int));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct fstab *fs;
+ register struct passwd *pw;
+ register struct group *gr;
+ struct quotaname *auxdata;
+ int i, argnum, maxrun, errs;
+ long done = 0;
+ char ch, *name;
+
+ errs = maxrun = 0;
+ while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'l':
+ maxrun = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if ((argc == 0 && !aflag) || (argc > 0 && aflag))
+ usage();
+ if (!gflag && !uflag) {
+ gflag++;
+ uflag++;
+ }
+ if (gflag) {
+ setgrent();
+ while ((gr = getgrent()) != 0)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag) {
+ setpwent();
+ while ((pw = getpwent()) != 0)
+ (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+ endpwent();
+ }
+ if (aflag)
+ exit(checkfstab(1, maxrun, needchk, chkquota));
+ if (setfsent() == 0)
+ err("%s: can't open", FSTAB);
+ while ((fs = getfsent()) != NULL) {
+ if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
+ (auxdata = needchk(fs)) &&
+ (name = blockcheck(fs->fs_spec))) {
+ done |= 1 << argnum;
+ errs += chkquota(name, fs->fs_file, auxdata);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ fprintf(stderr, "%s not found in %s\n",
+ argv[i], FSTAB);
+ exit(errs);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage:\t%s\n\t%s\n",
+ "quotacheck -a [-guv]",
+ "quotacheck [-guv] filesys ...");
+ exit(1);
+}
+
+void *
+needchk(fs)
+ register struct fstab *fs;
+{
+ register struct quotaname *qnp;
+ char *qfnp;
+
+ if (strcmp(fs->fs_vfstype, "ufs") ||
+ strcmp(fs->fs_type, FSTAB_RW))
+ return (NULL);
+ if ((qnp = malloc(sizeof(*qnp))) == NULL)
+ err("%s", strerror(errno));
+ qnp->flags = 0;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
+ strcpy(qnp->grpqfname, qfnp);
+ qnp->flags |= HASGRP;
+ }
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
+ strcpy(qnp->usrqfname, qfnp);
+ qnp->flags |= HASUSR;
+ }
+ if (qnp->flags)
+ return (qnp);
+ free(qnp);
+ return (NULL);
+}
+
+/*
+ * Scan the specified filesystem to check quota(s) present on it.
+ */
+int
+chkquota(fsname, mntpt, qnp)
+ char *fsname, *mntpt;
+ register struct quotaname *qnp;
+{
+ register struct fileusage *fup;
+ register struct dinode *dp;
+ int cg, i, mode, errs = 0;
+ ino_t ino;
+
+ if ((fi = open(fsname, O_RDONLY, 0)) < 0) {
+ perror(fsname);
+ return (1);
+ }
+ if (vflag) {
+ (void)printf("*** Checking ");
+ if (qnp->flags & HASUSR)
+ (void)printf("%s%s", qfextension[USRQUOTA],
+ (qnp->flags & HASGRP) ? " and " : "");
+ if (qnp->flags & HASGRP)
+ (void)printf("%s", qfextension[GRPQUOTA]);
+ (void)printf(" quotas for %s (%s)\n", fsname, mntpt);
+ }
+ sync();
+ bread(SBOFF, (char *)&sblock, (long)SBSIZE);
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ resetinodebuf();
+ for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
+ for (i = 0; i < sblock.fs_ipg; i++, ino++) {
+ if (ino < ROOTINO)
+ continue;
+ if ((dp = getnextinode(ino)) == NULL)
+ continue;
+ if ((mode = dp->di_mode & IFMT) == 0)
+ continue;
+ if (qnp->flags & HASGRP) {
+ fup = addid((u_long)dp->di_gid, GRPQUOTA,
+ (char *)0);
+ fup->fu_curinodes++;
+ if (mode == IFREG || mode == IFDIR ||
+ mode == IFLNK)
+ fup->fu_curblocks += dp->di_blocks;
+ }
+ if (qnp->flags & HASUSR) {
+ fup = addid((u_long)dp->di_uid, USRQUOTA,
+ (char *)0);
+ fup->fu_curinodes++;
+ if (mode == IFREG || mode == IFDIR ||
+ mode == IFLNK)
+ fup->fu_curblocks += dp->di_blocks;
+ }
+ }
+ }
+ freeinodebuf();
+ if (qnp->flags & HASUSR)
+ errs += update(mntpt, qnp->usrqfname, USRQUOTA);
+ if (qnp->flags & HASGRP)
+ errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
+ close(fi);
+ return (errs);
+}
+
+/*
+ * Update a specified quota file.
+ */
+int
+update(fsname, quotafile, type)
+ char *fsname, *quotafile;
+ register int type;
+{
+ register struct fileusage *fup;
+ register FILE *qfi, *qfo;
+ register u_long id, lastid;
+ struct dqblk dqbuf;
+ static int warned = 0;
+ static struct dqblk zerodqbuf;
+ static struct fileusage zerofileusage;
+
+ if ((qfo = fopen(quotafile, "r+")) == NULL) {
+ if (errno == ENOENT)
+ qfo = fopen(quotafile, "w+");
+ if (qfo) {
+ (void) fprintf(stderr,
+ "quotacheck: creating quota file %s\n", quotafile);
+#define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
+ (void) fchown(fileno(qfo), getuid(), getquotagid());
+ (void) fchmod(fileno(qfo), MODE);
+ } else {
+ (void) fprintf(stderr,
+ "quotacheck: %s: %s\n", quotafile, strerror(errno));
+ return (1);
+ }
+ }
+ if ((qfi = fopen(quotafile, "r")) == NULL) {
+ (void) fprintf(stderr,
+ "quotacheck: %s: %s\n", quotafile, strerror(errno));
+ (void) fclose(qfo);
+ return (1);
+ }
+ if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
+ errno == EOPNOTSUPP && !warned && vflag) {
+ warned++;
+ (void)printf("*** Warning: %s\n",
+ "Quotas are not compiled into this kernel");
+ }
+ for (lastid = highid[type], id = 0; id <= lastid; id++) {
+ if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
+ dqbuf = zerodqbuf;
+ if ((fup = lookup(id, type)) == 0)
+ fup = &zerofileusage;
+ if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
+ dqbuf.dqb_curblocks == fup->fu_curblocks) {
+ fup->fu_curinodes = 0;
+ fup->fu_curblocks = 0;
+ fseek(qfo, (long)sizeof(struct dqblk), 1);
+ continue;
+ }
+ if (vflag) {
+ if (aflag)
+ printf("%s: ", fsname);
+ printf("%-8s fixed:", fup->fu_name);
+ if (dqbuf.dqb_curinodes != fup->fu_curinodes)
+ (void)printf("\tinodes %d -> %d",
+ dqbuf.dqb_curinodes, fup->fu_curinodes);
+ if (dqbuf.dqb_curblocks != fup->fu_curblocks)
+ (void)printf("\tblocks %d -> %d",
+ dqbuf.dqb_curblocks, fup->fu_curblocks);
+ (void)printf("\n");
+ }
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it.
+ */
+ if (dqbuf.dqb_bsoftlimit &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
+ dqbuf.dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
+ fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
+ dqbuf.dqb_itime = 0;
+ dqbuf.dqb_curinodes = fup->fu_curinodes;
+ dqbuf.dqb_curblocks = fup->fu_curblocks;
+ fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
+ (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
+ (caddr_t)&dqbuf);
+ fup->fu_curinodes = 0;
+ fup->fu_curblocks = 0;
+ }
+ fclose(qfi);
+ fflush(qfo);
+ ftruncate(fileno(qfo),
+ (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
+ fclose(qfo);
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Determine the group identifier for quota files.
+ */
+int
+getquotagid()
+{
+ struct group *gr;
+
+ if (gr = getgrnam(quotagroup))
+ return (gr->gr_gid);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ (void)snprintf(usrname, sizeof(usrname),
+ "%s%s", qfextension[USRQUOTA], qfname);
+ (void)snprintf(grpname, sizeof(grpname),
+ "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if (cp = index(opt, '='))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp)
+ *qfnamep = cp;
+ else {
+ (void)snprintf(buf, sizeof(buf),
+ "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ }
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+ u_long id;
+ int type;
+{
+ register struct fileusage *fup;
+
+ for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+ if (fup->fu_id == id)
+ return (fup);
+ return (NULL);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+ u_long id;
+ int type;
+ char *name;
+{
+ struct fileusage *fup, **fhp;
+ int len;
+
+ if (fup = lookup(id, type))
+ return (fup);
+ if (name)
+ len = strlen(name);
+ else
+ len = 10;
+ if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
+ err("%s", strerror(errno));
+ fhp = &fuhead[type][id & (FUHASH - 1)];
+ fup->fu_next = *fhp;
+ *fhp = fup;
+ fup->fu_id = id;
+ if (id > highid[type])
+ highid[type] = id;
+ if (name)
+ bcopy(name, fup->fu_name, len + 1);
+ else
+ (void)sprintf(fup->fu_name, "%u", id);
+ return (fup);
+}
+
+/*
+ * Special purpose version of ginode used to optimize pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */
+
+struct dinode *
+getnextinode(inumber)
+ ino_t inumber;
+{
+ long size;
+ daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ err("bad inode number %d to nextinode", 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;
+ }
+ bread(dblk, (char *)inodebuf, size);
+ dp = inodebuf;
+ }
+ return (dp++);
+}
+
+/*
+ * Prepare to scan a set of inodes.
+ */
+void
+resetinodebuf()
+{
+
+ 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 = malloc((u_int)inobufsize)) == NULL)
+ err("%s", strerror(errno));
+ while (nextino < ROOTINO)
+ getnextinode(nextino);
+}
+
+/*
+ * Free up data structures used to scan inodes.
+ */
+void
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free(inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Read specified disk blocks.
+ */
+void
+bread(bno, buf, cnt)
+ daddr_t bno;
+ char *buf;
+ long cnt;
+{
+
+ if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 ||
+ read(fi, buf, cnt) != cnt)
+ err("block %ld", bno);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "quotacheck: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile
new file mode 100644
index 000000000000..f4e1a55aac87
--- /dev/null
+++ b/sbin/reboot/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= reboot
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN8= reboot.0 boot_hp300.0 boot_i386.0 boot_sparc.0 boot_tahoe.0 boot_vax.0
+MLINKS= reboot.8 halt.8
+LINKS= ${BINDIR}/reboot ${BINDIR}/halt
+
+afterinstall:
+ ${MINSTALL} boot_hp300.0 ${DESTDIR}${MANDIR}8/hp300/boot.0
+ ${MINSTALL} boot_i386.0 ${DESTDIR}${MANDIR}8/i386/boot.0
+ ${MINSTALL} boot_sparc.0 ${DESTDIR}${MANDIR}8/sparc/boot.0
+ ${MINSTALL} boot_tahoe.0 ${DESTDIR}${MANDIR}8/tahoe/boot.0
+ ${MINSTALL} boot_vax.0 ${DESTDIR}${MANDIR}8/vax/boot.0
+
+.include <bsd.man.mk>
+.include <bsd.prog.mk>
diff --git a/sbin/reboot/boot_hp300.8 b/sbin/reboot/boot_hp300.8
new file mode 100644
index 000000000000..030354264a77
--- /dev/null
+++ b/sbin/reboot/boot_hp300.8
@@ -0,0 +1,117 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Systems Programming Group of the University of Utah Computer
+.\" Science Department.
+.\"
+.\" 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.
+.\"
+.\" @(#)boot_hp300.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt BOOT_HP300 8 hp300
+.Os
+.Sh NAME
+.Nm boot
+.Nd
+system bootstrapping procedures
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery.
+Normally, the system will reboot itself at power-up or after crashes.
+An automatic consistency check of the file systems will be performed,
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts.
+On an HP300, the boot procedure uses the boot ROM to load a boot program
+from an
+.Tn LIF
+format directory at the beginning of an attached disk.
+The
+.Pa /usr/mdec
+directory contains a disk boot programs which should be placed in a
+new pack automatically by
+.Xr newfs 8
+when the ``a'' partition file system on the pack is created.
+.Pp
+This
+.Em boot
+program
+finds the corresponding file on the given device
+.Pf ( Ar vmunix
+by default),
+loads that file into memory,
+and starts the program at the entry address specified in the program header.
+.Pp
+The boot program can be interrupted by typing `^C' (ctrl-C).
+This will force the boot program to interactively prompt for a system to boot.
+If not interrupted, it will boot from the device from which the boot
+program itself was loaded.
+.Pp
+The file specifications used for an interactive boot are of the form:
+.Pp
+.Dl device(unit, minor)
+.Pp
+where
+.Ar device
+is the type of the device to be searched,
+.Ar unit
+is 8 * the hpib number plus the unit number of the disk or tape,
+and
+.Ar minor
+is the disk partition or tape file number.
+Normal line editing characters can be used when typing the file specification.
+Currently, ``rd'' and ``sd'' are the only valid
+.Ar device
+specifiers.
+.Pp
+For example,
+to boot from the `a' file system of unit 0 on HP-IB 2,
+type
+.Ql rd(16, 0)vmunix
+to the boot prompt.
+For tapes, the minor device number gives a file offset.
+.Pp
+In an emergency, the bootstrap methods described in the paper
+.%T Installing 4.3bsd on the HP300
+can be used to boot from a distribution tape.
+.Sh FILES
+.Bl -tag -width /usr/mdec/installboot -compact
+.It Pa /vmunix
+system code
+.It Pa /usr/mdec/bootrd
+.Tn LIF
+format boot block
+.It Pa /usr/mdec/installboot
+program to install boot blocks
+.El
+.Sh SEE ALSO
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8
new file mode 100644
index 000000000000..0dc3e0bf6be5
--- /dev/null
+++ b/sbin/reboot/boot_i386.8
@@ -0,0 +1,126 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software written and contributed
+.\" to Berkeley by William Jolitz.
+.\"
+.\" 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.
+.\"
+.\" @(#)boot_i386.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt BOOT 8 i386
+.Os
+.Sh NAME
+.Nm boot
+.Nd
+system bootstrapping procedures
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery.
+Normally, the system will reboot itself at power-up or after crashes.
+An automatic consistency check of the file systems will be performed,
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts.
+The 386
+.Tn "PC AT"
+clones attempt to boot the floppy disk drive A (otherwise known as drive
+0) first, and failing that, attempt to boot the hard disk C (otherwise
+known as hard disk controller 1, drive 0).
+The automatic boot will attempt to load
+.Pa vmunix
+from partition A of either the floppy or the hard disk.
+This boot may be aborted by typing any character on the keyboard repeatedly
+(four or five times at least) during the operating system load phase, after
+which the bootstrap will prompt for the file that you wish to load instead.
+.Pp
+One exception to this is the
+.Ql d
+key, which will not abort the load but instead silently force the
+.Dv DEBUG
+boot flags.
+The boot flags for an autoboot are 0, and 3 for the successive boot after
+an aborted autoboot sequence.
+No other provison is made for setting boot flags (yet).
+A specific device or bootstrap file may be used; for example,
+.Pp
+The file specifications used for the boostrap
+when loaded with the
+.Dq askme
+flag
+(e.g. an aborted autoboot)
+are of the form:
+.Pp
+.Dl device unit partition:
+.Pp
+where
+.Ar device
+is the type of the device, assumed to be on the ISA bus, to be searched,
+.Ar unit
+is the unit number of the disk or tape,
+and
+.Ar partition
+is the disk partition or tape file number.
+Normal line editing characters can be used when typing the file specification.
+The following list of supported devices may vary from installation to
+installation:
+.Bd -unfilled -offset indent
+wd ST506, IDE, ESDI, RLL disks on a WD100[2367] or lookalike
+ controller
+fd 5 1/4" or 3 1/2" High density floppies
+.Ed
+.Pp
+For example,
+to boot from a file system which starts at cylinder 0
+of unit 0 of an IDE disk, type
+.Dq Li wd0a:vmunix
+to the boot prompt;
+.Dq Li fd0a:vmunix
+would specify a 3 1/2" floppy drive 0 .
+.Pp
+In an emergency, the bootstrap methods described in the paper
+.%T "Installing and Operating 4.3 BSD-Reno UNIX on the AT/386"
+can be used
+to boot from a distribution tape.
+.Sh FILES
+.Bl -tag -width /vmunixxx -compact
+.It Pa /vmunix
+system code
+.It Pa /boot
+system bootstrap
+.El
+.Sh SEE ALSO
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
+.Sh BUGS
+The disklabel format used by this version of
+.Bx
+is quite
+different from that of other architectures.
diff --git a/sbin/reboot/boot_sparc.8 b/sbin/reboot/boot_sparc.8
new file mode 100644
index 000000000000..72e57b01c910
--- /dev/null
+++ b/sbin/reboot/boot_sparc.8
@@ -0,0 +1,89 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)boot_sparc.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt REBOOT 8 sparc
+.Os
+.Sh NAME
+.Nm reboot
+.Nd
+.Tn UNIX
+bootstrapping procedures
+.Sh SYNOPSIS
+.Nm reboot
+.Op Fl n
+.Op Fl q
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery.
+Normally, the system will reboot itself at power-up or after crashes.
+An automatic consistency check of the file systems will be performed
+as described in
+.Xr fsck 8 .
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts
+The SPARC system currently uses the SunOS bootstrap loaders.
+This will be changed in a future version of the system.
+The SunOS boot will attempt to load
+.Pa vmunix
+from partition A of the boot device,
+which must currently be an ``sd'' disk.
+.Pp
+The
+.Op Fl s
+flag to the SunOS boot loader will being the system up in single-user mode.
+The
+.Op Fl d
+flag to the SunOS boot loader will bring the system up in debug mode.
+Here it waits for a kernel debugger connect; see
+.Xr kgdb 8 .
+Other flags are currently ignored.
+.Sh FILES
+.Bl -tag -width /vmunixxx -compact
+.It Pa /vmunix
+system code
+.It Pa /boot
+system bootstrap
+.El
+.Sh SEE ALSO
+.Xr crash 8 ,
+.Xr disklabel 8 ,
+.Xr fsck 8 ,
+.Xr halt 8 ,
+.Xr init 8 ,
+.Xr rc 8 ,
+.Xr shutdown 8 ,
+.Xr syslogd 8
+.Sh BUGS
+The use of Sun disk labels, without the ability to write them,
+is problematic.
diff --git a/sbin/reboot/boot_tahoe.8 b/sbin/reboot/boot_tahoe.8
new file mode 100644
index 000000000000..01c7f81bfe77
--- /dev/null
+++ b/sbin/reboot/boot_tahoe.8
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)boot_tahoe.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt BOOT 8 tahoe
+.Os
+.Sh NAME
+.Nm boot
+.Nd
+system bootstrapping procedures
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery.
+Normally, the system will reboot itself at power-up or after crashes.
+An automatic consistency check of the file systems will be performed,
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts.
+These are processor-type dependent.
+On the
+.Tn CCI
+Power 6/32 and related processors,
+the system will do a standard autoboot from drive 0
+upon power-up or reset.
+This automatic boot may be cancelled by typing a
+.Ql \&#
+in the first few seconds after reset.
+This enters console mode; the console prompt is
+.Ql >
+or
+.Ql \&# .
+The boot flags can be set to any hexadecimal value
+.Fl n
+with the command
+.Pp
+.Bd -filled -offset indent -compact
+.Li \&#> p23
+.Ar n .
+.Ed
+.Pp
+The default device may be examined or set; see the Diagnostics and Debugging
+manual for the processor for details on device naming and syntax.
+After setting the boot flags and/or device,
+a bootstrap sequence can be initiated with
+.Pp
+.Dl #> fb
+.Pp
+A specific device or bootstrap file may be used; for example,
+.Pp
+.Dl \&#> \&fb xfd(1,0)
+.Pp
+would boot from the `a' partition on
+.Tn XFD
+drive 1.
+.Pp
+The file specifications used for the boostrap
+when loaded with the
+.Dq askme
+flag
+(register 23 set to 1 or 3)
+are of the form:
+.Pp
+.Dl device(adaptor,controller,unit,minor)
+.Pp
+where
+.Ar device
+is the type of the device to be searched,
+.Ar adaptor
+is number of the
+.Tn VERSAbus
+(or
+.Tn VMEbus )
+to which the device is attached,
+.Ar controller
+is the unit number of the controller on that buss,
+.Ar unit
+is the unit number of the disk or tape,
+and
+.Ar minor
+is the disk partition or tape file number.
+Leading adaptor or controller numbers default to 0.
+Normal line editing characters can be used when typing the file specification.
+The following list of supported devices may vary from installation to
+installation:
+.Pp
+.Bd -unfilled -offset indent -compact
+dk SMD or ESDI disks on VDDC or SMD-E
+cy tape on Ciprico Tapemaster controller
+.Ed
+.Pp
+For example,
+to boot from a file system which starts at cylinder 0
+of unit 0 of an
+.Tn SMD-E
+disk, type
+.Ql dk(0,0)vmunix
+to the boot prompt;
+.Ql dk(2,1,0)vmunix
+would specify drive 1 on
+.Tn SMD-E
+controller 2.
+.Pp
+In an emergency, the bootstrap methods described in the paper
+.%T "Installing and Operating 4.3 BSD-tahoe UNIX on the Tahoe"
+can be used
+to boot from a distribution tape.
+.Sh FILES
+.Bl -tag -width /vmunix -compact
+.It Pa /vmunix
+system code
+.It Pa /boot
+system bootstrap
+.El
+.Sh SEE ALSO
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
+.Sh BUGS
+The disklabel format used by some versions of the console processor
+is different than the format used by
+.Tn UNIX
+and the bootstrap.
+.Sh HISTORY
diff --git a/sbin/reboot/boot_vax.8 b/sbin/reboot/boot_vax.8
new file mode 100644
index 000000000000..dce6b69105ad
--- /dev/null
+++ b/sbin/reboot/boot_vax.8
@@ -0,0 +1,322 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)boot_vax.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt BOOT 8 vax
+.Os
+.Sh NAME
+.Nm boot
+.Nd
+system bootstrapping procedures
+.Sh DESCRIPTION
+.Sy Power fail and crash recovery.
+Normally, the system will reboot itself at power-up or after crashes.
+Provided the auto-restart is enabled on the machine front panel,
+an automatic consistency check of the file systems will be performed,
+and unless this fails, the system will resume multi-user operations.
+.Pp
+.Sy Cold starts.
+These are processor-type dependent.
+On an 11/780, there are two floppy files for each disk controller,
+both of which cause boots from unit 0 of the root file system
+of a controller located on mba0 or uba0.
+One gives a single user shell, while the other invokes the multi-user
+automatic reboot.
+Thus these files are
+.Tn HPS
+and
+.Tn HPM
+for the single
+and multi-user boot from
+.Tn MASSBUS
+RP06/RM03/RM05 disks,
+.Tn UPS
+and
+.Tn UPM
+for
+.Tn UNIBUS
+storage module controller and disks
+such as the
+.Tn EMULEX
+SC-21
+and
+.Tn AMPEX
+9300 pair,
+.Tn RAS
+and
+.Tn RAM
+to boot from
+.Tn MSCP
+controllers and disks such as the RA81,
+or
+.Tn HKS
+and
+.Tn HKM
+for RK07 disks.
+There is also a script for booting from the default device,
+which is normally a copy of one of the standard multi-user boot scripts,
+but which may be modified to perform other actions
+or to boot from a different unit.
+The situation on the 8600 is similar, with scripts loaded from the console RL02.
+.Pp
+Giving the command
+.Pp
+.Dl >>>BOOT HPM
+.Pp
+would boot the system from (e.g.) an RP06 and run the automatic consistency
+check as described in
+.Xr fsck 8 .
+(Note that it may
+be necessary to type control-P
+and halt the processor
+to gain the attention of the
+.Tn LSI-11
+before getting the >>> prompt.)
+The command
+.Pp
+.Dl >>>BOOT ANY
+.Pp
+invokes a version of the boot program in a way which allows you to
+specify any system as the system to be booted.
+It reads from the console a device specification (see below) followed
+immediately by a pathname.
+.Pp
+The scripts may be modified for local configuration if necessary.
+The flags are placed in register 11 (as defined in
+.Aq Pa sys/reboot.h ) .
+The boot device is specified in register 10.
+The encoding of this register is also defined in
+.Aq Pa sys/reboot.h .
+The current encoding has a historical basis, and is shown in the following
+table:
+.Pp
+.Bd -unfilled -offset indent -compact
+bits usage
+0-7 boot device type (the device major number)
+8-15 disk partition
+16-19 drive unit
+20-23 controller number
+24-27 adaptor number (UNIBUS or MASSBUS as appropriate)
+.Ed
+.Pp
+The adaptor number corresponds to the normal configuration on the 11/750,
+and to the order in which adaptors are found on the 11/780 and 8600
+(generally the same as the numbers used by
+.Tn UNIX ) .
+.Pp
+On an 11/750, the reset button will boot from the device
+selected by the front panel boot device switch. In systems
+with RK07's, position B normally selects the RK07 for boot.
+This will boot multi-user. To boot from RK07 with boot flags you
+may specify
+.Pp
+.Bd -unfilled -offset indent -compact
+.Li \&>>>B/ Ns Fl n No DMA0
+.Ed
+.Pp
+where, giving a
+.Ar n
+of 1 causes the boot program
+to ask for the name of the system to be bootstrapped,
+giving a
+.Ar n
+of 2 causes the boot program to come up single
+user, and a
+.Ar n
+of 3 causes both of these actions to occur.
+The ``DM'' specifies RK07, the ``A'' represents the adaptor number
+.Pf ( Tn UNIBUS
+or
+.Tn MASSBUS ) ,
+and the ``0'' is the drive unit number.
+Other disk types which may be used are DB
+.Pq Tn MASSBUS ,
+DD (TU58),
+and DU
+.Pf ( Tn UDA-50/RA
+disk).
+A non-zero disk partition can be used by adding (partition times 1000 hex)
+to
+.Ar n .
+.Pp
+The boot procedure on the Micro
+.Tn VAX
+II
+is similar.
+A switch on the back panel sets the power-up action
+to autoboot or to halt.
+When halted, the processor may be booted using the same syntax
+as on the 11/750.
+.Pp
+The 11/750 boot procedure uses the boot roms to load block 0 off of
+the specified device. The /usr/mdec directory contains a number
+of bootstrap programs for the various disks which should be placed
+in a new pack by
+.Xr disklabel 8 .
+Similarly, the Micro
+.Tn VAX
+II boot procedure loads a boot parameter block
+from block 0 of the disk.
+The
+.Xr rdboot
+.Dq bootstrap
+contains the correct parameters for an
+.Tn MSCP
+disk such
+as the RD53.
+.Pp
+On any processor, the
+.Em boot
+program
+finds the corresponding file on the given device
+.Pf ( Pa vmunix
+by default), loads that file
+into memory location zero, and starts the program at the entry address
+specified in the program header (after clearing off the high bit
+of the specified entry address).
+.Pp
+The file specifications used with
+.Dq BOOT ANY
+or
+.Dq \&B/3
+are of the form:
+.Pp
+.Dl device(adaptor,controller,unit,minor)
+.Pp
+where
+.Ar device
+is the type of the device to be searched,
+.Ar adaptor
+is the
+.Tn UNIBUS
+or
+.Tn MASSBUS
+number of the adaptor to which the device is attached,
+.Ar controller
+is the unit number of the controller or
+.Tn MASSBUS
+tape formatter on that adaptor,
+.Ar unit
+is the unit number of the disk or transport slave unit of the tape,
+and
+.Ar minor
+is the disk partition or tape file number.
+Leading adaptor or controller numbers default to 0.
+Normal line editing characters can be used when typing the file specification.
+The following list of supported devices may vary from installation to
+installation:
+.Pp
+.Bd -unfilled -offset indent -compact
+hp MASSBUS disk drive
+up UNIBUS storage module drive
+ht TE16,TU45,TU77 on MASSBUS
+kra storage module on a KDB50
+mt TU78 on MASSBUS
+hk RK07 on UNIBUS
+ra storage module on a MSCP-compatible UNIBUS controller
+rb storage module on a 730 IDC
+rl RL02 on UNIBUS
+tm TM11 emulation tape drives on UNIBUS
+tms TMSCP-compatible tape
+ts TS11 on UNIBUS
+ut UNIBUS TU45 emulator
+.Ed
+.Pp
+For example,
+to boot from a file system which starts at cylinder 0
+of unit 0 of a
+.Tn MASSBUS
+disk, type
+.Ql hp(0,0)vmunix
+to the boot prompt;
+.Ql hp(2,0,1,0)vmunix
+would specify drive 1 on
+.Tn MASSBUS
+adaptor 2;
+.Ql up(0,0)vmunix
+would specify a
+.Tn UNIBUS
+drive,
+.Ql hk(0,0)vmunix
+would specify
+an RK07 disk drive,
+.Ql ra(1,0,0,0)vmunix
+would specify a
+.Tn UDA50
+disk drive on a second
+.Tn UNIBUS ,
+and
+.Ql rb(0,0)vmunix
+would specify a
+disk on a 730
+.Tn IDC .
+For tapes, the minor device number gives a file offset;
+.Ql mt(1,2,3,4)
+would specify the fifth file on slave 3 of the formatter
+at
+.Ql drive
+2 on mba 1.
+.Pp
+On an 11/750 with patchable control store,
+microcode patches will be installed by
+.Em boot
+if the file
+.Pa psc750.bin
+exists in the root of the filesystem from which the system is booted.
+.Pp
+In an emergency, the bootstrap methods described in the paper
+.%T Installing and Operating 4.3bsd
+can be used to boot from a distribution tape.
+.Sh FILES
+.Bl -tag -width /usr/mdec/xxboot -compact
+.It Pa /vmunix
+system code
+.It Pa /boot
+system bootstrap
+.It Pa /usr/mdec/xxboot
+sector-0 boot block for 750, xx is disk type
+.It Pa /usr/mdec/bootxx
+second-stage boot for 750, xx is disk type
+.It Pa /pcs750.bin
+microcode patch file on 750
+.El
+.Sh SEE ALSO
+.Xr arff 8 ,
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8
new file mode 100644
index 000000000000..2fd2abbec9b5
--- /dev/null
+++ b/sbin/reboot/reboot.8
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1990, 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.
+.\"
+.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt REBOOT 8
+.Os
+.Sh NAME
+.Nm reboot ,
+.Nm halt
+.Nd
+stopping and restarting the system
+.Sh SYNOPSIS
+.Nm halt
+.Op Fl nq
+.Nm reboot
+.Op Fl nq
+.Sh DESCRIPTION
+The
+.Nm halt
+and
+.Nm reboot
+utilities flush the file system cache to disk, send all running processes
+a SIGTERM (and subsequently a SIGKILL) and, respectively, halt or restart
+the system.
+The action is logged, including entering a shutdown record into the login
+accounting file.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl n
+If the
+.Fl n
+option is specified,
+the file system cache is not flushed.
+This option should probably not be used.
+.It Fl q
+If the
+.Fl q
+option is specified,
+the system is halted or restarted quickly and ungracefully, and only
+the flushing of the file system cache is performed.
+This option should probably not be used.
+.El
+.Pp
+Normally, the
+.Xr shutdown 8
+utility is used when the system needs to be halted or restarted, giving
+users advance warning of their impending doom.
+.Sh SEE ALSO
+.Xr sync 1 ,
+.Xr utmp 5 ,
+.Xr boot 8 ,
+.Xr shutdown 8
+.Sh HISTORY
+A
+.Nm reboot
+command appeared in
+.At v6 .
diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c
new file mode 100644
index 000000000000..df5515a253b2
--- /dev/null
+++ b/sbin/reboot/reboot.c
@@ -0,0 +1,204 @@
+/*
+ * 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[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/reboot.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void err __P((const char *fmt, ...));
+void usage __P((void));
+
+int dohalt;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ struct passwd *pw;
+ int ch, howto, lflag, nflag, qflag, sverrno;
+ char *p, *user;
+
+ if (!strcmp((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
+ dohalt = 1;
+ howto = RB_HALT;
+ } else
+ howto = 0;
+ lflag = nflag = qflag = 0;
+ while ((ch = getopt(argc, argv, "lnq")) != EOF)
+ switch(ch) {
+ case 'l': /* Undocumented; used by shutdown. */
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ howto |= RB_NOSYNC;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (geteuid())
+ err("%s", strerror(EPERM));
+
+ if (qflag) {
+ reboot(howto);
+ err("%s", strerror(errno));
+ }
+
+ /* Log the reboot. */
+ if (!lflag) {
+ if ((user = getlogin()) == NULL)
+ user = (pw = getpwuid(getuid())) ?
+ pw->pw_name : "???";
+ if (dohalt) {
+ openlog("halt", 0, LOG_AUTH | LOG_CONS);
+ syslog(LOG_CRIT, "halted by %s", user);
+ } else {
+ openlog("reboot", 0, LOG_AUTH | LOG_CONS);
+ syslog(LOG_CRIT, "rebooted by %s", user);
+ }
+ }
+ logwtmp("~", "shutdown", "");
+
+ /*
+ * Do a sync early on, so disks start transfers while we're off
+ * killing processes. Don't worry about writes done before the
+ * processes die, the reboot system call syncs the disks.
+ */
+ if (!nflag)
+ sync();
+
+ /* Just stop init -- if we fail, we'll restart it. */
+ if (kill(1, SIGTSTP) == -1)
+ err("SIGTSTP init: %s", strerror(errno));
+
+ /* Ignore the SIGHUP we get when our parent shell dies. */
+ (void)signal(SIGHUP, SIG_IGN);
+
+ /* Send a SIGTERM first, a chance to save the buffers. */
+ if (kill(-1, SIGTERM) == -1)
+ err("SIGTERM processes: %s", strerror(errno));
+
+ /*
+ * After the processes receive the signal, start the rest of the
+ * buffers on their way. Wait 5 seconds between the SIGTERM and
+ * the SIGKILL to give everybody a chance.
+ */
+ sleep(2);
+ if (!nflag)
+ sync();
+ sleep(3);
+
+ for (i = 1;; ++i) {
+ if (kill(-1, SIGKILL) == -1) {
+ if (errno == ESRCH)
+ break;
+ goto restart;
+ }
+ if (i > 5) {
+ (void)fprintf(stderr,
+ "WARNING: some process(es) wouldn't die\n");
+ break;
+ }
+ (void)sleep(2 * i);
+ }
+
+ reboot(howto);
+ /* FALLTHROUGH */
+
+restart:
+ sverrno = errno;
+ err("%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
+ strerror(sverrno));
+ /* NOTREACHED */
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: %s [-nq]\n", dohalt ? "halt" : "reboot");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "%s: ", dohalt ? "halt" : "reboot");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile
new file mode 100644
index 000000000000..6d4da1fb3f97
--- /dev/null
+++ b/sbin/restore/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= restore
+LINKS= ${BINDIR}/restore ${BINDIR}/rrestore
+CFLAGS+=-DRRESTORE
+SRCS= main.c interactive.c restore.c dirs.c symtab.c tape.c utilities.c \
+ dumprmt.c
+BINOWN= root
+BINGRP= tty
+BINMODE=6555
+MAN8= restore.0
+MLINKS+=restore.8 rrestore.8
+.PATH: ${.CURDIR}/../dump
+
+all: ${PROG} ${MAN8}
+
+.include <bsd.prog.mk>
diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c
new file mode 100644
index 000000000000..5a2651c9eeee
--- /dev/null
+++ b/sbin/restore/dirs.c
@@ -0,0 +1,747 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dirs.c 8.2 (Berkeley) 1/21/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Symbol table of directories read from tape.
+ */
+#define HASHSIZE 1000
+#define INOHASH(val) (val % HASHSIZE)
+struct inotab {
+ struct inotab *t_next;
+ ino_t t_ino;
+ long t_seekpt;
+ long t_size;
+};
+static struct inotab *inotab[HASHSIZE];
+
+/*
+ * Information retained about directories.
+ */
+struct modeinfo {
+ ino_t ino;
+ struct timeval timep[2];
+ short mode;
+ short uid;
+ short gid;
+};
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+#undef DIRBLKSIZ
+#define DIRBLKSIZ 1024
+struct rstdirdesc {
+ int dd_fd;
+ long dd_loc;
+ long dd_size;
+ char dd_buf[DIRBLKSIZ];
+};
+
+/*
+ * Global variables for this file.
+ */
+static long seekpt;
+static FILE *df, *mf;
+static RST_DIR *dirp;
+static char dirfile[32] = "#"; /* No file */
+static char modefile[32] = "#"; /* No file */
+static char dot[2] = "."; /* So it can be modified */
+
+/*
+ * Format of old style directories.
+ */
+#define ODIRSIZ 14
+struct odirect {
+ u_short d_ino;
+ char d_name[ODIRSIZ];
+};
+
+static struct inotab *allocinotab __P((ino_t, struct dinode *, long));
+static void dcvt __P((struct odirect *, struct direct *));
+static void flushent __P((void));
+static struct inotab *inotablookup __P((ino_t));
+static RST_DIR *opendirfile __P((const char *));
+static void putdir __P((char *, long));
+static void putent __P((struct direct *));
+static void rst_seekdir __P((RST_DIR *, long, long));
+static long rst_telldir __P((RST_DIR *));
+static struct direct *searchdir __P((ino_t, char *));
+
+/*
+ * Extract directory contents, building up a directory structure
+ * on disk for extraction by name.
+ * If genmode is requested, save mode, owner, and times for all
+ * directories on the tape.
+ */
+void
+extractdirs(genmode)
+ int genmode;
+{
+ register int i;
+ register struct dinode *ip;
+ struct inotab *itp;
+ struct direct nulldir;
+
+ vprintf(stdout, "Extract directories from tape\n");
+ (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate);
+ df = fopen(dirfile, "w");
+ if (df == NULL) {
+ fprintf(stderr,
+ "restore: %s - cannot create directory temporary\n",
+ dirfile);
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ done(1);
+ }
+ if (genmode != 0) {
+ (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
+ mf = fopen(modefile, "w");
+ if (mf == NULL) {
+ fprintf(stderr,
+ "restore: %s - cannot create modefile \n",
+ modefile);
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ nulldir.d_ino = 0;
+ nulldir.d_type = DT_DIR;
+ nulldir.d_namlen = 1;
+ (void) strcpy(nulldir.d_name, "/");
+ nulldir.d_reclen = DIRSIZ(0, &nulldir);
+ for (;;) {
+ curfile.name = "<directory file - name unknown>";
+ curfile.action = USING;
+ ip = curfile.dip;
+ if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
+ (void) fclose(df);
+ dirp = opendirfile(dirfile);
+ if (dirp == NULL)
+ fprintf(stderr, "opendirfile: %s\n",
+ strerror(errno));
+ if (mf != NULL)
+ (void) fclose(mf);
+ i = dirlookup(dot);
+ if (i == 0)
+ panic("Root directory is not on tape\n");
+ return;
+ }
+ itp = allocinotab(curfile.ino, ip, seekpt);
+ getfile(putdir, xtrnull);
+ putent(&nulldir);
+ flushent();
+ itp->t_size = seekpt - itp->t_seekpt;
+ }
+}
+
+/*
+ * skip over all the directories on the tape
+ */
+void
+skipdirs()
+{
+
+ while ((curfile.dip->di_mode & IFMT) == IFDIR) {
+ skipfile();
+ }
+}
+
+/*
+ * Recursively find names and inumbers of all files in subtree
+ * pname and pass them off to be processed.
+ */
+void
+treescan(pname, ino, todo)
+ char *pname;
+ ino_t ino;
+ long (*todo) __P((char *, ino_t, int));
+{
+ register struct inotab *itp;
+ register struct direct *dp;
+ int namelen;
+ long bpt;
+ char locname[MAXPATHLEN + 1];
+
+ itp = inotablookup(ino);
+ if (itp == NULL) {
+ /*
+ * Pname is name of a simple file or an unchanged directory.
+ */
+ (void) (*todo)(pname, ino, LEAF);
+ return;
+ }
+ /*
+ * Pname is a dumped directory name.
+ */
+ if ((*todo)(pname, ino, NODE) == FAIL)
+ return;
+ /*
+ * begin search through the directory
+ * skipping over "." and ".."
+ */
+ (void) strncpy(locname, pname, MAXPATHLEN);
+ (void) strncat(locname, "/", MAXPATHLEN);
+ namelen = strlen(locname);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = rst_readdir(dirp); /* "." */
+ if (dp != NULL && strcmp(dp->d_name, ".") == 0)
+ dp = rst_readdir(dirp); /* ".." */
+ else
+ fprintf(stderr, "Warning: `.' missing from directory %s\n",
+ pname);
+ if (dp != NULL && strcmp(dp->d_name, "..") == 0)
+ dp = rst_readdir(dirp); /* first real entry */
+ else
+ fprintf(stderr, "Warning: `..' missing from directory %s\n",
+ pname);
+ bpt = rst_telldir(dirp);
+ /*
+ * a zero inode signals end of directory
+ */
+ while (dp != NULL && dp->d_ino != 0) {
+ locname[namelen] = '\0';
+ if (namelen + dp->d_namlen >= MAXPATHLEN) {
+ fprintf(stderr, "%s%s: name exceeds %d char\n",
+ locname, dp->d_name, MAXPATHLEN);
+ } else {
+ (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
+ treescan(locname, dp->d_ino, todo);
+ rst_seekdir(dirp, bpt, itp->t_seekpt);
+ }
+ dp = rst_readdir(dirp);
+ bpt = rst_telldir(dirp);
+ }
+ if (dp == NULL)
+ fprintf(stderr, "corrupted directory: %s.\n", locname);
+}
+
+/*
+ * Lookup a pathname which is always assumed to start from the ROOTINO.
+ */
+struct direct *
+pathsearch(pathname)
+ const char *pathname;
+{
+ ino_t ino;
+ struct direct *dp;
+ char *path, *name, buffer[MAXPATHLEN];
+
+ strcpy(buffer, pathname);
+ path = buffer;
+ ino = ROOTINO;
+ while (*path == '/')
+ path++;
+ dp = NULL;
+ while ((name = strsep(&path, "/")) != NULL && *name != NULL) {
+ if ((dp = searchdir(ino, name)) == NULL)
+ return (NULL);
+ ino = dp->d_ino;
+ }
+ return (dp);
+}
+
+/*
+ * Lookup the requested name in directory inum.
+ * Return its inode number if found, zero if it does not exist.
+ */
+static struct direct *
+searchdir(inum, name)
+ ino_t inum;
+ char *name;
+{
+ register struct direct *dp;
+ register struct inotab *itp;
+ int len;
+
+ itp = inotablookup(inum);
+ if (itp == NULL)
+ return (NULL);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ len = strlen(name);
+ do {
+ dp = rst_readdir(dirp);
+ if (dp == NULL || dp->d_ino == 0)
+ return (NULL);
+ } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
+ return (dp);
+}
+
+/*
+ * Put the directory entries in the directory file
+ */
+static void
+putdir(buf, size)
+ char *buf;
+ long size;
+{
+ struct direct cvtbuf;
+ register struct odirect *odp;
+ struct odirect *eodp;
+ register struct direct *dp;
+ long loc, i;
+
+ if (cvtflag) {
+ eodp = (struct odirect *)&buf[size];
+ for (odp = (struct odirect *)buf; odp < eodp; odp++)
+ if (odp->d_ino != 0) {
+ dcvt(odp, &cvtbuf);
+ putent(&cvtbuf);
+ }
+ } else {
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(buf + loc);
+ if (oldinofmt) {
+ if (Bcvt) {
+ swabst((u_char *)"l2s", (u_char *) dp);
+ }
+ } else {
+ if (Bcvt) {
+ swabst((u_char *)"ls", (u_char *) dp);
+ }
+ }
+ i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
+ if ((dp->d_reclen & 0x3) != 0 ||
+ dp->d_reclen > i ||
+ dp->d_reclen < DIRSIZ(0, dp) ||
+ dp->d_namlen > NAME_MAX) {
+ vprintf(stdout, "Mangled directory: ");
+ if ((dp->d_reclen & 0x3) != 0)
+ vprintf(stdout,
+ "reclen not multiple of 4 ");
+ if (dp->d_reclen < DIRSIZ(0, dp))
+ vprintf(stdout,
+ "reclen less than DIRSIZ (%d < %d) ",
+ dp->d_reclen, DIRSIZ(0, dp));
+ if (dp->d_namlen > NAME_MAX)
+ vprintf(stdout,
+ "reclen name too big (%d > %d) ",
+ dp->d_namlen, NAME_MAX);
+ vprintf(stdout, "\n");
+ loc += i;
+ continue;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino != 0) {
+ putent(dp);
+ }
+ }
+ }
+}
+
+/*
+ * These variables are "local" to the following two functions.
+ */
+char dirbuf[DIRBLKSIZ];
+long dirloc = 0;
+long prev = 0;
+
+/*
+ * add a new directory entry to a file.
+ */
+static void
+putent(dp)
+ struct direct *dp;
+{
+ dp->d_reclen = DIRSIZ(0, dp);
+ if (dirloc + dp->d_reclen > DIRBLKSIZ) {
+ ((struct direct *)(dirbuf + prev))->d_reclen =
+ DIRBLKSIZ - prev;
+ (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
+ dirloc = 0;
+ }
+ bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen);
+ prev = dirloc;
+ dirloc += dp->d_reclen;
+}
+
+/*
+ * flush out a directory that is finished.
+ */
+static void
+flushent()
+{
+ ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
+ (void) fwrite(dirbuf, (int)dirloc, 1, df);
+ seekpt = ftell(df);
+ dirloc = 0;
+}
+
+static void
+dcvt(odp, ndp)
+ register struct odirect *odp;
+ register struct direct *ndp;
+{
+
+ bzero((char *)ndp, (long)(sizeof *ndp));
+ ndp->d_ino = odp->d_ino;
+ ndp->d_type = DT_UNKNOWN;
+ (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
+ ndp->d_namlen = strlen(ndp->d_name);
+ ndp->d_reclen = DIRSIZ(0, ndp);
+}
+
+/*
+ * Seek to an entry in a directory.
+ * Only values returned by rst_telldir should be passed to rst_seekdir.
+ * This routine handles many directories in a single file.
+ * It takes the base of the directory in the file, plus
+ * the desired seek offset into it.
+ */
+static void
+rst_seekdir(dirp, loc, base)
+ register RST_DIR *dirp;
+ long loc, base;
+{
+
+ if (loc == rst_telldir(dirp))
+ return;
+ loc -= base;
+ if (loc < 0)
+ fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
+ (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
+ dirp->dd_loc = loc & (DIRBLKSIZ - 1);
+ if (dirp->dd_loc != 0)
+ dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+rst_readdir(dirp)
+ register RST_DIR *dirp;
+{
+ register struct direct *dp;
+
+ for (;;) {
+ if (dirp->dd_loc == 0) {
+ dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
+ DIRBLKSIZ);
+ if (dirp->dd_size <= 0) {
+ dprintf(stderr, "error reading directory\n");
+ return (NULL);
+ }
+ }
+ if (dirp->dd_loc >= dirp->dd_size) {
+ dirp->dd_loc = 0;
+ continue;
+ }
+ dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
+ if (dp->d_reclen == 0 ||
+ dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
+ dprintf(stderr, "corrupted directory: bad reclen %d\n",
+ dp->d_reclen);
+ return (NULL);
+ }
+ dirp->dd_loc += dp->d_reclen;
+ if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0)
+ continue;
+ if (dp->d_ino >= maxino) {
+ dprintf(stderr, "corrupted directory: bad inum %d\n",
+ dp->d_ino);
+ continue;
+ }
+ return (dp);
+ }
+}
+
+/*
+ * Simulate the opening of a directory
+ */
+RST_DIR *
+rst_opendir(name)
+ const char *name;
+{
+ struct inotab *itp;
+ RST_DIR *dirp;
+ ino_t ino;
+
+ if ((ino = dirlookup(name)) > 0 &&
+ (itp = inotablookup(ino)) != NULL) {
+ dirp = opendirfile(dirfile);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ return (dirp);
+ }
+ return (NULL);
+}
+
+/*
+ * In our case, there is nothing to do when closing a directory.
+ */
+void
+rst_closedir(dirp)
+ RST_DIR *dirp;
+{
+
+ (void)close(dirp->dd_fd);
+ free(dirp);
+ return;
+}
+
+/*
+ * Simulate finding the current offset in the directory.
+ */
+static long
+rst_telldir(dirp)
+ RST_DIR *dirp;
+{
+ return ((long)lseek(dirp->dd_fd,
+ (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
+}
+
+/*
+ * Open a directory file.
+ */
+static RST_DIR *
+opendirfile(name)
+ const char *name;
+{
+ register RST_DIR *dirp;
+ register int fd;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ return (NULL);
+ if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
+ (void)close(fd);
+ return (NULL);
+ }
+ dirp->dd_fd = fd;
+ dirp->dd_loc = 0;
+ return (dirp);
+}
+
+/*
+ * Set the mode, owner, and times for all new or changed directories
+ */
+void
+setdirmodes(flags)
+ int flags;
+{
+ FILE *mf;
+ struct modeinfo node;
+ struct entry *ep;
+ char *cp;
+
+ vprintf(stdout, "Set directory mode, owner, and times.\n");
+ (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
+ mf = fopen(modefile, "r");
+ if (mf == NULL) {
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ fprintf(stderr, "cannot open mode file %s\n", modefile);
+ fprintf(stderr, "directory mode, owner, and times not set\n");
+ return;
+ }
+ clearerr(mf);
+ for (;;) {
+ (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
+ if (feof(mf))
+ break;
+ ep = lookupino(node.ino);
+ if (command == 'i' || command == 'x') {
+ if (ep == NULL)
+ continue;
+ if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
+ ep->e_flags &= ~NEW;
+ continue;
+ }
+ if (node.ino == ROOTINO &&
+ reply("set owner/mode for '.'") == FAIL)
+ continue;
+ }
+ if (ep == NULL) {
+ panic("cannot find directory inode %d\n", node.ino);
+ } else {
+ cp = myname(ep);
+ (void) chown(cp, node.uid, node.gid);
+ (void) chmod(cp, node.mode);
+ utimes(cp, node.timep);
+ ep->e_flags &= ~NEW;
+ }
+ }
+ if (ferror(mf))
+ panic("error setting directory modes\n");
+ (void) fclose(mf);
+}
+
+/*
+ * Generate a literal copy of a directory.
+ */
+int
+genliteraldir(name, ino)
+ char *name;
+ ino_t ino;
+{
+ register struct inotab *itp;
+ int ofile, dp, i, size;
+ char buf[BUFSIZ];
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ panic("Cannot find directory inode %d named %s\n", ino, name);
+ if ((ofile = creat(name, 0666)) < 0) {
+ fprintf(stderr, "%s: ", name);
+ (void) fflush(stderr);
+ fprintf(stderr, "cannot create file: %s\n", strerror(errno));
+ return (FAIL);
+ }
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = dup(dirp->dd_fd);
+ for (i = itp->t_size; i > 0; i -= BUFSIZ) {
+ size = i < BUFSIZ ? i : BUFSIZ;
+ if (read(dp, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ done(1);
+ }
+ if (!Nflag && write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "write: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ (void) close(dp);
+ (void) close(ofile);
+ return (GOOD);
+}
+
+/*
+ * Determine the type of an inode
+ */
+int
+inodetype(ino)
+ ino_t ino;
+{
+ struct inotab *itp;
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ return (LEAF);
+ return (NODE);
+}
+
+/*
+ * Allocate and initialize a directory inode entry.
+ * If requested, save its pertinent mode, owner, and time info.
+ */
+static struct inotab *
+allocinotab(ino, dip, seekpt)
+ ino_t ino;
+ struct dinode *dip;
+ long seekpt;
+{
+ register struct inotab *itp;
+ struct modeinfo node;
+
+ itp = calloc(1, sizeof(struct inotab));
+ if (itp == NULL)
+ panic("no memory directory table\n");
+ itp->t_next = inotab[INOHASH(ino)];
+ inotab[INOHASH(ino)] = itp;
+ itp->t_ino = ino;
+ itp->t_seekpt = seekpt;
+ if (mf == NULL)
+ return (itp);
+ node.ino = ino;
+ node.timep[0].tv_sec = dip->di_atime.ts_sec;
+ node.timep[0].tv_usec = dip->di_atime.ts_nsec / 1000;
+ node.timep[1].tv_sec = dip->di_mtime.ts_sec;
+ node.timep[1].tv_usec = dip->di_mtime.ts_nsec / 1000;
+ node.mode = dip->di_mode;
+ node.uid = dip->di_uid;
+ node.gid = dip->di_gid;
+ (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
+ return (itp);
+}
+
+/*
+ * Look up an inode in the table of directories
+ */
+static struct inotab *
+inotablookup(ino)
+ ino_t ino;
+{
+ register struct inotab *itp;
+
+ for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
+ if (itp->t_ino == ino)
+ return (itp);
+ return (NULL);
+}
+
+/*
+ * Clean up and exit
+ */
+__dead void
+done(exitcode)
+ int exitcode;
+{
+
+ closemt();
+ if (modefile[0] != '#')
+ (void) unlink(modefile);
+ if (dirfile[0] != '#')
+ (void) unlink(dirfile);
+ exit(exitcode);
+}
diff --git a/sbin/restore/extern.h b/sbin/restore/extern.h
new file mode 100644
index 000000000000..d82569e3d724
--- /dev/null
+++ b/sbin/restore/extern.h
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 1/7/94
+ */
+
+struct entry *addentry __P((char *, ino_t, int));
+long addfile __P((char *, ino_t, int));
+void badentry __P((struct entry *, char *));
+void canon __P((char *, char *));
+void checkrestore __P((void));
+void closemt __P((void));
+void createfiles __P((void));
+void createleaves __P((char *));
+void createlinks __P((void));
+long deletefile __P((char *, ino_t, int));
+void deleteino __P((ino_t));
+ino_t dirlookup __P((const char *));
+__dead void done __P((int));
+void dumpsymtable __P((char *, long));
+void extractdirs __P((int));
+int extractfile __P((char *));
+void findunreflinks __P((void));
+char *flagvalues __P((struct entry *));
+void freeentry __P((struct entry *));
+void freename __P((char *));
+int genliteraldir __P((char *, ino_t));
+char *gentempname __P((struct entry *));
+void getfile __P((void (*)(char *, long), void (*)(char *, long)));
+void getvol __P((long));
+void initsymtable __P((char *));
+int inodetype __P((ino_t));
+int linkit __P((char *, char *, int));
+struct entry *lookupino __P((ino_t));
+struct entry *lookupname __P((char *));
+long listfile __P((char *, ino_t, int));
+ino_t lowerbnd __P((ino_t));
+void mktempname __P((struct entry *));
+void moveentry __P((struct entry *, char *));
+void msg __P((const char *, ...));
+char *myname __P((struct entry *));
+void newnode __P((struct entry *));
+void newtapebuf __P((long));
+long nodeupdates __P((char *, ino_t, int));
+void onintr __P((int));
+void panic __P((const char *, ...));
+void pathcheck __P((char *));
+struct direct *pathsearch __P((const char *));
+void printdumpinfo __P((void));
+void removeleaf __P((struct entry *));
+void removenode __P((struct entry *));
+void removeoldleaves __P((void));
+void removeoldnodes __P((void));
+void renameit __P((char *, char *));
+int reply __P((char *));
+RST_DIR *rst_opendir __P((const char *));
+struct direct *rst_readdir __P((RST_DIR *));
+void rst_closedir __P((RST_DIR *dirp));
+void runcmdshell __P((void));
+char *savename __P((char *));
+void setdirmodes __P((int));
+void setinput __P((char *));
+void setup __P((void));
+void skipdirs __P((void));
+void skipfile __P((void));
+void skipmaps __P((void));
+void swabst __P((u_char *, u_char *));
+void treescan __P((char *, ino_t, long (*)(char *, ino_t, int)));
+ino_t upperbnd __P((ino_t));
+long verifyfile __P((char *, ino_t, int));
+void xtrnull __P((char *, long));
+
+/* From ../dump/dumprmt.c */
+void rmtclose __P((void));
+int rmthost __P((char *));
+int rmtioctl __P((int, int));
+int rmtopen __P((char *, int));
+int rmtread __P((char *, int));
+int rmtseek __P((int, int));
diff --git a/sbin/restore/interactive.c b/sbin/restore/interactive.c
new file mode 100644
index 000000000000..1b9616cb3c25
--- /dev/null
+++ b/sbin/restore/interactive.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)interactive.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <protocols/dumprestore.h>
+
+#include <setjmp.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restore.h"
+#include "extern.h"
+
+#define round(a, b) (((a) + (b) - 1) / (b) * (b))
+
+/*
+ * Things to handle interruptions.
+ */
+static int runshell;
+static jmp_buf reset;
+static char *nextarg = NULL;
+
+/*
+ * Structure and routines associated with listing directories.
+ */
+struct afile {
+ ino_t fnum; /* inode number of file */
+ char *fname; /* file name */
+ short len; /* name length */
+ char prefix; /* prefix character */
+ char postfix; /* postfix character */
+};
+struct arglist {
+ int freeglob; /* glob structure needs to be freed */
+ int argcnt; /* next globbed argument to return */
+ glob_t glob; /* globbing information */
+ char *cmd; /* the current command */
+};
+
+static char *copynext __P((char *, char *));
+static int fcmp __P((const void *, const void *));
+static void formatf __P((struct afile *, int));
+static void getcmd __P((char *, char *, char *, struct arglist *));
+struct dirent *glob_readdir __P((RST_DIR *dirp));
+static int glob_stat __P((const char *, struct stat *));
+static void mkentry __P((struct direct *, struct afile *));
+static void printlist __P((char *, char *));
+
+/*
+ * Read and execute commands from the terminal.
+ */
+void
+runcmdshell()
+{
+ register struct entry *np;
+ ino_t ino;
+ struct arglist arglist;
+ char curdir[MAXPATHLEN];
+ char name[MAXPATHLEN];
+ char cmd[BUFSIZ];
+
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
+ arglist.glob.gl_opendir = (void *)rst_opendir;
+ arglist.glob.gl_readdir = (void *)glob_readdir;
+ arglist.glob.gl_closedir = (void *)rst_closedir;
+ arglist.glob.gl_lstat = glob_stat;
+ arglist.glob.gl_stat = glob_stat;
+ canon("/", curdir);
+loop:
+ if (setjmp(reset) != 0) {
+ if (arglist.freeglob != 0) {
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ globfree(&arglist.glob);
+ }
+ nextarg = NULL;
+ volno = 0;
+ }
+ runshell = 1;
+ getcmd(curdir, cmd, name, &arglist);
+ switch (cmd[0]) {
+ /*
+ * Add elements to the extraction list.
+ */
+ case 'a':
+ if (strncmp(cmd, "add", strlen(cmd)) != 0)
+ goto bad;
+ ino = dirlookup(name);
+ if (ino == 0)
+ break;
+ if (mflag)
+ pathcheck(name);
+ treescan(name, ino, addfile);
+ break;
+ /*
+ * Change working directory.
+ */
+ case 'c':
+ if (strncmp(cmd, "cd", strlen(cmd)) != 0)
+ goto bad;
+ ino = dirlookup(name);
+ if (ino == 0)
+ break;
+ if (inodetype(ino) == LEAF) {
+ fprintf(stderr, "%s: not a directory\n", name);
+ break;
+ }
+ (void) strcpy(curdir, name);
+ break;
+ /*
+ * Delete elements from the extraction list.
+ */
+ case 'd':
+ if (strncmp(cmd, "delete", strlen(cmd)) != 0)
+ goto bad;
+ np = lookupname(name);
+ if (np == NULL || (np->e_flags & NEW) == 0) {
+ fprintf(stderr, "%s: not on extraction list\n", name);
+ break;
+ }
+ treescan(name, np->e_ino, deletefile);
+ break;
+ /*
+ * Extract the requested list.
+ */
+ case 'e':
+ if (strncmp(cmd, "extract", strlen(cmd)) != 0)
+ goto bad;
+ createfiles();
+ createlinks();
+ setdirmodes(0);
+ if (dflag)
+ checkrestore();
+ volno = 0;
+ break;
+ /*
+ * List available commands.
+ */
+ case 'h':
+ if (strncmp(cmd, "help", strlen(cmd)) != 0)
+ goto bad;
+ case '?':
+ fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "Available commands are:\n",
+ "\tls [arg] - list directory\n",
+ "\tcd arg - change directory\n",
+ "\tpwd - print current directory\n",
+ "\tadd [arg] - add `arg' to list of",
+ " files to be extracted\n",
+ "\tdelete [arg] - delete `arg' from",
+ " list of files to be extracted\n",
+ "\textract - extract requested files\n",
+ "\tsetmodes - set modes of requested directories\n",
+ "\tquit - immediately exit program\n",
+ "\twhat - list dump header information\n",
+ "\tverbose - toggle verbose flag",
+ " (useful with ``ls'')\n",
+ "\thelp or `?' - print this list\n",
+ "If no `arg' is supplied, the current",
+ " directory is used\n");
+ break;
+ /*
+ * List a directory.
+ */
+ case 'l':
+ if (strncmp(cmd, "ls", strlen(cmd)) != 0)
+ goto bad;
+ printlist(name, curdir);
+ break;
+ /*
+ * Print current directory.
+ */
+ case 'p':
+ if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
+ goto bad;
+ if (curdir[1] == '\0')
+ fprintf(stderr, "/\n");
+ else
+ fprintf(stderr, "%s\n", &curdir[1]);
+ break;
+ /*
+ * Quit.
+ */
+ case 'q':
+ if (strncmp(cmd, "quit", strlen(cmd)) != 0)
+ goto bad;
+ return;
+ case 'x':
+ if (strncmp(cmd, "xit", strlen(cmd)) != 0)
+ goto bad;
+ return;
+ /*
+ * Toggle verbose mode.
+ */
+ case 'v':
+ if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
+ goto bad;
+ if (vflag) {
+ fprintf(stderr, "verbose mode off\n");
+ vflag = 0;
+ break;
+ }
+ fprintf(stderr, "verbose mode on\n");
+ vflag++;
+ break;
+ /*
+ * Just restore requested directory modes.
+ */
+ case 's':
+ if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
+ goto bad;
+ setdirmodes(FORCE);
+ break;
+ /*
+ * Print out dump header information.
+ */
+ case 'w':
+ if (strncmp(cmd, "what", strlen(cmd)) != 0)
+ goto bad;
+ printdumpinfo();
+ break;
+ /*
+ * Turn on debugging.
+ */
+ case 'D':
+ if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
+ goto bad;
+ if (dflag) {
+ fprintf(stderr, "debugging mode off\n");
+ dflag = 0;
+ break;
+ }
+ fprintf(stderr, "debugging mode on\n");
+ dflag++;
+ break;
+ /*
+ * Unknown command.
+ */
+ default:
+ bad:
+ fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
+ break;
+ }
+ goto loop;
+}
+
+/*
+ * Read and parse an interactive command.
+ * The first word on the line is assigned to "cmd". If
+ * there are no arguments on the command line, then "curdir"
+ * is returned as the argument. If there are arguments
+ * on the line they are returned one at a time on each
+ * successive call to getcmd. Each argument is first assigned
+ * to "name". If it does not start with "/" the pathname in
+ * "curdir" is prepended to it. Finally "canon" is called to
+ * eliminate any embedded ".." components.
+ */
+static void
+getcmd(curdir, cmd, name, ap)
+ char *curdir, *cmd, *name;
+ struct arglist *ap;
+{
+ register char *cp;
+ static char input[BUFSIZ];
+ char output[BUFSIZ];
+# define rawname input /* save space by reusing input buffer */
+
+ /*
+ * Check to see if still processing arguments.
+ */
+ if (ap->argcnt > 0)
+ goto retnext;
+ if (nextarg != NULL)
+ goto getnext;
+ /*
+ * Read a command line and trim off trailing white space.
+ */
+ do {
+ fprintf(stderr, "restore > ");
+ (void) fflush(stderr);
+ (void) fgets(input, BUFSIZ, terminal);
+ } while (!feof(terminal) && input[0] == '\n');
+ if (feof(terminal)) {
+ (void) strcpy(cmd, "quit");
+ return;
+ }
+ for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
+ /* trim off trailing white space and newline */;
+ *++cp = '\0';
+ /*
+ * Copy the command into "cmd".
+ */
+ cp = copynext(input, cmd);
+ ap->cmd = cmd;
+ /*
+ * If no argument, use curdir as the default.
+ */
+ if (*cp == '\0') {
+ (void) strcpy(name, curdir);
+ return;
+ }
+ nextarg = cp;
+ /*
+ * Find the next argument.
+ */
+getnext:
+ cp = copynext(nextarg, rawname);
+ if (*cp == '\0')
+ nextarg = NULL;
+ else
+ nextarg = cp;
+ /*
+ * If it is an absolute pathname, canonicalize it and return it.
+ */
+ if (rawname[0] == '/') {
+ canon(rawname, name);
+ } else {
+ /*
+ * For relative pathnames, prepend the current directory to
+ * it then canonicalize and return it.
+ */
+ (void) strcpy(output, curdir);
+ (void) strcat(output, "/");
+ (void) strcat(output, rawname);
+ canon(output, name);
+ }
+ if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
+ fprintf(stderr, "%s: out of memory\n", ap->cmd);
+ if (ap->glob.gl_pathc == 0)
+ return;
+ ap->freeglob = 1;
+ ap->argcnt = ap->glob.gl_pathc;
+
+retnext:
+ strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
+ if (--ap->argcnt == 0) {
+ ap->freeglob = 0;
+ globfree(&ap->glob);
+ }
+# undef rawname
+}
+
+/*
+ * Strip off the next token of the input.
+ */
+static char *
+copynext(input, output)
+ char *input, *output;
+{
+ register char *cp, *bp;
+ char quote;
+
+ for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
+ /* skip to argument */;
+ bp = output;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
+ /*
+ * Handle back slashes.
+ */
+ if (*cp == '\\') {
+ if (*++cp == '\0') {
+ fprintf(stderr,
+ "command lines cannot be continued\n");
+ continue;
+ }
+ *bp++ = *cp++;
+ continue;
+ }
+ /*
+ * The usual unquoted case.
+ */
+ if (*cp != '\'' && *cp != '"') {
+ *bp++ = *cp++;
+ continue;
+ }
+ /*
+ * Handle single and double quotes.
+ */
+ quote = *cp++;
+ while (*cp != quote && *cp != '\0')
+ *bp++ = *cp++ | 0200;
+ if (*cp++ == '\0') {
+ fprintf(stderr, "missing %c\n", quote);
+ cp--;
+ continue;
+ }
+ }
+ *bp = '\0';
+ return (cp);
+}
+
+/*
+ * Canonicalize file names to always start with ``./'' and
+ * remove any imbedded "." and ".." components.
+ */
+void
+canon(rawname, canonname)
+ char *rawname, *canonname;
+{
+ register char *cp, *np;
+
+ if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
+ (void) strcpy(canonname, "");
+ else if (rawname[0] == '/')
+ (void) strcpy(canonname, ".");
+ else
+ (void) strcpy(canonname, "./");
+ (void) strcat(canonname, rawname);
+ /*
+ * Eliminate multiple and trailing '/'s
+ */
+ for (cp = np = canonname; *np != '\0'; cp++) {
+ *cp = *np++;
+ while (*cp == '/' && *np == '/')
+ np++;
+ }
+ *cp = '\0';
+ if (*--cp == '/')
+ *cp = '\0';
+ /*
+ * Eliminate extraneous "." and ".." from pathnames.
+ */
+ for (np = canonname; *np != '\0'; ) {
+ np++;
+ cp = np;
+ while (*np != '/' && *np != '\0')
+ np++;
+ if (np - cp == 1 && *cp == '.') {
+ cp--;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
+ cp--;
+ while (cp > &canonname[1] && *--cp != '/')
+ /* find beginning of name */;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ }
+}
+
+/*
+ * Do an "ls" style listing of a directory
+ */
+static void
+printlist(name, basename)
+ char *name;
+ char *basename;
+{
+ register struct afile *fp, *list, *listp;
+ register struct direct *dp;
+ struct afile single;
+ RST_DIR *dirp;
+ int entries, len;
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
+ return;
+ if ((dirp = rst_opendir(name)) == NULL) {
+ entries = 1;
+ list = &single;
+ mkentry(dp, list);
+ len = strlen(basename) + 1;
+ if (strlen(name) - len > single.len) {
+ freename(single.fname);
+ single.fname = savename(&name[len]);
+ single.len = strlen(single.fname);
+ }
+ } else {
+ entries = 0;
+ while (dp = rst_readdir(dirp))
+ entries++;
+ rst_closedir(dirp);
+ list = (struct afile *)malloc(entries * sizeof(struct afile));
+ if (list == NULL) {
+ fprintf(stderr, "ls: out of memory\n");
+ return;
+ }
+ if ((dirp = rst_opendir(name)) == NULL)
+ panic("directory reopen failed\n");
+ fprintf(stderr, "%s:\n", name);
+ entries = 0;
+ listp = list;
+ while (dp = rst_readdir(dirp)) {
+ if (dp == NULL || dp->d_ino == 0)
+ break;
+ if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
+ continue;
+ if (vflag == 0 &&
+ (strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0))
+ continue;
+ mkentry(dp, listp++);
+ entries++;
+ }
+ rst_closedir(dirp);
+ if (entries == 0) {
+ fprintf(stderr, "\n");
+ free(list);
+ return;
+ }
+ qsort((char *)list, entries, sizeof(struct afile), fcmp);
+ }
+ formatf(list, entries);
+ if (dirp != NULL) {
+ for (fp = listp - 1; fp >= list; fp--)
+ freename(fp->fname);
+ fprintf(stderr, "\n");
+ free(list);
+ }
+}
+
+/*
+ * Read the contents of a directory.
+ */
+static void
+mkentry(dp, fp)
+ struct direct *dp;
+ register struct afile *fp;
+{
+ char *cp;
+ struct entry *np;
+
+ fp->fnum = dp->d_ino;
+ fp->fname = savename(dp->d_name);
+ for (cp = fp->fname; *cp; cp++)
+ if (!vflag && (*cp < ' ' || *cp >= 0177))
+ *cp = '?';
+ fp->len = cp - fp->fname;
+ if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
+ fp->prefix = '^';
+ else if ((np = lookupino(fp->fnum)) != NULL && (np->e_flags & NEW))
+ fp->prefix = '*';
+ else
+ fp->prefix = ' ';
+ switch(dp->d_type) {
+
+ default:
+ fprintf(stderr, "Warning: undefined file type %d\n",
+ dp->d_type);
+ /* fall through */
+ case DT_REG:
+ fp->postfix = ' ';
+ break;
+
+ case DT_LNK:
+ fp->postfix = '@';
+ break;
+
+ case DT_FIFO:
+ case DT_SOCK:
+ fp->postfix = '=';
+ break;
+
+ case DT_CHR:
+ case DT_BLK:
+ fp->postfix = '#';
+ break;
+
+ case DT_UNKNOWN:
+ case DT_DIR:
+ if (inodetype(dp->d_ino) == NODE)
+ fp->postfix = '/';
+ else
+ fp->postfix = ' ';
+ break;
+ }
+ return;
+}
+
+/*
+ * Print out a pretty listing of a directory
+ */
+static void
+formatf(list, nentry)
+ register struct afile *list;
+ int nentry;
+{
+ register struct afile *fp, *endlist;
+ int width, bigino, haveprefix, havepostfix;
+ int i, j, w, precision, columns, lines;
+
+ width = 0;
+ haveprefix = 0;
+ havepostfix = 0;
+ bigino = ROOTINO;
+ endlist = &list[nentry];
+ for (fp = &list[0]; fp < endlist; fp++) {
+ if (bigino < fp->fnum)
+ bigino = fp->fnum;
+ if (width < fp->len)
+ width = fp->len;
+ if (fp->prefix != ' ')
+ haveprefix = 1;
+ if (fp->postfix != ' ')
+ havepostfix = 1;
+ }
+ if (haveprefix)
+ width++;
+ if (havepostfix)
+ width++;
+ if (vflag) {
+ for (precision = 0, i = bigino; i > 0; i /= 10)
+ precision++;
+ width += precision + 1;
+ }
+ width++;
+ columns = 81 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (nentry + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ fp = &list[j * lines + i];
+ if (vflag) {
+ fprintf(stderr, "%*d ", precision, fp->fnum);
+ fp->len += precision + 1;
+ }
+ if (haveprefix) {
+ putc(fp->prefix, stderr);
+ fp->len++;
+ }
+ fprintf(stderr, "%s", fp->fname);
+ if (havepostfix) {
+ putc(fp->postfix, stderr);
+ fp->len++;
+ }
+ if (fp + lines >= endlist) {
+ fprintf(stderr, "\n");
+ break;
+ }
+ for (w = fp->len; w < width; w++)
+ putc(' ', stderr);
+ }
+ }
+}
+
+/*
+ * Skip over directory entries that are not on the tape
+ *
+ * First have to get definition of a dirent.
+ */
+#undef DIRBLKSIZ
+#include <dirent.h>
+#undef d_ino
+
+struct dirent *
+glob_readdir(dirp)
+ RST_DIR *dirp;
+{
+ struct direct *dp;
+ static struct dirent adirent;
+
+ while ((dp = rst_readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dflag || TSTINO(dp->d_ino, dumpmap))
+ break;
+ }
+ if (dp == NULL)
+ return (NULL);
+ adirent.d_fileno = dp->d_ino;
+ adirent.d_namlen = dp->d_namlen;
+ bcopy(dp->d_name, adirent.d_name, dp->d_namlen + 1);
+ return (&adirent);
+}
+
+/*
+ * Return st_mode information in response to stat or lstat calls
+ */
+static int
+glob_stat(name, stp)
+ const char *name;
+ struct stat *stp;
+{
+ register struct direct *dp;
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
+ return (-1);
+ if (inodetype(dp->d_ino) == NODE)
+ stp->st_mode = IFDIR;
+ else
+ stp->st_mode = IFREG;
+ return (0);
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+static int
+fcmp(f1, f2)
+ register const void *f1, *f2;
+{
+ return (strcmp(((struct afile *)f1)->fname,
+ ((struct afile *)f2)->fname));
+}
+
+/*
+ * respond to interrupts
+ */
+void
+onintr(signo)
+ int signo;
+{
+ if (command == 'i' && runshell)
+ longjmp(reset, 1);
+ if (reply("restore interrupted, continue") == FAIL)
+ done(1);
+}
diff --git a/sbin/restore/main.c b/sbin/restore/main.c
new file mode 100644
index 000000000000..e2d8eeb34d3a
--- /dev/null
+++ b/sbin/restore/main.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/7/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <protocols/dumprestore.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pathnames.h"
+#include "restore.h"
+#include "extern.h"
+
+int bflag = 0, cvtflag = 0, dflag = 0, vflag = 0, yflag = 0;
+int hflag = 1, mflag = 1, Nflag = 0;
+char command = '\0';
+long dumpnum = 1;
+long volno = 0;
+long ntrec;
+char *dumpmap;
+char *clrimap;
+ino_t maxino;
+time_t dumptime;
+time_t dumpdate;
+FILE *terminal;
+
+static void obsolete __P((int *, char **[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ ino_t ino;
+ char *inputdev = _PATH_DEFTAPE;
+ char *symtbl = "./restoresymtable";
+ char *p, name[MAXPATHLEN];
+
+ if (argc < 2)
+ usage();
+
+ obsolete(&argc, &argv);
+ while ((ch = getopt(argc, argv, "b:cdf:himNRrs:tvxy")) != EOF)
+ switch(ch) {
+ case 'b':
+ /* Change default tape blocksize. */
+ bflag = 1;
+ ntrec = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal blocksize -- %s", optarg);
+ if (ntrec <= 0)
+ errx(1, "block size must be greater than 0");
+ break;
+ case 'c':
+ cvtflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ inputdev = optarg;
+ break;
+ case 'h':
+ hflag = 0;
+ break;
+ case 'i':
+ case 'R':
+ case 'r':
+ case 't':
+ case 'x':
+ if (command != '\0')
+ errx(1,
+ "%c and %c options are mutually exclusive",
+ ch, command);
+ command = ch;
+ break;
+ case 'm':
+ mflag = 0;
+ break;
+ case 'N':
+ Nflag = 1;
+ break;
+ case 's':
+ /* Dumpnum (skip to) for multifile dump tapes. */
+ dumpnum = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal dump number -- %s", optarg);
+ if (dumpnum <= 0)
+ errx(1, "dump number must be greater than 0");
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'y':
+ yflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (command == '\0')
+ errx(1, "none of i, R, r, t or x options specified");
+
+ if (signal(SIGINT, onintr) == SIG_IGN)
+ (void) signal(SIGINT, SIG_IGN);
+ if (signal(SIGTERM, onintr) == SIG_IGN)
+ (void) signal(SIGTERM, SIG_IGN);
+ setlinebuf(stderr);
+
+ setinput(inputdev);
+
+ if (argc == 0) {
+ argc = 1;
+ *--argv = ".";
+ }
+
+ switch (command) {
+ /*
+ * Interactive mode.
+ */
+ case 'i':
+ setup();
+ extractdirs(1);
+ initsymtable(NULL);
+ runcmdshell();
+ break;
+ /*
+ * Incremental restoration of a file system.
+ */
+ case 'r':
+ setup();
+ if (dumptime > 0) {
+ /*
+ * This is an incremental dump tape.
+ */
+ vprintf(stdout, "Begin incremental restore\n");
+ initsymtable(symtbl);
+ extractdirs(1);
+ removeoldleaves();
+ vprintf(stdout, "Calculate node updates.\n");
+ treescan(".", ROOTINO, nodeupdates);
+ findunreflinks();
+ removeoldnodes();
+ } else {
+ /*
+ * This is a level zero dump tape.
+ */
+ vprintf(stdout, "Begin level 0 restore\n");
+ initsymtable((char *)0);
+ extractdirs(1);
+ vprintf(stdout, "Calculate extraction list.\n");
+ treescan(".", ROOTINO, nodeupdates);
+ }
+ createleaves(symtbl);
+ createlinks();
+ setdirmodes(FORCE);
+ checkrestore();
+ if (dflag) {
+ vprintf(stdout, "Verify the directory structure\n");
+ treescan(".", ROOTINO, verifyfile);
+ }
+ dumpsymtable(symtbl, (long)1);
+ break;
+ /*
+ * Resume an incremental file system restoration.
+ */
+ case 'R':
+ initsymtable(symtbl);
+ skipmaps();
+ skipdirs();
+ createleaves(symtbl);
+ createlinks();
+ setdirmodes(FORCE);
+ checkrestore();
+ dumpsymtable(symtbl, (long)1);
+ break;
+ /*
+ * List contents of tape.
+ */
+ case 't':
+ setup();
+ extractdirs(0);
+ initsymtable((char *)0);
+ while (argc--) {
+ canon(*argv++, name);
+ ino = dirlookup(name);
+ if (ino == 0)
+ continue;
+ treescan(name, ino, listfile);
+ }
+ break;
+ /*
+ * Batch extraction of tape contents.
+ */
+ case 'x':
+ setup();
+ extractdirs(1);
+ initsymtable((char *)0);
+ while (argc--) {
+ canon(*argv++, name);
+ ino = dirlookup(name);
+ if (ino == 0)
+ continue;
+ if (mflag)
+ pathcheck(name);
+ treescan(name, ino, addfile);
+ }
+ createfiles();
+ createlinks();
+ setdirmodes(0);
+ if (dflag)
+ checkrestore();
+ break;
+ }
+ done(0);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage:\t%s%s%s%s%s",
+ "restore tfhsvy [file ...]\n",
+ "\trestore xfhmsvy [file ...]\n",
+ "\trestore ifhmsvy\n",
+ "\trestore rfsvy\n",
+ "\trestore Rfsvy\n");
+ done(1);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(argcp, argvp)
+ int *argcp;
+ char **argvp[];
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch(*ap) {
+ case 'b':
+ case 'f':
+ case 's':
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ if (*argv != NULL)
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while (*nargv++ = *argv++);
+}
diff --git a/sbin/restore/pathnames.h b/sbin/restore/pathnames.h
new file mode 100644
index 000000000000..b9541f96db60
--- /dev/null
+++ b/sbin/restore/pathnames.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.2 (Berkeley) 1/21/94
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFTAPE "/dev/rmt8"
diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8
new file mode 100644
index 000000000000..dfe7d681ee95
--- /dev/null
+++ b/sbin/restore/restore.8
@@ -0,0 +1,405 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)restore.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt RESTORE 8
+.Os BSD 4
+.Sh NAME
+.Nm restore
+.Nd "restore files or file systems from backups made with dump"
+.Sh SYNOPSIS
+.Nm restore
+.Ar key
+.Op Ar name Ar ...
+.Sh DESCRIPTION
+The
+.Nm restore
+command performs the inverse function of
+.Xr dump 8 .
+A full backup of a file system may be restored and
+subsequent incremental backups layered on top of it.
+Single files and
+directory subtrees may be restored from full or partial
+backups.
+.Nm Restore
+works across a network;
+to do this see the
+.Fl f
+flag described below.
+The actions
+of
+.Nm restore
+are controlled by the given
+.Cm key ,
+which
+is a string of characters containing
+at most one function letter and possibly
+one or more function modifiers.
+Other arguments to the command are file or directory
+names specifying the files that are to be restored.
+Unless the
+.Cm h
+key is specified (see below),
+the appearance of a directory name refers to
+the files and (recursively) subdirectories of that directory.
+.Pp
+The function portion of
+the key is specified by one of the following letters:
+.Bl -tag -width Ds
+.It Cm r
+Restore (rebuild a file system).
+The target file system should be made pristine with
+.Xr newfs 8 ,
+mounted and the
+user
+.Xr cd Ns 'd
+into the pristine file system
+before starting the restoration of the initial level 0 backup. If the
+level 0 restores successfully, the
+.Cm r
+key may be used to restore
+any necessary incremental backups on top of the level 0.
+The
+.Cm r
+key precludes an interactive file extraction and can be
+detrimental to one's health if not used carefully (not to mention
+the disk). An example:
+.Bd -literal -offset indent
+newfs /dev/rrp0g eagle
+mount /dev/rp0g /mnt
+cd /mnt
+
+restore rf /dev/rst8
+.Ed
+.Pp
+Note that
+.Nm restore
+leaves a file
+.Pa restoresymtable
+in the root directory to pass information between incremental
+restore passes.
+This file should be removed when the last incremental has been
+restored.
+.Pp
+.Nm Restore ,
+in conjunction with
+.Xr newfs 8
+and
+.Xr dump 8 ,
+may be used to modify file system parameters
+such as size or block size.
+.It Cm R
+.Nm Restore
+requests a particular tape of a multi volume set on which to restart
+a full restore
+(see the
+.Cm r
+key above).
+This is useful if the restore has been interrupted.
+.It Cm x
+The named files are read from the given media.
+If a named file matches a directory whose contents
+are on the backup
+and the
+.Cm h
+key is not specified,
+the directory is recursively extracted.
+The owner, modification time,
+and mode are restored (if possible).
+If no file argument is given,
+then the root directory is extracted,
+which results in the entire content of the
+backup being extracted,
+unless the
+.Cm h
+key has been specified.
+.It Cm t
+The names of the specified files are listed if they occur
+on the backup.
+If no file argument is given,
+then the root directory is listed,
+which results in the entire content of the
+backup being listed,
+unless the
+.Cm h
+key has been specified.
+Note that the
+.Cm t
+key replaces the function of the old
+.Xr dumpdir 8
+program.
+.It Cm i
+This mode allows interactive restoration of files from a dump.
+After reading in the directory information from the dump,
+.Nm restore
+provides a shell like interface that allows the user to move
+around the directory tree selecting files to be extracted.
+The available commands are given below;
+for those commands that require an argument,
+the default is the current directory.
+.Bl -tag -width Fl
+.It Ic add Op Ar arg
+The current directory or specified argument is added to the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+added to the extraction list
+(unless the
+.Cm h
+key is specified on the command line).
+Files that are on the extraction list are prepended with a ``*''
+when they are listed by
+.Ic ls .
+.It Ic \&cd Ar arg
+Change the current working directory to the specified argument.
+.It Ic delete Op Ar arg
+The current directory or specified argument is deleted from the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+deleted from the extraction list
+(unless the
+.Cm h
+key is specified on the command line).
+The most expedient way to extract most of the files from a directory
+is to add the directory to the extraction list and then delete
+those files that are not needed.
+.It Ic extract
+All the files that are on the extraction list are extracted
+from the dump.
+.Nm Restore
+will ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.It Ic help
+List a summary of the available commands.
+.It Ic \&ls Op Ar arg
+List the current or specified directory.
+Entries that are directories are appended with a ``/''.
+Entries that have been marked for extraction are prepended with a ``*''.
+If the verbose key is set the inode number of each entry is also listed.
+.It Ic pwd
+Print the full pathname of the current working directory.
+.It Ic quit
+Restore immediately exits,
+even if the extraction list is not empty.
+.It Ic setmodes
+All the directories that have been added to the extraction list
+have their owner, modes, and times set;
+nothing is extracted from the dump.
+This is useful for cleaning up after a restore has been prematurely aborted.
+.It Ic verbose
+The sense of the
+.Cm v
+key is toggled.
+When set, the verbose key causes the
+.Ic ls
+command to list the inode numbers of all entries.
+It also causes
+.Nm restore
+to print out information about each file as it is extracted.
+.El
+.El
+.Pp
+The following characters may be used in addition to the letter
+that selects the function desired.
+.Bl -tag -width Ds
+.It Cm b
+The next argument to
+.Nm restore
+is used as the block size of the media (in kilobytes).
+If the
+.Fl b
+option is not specified,
+.Nm restore
+tries to determine the media block size dynamically.
+.It Cm f
+The next argument to
+.Nm restore
+is used as the name of the archive instead
+of
+.Pa /dev/rmt? .
+If the name of the file is of the form
+.Dq host:file ,
+.Nm restore
+reads from the named file on the remote host using
+.Xr rmt 8 .
+If the name of the file is
+.Ql Fl ,
+.Nm restore
+reads from standard input.
+Thus,
+.Xr dump 8
+and
+.Nm restore
+can be used in a pipeline to dump and restore a file system
+with the command
+.Bd -literal -offset indent
+dump 0f - /usr | (cd /mnt; restore xf -)
+.Ed
+.Pp
+.It Cm h
+.Nm Restore
+extracts the actual directory,
+rather than the files that it references.
+This prevents hierarchical restoration of complete subtrees
+from the dump.
+.It Cm m
+.Nm Restore
+will extract by inode numbers rather than by file name.
+This is useful if only a few files are being extracted,
+and one wants to avoid regenerating the complete pathname
+to the file.
+.It Cm s
+The next argument to
+.Nm restore
+is a number which
+selects the file on a multi-file dump tape. File numbering
+starts at 1.
+.It Cm v
+Normally
+.Nm restore
+does its work silently.
+The
+.Cm v
+(verbose)
+key causes it to type the name of each file it treats
+preceded by its file type.
+.It Cm y
+.Nm Restore
+will not ask whether it should abort the restore if it gets an error.
+It will always try to skip over the bad block(s) and continue as
+best it can.
+.El
+.Sh DIAGNOSTICS
+Complaints about bad key characters.
+.Pp
+Complaints if it gets a read error.
+If
+.Cm y
+has been specified, or the user responds
+.Ql y ,
+.Nm restore
+will attempt to continue the restore.
+.Pp
+If a backup was made using more than one tape volume,
+.Nm restore
+will notify the user when it is time to mount the next volume.
+If the
+.Cm x
+or
+.Cm i
+key has been specified,
+.Nm restore
+will also ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.Pp
+There are numerous consistency checks that can be listed by
+.Nm restore .
+Most checks are self-explanatory or can ``never happen''.
+Common errors are given below.
+.Pp
+.Bl -tag -width Ds -compact
+.It Converting to new file system format.
+A dump tape created from the old file system has been loaded.
+It is automatically converted to the new file system format.
+.Pp
+.It <filename>: not found on tape
+The specified file name was listed in the tape directory,
+but was not found on the tape.
+This is caused by tape read errors while looking for the file,
+and from using a dump tape created on an active file system.
+.Pp
+.It expected next file <inumber>, got <inumber>
+A file that was not listed in the directory showed up.
+This can occur when using a dump created on an active file system.
+.Pp
+.It Incremental dump too low
+When doing incremental restore,
+a dump that was written before the previous incremental dump,
+or that has too low an incremental level has been loaded.
+.Pp
+.It Incremental dump too high
+When doing incremental restore,
+a dump that does not begin its coverage where the previous incremental
+dump left off,
+or that has too high an incremental level has been loaded.
+.Pp
+.It Tape read error while restoring <filename>
+.It Tape read error while skipping over inode <inumber>
+.It Tape read error while trying to resynchronize
+A tape (or other media) read error has occurred.
+If a file name is specified,
+then its contents are probably partially wrong.
+If an inode is being skipped or the tape is trying to resynchronize,
+then no extracted files have been corrupted,
+though files may not be found on the tape.
+.Pp
+.It resync restore, skipped <num> blocks
+After a dump read error,
+.Nm restore
+may have to resynchronize itself.
+This message lists the number of blocks that were skipped over.
+.El
+.Sh FILES
+.Bl -tag -width "./restoresymtable" -compact
+.It Pa /dev/rmt?
+the default tape drive
+.It Pa /tmp/rstdir*
+file containing directories on the tape.
+.It Pa /tmp/rstmode*
+owner, mode, and time stamps for directories.
+.It Pa \&./restoresymtable
+information passed between incremental restores.
+.El
+.Sh SEE ALSO
+.Xr dump 8 ,
+.Xr newfs 8 ,
+.Xr mount 8 ,
+.Xr mkfs 8 ,
+.Xr rmt 8
+.Sh BUGS
+.Nm Restore
+can get confused when doing incremental restores from
+dump that were made on active file systems.
+.Pp
+A level zero dump must be done after a full restore.
+Because restore runs in user code,
+it has no control over inode allocation;
+thus a full restore must be done to get a new set of directories
+reflecting the new inode numbering,
+even though the contents of the files is unchanged.
+.Sh HISTORY
+The
+.Nm restore
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c
new file mode 100644
index 000000000000..dea964c7649a
--- /dev/null
+++ b/sbin/restore/restore.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)restore.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "restore.h"
+#include "extern.h"
+
+static char *keyval __P((int));
+
+/*
+ * This implements the 't' option.
+ * List entries on the tape.
+ */
+long
+listfile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ long descend = hflag ? GOOD : FAIL;
+
+ if (TSTINO(ino, dumpmap) == 0)
+ return (descend);
+ vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
+ fprintf(stdout, "%10d\t%s\n", ino, name);
+ return (descend);
+}
+
+/*
+ * This implements the 'x' option.
+ * Request that new entries be extracted.
+ */
+long
+addfile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ register struct entry *ep;
+ long descend = hflag ? GOOD : FAIL;
+ char buf[100];
+
+ if (TSTINO(ino, dumpmap) == 0) {
+ dprintf(stdout, "%s: not on the tape\n", name);
+ return (descend);
+ }
+ if (!mflag) {
+ (void) sprintf(buf, "./%u", ino);
+ name = buf;
+ if (type == NODE) {
+ (void) genliteraldir(name, ino);
+ return (descend);
+ }
+ }
+ ep = lookupino(ino);
+ if (ep != NULL) {
+ if (strcmp(name, myname(ep)) == 0) {
+ ep->e_flags |= NEW;
+ return (descend);
+ }
+ type |= LINK;
+ }
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW;
+ return (descend);
+}
+
+/*
+ * This is used by the 'i' option to undo previous requests made by addfile.
+ * Delete entries from the request queue.
+ */
+/* ARGSUSED */
+long
+deletefile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ long descend = hflag ? GOOD : FAIL;
+ struct entry *ep;
+
+ if (TSTINO(ino, dumpmap) == 0)
+ return (descend);
+ ep = lookupino(ino);
+ if (ep != NULL)
+ ep->e_flags &= ~NEW;
+ return (descend);
+}
+
+/*
+ * The following four routines implement the incremental
+ * restore algorithm. The first removes old entries, the second
+ * does renames and calculates the extraction list, the third
+ * cleans up link names missed by the first two, and the final
+ * one deletes old directories.
+ *
+ * Directories cannot be immediately deleted, as they may have
+ * other files in them which need to be moved out first. As
+ * directories to be deleted are found, they are put on the
+ * following deletion list. After all deletions and renames
+ * are done, this list is actually deleted.
+ */
+static struct entry *removelist;
+
+/*
+ * Remove unneeded leaves from the old tree.
+ * Remove directories from the lookup chains.
+ */
+void
+removeoldleaves()
+{
+ register struct entry *ep;
+ register ino_t i;
+
+ vprintf(stdout, "Mark entries to be removed.\n");
+ for (i = ROOTINO + 1; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ if (TSTINO(i, clrimap))
+ continue;
+ for ( ; ep != NULL; ep = ep->e_links) {
+ dprintf(stdout, "%s: REMOVE\n", myname(ep));
+ if (ep->e_type == LEAF) {
+ removeleaf(ep);
+ freeentry(ep);
+ } else {
+ mktempname(ep);
+ deleteino(ep->e_ino);
+ ep->e_next = removelist;
+ removelist = ep;
+ }
+ }
+ }
+}
+
+/*
+ * For each directory entry on the incremental tape, determine which
+ * category it falls into as follows:
+ * KEEP - entries that are to be left alone.
+ * NEW - new entries to be added.
+ * EXTRACT - files that must be updated with new contents.
+ * LINK - new links to be added.
+ * Renames are done at the same time.
+ */
+long
+nodeupdates(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ register struct entry *ep, *np, *ip;
+ long descend = GOOD;
+ int lookuptype = 0;
+ int key = 0;
+ /* key values */
+# define ONTAPE 0x1 /* inode is on the tape */
+# define INOFND 0x2 /* inode already exists */
+# define NAMEFND 0x4 /* name already exists */
+# define MODECHG 0x8 /* mode of inode changed */
+
+ /*
+ * This routine is called once for each element in the
+ * directory hierarchy, with a full path name.
+ * The "type" value is incorrectly specified as LEAF for
+ * directories that are not on the dump tape.
+ *
+ * Check to see if the file is on the tape.
+ */
+ if (TSTINO(ino, dumpmap))
+ key |= ONTAPE;
+ /*
+ * Check to see if the name exists, and if the name is a link.
+ */
+ np = lookupname(name);
+ if (np != NULL) {
+ key |= NAMEFND;
+ ip = lookupino(np->e_ino);
+ if (ip == NULL)
+ panic("corrupted symbol table\n");
+ if (ip != np)
+ lookuptype = LINK;
+ }
+ /*
+ * Check to see if the inode exists, and if one of its links
+ * corresponds to the name (if one was found).
+ */
+ ip = lookupino(ino);
+ if (ip != NULL) {
+ key |= INOFND;
+ for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
+ if (ep == np) {
+ ip = ep;
+ break;
+ }
+ }
+ }
+ /*
+ * If both a name and an inode are found, but they do not
+ * correspond to the same file, then both the inode that has
+ * been found and the inode corresponding to the name that
+ * has been found need to be renamed. The current pathname
+ * is the new name for the inode that has been found. Since
+ * all files to be deleted have already been removed, the
+ * named file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number.
+ */
+ if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ dprintf(stdout, "name/inode conflict, mktempname %s\n",
+ myname(np));
+ mktempname(np);
+ }
+ np = NULL;
+ key &= ~NAMEFND;
+ }
+ if ((key & ONTAPE) &&
+ (((key & INOFND) && ip->e_type != type) ||
+ ((key & NAMEFND) && np->e_type != type)))
+ key |= MODECHG;
+
+ /*
+ * Decide on the disposition of the file based on its flags.
+ * Note that we have already handled the case in which
+ * a name and inode are found that correspond to different files.
+ * Thus if both NAMEFND and INOFND are set then ip == np.
+ */
+ switch (key) {
+
+ /*
+ * A previously existing file has been found.
+ * Mark it as KEEP so that other links to the inode can be
+ * detected, and so that it will not be reclaimed by the search
+ * for unreferenced names.
+ */
+ case INOFND|NAMEFND:
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A file on the tape has a name which is the same as a name
+ * corresponding to a different file in the previous dump.
+ * Since all files to be deleted have already been removed,
+ * this file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can simply be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number (see INOFND case
+ * below). The entry is then treated as a new file.
+ */
+ case ONTAPE|NAMEFND:
+ case ONTAPE|NAMEFND|MODECHG:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ mktempname(np);
+ }
+ /* fall through */
+
+ /*
+ * A previously non-existent file.
+ * Add it to the file system, and request its extraction.
+ * If it is a directory, create it immediately.
+ * (Since the name is unused there can be no conflict)
+ */
+ case ONTAPE:
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A file with the same inode number, but a different
+ * name has been found. If the other name has not already
+ * been found (indicated by the KEEP flag, see above) then
+ * this must be a new name for the file, and it is renamed.
+ * If the other name has been found then this must be a
+ * link to the file. Hard links to directories are not
+ * permitted, and are either deleted or converted to
+ * symbolic links. Finally, if the file is on the tape,
+ * a request is made to extract it.
+ */
+ case ONTAPE|INOFND:
+ if (type == LEAF && (ip->e_flags & KEEP) == 0)
+ ip->e_flags |= EXTRACT;
+ /* fall through */
+ case INOFND:
+ if ((ip->e_flags & KEEP) == 0) {
+ renameit(myname(ip), name);
+ moveentry(ip, name);
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+ }
+ if (ip->e_type == NODE) {
+ descend = FAIL;
+ fprintf(stderr,
+ "deleted hard link %s to directory %s\n",
+ name, myname(ip));
+ break;
+ }
+ ep = addentry(name, ino, type|LINK);
+ ep->e_flags |= NEW;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A previously known file which is to be updated. If it is a link,
+ * then all names referring to the previous file must be removed
+ * so that the subset of them that remain can be recreated.
+ */
+ case ONTAPE|INOFND|NAMEFND:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ ep = addentry(name, ino, type|LINK);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+ }
+ if (type == LEAF && lookuptype != LINK)
+ np->e_flags |= EXTRACT;
+ np->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(np));
+ break;
+
+ /*
+ * An inode is being reused in a completely different way.
+ * Normally an extract can simply do an "unlink" followed
+ * by a "creat". Here we must do effectively the same
+ * thing. The complications arise because we cannot really
+ * delete a directory since it may still contain files
+ * that we need to rename, so we delete it from the symbol
+ * table, and put it on the list to be deleted eventually.
+ * Conversely if a directory is to be created, it must be
+ * done immediately, rather than waiting until the
+ * extraction phase.
+ */
+ case ONTAPE|INOFND|MODECHG:
+ case ONTAPE|INOFND|NAMEFND|MODECHG:
+ if (ip->e_flags & KEEP) {
+ badentry(ip, "cannot KEEP and change modes");
+ break;
+ }
+ if (ip->e_type == LEAF) {
+ /* changing from leaf to node */
+ removeleaf(ip);
+ freeentry(ip);
+ ip = addentry(name, ino, type);
+ newnode(ip);
+ } else {
+ /* changing from node to leaf */
+ if ((ip->e_flags & TMPNAME) == 0)
+ mktempname(ip);
+ deleteino(ip->e_ino);
+ ip->e_next = removelist;
+ removelist = ip;
+ ip = addentry(name, ino, type);
+ }
+ ip->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A hard link to a diirectory that has been removed.
+ * Ignore it.
+ */
+ case NAMEFND:
+ dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
+ name);
+ descend = FAIL;
+ break;
+
+ /*
+ * If we find a directory entry for a file that is not on
+ * the tape, then we must have found a file that was created
+ * while the dump was in progress. Since we have no contents
+ * for it, we discard the name knowing that it will be on the
+ * next incremental tape.
+ */
+ case NULL:
+ fprintf(stderr, "%s: (inode %d) not found on tape\n",
+ name, ino);
+ break;
+
+ /*
+ * If any of these arise, something is grievously wrong with
+ * the current state of the symbol table.
+ */
+ case INOFND|NAMEFND|MODECHG:
+ case NAMEFND|MODECHG:
+ case INOFND|MODECHG:
+ fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key),
+ name);
+ break;
+
+ /*
+ * These states "cannot" arise for any state of the symbol table.
+ */
+ case ONTAPE|MODECHG:
+ case MODECHG:
+ default:
+ panic("[%s] %s: impossible state\n", keyval(key), name);
+ break;
+ }
+ return (descend);
+}
+
+/*
+ * Calculate the active flags in a key.
+ */
+static char *
+keyval(key)
+ int key;
+{
+ static char keybuf[32];
+
+ (void) strcpy(keybuf, "|NIL");
+ keybuf[0] = '\0';
+ if (key & ONTAPE)
+ (void) strcat(keybuf, "|ONTAPE");
+ if (key & INOFND)
+ (void) strcat(keybuf, "|INOFND");
+ if (key & NAMEFND)
+ (void) strcat(keybuf, "|NAMEFND");
+ if (key & MODECHG)
+ (void) strcat(keybuf, "|MODECHG");
+ return (&keybuf[1]);
+}
+
+/*
+ * Find unreferenced link names.
+ */
+void
+findunreflinks()
+{
+ register struct entry *ep, *np;
+ register ino_t i;
+
+ vprintf(stdout, "Find unreferenced names.\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0)
+ continue;
+ for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_flags == 0) {
+ dprintf(stdout,
+ "%s: remove unreferenced name\n",
+ myname(np));
+ removeleaf(np);
+ freeentry(np);
+ }
+ }
+ }
+ /*
+ * Any leaves remaining in removed directories is unreferenced.
+ */
+ for (ep = removelist; ep != NULL; ep = ep->e_next) {
+ for (np = ep->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_type == LEAF) {
+ if (np->e_flags != 0)
+ badentry(np, "unreferenced with flags");
+ dprintf(stdout,
+ "%s: remove unreferenced name\n",
+ myname(np));
+ removeleaf(np);
+ freeentry(np);
+ }
+ }
+ }
+}
+
+/*
+ * Remove old nodes (directories).
+ * Note that this routine runs in O(N*D) where:
+ * N is the number of directory entries to be removed.
+ * D is the maximum depth of the tree.
+ * If N == D this can be quite slow. If the list were
+ * topologically sorted, the deletion could be done in
+ * time O(N).
+ */
+void
+removeoldnodes()
+{
+ register struct entry *ep, **prev;
+ long change;
+
+ vprintf(stdout, "Remove old nodes (directories).\n");
+ do {
+ change = 0;
+ prev = &removelist;
+ for (ep = removelist; ep != NULL; ep = *prev) {
+ if (ep->e_entries != NULL) {
+ prev = &ep->e_next;
+ continue;
+ }
+ *prev = ep->e_next;
+ removenode(ep);
+ freeentry(ep);
+ change++;
+ }
+ } while (change);
+ for (ep = removelist; ep != NULL; ep = ep->e_next)
+ badentry(ep, "cannot remove, non-empty");
+}
+
+/*
+ * This is the routine used to extract files for the 'r' command.
+ * Extract new leaves.
+ */
+void
+createleaves(symtabfile)
+ char *symtabfile;
+{
+ register struct entry *ep;
+ ino_t first;
+ long curvol;
+
+ if (command == 'R') {
+ vprintf(stdout, "Continue extraction of new leaves\n");
+ } else {
+ vprintf(stdout, "Extract new leaves.\n");
+ dumpsymtable(symtabfile, volno);
+ }
+ first = lowerbnd(ROOTINO);
+ curvol = volno;
+ while (curfile.ino < maxino) {
+ first = lowerbnd(first);
+ /*
+ * If the next available file is not the one which we
+ * expect then we have missed one or more files. Since
+ * we do not request files that were not on the tape,
+ * the lost files must have been due to a tape read error,
+ * or a file that was removed while the dump was in progress.
+ */
+ while (first < curfile.ino) {
+ ep = lookupino(first);
+ if (ep == NULL)
+ panic("%d: bad first\n", first);
+ fprintf(stderr, "%s: not found on tape\n", myname(ep));
+ ep->e_flags &= ~(NEW|EXTRACT);
+ first = lowerbnd(first);
+ }
+ /*
+ * If we find files on the tape that have no corresponding
+ * directory entries, then we must have found a file that
+ * was created while the dump was in progress. Since we have
+ * no name for it, we discard it knowing that it will be
+ * on the next incremental tape.
+ */
+ if (first != curfile.ino) {
+ fprintf(stderr, "expected next file %d, got %d\n",
+ first, curfile.ino);
+ skipfile();
+ goto next;
+ }
+ ep = lookupino(curfile.ino);
+ if (ep == NULL)
+ panic("unknown file on tape\n");
+ if ((ep->e_flags & (NEW|EXTRACT)) == 0)
+ badentry(ep, "unexpected file on tape");
+ /*
+ * If the file is to be extracted, then the old file must
+ * be removed since its type may change from one leaf type
+ * to another (eg "file" to "character special").
+ */
+ if ((ep->e_flags & EXTRACT) != 0) {
+ removeleaf(ep);
+ ep->e_flags &= ~REMOVED;
+ }
+ (void) extractfile(myname(ep));
+ ep->e_flags &= ~(NEW|EXTRACT);
+ /*
+ * We checkpoint the restore after every tape reel, so
+ * as to simplify the amount of work re quired by the
+ * 'R' command.
+ */
+ next:
+ if (curvol != volno) {
+ dumpsymtable(symtabfile, volno);
+ skipmaps();
+ curvol = volno;
+ }
+ }
+}
+
+/*
+ * This is the routine used to extract files for the 'x' and 'i' commands.
+ * Efficiently extract a subset of the files on a tape.
+ */
+void
+createfiles()
+{
+ register ino_t first, next, last;
+ register struct entry *ep;
+ long curvol;
+
+ vprintf(stdout, "Extract requested files\n");
+ curfile.action = SKIP;
+ getvol((long)1);
+ skipmaps();
+ skipdirs();
+ first = lowerbnd(ROOTINO);
+ last = upperbnd(maxino - 1);
+ for (;;) {
+ first = lowerbnd(first);
+ last = upperbnd(last);
+ /*
+ * Check to see if any files remain to be extracted
+ */
+ if (first > last)
+ return;
+ /*
+ * Reject any volumes with inodes greater
+ * than the last one needed
+ */
+ while (curfile.ino > last) {
+ curfile.action = SKIP;
+ getvol((long)0);
+ skipmaps();
+ skipdirs();
+ }
+ /*
+ * Decide on the next inode needed.
+ * Skip across the inodes until it is found
+ * or an out of order volume change is encountered
+ */
+ next = lowerbnd(curfile.ino);
+ do {
+ curvol = volno;
+ while (next > curfile.ino && volno == curvol)
+ skipfile();
+ skipmaps();
+ skipdirs();
+ } while (volno == curvol + 1);
+ /*
+ * If volume change out of order occurred the
+ * current state must be recalculated
+ */
+ if (volno != curvol)
+ continue;
+ /*
+ * If the current inode is greater than the one we were
+ * looking for then we missed the one we were looking for.
+ * Since we only attempt to extract files listed in the
+ * dump map, the lost files must have been due to a tape
+ * read error, or a file that was removed while the dump
+ * was in progress. Thus we report all requested files
+ * between the one we were looking for, and the one we
+ * found as missing, and delete their request flags.
+ */
+ while (next < curfile.ino) {
+ ep = lookupino(next);
+ if (ep == NULL)
+ panic("corrupted symbol table\n");
+ fprintf(stderr, "%s: not found on tape\n", myname(ep));
+ ep->e_flags &= ~NEW;
+ next = lowerbnd(next);
+ }
+ /*
+ * The current inode is the one that we are looking for,
+ * so extract it per its requested name.
+ */
+ if (next == curfile.ino && next <= last) {
+ ep = lookupino(next);
+ if (ep == NULL)
+ panic("corrupted symbol table\n");
+ (void) extractfile(myname(ep));
+ ep->e_flags &= ~NEW;
+ if (volno != curvol)
+ skipmaps();
+ }
+ }
+}
+
+/*
+ * Add links.
+ */
+void
+createlinks()
+{
+ register struct entry *np, *ep;
+ register ino_t i;
+ char name[BUFSIZ];
+
+ vprintf(stdout, "Add links\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ for (np = ep->e_links; np != NULL; np = np->e_links) {
+ if ((np->e_flags & NEW) == 0)
+ continue;
+ (void) strcpy(name, myname(ep));
+ if (ep->e_type == NODE) {
+ (void) linkit(name, myname(np), SYMLINK);
+ } else {
+ (void) linkit(name, myname(np), HARDLINK);
+ }
+ np->e_flags &= ~NEW;
+ }
+ }
+}
+
+/*
+ * Check the symbol table.
+ * We do this to insure that all the requested work was done, and
+ * that no temporary names remain.
+ */
+void
+checkrestore()
+{
+ register struct entry *ep;
+ register ino_t i;
+
+ vprintf(stdout, "Check the symbol table.\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_flags &= ~KEEP;
+ if (ep->e_type == NODE)
+ ep->e_flags &= ~(NEW|EXISTED);
+ if (ep->e_flags != NULL)
+ badentry(ep, "incomplete operations");
+ }
+ }
+}
+
+/*
+ * Compare with the directory structure on the tape
+ * A paranoid check that things are as they should be.
+ */
+long
+verifyfile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ struct entry *np, *ep;
+ long descend = GOOD;
+
+ ep = lookupname(name);
+ if (ep == NULL) {
+ fprintf(stderr, "Warning: missing name %s\n", name);
+ return (FAIL);
+ }
+ np = lookupino(ino);
+ if (np != ep)
+ descend = FAIL;
+ for ( ; np != NULL; np = np->e_links)
+ if (np == ep)
+ break;
+ if (np == NULL)
+ panic("missing inumber %d\n", ino);
+ if (ep->e_type == LEAF && type != LEAF)
+ badentry(ep, "type should be LEAF");
+ return (descend);
+}
diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h
new file mode 100644
index 000000000000..2202ccdc31fa
--- /dev/null
+++ b/sbin/restore/restore.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)restore.h 8.2 (Berkeley) 1/21/94
+ */
+
+/*
+ * Flags
+ */
+extern int cvtflag; /* convert from old to new tape format */
+extern int bflag; /* set input block size */
+extern int dflag; /* print out debugging info */
+extern int hflag; /* restore heirarchies */
+extern int mflag; /* restore by name instead of inode number */
+extern int Nflag; /* do not write the disk */
+extern int vflag; /* print out actions taken */
+extern int yflag; /* always try to recover from tape errors */
+/*
+ * Global variables
+ */
+extern char *dumpmap; /* map of inodes on this dump tape */
+extern char *clrimap; /* map of inodes to be deleted */
+extern ino_t maxino; /* highest numbered inode in this file system */
+extern long dumpnum; /* location of the dump on this tape */
+extern long volno; /* current volume being read */
+extern long ntrec; /* number of TP_BSIZE records per tape block */
+extern time_t dumptime; /* time that this dump begins */
+extern time_t dumpdate; /* time that this dump was made */
+extern char command; /* opration being performed */
+extern FILE *terminal; /* file descriptor for the terminal input */
+extern int oldinofmt; /* reading tape with old format inodes */
+extern int Bcvt; /* need byte swapping on inodes and dirs */
+
+/*
+ * Each file in the file system is described by one of these entries
+ */
+struct entry {
+ char *e_name; /* the current name of this entry */
+ u_char e_namlen; /* length of this name */
+ char e_type; /* type of this entry, see below */
+ short e_flags; /* status flags, see below */
+ ino_t e_ino; /* inode number in previous file sys */
+ long e_index; /* unique index (for dumpped table) */
+ struct entry *e_parent; /* pointer to parent directory (..) */
+ struct entry *e_sibling; /* next element in this directory (.) */
+ struct entry *e_links; /* hard links to this inode */
+ struct entry *e_entries; /* for directories, their entries */
+ struct entry *e_next; /* hash chain list */
+};
+/* types */
+#define LEAF 1 /* non-directory entry */
+#define NODE 2 /* directory entry */
+#define LINK 4 /* synthesized type, stripped by addentry */
+/* flags */
+#define EXTRACT 0x0001 /* entry is to be replaced from the tape */
+#define NEW 0x0002 /* a new entry to be extracted */
+#define KEEP 0x0004 /* entry is not to change */
+#define REMOVED 0x0010 /* entry has been removed */
+#define TMPNAME 0x0020 /* entry has been given a temporary name */
+#define EXISTED 0x0040 /* directory already existed during extract */
+
+/*
+ * Constants associated with entry structs
+ */
+#define HARDLINK 1
+#define SYMLINK 2
+#define TMPHDR "RSTTMP"
+
+/*
+ * The entry describes the next file available on the tape
+ */
+struct context {
+ char *name; /* name of file */
+ ino_t ino; /* inumber of file */
+ struct dinode *dip; /* pointer to inode */
+ char action; /* action being taken on this file */
+} curfile;
+/* actions */
+#define USING 1 /* extracting from the tape */
+#define SKIP 2 /* skipping */
+#define UNKNOWN 3 /* disposition or starting point is unknown */
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+typedef struct rstdirdesc RST_DIR;
+
+/*
+ * Flags to setdirmodes.
+ */
+#define FORCE 0x0001
+
+/*
+ * Useful macros
+ */
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY)))
+
+#define dprintf if (dflag) fprintf
+#define vprintf if (vflag) fprintf
+
+#define GOOD 1
+#define FAIL 0
diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c
new file mode 100644
index 000000000000..3895ec019483
--- /dev/null
+++ b/sbin/restore/symtab.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)symtab.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * These routines maintain the symbol table which tracks the state
+ * of the file system being restored. They provide lookup by either
+ * name or inode number. They also provide for creation, deletion,
+ * and renaming of entries. Because of the dynamic nature of pathnames,
+ * names should not be saved, but always constructed just before they
+ * are needed, by calling "myname".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * The following variables define the inode symbol table.
+ * The primary hash table is dynamically allocated based on
+ * the number of inodes in the file system (maxino), scaled by
+ * HASHFACTOR. The variable "entry" points to the hash table;
+ * the variable "entrytblsize" indicates its size (in entries).
+ */
+#define HASHFACTOR 5
+static struct entry **entry;
+static long entrytblsize;
+
+static void addino __P((ino_t, struct entry *));
+static struct entry *lookupparent __P((char *));
+static void removeentry __P((struct entry *));
+
+/*
+ * Look up an entry by inode number
+ */
+struct entry *
+lookupino(inum)
+ ino_t inum;
+{
+ register struct entry *ep;
+
+ if (inum < ROOTINO || inum >= maxino)
+ return (NULL);
+ for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
+ if (ep->e_ino == inum)
+ return (ep);
+ return (NULL);
+}
+
+/*
+ * Add an entry into the entry table
+ */
+static void
+addino(inum, np)
+ ino_t inum;
+ struct entry *np;
+{
+ struct entry **epp;
+
+ if (inum < ROOTINO || inum >= maxino)
+ panic("addino: out of range %d\n", inum);
+ epp = &entry[inum % entrytblsize];
+ np->e_ino = inum;
+ np->e_next = *epp;
+ *epp = np;
+ if (dflag)
+ for (np = np->e_next; np != NULL; np = np->e_next)
+ if (np->e_ino == inum)
+ badentry(np, "duplicate inum");
+}
+
+/*
+ * Delete an entry from the entry table
+ */
+void
+deleteino(inum)
+ ino_t inum;
+{
+ register struct entry *next;
+ struct entry **prev;
+
+ if (inum < ROOTINO || inum >= maxino)
+ panic("deleteino: out of range %d\n", inum);
+ prev = &entry[inum % entrytblsize];
+ for (next = *prev; next != NULL; next = next->e_next) {
+ if (next->e_ino == inum) {
+ next->e_ino = 0;
+ *prev = next->e_next;
+ return;
+ }
+ prev = &next->e_next;
+ }
+ panic("deleteino: %d not found\n", inum);
+}
+
+/*
+ * Look up an entry by name
+ */
+struct entry *
+lookupname(name)
+ char *name;
+{
+ register struct entry *ep;
+ register char *np, *cp;
+ char buf[MAXPATHLEN];
+
+ cp = name;
+ for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
+ for (np = buf; *cp != '/' && *cp != '\0'; )
+ *np++ = *cp++;
+ *np = '\0';
+ for ( ; ep != NULL; ep = ep->e_sibling)
+ if (strcmp(ep->e_name, buf) == 0)
+ break;
+ if (ep == NULL)
+ break;
+ if (*cp++ == '\0')
+ return (ep);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up the parent of a pathname
+ */
+static struct entry *
+lookupparent(name)
+ char *name;
+{
+ struct entry *ep;
+ char *tailindex;
+
+ tailindex = rindex(name, '/');
+ if (tailindex == NULL)
+ return (NULL);
+ *tailindex = '\0';
+ ep = lookupname(name);
+ *tailindex = '/';
+ if (ep == NULL)
+ return (NULL);
+ if (ep->e_type != NODE)
+ panic("%s is not a directory\n", name);
+ return (ep);
+}
+
+/*
+ * Determine the current pathname of a node or leaf
+ */
+char *
+myname(ep)
+ register struct entry *ep;
+{
+ register char *cp;
+ static char namebuf[MAXPATHLEN];
+
+ for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
+ cp -= ep->e_namlen;
+ bcopy(ep->e_name, cp, (long)ep->e_namlen);
+ if (ep == lookupino(ROOTINO))
+ return (cp);
+ *(--cp) = '/';
+ ep = ep->e_parent;
+ }
+ panic("%s: pathname too long\n", cp);
+ return(cp);
+}
+
+/*
+ * Unused symbol table entries are linked together on a freelist
+ * headed by the following pointer.
+ */
+static struct entry *freelist = NULL;
+
+/*
+ * add an entry to the symbol table
+ */
+struct entry *
+addentry(name, inum, type)
+ char *name;
+ ino_t inum;
+ int type;
+{
+ register struct entry *np, *ep;
+
+ if (freelist != NULL) {
+ np = freelist;
+ freelist = np->e_next;
+ bzero((char *)np, (long)sizeof(struct entry));
+ } else {
+ np = (struct entry *)calloc(1, sizeof(struct entry));
+ if (np == NULL)
+ panic("no memory to extend symbol table\n");
+ }
+ np->e_type = type & ~LINK;
+ ep = lookupparent(name);
+ if (ep == NULL) {
+ if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
+ panic("bad name to addentry %s\n", name);
+ np->e_name = savename(name);
+ np->e_namlen = strlen(name);
+ np->e_parent = np;
+ addino(ROOTINO, np);
+ return (np);
+ }
+ np->e_name = savename(rindex(name, '/') + 1);
+ np->e_namlen = strlen(np->e_name);
+ np->e_parent = ep;
+ np->e_sibling = ep->e_entries;
+ ep->e_entries = np;
+ if (type & LINK) {
+ ep = lookupino(inum);
+ if (ep == NULL)
+ panic("link to non-existant name\n");
+ np->e_ino = inum;
+ np->e_links = ep->e_links;
+ ep->e_links = np;
+ } else if (inum != 0) {
+ if (lookupino(inum) != NULL)
+ panic("duplicate entry\n");
+ addino(inum, np);
+ }
+ return (np);
+}
+
+/*
+ * delete an entry from the symbol table
+ */
+void
+freeentry(ep)
+ register struct entry *ep;
+{
+ register struct entry *np;
+ ino_t inum;
+
+ if (ep->e_flags != REMOVED)
+ badentry(ep, "not marked REMOVED");
+ if (ep->e_type == NODE) {
+ if (ep->e_links != NULL)
+ badentry(ep, "freeing referenced directory");
+ if (ep->e_entries != NULL)
+ badentry(ep, "freeing non-empty directory");
+ }
+ if (ep->e_ino != 0) {
+ np = lookupino(ep->e_ino);
+ if (np == NULL)
+ badentry(ep, "lookupino failed");
+ if (np == ep) {
+ inum = ep->e_ino;
+ deleteino(inum);
+ if (ep->e_links != NULL)
+ addino(inum, ep->e_links);
+ } else {
+ for (; np != NULL; np = np->e_links) {
+ if (np->e_links == ep) {
+ np->e_links = ep->e_links;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "link not found");
+ }
+ }
+ removeentry(ep);
+ freename(ep->e_name);
+ ep->e_next = freelist;
+ freelist = ep;
+}
+
+/*
+ * Relocate an entry in the tree structure
+ */
+void
+moveentry(ep, newname)
+ register struct entry *ep;
+ char *newname;
+{
+ struct entry *np;
+ char *cp;
+
+ np = lookupparent(newname);
+ if (np == NULL)
+ badentry(ep, "cannot move ROOT");
+ if (np != ep->e_parent) {
+ removeentry(ep);
+ ep->e_parent = np;
+ ep->e_sibling = np->e_entries;
+ np->e_entries = ep;
+ }
+ cp = rindex(newname, '/') + 1;
+ freename(ep->e_name);
+ ep->e_name = savename(cp);
+ ep->e_namlen = strlen(cp);
+ if (strcmp(gentempname(ep), ep->e_name) == 0)
+ ep->e_flags |= TMPNAME;
+ else
+ ep->e_flags &= ~TMPNAME;
+}
+
+/*
+ * Remove an entry in the tree structure
+ */
+static void
+removeentry(ep)
+ register struct entry *ep;
+{
+ register struct entry *np;
+
+ np = ep->e_parent;
+ if (np->e_entries == ep) {
+ np->e_entries = ep->e_sibling;
+ } else {
+ for (np = np->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_sibling == ep) {
+ np->e_sibling = ep->e_sibling;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "cannot find entry in parent list");
+ }
+}
+
+/*
+ * Table of unused string entries, sorted by length.
+ *
+ * Entries are allocated in STRTBLINCR sized pieces so that names
+ * of similar lengths can use the same entry. The value of STRTBLINCR
+ * is chosen so that every entry has at least enough space to hold
+ * a "struct strtbl" header. Thus every entry can be linked onto an
+ * apprpriate free list.
+ *
+ * NB. The macro "allocsize" below assumes that "struct strhdr"
+ * has a size that is a power of two.
+ */
+struct strhdr {
+ struct strhdr *next;
+};
+
+#define STRTBLINCR (sizeof(struct strhdr))
+#define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
+
+static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
+
+/*
+ * Allocate space for a name. It first looks to see if it already
+ * has an appropriate sized entry, and if not allocates a new one.
+ */
+char *
+savename(name)
+ char *name;
+{
+ struct strhdr *np;
+ long len;
+ char *cp;
+
+ if (name == NULL)
+ panic("bad name\n");
+ len = strlen(name);
+ np = strtblhdr[len / STRTBLINCR].next;
+ if (np != NULL) {
+ strtblhdr[len / STRTBLINCR].next = np->next;
+ cp = (char *)np;
+ } else {
+ cp = malloc((unsigned)allocsize(len));
+ if (cp == NULL)
+ panic("no space for string table\n");
+ }
+ (void) strcpy(cp, name);
+ return (cp);
+}
+
+/*
+ * Free space for a name. The resulting entry is linked onto the
+ * appropriate free list.
+ */
+void
+freename(name)
+ char *name;
+{
+ struct strhdr *tp, *np;
+
+ tp = &strtblhdr[strlen(name) / STRTBLINCR];
+ np = (struct strhdr *)name;
+ np->next = tp->next;
+ tp->next = np;
+}
+
+/*
+ * Useful quantities placed at the end of a dumped symbol table.
+ */
+struct symtableheader {
+ long volno;
+ long stringsize;
+ long entrytblsize;
+ time_t dumptime;
+ time_t dumpdate;
+ ino_t maxino;
+ long ntrec;
+};
+
+/*
+ * dump a snapshot of the symbol table
+ */
+void
+dumpsymtable(filename, checkpt)
+ char *filename;
+ long checkpt;
+{
+ register struct entry *ep, *tep;
+ register ino_t i;
+ struct entry temp, *tentry;
+ long mynum = 1, stroff = 0;
+ FILE *fd;
+ struct symtableheader hdr;
+
+ vprintf(stdout, "Check pointing the restore\n");
+ if (Nflag)
+ return;
+ if ((fd = fopen(filename, "w")) == NULL) {
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ panic("cannot create save file %s for symbol table\n",
+ filename);
+ }
+ clearerr(fd);
+ /*
+ * Assign indicies to each entry
+ * Write out the string entries
+ */
+ for (i = ROOTINO; i < maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_index = mynum++;
+ (void) fwrite(ep->e_name, sizeof(char),
+ (int)allocsize(ep->e_namlen), fd);
+ }
+ }
+ /*
+ * Convert pointers to indexes, and output
+ */
+ tep = &temp;
+ stroff = 0;
+ for (i = ROOTINO; i < maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ bcopy((char *)ep, (char *)tep,
+ (long)sizeof(struct entry));
+ tep->e_name = (char *)stroff;
+ stroff += allocsize(ep->e_namlen);
+ tep->e_parent = (struct entry *)ep->e_parent->e_index;
+ if (ep->e_links != NULL)
+ tep->e_links =
+ (struct entry *)ep->e_links->e_index;
+ if (ep->e_sibling != NULL)
+ tep->e_sibling =
+ (struct entry *)ep->e_sibling->e_index;
+ if (ep->e_entries != NULL)
+ tep->e_entries =
+ (struct entry *)ep->e_entries->e_index;
+ if (ep->e_next != NULL)
+ tep->e_next =
+ (struct entry *)ep->e_next->e_index;
+ (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
+ }
+ }
+ /*
+ * Convert entry pointers to indexes, and output
+ */
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ tentry = NULL;
+ else
+ tentry = (struct entry *)entry[i]->e_index;
+ (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
+ }
+ hdr.volno = checkpt;
+ hdr.maxino = maxino;
+ hdr.entrytblsize = entrytblsize;
+ hdr.stringsize = stroff;
+ hdr.dumptime = dumptime;
+ hdr.dumpdate = dumpdate;
+ hdr.ntrec = ntrec;
+ (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
+ if (ferror(fd)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ panic("output error to file %s writing symbol table\n",
+ filename);
+ }
+ (void) fclose(fd);
+}
+
+/*
+ * Initialize a symbol table from a file
+ */
+void
+initsymtable(filename)
+ char *filename;
+{
+ char *base;
+ long tblsize;
+ register struct entry *ep;
+ struct entry *baseep, *lep;
+ struct symtableheader hdr;
+ struct stat stbuf;
+ register long i;
+ int fd;
+
+ vprintf(stdout, "Initialize symbol table.\n");
+ if (filename == NULL) {
+ entrytblsize = maxino / HASHFACTOR;
+ entry = (struct entry **)
+ calloc((unsigned)entrytblsize, sizeof(struct entry *));
+ if (entry == (struct entry **)NULL)
+ panic("no memory for entry table\n");
+ ep = addentry(".", ROOTINO, NODE);
+ ep->e_flags |= NEW;
+ return;
+ }
+ if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ panic("cannot open symbol table file %s\n", filename);
+ }
+ if (fstat(fd, &stbuf) < 0) {
+ fprintf(stderr, "stat: %s\n", strerror(errno));
+ panic("cannot stat symbol table file %s\n", filename);
+ }
+ tblsize = stbuf.st_size - sizeof(struct symtableheader);
+ base = calloc(sizeof(char), (unsigned)tblsize);
+ if (base == NULL)
+ panic("cannot allocate space for symbol table\n");
+ if (read(fd, base, (int)tblsize) < 0 ||
+ read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ panic("cannot read symbol table file %s\n", filename);
+ }
+ switch (command) {
+ case 'r':
+ /*
+ * For normal continuation, insure that we are using
+ * the next incremental tape
+ */
+ if (hdr.dumpdate != dumptime) {
+ if (hdr.dumpdate < dumptime)
+ fprintf(stderr, "Incremental tape too low\n");
+ else
+ fprintf(stderr, "Incremental tape too high\n");
+ done(1);
+ }
+ break;
+ case 'R':
+ /*
+ * For restart, insure that we are using the same tape
+ */
+ curfile.action = SKIP;
+ dumptime = hdr.dumptime;
+ dumpdate = hdr.dumpdate;
+ if (!bflag)
+ newtapebuf(hdr.ntrec);
+ getvol(hdr.volno);
+ break;
+ default:
+ panic("initsymtable called from command %c\n", command);
+ break;
+ }
+ maxino = hdr.maxino;
+ entrytblsize = hdr.entrytblsize;
+ entry = (struct entry **)
+ (base + tblsize - (entrytblsize * sizeof(struct entry *)));
+ baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
+ lep = (struct entry *)entry;
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ continue;
+ entry[i] = &baseep[(long)entry[i]];
+ }
+ for (ep = &baseep[1]; ep < lep; ep++) {
+ ep->e_name = base + (long)ep->e_name;
+ ep->e_parent = &baseep[(long)ep->e_parent];
+ if (ep->e_sibling != NULL)
+ ep->e_sibling = &baseep[(long)ep->e_sibling];
+ if (ep->e_links != NULL)
+ ep->e_links = &baseep[(long)ep->e_links];
+ if (ep->e_entries != NULL)
+ ep->e_entries = &baseep[(long)ep->e_entries];
+ if (ep->e_next != NULL)
+ ep->e_next = &baseep[(long)ep->e_next];
+ }
+}
diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c
new file mode 100644
index 000000000000..368c59ca4274
--- /dev/null
+++ b/sbin/restore/tape.c
@@ -0,0 +1,1348 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tape.c 8.3 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+#include "pathnames.h"
+
+static long fssize = MAXBSIZE;
+static int mt = -1;
+static int pipein = 0;
+static char magtape[BUFSIZ];
+static int blkcnt;
+static int numtrec;
+static char *tapebuf;
+static union u_spcl endoftapemark;
+static long blksread; /* blocks read since last header */
+static long tpblksread = 0; /* TP_BSIZE blocks read */
+static long tapesread;
+static jmp_buf restart;
+static int gettingfile = 0; /* restart has a valid frame */
+static char *host = NULL;
+
+static int ofile;
+static char *map;
+static char lnkbuf[MAXPATHLEN + 1];
+static int pathlen;
+
+int oldinofmt; /* old inode format conversion required */
+int Bcvt; /* Swap Bytes (for CCI or sun) */
+static int Qcvt; /* Swap quads (for sun) */
+
+#define FLUSHTAPEBUF() blkcnt = ntrec + 1
+
+static void accthdr __P((struct s_spcl *));
+static int checksum __P((int *));
+static void findinode __P((struct s_spcl *));
+static void findtapeblksize __P((void));
+static int gethead __P((struct s_spcl *));
+static void readtape __P((char *));
+static void setdumpnum __P((void));
+static u_long swabl __P((u_long));
+static u_char *swablong __P((u_char *, int));
+static u_char *swabshort __P((u_char *, int));
+static void terminateinput __P((void));
+static void xtrfile __P((char *, long));
+static void xtrlnkfile __P((char *, long));
+static void xtrlnkskip __P((char *, long));
+static void xtrmap __P((char *, long));
+static void xtrmapskip __P((char *, long));
+static void xtrskip __P((char *, long));
+
+/*
+ * Set up an input source
+ */
+void
+setinput(source)
+ char *source;
+{
+ FLUSHTAPEBUF();
+ if (bflag)
+ newtapebuf(ntrec);
+ else
+ newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+ terminal = stdin;
+
+#ifdef RRESTORE
+ if (index(source, ':')) {
+ host = source;
+ source = index(host, ':');
+ *source++ = '\0';
+ if (rmthost(host) == 0)
+ done(1);
+ } else
+#endif
+ if (strcmp(source, "-") == 0) {
+ /*
+ * Since input is coming from a pipe we must establish
+ * our own connection to the terminal.
+ */
+ terminal = fopen(_PATH_TTY, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_TTY, strerror(errno));
+ terminal = fopen(_PATH_DEVNULL, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ done(1);
+ }
+ }
+ pipein++;
+ }
+ setuid(getuid()); /* no longer need or want root privileges */
+ (void) strcpy(magtape, source);
+}
+
+void
+newtapebuf(size)
+ long size;
+{
+ static tapebufsize = -1;
+
+ ntrec = size;
+ if (size <= tapebufsize)
+ return;
+ if (tapebuf != NULL)
+ free(tapebuf);
+ tapebuf = malloc(size * TP_BSIZE);
+ if (tapebuf == NULL) {
+ fprintf(stderr, "Cannot allocate space for tape buffer\n");
+ done(1);
+ }
+ tapebufsize = size;
+}
+
+/*
+ * Verify that the tape drive can be accessed and
+ * that it actually is a dump tape.
+ */
+void
+setup()
+{
+ int i, j, *ip;
+ struct stat stbuf;
+
+ vprintf(stdout, "Verify tape and initialize maps\n");
+#ifdef RRESTORE
+ if (host)
+ mt = rmtopen(magtape, 0);
+ else
+#endif
+ if (pipein)
+ mt = 0;
+ else
+ mt = open(magtape, O_RDONLY, 0);
+ if (mt < 0) {
+ fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
+ done(1);
+ }
+ volno = 1;
+ setdumpnum();
+ FLUSHTAPEBUF();
+ if (!pipein && !bflag)
+ findtapeblksize();
+ if (gethead(&spcl) == FAIL) {
+ blkcnt--; /* push back this block */
+ blksread--;
+ tpblksread--;
+ cvtflag++;
+ if (gethead(&spcl) == FAIL) {
+ fprintf(stderr, "Tape is not a dump tape\n");
+ done(1);
+ }
+ fprintf(stderr, "Converting to new file system format.\n");
+ }
+ if (pipein) {
+ endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
+ endoftapemark.s_spcl.c_type = TS_END;
+ ip = (int *)&endoftapemark;
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ do
+ i += *ip++;
+ while (--j);
+ endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
+ }
+ if (vflag || command == 't')
+ printdumpinfo();
+ dumptime = spcl.c_ddate;
+ dumpdate = spcl.c_date;
+ if (stat(".", &stbuf) < 0) {
+ fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
+ done(1);
+ }
+ if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
+ fssize = stbuf.st_blksize;
+ if (((fssize - 1) & fssize) != 0) {
+ fprintf(stderr, "bad block size %d\n", fssize);
+ done(1);
+ }
+ if (spcl.c_volume != 1) {
+ fprintf(stderr, "Tape is not volume 1 of the dump\n");
+ done(1);
+ }
+ if (gethead(&spcl) == FAIL) {
+ dprintf(stdout, "header read failed at %d blocks\n", blksread);
+ panic("no header after volume mark!\n");
+ }
+ findinode(&spcl);
+ if (spcl.c_type != TS_CLRI) {
+ fprintf(stderr, "Cannot find file removal list\n");
+ done(1);
+ }
+ maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
+ dprintf(stdout, "maxino = %d\n", maxino);
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == NULL)
+ panic("no memory for file removal list\n");
+ clrimap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ if (spcl.c_type != TS_BITS) {
+ fprintf(stderr, "Cannot find file dump list\n");
+ done(1);
+ }
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == (char *)NULL)
+ panic("no memory for file dump list\n");
+ dumpmap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overrridden by
+ * the user when only extracting a subset of the files.
+ */
+void
+getvol(nextvol)
+ long nextvol;
+{
+ long newvol, savecnt, wantnext, i;
+ union u_spcl tmpspcl;
+# define tmpbuf tmpspcl.s_spcl
+ char buf[TP_BSIZE];
+
+ if (nextvol == 1) {
+ tapesread = 0;
+ gettingfile = 0;
+ }
+ if (pipein) {
+ if (nextvol != 1)
+ panic("Changing volumes on pipe input?\n");
+ if (volno == 1)
+ return;
+ goto gethdr;
+ }
+ savecnt = blksread;
+again:
+ if (pipein)
+ done(1); /* pipes do not get a second chance */
+ if (command == 'R' || command == 'r' || curfile.action != SKIP) {
+ newvol = nextvol;
+ wantnext = 1;
+ } else {
+ newvol = 0;
+ wantnext = 0;
+ }
+ while (newvol <= 0) {
+ if (tapesread == 0) {
+ fprintf(stderr, "%s%s%s%s%s",
+ "You have not read any tapes yet.\n",
+ "Unless you know which volume your",
+ " file(s) are on you should start\n",
+ "with the last volume and work",
+ " towards towards the first.\n");
+ } else {
+ fprintf(stderr, "You have read volumes");
+ strcpy(buf, ": ");
+ for (i = 1; i < 32; i++)
+ if (tapesread & (1 << i)) {
+ fprintf(stderr, "%s%d", buf, i);
+ strcpy(buf, ", ");
+ }
+ fprintf(stderr, "\n");
+ }
+ do {
+ fprintf(stderr, "Specify next volume #: ");
+ (void) fflush(stderr);
+ (void) fgets(buf, BUFSIZ, terminal);
+ } while (!feof(terminal) && buf[0] == '\n');
+ if (feof(terminal))
+ done(1);
+ newvol = atoi(buf);
+ if (newvol <= 0) {
+ fprintf(stderr,
+ "Volume numbers are positive numerics\n");
+ }
+ }
+ if (newvol == volno) {
+ tapesread |= 1 << volno;
+ return;
+ }
+ closemt();
+ fprintf(stderr, "Mount tape volume %d\n", newvol);
+ fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
+ fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
+ (void) fflush(stderr);
+ (void) fgets(buf, BUFSIZ, terminal);
+ if (feof(terminal))
+ done(1);
+ if (!strcmp(buf, "none\n")) {
+ terminateinput();
+ return;
+ }
+ if (buf[0] != '\n') {
+ (void) strcpy(magtape, buf);
+ magtape[strlen(magtape) - 1] = '\0';
+ }
+#ifdef RRESTORE
+ if (host)
+ mt = rmtopen(magtape, 0);
+ else
+#endif
+ mt = open(magtape, O_RDONLY, 0);
+
+ if (mt == -1) {
+ fprintf(stderr, "Cannot open %s\n", magtape);
+ volno = -1;
+ goto again;
+ }
+gethdr:
+ volno = newvol;
+ setdumpnum();
+ FLUSHTAPEBUF();
+ if (gethead(&tmpbuf) == FAIL) {
+ dprintf(stdout, "header read failed at %d blocks\n", blksread);
+ fprintf(stderr, "tape is not dump tape\n");
+ volno = 0;
+ goto again;
+ }
+ if (tmpbuf.c_volume != volno) {
+ fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
+ volno = 0;
+ goto again;
+ }
+ if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
+ fprintf(stderr, "Wrong dump date\n\tgot: %s",
+ ctime(&tmpbuf.c_date));
+ fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
+ volno = 0;
+ goto again;
+ }
+ tapesread |= 1 << volno;
+ blksread = savecnt;
+ /*
+ * If continuing from the previous volume, skip over any
+ * blocks read already at the end of the previous volume.
+ *
+ * If coming to this volume at random, skip to the beginning
+ * of the next record.
+ */
+ dprintf(stdout, "read %ld recs, tape starts with %ld\n",
+ tpblksread, tmpbuf.c_firstrec);
+ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
+ if (!wantnext) {
+ tpblksread = tmpbuf.c_firstrec;
+ for (i = tmpbuf.c_count; i > 0; i--)
+ readtape(buf);
+ } else if (tmpbuf.c_firstrec > 0 &&
+ tmpbuf.c_firstrec < tpblksread - 1) {
+ /*
+ * -1 since we've read the volume header
+ */
+ i = tpblksread - tmpbuf.c_firstrec - 1;
+ dprintf(stderr, "Skipping %d duplicate record%s.\n",
+ i, i > 1 ? "s" : "");
+ while (--i >= 0)
+ readtape(buf);
+ }
+ }
+ if (curfile.action == USING) {
+ if (volno == 1)
+ panic("active file into volume 1\n");
+ return;
+ }
+ /*
+ * Skip up to the beginning of the next record
+ */
+ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
+ for (i = tmpbuf.c_count; i > 0; i--)
+ readtape(buf);
+ (void) gethead(&spcl);
+ findinode(&spcl);
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+}
+
+/*
+ * Handle unexpected EOF.
+ */
+static void
+terminateinput()
+{
+
+ if (gettingfile && curfile.action == USING) {
+ printf("Warning: %s %s\n",
+ "End-of-input encountered while extracting", curfile.name);
+ }
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.dip = NULL;
+ curfile.ino = maxino;
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+}
+
+/*
+ * handle multiple dumps per tape by skipping forward to the
+ * appropriate one.
+ */
+static void
+setdumpnum()
+{
+ struct mtop tcom;
+
+ if (dumpnum == 1 || volno != 1)
+ return;
+ if (pipein) {
+ fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
+ done(1);
+ }
+ tcom.mt_op = MTFSF;
+ tcom.mt_count = dumpnum - 1;
+#ifdef RRESTORE
+ if (host)
+ rmtioctl(MTFSF, dumpnum - 1);
+ else
+#endif
+ if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
+ fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
+}
+
+void
+printdumpinfo()
+{
+ fprintf(stdout, "Dump date: %s", ctime(&spcl.c_date));
+ fprintf(stdout, "Dumped from: %s",
+ (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
+ if (spcl.c_host[0] == '\0')
+ return;
+ fprintf(stderr, "Level %d dump of %s on %s:%s\n",
+ spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
+ fprintf(stderr, "Label: %s\n", spcl.c_label);
+}
+
+int
+extractfile(name)
+ char *name;
+{
+ int mode;
+ struct timeval timep[2];
+ struct entry *ep;
+
+ curfile.name = name;
+ curfile.action = USING;
+ timep[0].tv_sec = curfile.dip->di_atime.ts_sec;
+ timep[0].tv_usec = curfile.dip->di_atime.ts_nsec / 1000;
+ timep[1].tv_sec = curfile.dip->di_mtime.ts_sec;
+ timep[1].tv_usec = curfile.dip->di_mtime.ts_nsec / 1000;
+ mode = curfile.dip->di_mode;
+ switch (mode & IFMT) {
+
+ default:
+ fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+ skipfile();
+ return (FAIL);
+
+ case IFSOCK:
+ vprintf(stdout, "skipped socket %s\n", name);
+ skipfile();
+ return (GOOD);
+
+ case IFDIR:
+ if (mflag) {
+ ep = lookupname(name);
+ if (ep == NULL || ep->e_flags & EXTRACT)
+ panic("unextracted directory %s\n", name);
+ skipfile();
+ return (GOOD);
+ }
+ vprintf(stdout, "extract file %s\n", name);
+ return (genliteraldir(name, curfile.ino));
+
+ case IFLNK:
+ lnkbuf[0] = '\0';
+ pathlen = 0;
+ getfile(xtrlnkfile, xtrlnkskip);
+ if (pathlen == 0) {
+ vprintf(stdout,
+ "%s: zero length symbolic link (ignored)\n", name);
+ return (GOOD);
+ }
+ return (linkit(lnkbuf, name, SYMLINK));
+
+ case IFCHR:
+ case IFBLK:
+ vprintf(stdout, "extract special file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
+ fprintf(stderr, "%s: cannot create special file: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) chmod(name, mode);
+ skipfile();
+ utimes(name, timep);
+ return (GOOD);
+
+ case IFREG:
+ vprintf(stdout, "extract file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if ((ofile = creat(name, 0666)) < 0) {
+ fprintf(stderr, "%s: cannot create file: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) fchmod(ofile, mode);
+ getfile(xtrfile, xtrskip);
+ (void) close(ofile);
+ utimes(name, timep);
+ return (GOOD);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+void
+skipmaps()
+{
+
+ while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
+ skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+void
+skipfile()
+{
+
+ curfile.action = SKIP;
+ getfile(xtrnull, xtrnull);
+}
+
+/*
+ * Extract a file from the tape.
+ * When an allocated block is found it is passed to the fill function;
+ * when an unallocated block (hole) is found, a zeroed buffer is passed
+ * to the skip function.
+ */
+void
+getfile(fill, skip)
+ void (*fill) __P((char *, long));
+ void (*skip) __P((char *, long));
+{
+ register int i;
+ int curblk = 0;
+ long size = spcl.c_dinode.di_size;
+ static char clearedbuf[MAXBSIZE];
+ char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+ char junk[TP_BSIZE];
+
+ if (spcl.c_type == TS_END)
+ panic("ran off end of tape\n");
+ if (spcl.c_magic != NFS_MAGIC)
+ panic("not at beginning of a file\n");
+ if (!gettingfile && setjmp(restart) != 0)
+ return;
+ gettingfile++;
+loop:
+ for (i = 0; i < spcl.c_count; i++) {
+ if (spcl.c_addr[i]) {
+ readtape(&buf[curblk++][0]);
+ if (curblk == fssize / TP_BSIZE) {
+ (*fill)((char *)buf, size > TP_BSIZE ?
+ (long) (fssize) :
+ (curblk - 1) * TP_BSIZE + size);
+ curblk = 0;
+ }
+ } else {
+ if (curblk > 0) {
+ (*fill)((char *)buf, size > TP_BSIZE ?
+ (long) (curblk * TP_BSIZE) :
+ (curblk - 1) * TP_BSIZE + size);
+ curblk = 0;
+ }
+ (*skip)(clearedbuf, size > TP_BSIZE ?
+ (long) TP_BSIZE : size);
+ }
+ if ((size -= TP_BSIZE) <= 0) {
+ for (i++; i < spcl.c_count; i++)
+ if (spcl.c_addr[i])
+ readtape(junk);
+ break;
+ }
+ }
+ if (gethead(&spcl) == GOOD && size > 0) {
+ if (spcl.c_type == TS_ADDR)
+ goto loop;
+ dprintf(stdout,
+ "Missing address (header) block for %s at %d blocks\n",
+ curfile.name, blksread);
+ }
+ if (curblk > 0)
+ (*fill)((char *)buf, (curblk * TP_BSIZE) + size);
+ findinode(&spcl);
+ gettingfile = 0;
+}
+
+/*
+ * Write out the next block of a file.
+ */
+static void
+xtrfile(buf, size)
+ char *buf;
+ long size;
+{
+
+ if (Nflag)
+ return;
+ if (write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\nwrite: %s\n",
+ curfile.ino, curfile.name, strerror(errno));
+ done(1);
+ }
+}
+
+/*
+ * Skip over a hole in a file.
+ */
+/* ARGSUSED */
+static void
+xtrskip(buf, size)
+ char *buf;
+ long size;
+{
+
+ if (lseek(ofile, size, SEEK_CUR) == -1) {
+ fprintf(stderr,
+ "seek error extracting inode %d, name %s\nlseek: %s\n",
+ curfile.ino, curfile.name, strerror(errno));
+ done(1);
+ }
+}
+
+/*
+ * Collect the next block of a symbolic link.
+ */
+static void
+xtrlnkfile(buf, size)
+ char *buf;
+ long size;
+{
+
+ pathlen += size;
+ if (pathlen > MAXPATHLEN) {
+ fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
+ curfile.name, lnkbuf, buf, pathlen);
+ done(1);
+ }
+ (void) strcat(lnkbuf, buf);
+}
+
+/*
+ * Skip over a hole in a symbolic link (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrlnkskip(buf, size)
+ char *buf;
+ long size;
+{
+
+ fprintf(stderr, "unallocated block in symbolic link %s\n",
+ curfile.name);
+ done(1);
+}
+
+/*
+ * Collect the next block of a bit map.
+ */
+static void
+xtrmap(buf, size)
+ char *buf;
+ long size;
+{
+
+ bcopy(buf, map, size);
+ map += size;
+}
+
+/*
+ * Skip over a hole in a bit map (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrmapskip(buf, size)
+ char *buf;
+ long size;
+{
+
+ panic("hole in map\n");
+ map += size;
+}
+
+/*
+ * Noop, when an extraction function is not needed.
+ */
+/* ARGSUSED */
+void
+xtrnull(buf, size)
+ char *buf;
+ long size;
+{
+
+ return;
+}
+
+/*
+ * Read TP_BSIZE blocks from the input.
+ * Handle read errors, and end of media.
+ */
+static void
+readtape(buf)
+ char *buf;
+{
+ long rd, newvol, i;
+ int cnt, seek_failed;
+
+ if (blkcnt < numtrec) {
+ bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
+ blksread++;
+ tpblksread++;
+ return;
+ }
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ if (numtrec == 0)
+ numtrec = ntrec;
+ cnt = ntrec * TP_BSIZE;
+ rd = 0;
+getmore:
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(&tapebuf[rd], cnt);
+ else
+#endif
+ i = read(mt, &tapebuf[rd], cnt);
+ /*
+ * Check for mid-tape short read error.
+ * If found, skip rest of buffer and start with the next.
+ */
+ if (!pipein && numtrec < ntrec && i > 0) {
+ dprintf(stdout, "mid-media short read error.\n");
+ numtrec = ntrec;
+ }
+ /*
+ * Handle partial block read.
+ */
+ if (pipein && i == 0 && rd > 0)
+ i = rd;
+ else if (i > 0 && i != ntrec * TP_BSIZE) {
+ if (pipein) {
+ rd += i;
+ cnt -= i;
+ if (cnt > 0)
+ goto getmore;
+ i = rd;
+ } else {
+ /*
+ * Short read. Process the blocks read.
+ */
+ if (i % TP_BSIZE != 0)
+ vprintf(stdout,
+ "partial block read: %d should be %d\n",
+ i, ntrec * TP_BSIZE);
+ numtrec = i / TP_BSIZE;
+ }
+ }
+ /*
+ * Handle read error.
+ */
+ if (i < 0) {
+ fprintf(stderr, "Tape read error while ");
+ switch (curfile.action) {
+ default:
+ fprintf(stderr, "trying to set up tape\n");
+ break;
+ case UNKNOWN:
+ fprintf(stderr, "trying to resynchronize\n");
+ break;
+ case USING:
+ fprintf(stderr, "restoring %s\n", curfile.name);
+ break;
+ case SKIP:
+ fprintf(stderr, "skipping over inode %d\n",
+ curfile.ino);
+ break;
+ }
+ if (!yflag && !reply("continue"))
+ done(1);
+ i = ntrec * TP_BSIZE;
+ bzero(tapebuf, i);
+#ifdef RRESTORE
+ if (host)
+ seek_failed = (rmtseek(i, 1) < 0);
+ else
+#endif
+ seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
+
+ if (seek_failed) {
+ fprintf(stderr,
+ "continuation failed: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ /*
+ * Handle end of tape.
+ */
+ if (i == 0) {
+ vprintf(stdout, "End-of-tape encountered\n");
+ if (!pipein) {
+ newvol = volno + 1;
+ volno = 0;
+ numtrec = 0;
+ getvol(newvol);
+ readtape(buf);
+ return;
+ }
+ if (rd % TP_BSIZE != 0)
+ panic("partial block read: %d should be %d\n",
+ rd, ntrec * TP_BSIZE);
+ terminateinput();
+ bcopy((char *)&endoftapemark, &tapebuf[rd], (long)TP_BSIZE);
+ }
+ blkcnt = 0;
+ bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
+ blksread++;
+ tpblksread++;
+}
+
+static void
+findtapeblksize()
+{
+ register long i;
+
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ blkcnt = 0;
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(tapebuf, ntrec * TP_BSIZE);
+ else
+#endif
+ i = read(mt, tapebuf, ntrec * TP_BSIZE);
+
+ if (i <= 0) {
+ fprintf(stderr, "tape read error: %s\n", strerror(errno));
+ done(1);
+ }
+ if (i % TP_BSIZE != 0) {
+ fprintf(stderr, "Tape block size (%d) %s (%d)\n",
+ i, "is not a multiple of dump block size", TP_BSIZE);
+ done(1);
+ }
+ ntrec = i / TP_BSIZE;
+ numtrec = ntrec;
+ vprintf(stdout, "Tape block size is %d\n", ntrec);
+}
+
+void
+closemt()
+{
+
+ if (mt < 0)
+ return;
+#ifdef RRESTORE
+ if (host)
+ rmtclose();
+ else
+#endif
+ (void) close(mt);
+}
+
+/*
+ * Read the next block from the tape.
+ * Check to see if it is one of several vintage headers.
+ * If it is an old style header, convert it to a new style header.
+ * If it is not any valid header, return an error.
+ */
+static int
+gethead(buf)
+ struct s_spcl *buf;
+{
+ long i;
+ union {
+ quad_t qval;
+ long val[2];
+ } qcvt;
+ union u_ospcl {
+ char dummy[TP_BSIZE];
+ struct s_ospcl {
+ long c_type;
+ long c_date;
+ long c_ddate;
+ long c_volume;
+ long c_tapea;
+ u_short c_inumber;
+ long c_magic;
+ long c_checksum;
+ struct odinode {
+ unsigned short odi_mode;
+ u_short odi_nlink;
+ u_short odi_uid;
+ u_short odi_gid;
+ long odi_size;
+ long odi_rdev;
+ char odi_addr[36];
+ long odi_atime;
+ long odi_mtime;
+ long odi_ctime;
+ } c_dinode;
+ long c_count;
+ char c_addr[256];
+ } s_ospcl;
+ } u_ospcl;
+
+ if (!cvtflag) {
+ readtape((char *)buf);
+ if (buf->c_magic != NFS_MAGIC) {
+ if (swabl(buf->c_magic) != NFS_MAGIC)
+ return (FAIL);
+ if (!Bcvt) {
+ vprintf(stdout, "Note: Doing Byte swapping\n");
+ Bcvt = 1;
+ }
+ }
+ if (checksum((int *)buf) == FAIL)
+ return (FAIL);
+ if (Bcvt)
+ swabst((u_char *)"8l4s31l", (u_char *)buf);
+ goto good;
+ }
+ readtape((char *)(&u_ospcl.s_ospcl));
+ bzero((char *)buf, (long)TP_BSIZE);
+ buf->c_type = u_ospcl.s_ospcl.c_type;
+ buf->c_date = u_ospcl.s_ospcl.c_date;
+ buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
+ buf->c_volume = u_ospcl.s_ospcl.c_volume;
+ buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
+ buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
+ buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
+ buf->c_magic = u_ospcl.s_ospcl.c_magic;
+ buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
+ buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
+ buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
+ buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
+ buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
+ buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
+ buf->c_dinode.di_atime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
+ buf->c_dinode.di_mtime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+ buf->c_dinode.di_ctime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+ buf->c_count = u_ospcl.s_ospcl.c_count;
+ bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
+ if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
+ checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
+ return(FAIL);
+ buf->c_magic = NFS_MAGIC;
+
+good:
+ if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
+ (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
+ qcvt.qval = buf->c_dinode.di_size;
+ if (qcvt.val[0] || qcvt.val[1]) {
+ printf("Note: Doing Quad swapping\n");
+ Qcvt = 1;
+ }
+ }
+ if (Qcvt) {
+ qcvt.qval = buf->c_dinode.di_size;
+ i = qcvt.val[1];
+ qcvt.val[1] = qcvt.val[0];
+ qcvt.val[0] = i;
+ buf->c_dinode.di_size = qcvt.qval;
+ }
+
+ switch (buf->c_type) {
+
+ case TS_CLRI:
+ case TS_BITS:
+ /*
+ * Have to patch up missing information in bit map headers
+ */
+ buf->c_inumber = 0;
+ buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
+ for (i = 0; i < buf->c_count; i++)
+ buf->c_addr[i]++;
+ break;
+
+ case TS_TAPE:
+ if ((buf->c_flags & DR_NEWINODEFMT) == 0)
+ oldinofmt = 1;
+ /* fall through */
+ case TS_END:
+ buf->c_inumber = 0;
+ break;
+
+ case TS_INODE:
+ case TS_ADDR:
+ break;
+
+ default:
+ panic("gethead: unknown inode type %d\n", buf->c_type);
+ break;
+ }
+ /*
+ * If we are restoring a filesystem with old format inodes,
+ * copy the uid/gid to the new location.
+ */
+ if (oldinofmt) {
+ buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
+ buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
+ }
+ if (dflag)
+ accthdr(buf);
+ return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+static void
+accthdr(header)
+ struct s_spcl *header;
+{
+ static ino_t previno = 0x7fffffff;
+ static int prevtype;
+ static long predict;
+ long blks, i;
+
+ if (header->c_type == TS_TAPE) {
+ fprintf(stderr, "Volume header (%s inode format) ",
+ oldinofmt ? "old" : "new");
+ if (header->c_firstrec)
+ fprintf(stderr, "begins with record %d",
+ header->c_firstrec);
+ fprintf(stderr, "\n");
+ previno = 0x7fffffff;
+ return;
+ }
+ if (previno == 0x7fffffff)
+ goto newcalc;
+ switch (prevtype) {
+ case TS_BITS:
+ fprintf(stderr, "Dump mask header");
+ break;
+ case TS_CLRI:
+ fprintf(stderr, "Remove mask header");
+ break;
+ case TS_INODE:
+ fprintf(stderr, "File header, ino %d", previno);
+ break;
+ case TS_ADDR:
+ fprintf(stderr, "File continuation header, ino %d", previno);
+ break;
+ case TS_END:
+ fprintf(stderr, "End of tape header");
+ break;
+ }
+ if (predict != blksread - 1)
+ fprintf(stderr, "; predicted %d blocks, got %d blocks",
+ predict, blksread - 1);
+ fprintf(stderr, "\n");
+newcalc:
+ blks = 0;
+ if (header->c_type != TS_END)
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i] != 0)
+ blks++;
+ predict = blks;
+ blksread = 0;
+ prevtype = header->c_type;
+ previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip, and complain is set.
+ */
+static void
+findinode(header)
+ struct s_spcl *header;
+{
+ static long skipcnt = 0;
+ long i;
+ char buf[TP_BSIZE];
+
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.dip = NULL;
+ curfile.ino = 0;
+ do {
+ if (header->c_magic != NFS_MAGIC) {
+ skipcnt++;
+ while (gethead(header) == FAIL ||
+ header->c_date != dumpdate)
+ skipcnt++;
+ }
+ switch (header->c_type) {
+
+ case TS_ADDR:
+ /*
+ * Skip up to the beginning of the next record
+ */
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i])
+ readtape(buf);
+ while (gethead(header) == FAIL ||
+ header->c_date != dumpdate)
+ skipcnt++;
+ break;
+
+ case TS_INODE:
+ curfile.dip = &header->c_dinode;
+ curfile.ino = header->c_inumber;
+ break;
+
+ case TS_END:
+ curfile.ino = maxino;
+ break;
+
+ case TS_CLRI:
+ curfile.name = "<file removal list>";
+ break;
+
+ case TS_BITS:
+ curfile.name = "<file dump list>";
+ break;
+
+ case TS_TAPE:
+ panic("unexpected tape header\n");
+ /* NOTREACHED */
+
+ default:
+ panic("unknown tape header type %d\n", spcl.c_type);
+ /* NOTREACHED */
+
+ }
+ } while (header->c_type == TS_ADDR);
+ if (skipcnt > 0)
+ fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
+ skipcnt = 0;
+}
+
+static int
+checksum(buf)
+ register int *buf;
+{
+ register int i, j;
+
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ if(!Bcvt) {
+ do
+ i += *buf++;
+ while (--j);
+ } else {
+ /* What happens if we want to read restore tapes
+ for a 16bit int machine??? */
+ do
+ i += swabl(*buf++);
+ while (--j);
+ }
+
+ if (i != CHECKSUM) {
+ fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+ curfile.ino, curfile.name);
+ return(FAIL);
+ }
+ return(GOOD);
+}
+
+#ifdef RRESTORE
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+msg(const char *fmt, ...)
+#else
+msg(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* RRESTORE */
+
+static u_char *
+swabshort(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+ sp += 2;
+ }
+ return (sp);
+}
+
+static u_char *
+swablong(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+ c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+ sp += 4;
+ }
+ return (sp);
+}
+
+void
+swabst(cp, sp)
+ register u_char *cp, *sp;
+{
+ int n = 0;
+
+ while (*cp) {
+ switch (*cp) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = (n * 10) + (*cp++ - '0');
+ continue;
+
+ case 's': case 'w': case 'h':
+ if (n == 0)
+ n = 1;
+ sp = swabshort(sp, n);
+ break;
+
+ case 'l':
+ if (n == 0)
+ n = 1;
+ sp = swablong(sp, n);
+ break;
+
+ default: /* Any other character, like 'b' counts as byte. */
+ if (n == 0)
+ n = 1;
+ sp += n;
+ break;
+ }
+ cp++;
+ n = 0;
+ }
+}
+
+static u_long
+swabl(x)
+ u_long x;
+{
+ swabst((u_char *)"l", (u_char *)&x);
+ return (x);
+}
diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c
new file mode 100644
index 000000000000..31fd11c50289
--- /dev/null
+++ b/sbin/restore/utilities.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utilities.c 8.2 (Berkeley) 3/25/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "restore.h"
+#include "extern.h"
+
+/*
+ * Insure that all the components of a pathname exist.
+ */
+void
+pathcheck(name)
+ char *name;
+{
+ register char *cp;
+ struct entry *ep;
+ char *start;
+
+ start = index(name, '/');
+ if (start == 0)
+ return;
+ for (cp = start; *cp != '\0'; cp++) {
+ if (*cp != '/')
+ continue;
+ *cp = '\0';
+ ep = lookupname(name);
+ if (ep == NULL) {
+ /* Safe; we know the pathname exists in the dump. */
+ ep = addentry(name, pathsearch(name)->d_ino, NODE);
+ newnode(ep);
+ }
+ ep->e_flags |= NEW|KEEP;
+ *cp = '/';
+ }
+}
+
+/*
+ * Change a name to a unique temporary name.
+ */
+void
+mktempname(ep)
+ register struct entry *ep;
+{
+ char oldname[MAXPATHLEN];
+
+ if (ep->e_flags & TMPNAME)
+ badentry(ep, "mktempname: called with TMPNAME");
+ ep->e_flags |= TMPNAME;
+ (void) strcpy(oldname, myname(ep));
+ freename(ep->e_name);
+ ep->e_name = savename(gentempname(ep));
+ ep->e_namlen = strlen(ep->e_name);
+ renameit(oldname, myname(ep));
+}
+
+/*
+ * Generate a temporary name for an entry.
+ */
+char *
+gentempname(ep)
+ struct entry *ep;
+{
+ static char name[MAXPATHLEN];
+ struct entry *np;
+ long i = 0;
+
+ for (np = lookupino(ep->e_ino);
+ np != NULL && np != ep; np = np->e_links)
+ i++;
+ if (np == NULL)
+ badentry(ep, "not on ino list");
+ (void) sprintf(name, "%s%d%d", TMPHDR, i, ep->e_ino);
+ return (name);
+}
+
+/*
+ * Rename a file or directory.
+ */
+void
+renameit(from, to)
+ char *from, *to;
+{
+ if (!Nflag && rename(from, to) < 0) {
+ fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
+ from, to, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "rename %s to %s\n", from, to);
+}
+
+/*
+ * Create a new node (directory).
+ */
+void
+newnode(np)
+ struct entry *np;
+{
+ char *cp;
+
+ if (np->e_type != NODE)
+ badentry(np, "newnode: not a node");
+ cp = myname(np);
+ if (!Nflag && mkdir(cp, 0777) < 0) {
+ np->e_flags |= EXISTED;
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Make node %s\n", cp);
+}
+
+/*
+ * Remove an old node (directory).
+ */
+void
+removenode(ep)
+ register struct entry *ep;
+{
+ char *cp;
+
+ if (ep->e_type != NODE)
+ badentry(ep, "removenode: not a node");
+ if (ep->e_entries != NULL)
+ badentry(ep, "removenode: non-empty directory");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ cp = myname(ep);
+ if (!Nflag && rmdir(cp) < 0) {
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Remove node %s\n", cp);
+}
+
+/*
+ * Remove a leaf.
+ */
+void
+removeleaf(ep)
+ register struct entry *ep;
+{
+ char *cp;
+
+ if (ep->e_type != LEAF)
+ badentry(ep, "removeleaf: not a leaf");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ cp = myname(ep);
+ if (!Nflag && unlink(cp) < 0) {
+ fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Remove leaf %s\n", cp);
+}
+
+/*
+ * Create a link.
+ */
+int
+linkit(existing, new, type)
+ char *existing, *new;
+ int type;
+{
+
+ if (type == SYMLINK) {
+ if (!Nflag && symlink(existing, new) < 0) {
+ fprintf(stderr,
+ "warning: cannot create symbolic link %s->%s: %s\n",
+ new, existing, strerror(errno));
+ return (FAIL);
+ }
+ } else if (type == HARDLINK) {
+ if (!Nflag && link(existing, new) < 0) {
+ fprintf(stderr,
+ "warning: cannot create hard link %s->%s: %s\n",
+ new, existing, strerror(errno));
+ return (FAIL);
+ }
+ } else {
+ panic("linkit: unknown type %d\n", type);
+ return (FAIL);
+ }
+ vprintf(stdout, "Create %s link %s->%s\n",
+ type == SYMLINK ? "symbolic" : "hard", new, existing);
+ return (GOOD);
+}
+
+/*
+ * find lowest number file (above "start") that needs to be extracted
+ */
+ino_t
+lowerbnd(start)
+ ino_t start;
+{
+ register struct entry *ep;
+
+ for ( ; start < maxino; start++) {
+ ep = lookupino(start);
+ if (ep == NULL || ep->e_type == NODE)
+ continue;
+ if (ep->e_flags & (NEW|EXTRACT))
+ return (start);
+ }
+ return (start);
+}
+
+/*
+ * find highest number file (below "start") that needs to be extracted
+ */
+ino_t
+upperbnd(start)
+ ino_t start;
+{
+ register struct entry *ep;
+
+ for ( ; start > ROOTINO; start--) {
+ ep = lookupino(start);
+ if (ep == NULL || ep->e_type == NODE)
+ continue;
+ if (ep->e_flags & (NEW|EXTRACT))
+ return (start);
+ }
+ return (start);
+}
+
+/*
+ * report on a badly formed entry
+ */
+void
+badentry(ep, msg)
+ register struct entry *ep;
+ char *msg;
+{
+
+ fprintf(stderr, "bad entry: %s\n", msg);
+ fprintf(stderr, "name: %s\n", myname(ep));
+ fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
+ if (ep->e_sibling != NULL)
+ fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
+ if (ep->e_entries != NULL)
+ fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
+ if (ep->e_links != NULL)
+ fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
+ if (ep->e_next != NULL)
+ fprintf(stderr,
+ "next hashchain name: %s\n", myname(ep->e_next));
+ fprintf(stderr, "entry type: %s\n",
+ ep->e_type == NODE ? "NODE" : "LEAF");
+ fprintf(stderr, "inode number: %ld\n", ep->e_ino);
+ panic("flags: %s\n", flagvalues(ep));
+}
+
+/*
+ * Construct a string indicating the active flag bits of an entry.
+ */
+char *
+flagvalues(ep)
+ register struct entry *ep;
+{
+ static char flagbuf[BUFSIZ];
+
+ (void) strcpy(flagbuf, "|NIL");
+ flagbuf[0] = '\0';
+ if (ep->e_flags & REMOVED)
+ (void) strcat(flagbuf, "|REMOVED");
+ if (ep->e_flags & TMPNAME)
+ (void) strcat(flagbuf, "|TMPNAME");
+ if (ep->e_flags & EXTRACT)
+ (void) strcat(flagbuf, "|EXTRACT");
+ if (ep->e_flags & NEW)
+ (void) strcat(flagbuf, "|NEW");
+ if (ep->e_flags & KEEP)
+ (void) strcat(flagbuf, "|KEEP");
+ if (ep->e_flags & EXISTED)
+ (void) strcat(flagbuf, "|EXISTED");
+ return (&flagbuf[1]);
+}
+
+/*
+ * Check to see if a name is on a dump tape.
+ */
+ino_t
+dirlookup(name)
+ const char *name;
+{
+ struct direct *dp;
+ ino_t ino;
+
+ ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
+
+ if (ino == 0 || TSTINO(ino, dumpmap) == 0)
+ fprintf(stderr, "%s is not on the tape\n", name);
+ return (ino);
+}
+
+/*
+ * Elicit a reply.
+ */
+int
+reply(question)
+ char *question;
+{
+ char c;
+
+ do {
+ fprintf(stderr, "%s? [yn] ", question);
+ (void) fflush(stderr);
+ c = getc(terminal);
+ while (c != '\n' && getc(terminal) != '\n')
+ if (feof(terminal))
+ return (FAIL);
+ } while (c != 'y' && c != 'n');
+ if (c == 'y')
+ return (GOOD);
+ return (FAIL);
+}
+
+/*
+ * handle unexpected inconsistencies
+ */
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ vfprintf(stderr, fmt, ap);
+ if (yflag)
+ return;
+ if (reply("abort") == GOOD) {
+ if (reply("dump core") == GOOD)
+ abort();
+ done(1);
+ }
+}
diff --git a/sbin/route/Makefile b/sbin/route/Makefile
new file mode 100644
index 000000000000..f7de8783c22c
--- /dev/null
+++ b/sbin/route/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= route
+MAN8= route.0
+SRCS= route.c ccitt_addr.c
+CFLAGS+=-I.
+CLEANFILES+=keywords.h
+BINOWN= root
+BINMODE=4555
+
+all: route ${MAN8}
+
+keywords.h: keywords
+ sed -e '/^#/d' -e '/^$$/d' ${.CURDIR}/keywords > _keywords.tmp
+ tr a-z A-Z < _keywords.tmp | paste _keywords.tmp - | \
+ awk '{ \
+ if (NF > 1) \
+ printf "#define\tK_%s\t%d\n\t{\"%s\", K_%s},\n", \
+ $$2, NR, $$1, $$2 }' \
+ > ${.TARGET}
+ rm -f _keywords.tmp
+
+.include <bsd.prog.mk>
+
+route .depend lint tags: keywords.h
diff --git a/sbin/route/ccitt_addr.c b/sbin/route/ccitt_addr.c
new file mode 100644
index 000000000000..868f6fc56289
--- /dev/null
+++ b/sbin/route/ccitt_addr.c
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ *
+ * @(#)ccitt_addr.c 8.1 (Berkeley) 6/5/93
+ */
+/*
+ * parse CCITT addresses
+ *
+ * Addresses must have the format: [hpr],x121address[,userdata][,protocol]
+ * items enclosed with square brackets are optional
+ * 'h' or 'p' means hi priority (packet size = 128; specific to Datapac
+ * and necessary only for X.25(76) and non-negotiating X.25(80) DTE's)
+ * 'r' means reverse charge (remote DTE pays for call).
+ * The x121address consists of an optional netid and dot, followed
+ * by a dte address.
+ *
+ * Frank Pronk
+ * The University of British Columbia
+ * Laboratory for Computational Vision
+ * Copyright (c) 1984
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netccitt/x25.h>
+
+static char *copychar ();
+
+ccitt_addr (addr, xp)
+char *addr;
+register struct sockaddr_x25 *xp;
+{
+ register char *p, *ap, *limit;
+ int havenet = 0;
+
+ bzero ((char *)xp, sizeof (*xp));
+ xp->x25_family = AF_CCITT;
+ xp->x25_len = sizeof(*xp);
+ p = addr;
+
+ /*
+ * process optional priority and reverse charging flags
+ */
+
+ if (*p == 'p' || *p == 'r' || *p == 'h') {
+ while (*p == 'p' || *p == 'r' || *p == 'h') {
+ if (*p == 'p' || *p == 'h')
+ xp->x25_opts.op_psize = X25_PS128;
+ else if (*p == 'r')
+ xp->x25_opts.op_flags |= X25_REVERSE_CHARGE;
+ p++;
+ }
+ if (*p != ',')
+ return (0);
+ p++;
+ }
+ if (*p == '\0')
+ return (0);
+
+ /*
+ * [network id:]X.121 address
+ */
+
+ ap = xp->x25_addr;
+ limit = ap + sizeof (xp->x25_addr) - 1;
+ while (*p) {
+ if (*p == ',')
+ break;
+ if (*p == '.' || *p == ':') {
+ if (havenet)
+ return (0);
+ havenet++;
+ xp->x25_net = atoi (xp->x25_addr);
+ p++;
+ ap = xp->x25_addr;
+ *ap = '\0';
+ }
+ if (*p < '0' || *p > '9')
+ return (0);
+ if (ap >= limit)
+ return (0);
+ *ap++ = *p++;
+ }
+ if (*p == '\0')
+ return (1);
+
+ /*
+ * optional user data, bytes 4 to 16
+ */
+
+ p++;
+ ap = xp->x25_udata + 4; /* first four bytes are protocol id */
+ limit = ap + sizeof (xp->x25_udata) - 4;
+ xp->x25_udlen = 4;
+ while (*p) {
+ if (*p == ',')
+ break;
+ if (ap >= limit)
+ return (0);
+ p = copychar (p, ap++);
+ xp->x25_udlen++;
+ }
+ if (xp->x25_udlen == 4)
+ xp->x25_udlen = 0;
+ if (*p == '\0')
+ return (1);
+
+ p++;
+ ap = xp->x25_udata; /* protocol id */
+ limit = ap + (xp->x25_udlen ? 4 : sizeof(xp->x25_udata));
+ while (*p) {
+ if (*p == ',')
+ return (0);
+ if (ap >= limit)
+ return (0);
+ p = copychar (p, ap++);
+ }
+ if (xp->x25_udlen == 0)
+ xp->x25_udlen = ap - xp->x25_udata;
+ return (1);
+}
+
+static char *
+copychar (from, to)
+register char *from, *to;
+{
+ register int n;
+
+ if (*from != '\\' || from[1] < '0' || from[1] > '7') {
+ *to = *from++;
+ return (from);
+ }
+ n = *++from - '0';
+ from++;
+ if (*from >= '0' && *from <= '7') {
+ register int n1;
+
+ n = n*8 + *from++ - '0';
+ if (*from >= '0' && *from <= '7' && (n1 = n*8 + *from-'0') < 256) {
+ n = n1;
+ from++;
+ }
+ }
+ *to = n;
+ return (from);
+}
diff --git a/sbin/route/keywords b/sbin/route/keywords
new file mode 100644
index 000000000000..e4860421467e
--- /dev/null
+++ b/sbin/route/keywords
@@ -0,0 +1,44 @@
+# @(#)keywords 8.2 (Berkeley) 3/19/94
+
+add
+blackhole
+change
+cloning
+delete
+dst
+expire
+flush
+gateway
+genmask
+get
+host
+hopcount
+iface
+interface
+ifa
+ifp
+inet
+iso
+link
+lock
+lockrest
+mask
+monitor
+mtu
+net
+netmask
+nostatic
+osi
+proto1
+proto2
+recvpipe
+reject
+rtt
+rttvar
+sa
+sendpipe
+ssthresh
+static
+x25
+xns
+xresolve
diff --git a/sbin/route/route.8 b/sbin/route/route.8
new file mode 100644
index 000000000000..906f8c21babe
--- /dev/null
+++ b/sbin/route/route.8
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)route.8 8.3 (Berkeley) 3/19/94
+.\"
+.Dd March 19, 1994
+.Dt ROUTE 8
+.Os BSD 4.4
+.Sh NAME
+.Nm route
+.Nd manually manipulate the routing tables.
+.Sh SYNOPSIS
+.Nm route
+.Op Fl nqv
+.Ar command
+.Oo
+.Op Ar modifiers
+.Ar args
+.Oc
+.Sh DESCRIPTION
+.Nm Route
+is a utility used to manually manipulate the network
+routing tables. It normally is not needed, as a
+system routing table management daemon such as
+.Xr routed 8 ,
+should tend to this task.
+.Pp
+The
+.Nm route :
+utility supports a limited number of general options,
+but a rich command language, enabling the user to specify
+any arbitrary request that could be delivered via the
+programmatic interface discussed in
+.Xr route 4 .
+.Pp
+.Bl -tag -width Ds
+.It Fl n
+Bypasses attempts to print host and network names symbolically
+when reporting actions. (The process of translating between symbolic
+names and numerical equivalents can be quite time consuming, and
+may require correct operation of the network; thus it may be expedient
+to forgo this, especially when attempting to repair networking operations),
+.It Fl v
+(verbose) Print additional details.
+.It Fl q
+Suppress all output.
+.El
+.Pp
+The
+.Nm route :
+utility provides six commands:
+.Pp
+.Bl -tag -width Fl -compact
+.It Cm add
+Add a route.
+.It Cm flush
+Remove all routes.
+.It Cm delete
+Delete a specific route.
+.It Cm change
+Change aspects of a route (such as its gateway).
+.It Cm get
+Lookup and display the route for a destination.
+.It Cm monitor
+Continuously report any changes to the routing information base,
+routing lookup misses, or suspected network partitionings.
+.El
+.Pp
+The monitor command has the syntax
+.Pp
+.Bd -filled -offset indent -compact
+.Nm route Op Fl n
+.Cm monitor
+.Ed
+.Pp
+The flush command has the syntax
+.Pp
+.Bd -filled -offset indent -compact
+.Nm route Op Fl n
+.Cm flush
+.Op Ar family
+.Ed
+.Pp
+If the
+.Cm flush
+command is specified,
+.Nm route
+will ``flush'' the routing tables of all gateway entries.
+When the address family may is specified by any of the
+.Fl osi ,
+.Fl xns ,
+or
+.Fl inet
+modifiers, only routes having destinations with addresses in the
+delineated family will be deleted.
+.Pp
+The other commands have the following syntax:
+.Pp
+.Bd -filled -offset indent -compact
+.Nm route Op Fl n
+.Ar command
+.Op Fl net No \&| Fl host
+.Ar destination gateway
+.Ed
+.Pp
+where
+.Ar destination
+is the destination host or network,
+.Ar gateway
+is the next-hop intermediary via which packets should be routed.
+Routes to a particular host may be distinguished from those to
+a network by interpreting the Internet address specified as the
+.Ar destination argument.
+The optional modifiers
+.Fl net
+and
+.Fl host
+force the destination to be interpreted as a network or a host, respectively.
+Otherwise, if the
+.Ar destination
+has a ``local address part'' of
+INADDR_ANY ,
+or if the
+.Ar destination
+is the symbolic name of a network, then the route is
+assumed to be to a network; otherwise, it is presumed to be a
+route to a host.
+.Pp
+For example,
+.Li 128.32
+is interpreted as
+.Fl host Li 128.0.0.32 ;
+.Li 128.32.130
+is interpreted as
+.Fl host Li 128.32.0.130 ;
+.Fl net Li 128.32
+is interpreted as
+.Li 128.32.0.0;
+and
+.Fl net Li 128.32.130
+is interpreted as
+.Li 128.32.130.0 .
+.Pp
+If the destination is directly reachable
+via an interface requiring
+no intermediary system to act as a gateway, the
+.Fl interface
+modifier should be specified;
+the gateway given is the address of this host on the common network,
+indicating the interface to be used for transmission.
+.Pp
+The optional modifiers
+.Fl xns ,
+.Fl osi ,
+and
+.Fl link
+specify that all subsequent addresses are in the
+.Tn XNS
+.Tn OSI
+address families,
+or are specified as link-level addresses,
+and the names must be numeric specifications rather than
+symbolic names.
+.Pp
+The optional
+.Fl netmask
+qualifier is intended
+to achieve the effect of an
+.Tn OSI
+.Tn ESIS
+redirect with the netmask option,
+or to manually add subnet routes with
+netmasks different from that of the implied network interface
+(as would otherwise be communicated using the OSPF or ISIS routing protocols).
+One specifies an additional ensuing address parameter
+(to be interpreted as a network mask).
+The implicit network mask generated in the AF_INET case
+can be overridden by making sure this option follows the destination parameter.
+.Pp
+Routes have associated flags which influence operation of the protocols
+when sending to destinations matched by the routes.
+These flags may be set (or sometimes cleared)
+by indicating the following corresponding modifiers:
+.Bd -literal
+-cloning RTF_CLONING - generates a new route on use
+-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup)
+-iface ~RTF_GATEWAY - destination is directly reachable
+-static RTF_STATIC - manually added route
+-nostatic ~RTF_STATIC - pretend route added by kernel or daemon
+-reject RTF_REJECT - emit an ICMP unreachable when matched
+-blackhole RTF_BLACKHOLE - silently discard pkts (during updates)
+-proto1 RTF_PROTO1 - set protocol specific routing flag #1
+-proto2 RTF_PROTO2 - set protocol specific routing flag #2
+-llinfo RTF_LLINFO - validly translates proto addr to link addr
+.Ed
+.Pp
+The optional modifiers
+.Fl rtt ,
+.Fl rttvar ,
+.Fl sendpipe ,
+.Fl recvpipe ,
+.Fl mtu ,
+.Fl hopcount ,
+.Fl expire ,
+and
+.Fl ssthresh
+provide initial values to quantities maintained in the routing entry
+by transport level protocols, such as TCP or TP4.
+These may be individually locked by preceding each such modifier to
+be locked by
+the
+.Fl lock
+meta-modifier, or one can
+specify that all ensuing metrics may be locked by the
+.Fl lockrest
+meta-modifier.
+.Pp
+In a
+.Cm change
+or
+.Cm add
+command where the destination and gateway are not sufficient to specify
+the route (as in the
+.Tn ISO
+case where several interfaces may have the
+same address), the
+.Fl ifp
+or
+.Fl ifa
+modifiers may be used to determine the interface or interface address.
+.Pp
+All symbolic names specified for a
+.Ar destination
+or
+.Ar gateway
+are looked up first as a host name using
+.Xr gethostbyname 3 .
+If this lookup fails,
+.Xr getnetbyname 3
+is then used to interpret the name as that of a network.
+.Pp
+.Nm Route
+uses a routing socket and the new message types
+RTM_ADD,
+RTM_DELETE,
+RTM_GET,
+and
+RTM_CHANGE.
+As such, only the super-user may modify
+the routing tables.
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds
+.It Sy "add [host \&| network ] %s: gateway %s flags %x"
+The specified route is being added to the tables. The
+values printed are from the routing table entry supplied
+in the
+.Xr ioctl 2
+call.
+If the gateway address used was not the primary address of the gateway
+(the first one returned by
+.Xr gethostbyname 3 ) ,
+the gateway address is printed numerically as well as symbolically.
+.It Sy "delete [ host &| network ] %s: gateway %s flags %x"
+As above, but when deleting an entry.
+.It Sy "%s %s done"
+When the
+.Cm flush
+command is specified, each routing table entry deleted
+is indicated with a message of this form.
+.It Sy "Network is unreachable"
+An attempt to add a route failed because the gateway listed was not
+on a directly-connected network.
+The next-hop gateway must be given.
+.It Sy "not in table"
+A delete operation was attempted for an entry which
+wasn't present in the tables.
+.It Sy "routing table overflow"
+An add operation was attempted, but the system was
+low on resources and was unable to allocate memory
+to create the new entry.
+.El
+.Sh SEE ALSO
+.Xr netintro 4 ,
+.Xr route 4 ,
+.Xr esis 4 ,
+.Xr routed 8 ,
+.Xr XNSrouted 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+The first paragraph may have slightly exaggerated
+.Xr routed Ns 's
+abilities.
diff --git a/sbin/route/route.c b/sbin/route/route.c
new file mode 100644
index 000000000000..893996c618fa
--- /dev/null
+++ b/sbin/route/route.c
@@ -0,0 +1,1415 @@
+/*
+ *
+ * Copyright (c) 1983, 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)route.c 8.3 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netns/ns.h>
+#include <netiso/iso.h>
+#include <netccitt/x25.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+
+struct keytab {
+ char *kt_cp;
+ int kt_i;
+} keywords[] = {
+#include "keywords.h"
+ {0, 0}
+};
+
+struct ortentry route;
+union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_ns sns;
+ struct sockaddr_iso siso;
+ struct sockaddr_dl sdl;
+ struct sockaddr_x25 sx25;
+} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp;
+
+typedef union sockunion *sup;
+int pid, rtm_addrs, uid;
+int s;
+int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword();
+int iflag, verbose, aflen = sizeof (struct sockaddr_in);
+int locking, lockrest, debugonly;
+struct rt_metrics rt_metrics;
+u_long rtm_inits;
+struct in_addr inet_makeaddr();
+char *routename(), *netname();
+void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf();
+void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr();
+int getaddr(), rtmsg(), x25_makemask();
+extern char *inet_ntoa(), *iso_ntoa(), *link_ntoa();
+
+__dead void
+usage(cp)
+ char *cp;
+{
+ if (cp)
+ (void) fprintf(stderr, "route: botched keyword: %s\n", cp);
+ (void) fprintf(stderr,
+ "usage: route [ -nqv ] cmd [[ -<qualifers> ] args ]\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+quit(s)
+ char *s;
+{
+ int sverrno = errno;
+
+ (void) fprintf(stderr, "route: ");
+ if (s)
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "%s\n", strerror(sverrno));
+ exit(1);
+ /* NOTREACHED */
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch;
+
+ if (argc < 2)
+ usage((char *)NULL);
+
+ while ((ch = getopt(argc, argv, "nqdtv")) != EOF)
+ switch(ch) {
+ case 'n':
+ nflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'd':
+ debugonly = 1;
+ break;
+ case '?':
+ default:
+ usage((char *)NULL);
+ }
+ argc -= optind;
+ argv += optind;
+
+ pid = getpid();
+ uid = getuid();
+ if (tflag)
+ s = open("/dev/null", O_WRONLY, 0);
+ else
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ quit("socket");
+ if (*argv)
+ switch (keyword(*argv)) {
+ case K_GET:
+ uid = 0;
+ /* FALLTHROUGH */
+
+ case K_CHANGE:
+ case K_ADD:
+ case K_DELETE:
+ newroute(argc, argv);
+ exit(0);
+ /* NOTREACHED */
+
+ case K_MONITOR:
+ monitor();
+ /* NOTREACHED */
+
+ case K_FLUSH:
+ flushroutes(argc, argv);
+ exit(0);
+ /* NOTREACHED */
+ }
+ usage(*argv);
+ /* NOTREACHED */
+}
+
+/*
+ * Purge all entries in the routing tables not
+ * associated with network interfaces.
+ */
+void
+flushroutes(argc, argv)
+ int argc;
+ char *argv[];
+{
+ size_t needed;
+ int mib[6], rlen, seqno;
+ char *buf, *next, *lim;
+ register struct rt_msghdr *rtm;
+
+ if (uid) {
+ errno = EACCES;
+ quit("must be root to alter routing table");
+ }
+ shutdown(s, 0); /* Don't want to read back our messages */
+ if (argc > 1) {
+ argv++;
+ if (argc == 2 && **argv == '-')
+ switch (keyword(*argv + 1)) {
+ case K_INET:
+ af = AF_INET;
+ break;
+ case K_XNS:
+ af = AF_NS;
+ break;
+ case K_LINK:
+ af = AF_LINK;
+ break;
+ case K_ISO:
+ case K_OSI:
+ af = AF_ISO;
+ break;
+ case K_X25:
+ af = AF_CCITT;
+ default:
+ goto bad;
+ } else
+bad: usage(*argv);
+ }
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0; /* no flags */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ quit("actual retrieval of routing table");
+ lim = buf + needed;
+ if (verbose)
+ (void) printf("Examining routing table from sysctl\n");
+ seqno = 0; /* ??? */
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (verbose)
+ print_rtmsg(rtm, rtm->rtm_msglen);
+ if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
+ continue;
+ if (af) {
+ struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+
+ if (sa->sa_family != af)
+ continue;
+ }
+ if (debugonly)
+ continue;
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_seq = seqno;
+ rlen = write(s, next, rtm->rtm_msglen);
+ if (rlen < (int)rtm->rtm_msglen) {
+ (void) fprintf(stderr,
+ "route: write to routing socket: %s\n",
+ strerror(errno));
+ (void) printf("got only %d for rlen\n", rlen);
+ break;
+ }
+ seqno++;
+ if (qflag)
+ continue;
+ if (verbose)
+ print_rtmsg(rtm, rlen);
+ else {
+ struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+ (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
+ routename(sa) : netname(sa));
+ sa = (struct sockaddr *)(sa->sa_len + (char *)sa);
+ (void) printf("%-20.20s ", routename(sa));
+ (void) printf("done\n");
+ }
+ }
+}
+
+char *
+routename(sa)
+ struct sockaddr *sa;
+{
+ register char *cp;
+ static char line[50];
+ struct hostent *hp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+ char *ns_print();
+
+ if (first) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.')))
+ (void) strcpy(domain, cp + 1);
+ else
+ domain[0] = 0;
+ }
+
+ if (sa->sa_len == 0)
+ strcpy(line, "default");
+ else switch (sa->sa_family) {
+
+ case AF_INET:
+ { struct in_addr in;
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ cp = 0;
+ if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
+ cp = "default";
+ if (cp == 0 && !nflag) {
+ hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+ AF_INET);
+ if (hp) {
+ if ((cp = index(hp->h_name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp)
+ strcpy(line, cp);
+ else {
+#define C(x) ((x) & 0xff)
+ in.s_addr = ntohl(in.s_addr);
+ (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
+ }
+ break;
+ }
+
+ case AF_NS:
+ return (ns_print((struct sockaddr_ns *)sa));
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+ case AF_ISO:
+ (void) sprintf(line, "iso %s",
+ iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr));
+ break;
+
+ default:
+ { u_short *s = (u_short *)sa;
+ u_short *slim = s + ((sa->sa_len + 1) >> 1);
+ char *cp = line + sprintf(line, "(%d)", sa->sa_family);
+
+ while (++s < slim) /* start with sa->sa_data */
+ cp += sprintf(cp, " %x", *s);
+ break;
+ }
+ }
+ return (line);
+}
+
+/*
+ * Return the name of the network whose address is given.
+ * The address is assumed to be that of a net or subnet, not a host.
+ */
+char *
+netname(sa)
+ struct sockaddr *sa;
+{
+ char *cp = 0;
+ static char line[50];
+ struct netent *np = 0;
+ u_long net, mask;
+ register u_long i;
+ int subnetshift;
+ char *ns_print();
+
+ switch (sa->sa_family) {
+
+ case AF_INET:
+ { struct in_addr in;
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ i = in.s_addr = ntohl(in.s_addr);
+ if (in.s_addr == 0)
+ cp = "default";
+ else if (!nflag) {
+ if (IN_CLASSA(i)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(i)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ */
+ while (in.s_addr &~ mask)
+ mask = (long)mask >> subnetshift;
+ net = in.s_addr & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ cp = np->n_name;
+ }
+ if (cp)
+ strcpy(line, cp);
+ else if ((in.s_addr & 0xffffff) == 0)
+ (void) sprintf(line, "%u", C(in.s_addr >> 24));
+ else if ((in.s_addr & 0xffff) == 0)
+ (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16));
+ else if ((in.s_addr & 0xff) == 0)
+ (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8));
+ else
+ (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8),
+ C(in.s_addr));
+ break;
+ }
+
+ case AF_NS:
+ return (ns_print((struct sockaddr_ns *)sa));
+ break;
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+ case AF_ISO:
+ (void) sprintf(line, "iso %s",
+ iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr));
+ break;
+
+ default:
+ { u_short *s = (u_short *)sa->sa_data;
+ u_short *slim = s + ((sa->sa_len + 1)>>1);
+ char *cp = line + sprintf(line, "af %d:", sa->sa_family);
+
+ while (s < slim)
+ cp += sprintf(cp, " %x", *s++);
+ break;
+ }
+ }
+ return (line);
+}
+
+void
+set_metric(value, key)
+ char *value;
+ int key;
+{
+ int flag = 0;
+ u_long noval, *valp = &noval;
+
+ switch (key) {
+#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break
+ caseof(K_MTU, RTV_MTU, rmx_mtu);
+ caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
+ caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
+ caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
+ caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
+ caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
+ caseof(K_RTT, RTV_RTT, rmx_rtt);
+ caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
+ }
+ rtm_inits |= flag;
+ if (lockrest || locking)
+ rt_metrics.rmx_locks |= flag;
+ if (locking)
+ locking = 0;
+ *valp = atoi(value);
+}
+
+void
+newroute(argc, argv)
+ int argc;
+ register char **argv;
+{
+ char *cmd, *dest = "", *gateway = "", *err;
+ int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC;
+ int key;
+ struct hostent *hp = 0;
+
+ if (uid) {
+ errno = EACCES;
+ quit("must be root to alter routing table");
+ }
+ cmd = argv[0];
+ if (*cmd != 'g')
+ shutdown(s, 0); /* Don't want to read back our messages */
+ while (--argc > 0) {
+ if (**(++argv)== '-') {
+ switch (key = keyword(1 + *argv)) {
+ case K_LINK:
+ af = AF_LINK;
+ aflen = sizeof(struct sockaddr_dl);
+ break;
+ case K_OSI:
+ case K_ISO:
+ af = AF_ISO;
+ aflen = sizeof(struct sockaddr_iso);
+ break;
+ case K_INET:
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ break;
+ case K_X25:
+ af = AF_CCITT;
+ aflen = sizeof(struct sockaddr_x25);
+ break;
+ case K_SA:
+ af = PF_ROUTE;
+ aflen = sizeof(union sockunion);
+ break;
+ case K_XNS:
+ af = AF_NS;
+ aflen = sizeof(struct sockaddr_ns);
+ break;
+ case K_IFACE:
+ case K_INTERFACE:
+ iflag++;
+ case K_NOSTATIC:
+ flags &= ~RTF_STATIC;
+ break;
+ case K_LOCK:
+ locking = 1;
+ break;
+ case K_LOCKREST:
+ lockrest = 1;
+ break;
+ case K_HOST:
+ forcehost++;
+ break;
+ case K_REJECT:
+ flags |= RTF_REJECT;
+ break;
+ case K_BLACKHOLE:
+ flags |= RTF_BLACKHOLE;
+ break;
+ case K_PROTO1:
+ flags |= RTF_PROTO1;
+ break;
+ case K_PROTO2:
+ flags |= RTF_PROTO2;
+ break;
+ case K_CLONING:
+ flags |= RTF_CLONING;
+ break;
+ case K_XRESOLVE:
+ flags |= RTF_XRESOLVE;
+ break;
+ case K_STATIC:
+ flags |= RTF_STATIC;
+ break;
+ case K_IFA:
+ argc--;
+ (void) getaddr(RTA_IFA, *++argv, 0);
+ break;
+ case K_IFP:
+ argc--;
+ (void) getaddr(RTA_IFP, *++argv, 0);
+ break;
+ case K_GENMASK:
+ argc--;
+ (void) getaddr(RTA_GENMASK, *++argv, 0);
+ break;
+ case K_GATEWAY:
+ argc--;
+ (void) getaddr(RTA_GATEWAY, *++argv, 0);
+ break;
+ case K_DST:
+ argc--;
+ ishost = getaddr(RTA_DST, *++argv, &hp);
+ dest = *argv;
+ break;
+ case K_NETMASK:
+ argc--;
+ (void) getaddr(RTA_NETMASK, *++argv, 0);
+ /* FALLTHROUGH */
+ case K_NET:
+ forcenet++;
+ break;
+ case K_MTU:
+ case K_HOPCOUNT:
+ case K_EXPIRE:
+ case K_RECVPIPE:
+ case K_SENDPIPE:
+ case K_SSTHRESH:
+ case K_RTT:
+ case K_RTTVAR:
+ argc--;
+ set_metric(*++argv, key);
+ break;
+ default:
+ usage(1+*argv);
+ }
+ } else {
+ if ((rtm_addrs & RTA_DST) == 0) {
+ dest = *argv;
+ ishost = getaddr(RTA_DST, *argv, &hp);
+ } else if ((rtm_addrs & RTA_GATEWAY) == 0) {
+ gateway = *argv;
+ (void) getaddr(RTA_GATEWAY, *argv, &hp);
+ } else {
+ int ret = atoi(*argv);
+
+ if (ret == 0) {
+ if (strcmp(*argv, "0") == 0)
+ printf("%s,%s",
+ "old usage of trailing 0",
+ "assuming route to if\n");
+ else
+ usage((char *)NULL);
+ iflag = 1;
+ continue;
+ } else if (ret > 0 && ret < 10) {
+ printf("old usage of trailing digit, ");
+ printf("assuming route via gateway\n");
+ iflag = 0;
+ continue;
+ }
+ (void) getaddr(RTA_NETMASK, *argv, 0);
+ }
+ }
+ }
+ if (forcehost)
+ ishost = 1;
+ if (forcenet)
+ ishost = 0;
+ flags |= RTF_UP;
+ if (ishost)
+ flags |= RTF_HOST;
+ if (iflag == 0)
+ flags |= RTF_GATEWAY;
+ for (attempts = 1; ; attempts++) {
+ errno = 0;
+ if ((ret = rtmsg(*cmd, flags)) == 0)
+ break;
+ if (errno != ENETUNREACH && errno != ESRCH)
+ break;
+ if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) {
+ hp->h_addr_list++;
+ bcopy(hp->h_addr_list[0], &so_gate.sin.sin_addr,
+ hp->h_length);
+ } else
+ break;
+ }
+ if (*cmd == 'g')
+ exit(0);
+ oerrno = errno;
+ (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest);
+ if (*gateway) {
+ (void) printf(": gateway %s", gateway);
+ if (attempts > 1 && ret == 0 && af == AF_INET)
+ (void) printf(" (%s)",
+ inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr));
+ }
+ if (ret == 0)
+ (void) printf("\n");
+ else {
+ switch (oerrno) {
+ case ESRCH:
+ err = "not in table";
+ break;
+ case EBUSY:
+ err = "entry in use";
+ break;
+ case ENOBUFS:
+ err = "routing table overflow";
+ break;
+ default:
+ err = strerror(oerrno);
+ break;
+ }
+ (void) printf(": %s\n", err);
+ }
+}
+
+void
+inet_makenetandmask(net, sin)
+ u_long net;
+ register struct sockaddr_in *sin;
+{
+ u_long addr, mask = 0;
+ register char *cp;
+
+ rtm_addrs |= RTA_NETMASK;
+ if (net == 0)
+ mask = addr = 0;
+ else if (net < 128) {
+ addr = net << IN_CLASSA_NSHIFT;
+ mask = IN_CLASSA_NET;
+ } else if (net < 65536) {
+ addr = net << IN_CLASSB_NSHIFT;
+ mask = IN_CLASSB_NET;
+ } else if (net < 16777216L) {
+ addr = net << IN_CLASSC_NSHIFT;
+ mask = IN_CLASSC_NET;
+ } else {
+ addr = net;
+ if ((addr & IN_CLASSA_HOST) == 0)
+ mask = IN_CLASSA_NET;
+ else if ((addr & IN_CLASSB_HOST) == 0)
+ mask = IN_CLASSB_NET;
+ else if ((addr & IN_CLASSC_HOST) == 0)
+ mask = IN_CLASSC_NET;
+ else
+ mask = -1;
+ }
+ sin->sin_addr.s_addr = htonl(addr);
+ sin = &so_mask.sin;
+ sin->sin_addr.s_addr = htonl(mask);
+ sin->sin_len = 0;
+ sin->sin_family = 0;
+ cp = (char *)(&sin->sin_addr + 1);
+ while (*--cp == 0 && cp > (char *)sin)
+ ;
+ sin->sin_len = 1 + cp - (char *)sin;
+}
+
+/*
+ * Interpret an argument as a network address of some kind,
+ * returning 1 if a host address, 0 if a network address.
+ */
+int
+getaddr(which, s, hpp)
+ int which;
+ char *s;
+ struct hostent **hpp;
+{
+ register sup su;
+ struct ns_addr ns_addr();
+ struct iso_addr *iso_addr();
+ struct hostent *hp;
+ struct netent *np;
+ u_long val;
+
+ if (af == 0) {
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ }
+ rtm_addrs |= which;
+ switch (which) {
+ case RTA_DST:
+ su = &so_dst;
+ su->sa.sa_family = af;
+ break;
+ case RTA_GATEWAY:
+ su = &so_gate;
+ su->sa.sa_family = af;
+ break;
+ case RTA_NETMASK:
+ su = &so_mask;
+ break;
+ case RTA_GENMASK:
+ su = &so_genmask;
+ break;
+ case RTA_IFP:
+ su = &so_ifp;
+ su->sa.sa_family = af;
+ break;
+ case RTA_IFA:
+ su = &so_ifa;
+ su->sa.sa_family = af;
+ break;
+ default:
+ usage("Internal Error");
+ /*NOTREACHED*/
+ }
+ su->sa.sa_len = aflen;
+ if (strcmp(s, "default") == 0) {
+ switch (which) {
+ case RTA_DST:
+ forcenet++;
+ (void) getaddr(RTA_NETMASK, s, 0);
+ break;
+ case RTA_NETMASK:
+ case RTA_GENMASK:
+ su->sa.sa_len = 0;
+ }
+ return (0);
+ }
+ switch (af) {
+ case AF_NS:
+ if (which == RTA_DST) {
+ extern short ns_bh[3];
+ struct sockaddr_ns *sms = &(so_mask.sns);
+ bzero((char *)sms, sizeof(*sms));
+ sms->sns_family = 0;
+ sms->sns_len = 6;
+ sms->sns_addr.x_net = *(union ns_net *)ns_bh;
+ rtm_addrs |= RTA_NETMASK;
+ }
+ su->sns.sns_addr = ns_addr(s);
+ return (!ns_nullhost(su->sns.sns_addr));
+
+ case AF_OSI:
+ su->siso.siso_addr = *iso_addr(s);
+ if (which == RTA_NETMASK || which == RTA_GENMASK) {
+ register char *cp = (char *)TSEL(&su->siso);
+ su->siso.siso_nlen = 0;
+ do {--cp ;} while ((cp > (char *)su) && (*cp == 0));
+ su->siso.siso_len = 1 + cp - (char *)su;
+ }
+ return (1);
+
+ case AF_LINK:
+ link_addr(s, &su->sdl);
+ return (1);
+
+ case AF_CCITT:
+ ccitt_addr(s, &su->sx25);
+ return (which == RTA_DST ? x25_makemask() : 1);
+
+ case PF_ROUTE:
+ su->sa.sa_len = sizeof(*su);
+ sockaddr(s, &su->sa);
+ return (1);
+
+ case AF_INET:
+ default:
+ break;
+ }
+
+ if (hpp == NULL)
+ hpp = &hp;
+ *hpp = NULL;
+ if (((val = inet_addr(s)) != -1) &&
+ (which != RTA_DST || forcenet == 0)) {
+ su->sin.sin_addr.s_addr = val;
+ if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY)
+ return (1);
+ else {
+ val = ntohl(val);
+ goto netdone;
+ }
+ }
+ if ((val = inet_network(s)) != -1 ||
+ ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0)) {
+netdone:
+ if (which == RTA_DST)
+ inet_makenetandmask(val, &su->sin);
+ return (0);
+ }
+ hp = gethostbyname(s);
+ if (hp) {
+ *hpp = hp;
+ su->sin.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&su->sin.sin_addr, hp->h_length);
+ return (1);
+ }
+ (void) fprintf(stderr, "%s: bad value\n", s);
+ exit(1);
+}
+
+int
+x25_makemask()
+{
+ register char *cp;
+
+ if ((rtm_addrs & RTA_NETMASK) == 0) {
+ rtm_addrs |= RTA_NETMASK;
+ for (cp = (char *)&so_mask.sx25.x25_net;
+ cp < &so_mask.sx25.x25_opts.op_flags; cp++)
+ *cp = -1;
+ so_mask.sx25.x25_len = (u_char)&(((sup)0)->sx25.x25_opts);
+ }
+ return 0;
+}
+
+short ns_nullh[] = {0,0,0};
+short ns_bh[] = {-1,-1,-1};
+
+char *
+ns_print(sns)
+ struct sockaddr_ns *sns;
+{
+ struct ns_addr work;
+ union { union ns_net net_e; u_long long_e; } net;
+ u_short port;
+ static char mybuf[50], cport[10], chost[25];
+ char *host = "";
+ register char *p;
+ register u_char *q;
+
+ work = sns->sns_addr;
+ port = ntohs(work.x_port);
+ work.x_port = 0;
+ net.net_e = work.x_net;
+ if (ns_nullhost(work) && net.long_e == 0) {
+ if (!port)
+ return ("*.*");
+ (void) sprintf(mybuf, "*.%XH", port);
+ return (mybuf);
+ }
+
+ if (bcmp((char *)ns_bh, (char *)work.x_host.c_host, 6) == 0)
+ host = "any";
+ else if (bcmp((char *)ns_nullh, (char *)work.x_host.c_host, 6) == 0)
+ host = "*";
+ else {
+ q = work.x_host.c_host;
+ (void) sprintf(chost, "%02X%02X%02X%02X%02X%02XH",
+ q[0], q[1], q[2], q[3], q[4], q[5]);
+ for (p = chost; *p == '0' && p < chost + 12; p++)
+ /* void */;
+ host = p;
+ }
+ if (port)
+ (void) sprintf(cport, ".%XH", htons(port));
+ else
+ *cport = 0;
+
+ (void) sprintf(mybuf,"%XH.%s%s", ntohl(net.long_e), host, cport);
+ return (mybuf);
+}
+
+void
+interfaces()
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next;
+ register struct rt_msghdr *rtm;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* no flags */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ quit("actual retrieval of interface table");
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ print_rtmsg(rtm, rtm->rtm_msglen);
+ }
+}
+
+void
+monitor()
+{
+ int n;
+ char msg[2048];
+
+ verbose = 1;
+ if (debugonly) {
+ interfaces();
+ exit(0);
+ }
+ for(;;) {
+ n = read(s, msg, 2048);
+ (void) printf("got message of size %d\n", n);
+ print_rtmsg((struct rt_msghdr *)msg, n);
+ }
+}
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+int
+rtmsg(cmd, flags)
+ int cmd, flags;
+{
+ static int seq;
+ int rlen;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\
+ if (verbose) sodump(&(u),"u");\
+ }
+
+ errno = 0;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ if (cmd == 'a')
+ cmd = RTM_ADD;
+ else if (cmd == 'c')
+ cmd = RTM_CHANGE;
+ else if (cmd == 'g') {
+ cmd = RTM_GET;
+ if (so_ifp.sa.sa_family == 0) {
+ so_ifp.sa.sa_family == AF_LINK;
+ so_ifp.sa.sa_len == sizeof(struct sockaddr_dl);
+ rtm_addrs |= RTA_IFP;
+ }
+ } else
+ cmd = RTM_DELETE;
+#define rtm m_rtmsg.m_rtm
+ rtm.rtm_type = cmd;
+ rtm.rtm_flags = flags;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+ rtm.rtm_rmx = rt_metrics;
+ rtm.rtm_inits = rtm_inits;
+
+ if (rtm_addrs & RTA_NETMASK)
+ mask_addr();
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_GATEWAY, so_gate);
+ NEXTADDR(RTA_NETMASK, so_mask);
+ NEXTADDR(RTA_GENMASK, so_genmask);
+ NEXTADDR(RTA_IFP, so_ifp);
+ NEXTADDR(RTA_IFA, so_ifa);
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+ if (verbose)
+ print_rtmsg(&rtm, l);
+ if (debugonly)
+ return (0);
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ perror("writing to routing socket");
+ return (-1);
+ }
+ if (cmd == RTM_GET) {
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ if (l < 0)
+ (void) fprintf(stderr,
+ "route: read from routing socket: %s\n",
+ strerror(errno));
+ else
+ print_getmsg(&rtm, l);
+ }
+#undef rtm
+ return (0);
+}
+
+void
+mask_addr()
+{
+ int olen = so_mask.sa.sa_len;
+ register char *cp1 = olen + (char *)&so_mask, *cp2;
+
+ for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; )
+ if (*--cp1 != 0) {
+ so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask;
+ break;
+ }
+ if ((rtm_addrs & RTA_DST) == 0)
+ return;
+ switch (so_dst.sa.sa_family) {
+ case AF_NS:
+ case AF_INET:
+ case AF_CCITT:
+ case 0:
+ return;
+ case AF_ISO:
+ olen = MIN(so_dst.siso.siso_nlen,
+ MAX(so_mask.sa.sa_len - 6, 0));
+ break;
+ }
+ cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst;
+ cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst;
+ while (cp2 > cp1)
+ *--cp2 = 0;
+ cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask;
+ while (cp1 > so_dst.sa.sa_data)
+ *--cp1 &= *--cp2;
+ switch (so_dst.sa.sa_family) {
+ case AF_ISO:
+ so_dst.siso.siso_nlen = olen;
+ break;
+ }
+}
+
+char *msgtypes[] = {
+ "",
+ "RTM_ADD: Add Route",
+ "RTM_DELETE: Delete Route",
+ "RTM_CHANGE: Change Metrics or flags",
+ "RTM_GET: Report Metrics",
+ "RTM_LOSING: Kernel Suspects Partitioning",
+ "RTM_REDIRECT: Told to use different route",
+ "RTM_MISS: Lookup failed on this address",
+ "RTM_LOCK: fix specified metrics",
+ "RTM_OLDADD: caused by SIOCADDRT",
+ "RTM_OLDDEL: caused by SIOCDELRT",
+ "RTM_RESOLVE: Route created by cloning",
+ "RTM_NEWADDR: address being added to iface",
+ "RTM_DELADDR: address being removed from iface",
+ "RTM_IFINFO: iface status change",
+ 0,
+};
+
+char metricnames[] =
+"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu";
+char routeflags[] =
+"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\017PROTO2\020PROTO1";
+char ifnetflags[] =
+"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST";
+char addrnames[] =
+"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
+
+void
+print_rtmsg(rtm, msglen)
+ register struct rt_msghdr *rtm;
+ int msglen;
+{
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+
+ if (verbose == 0)
+ return;
+ if (rtm->rtm_version != RTM_VERSION) {
+ (void) printf("routing message version %d not understood\n",
+ rtm->rtm_version);
+ return;
+ }
+ (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen);
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ (void) printf("if# %d, flags:", ifm->ifm_index);
+ bprintf(stdout, ifm->ifm_flags, ifnetflags);
+ pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ (void) printf("metric %d, flags:", ifam->ifam_metric);
+ bprintf(stdout, ifam->ifam_flags, routeflags);
+ pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
+ break;
+ default:
+ (void) printf("pid: %d, seq %d, errno %d, flags:",
+ rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+ pmsg_common(rtm);
+ }
+}
+
+void
+print_getmsg(rtm, msglen)
+ register struct rt_msghdr *rtm;
+ int msglen;
+{
+ struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ register struct sockaddr *sa;
+ register char *cp;
+ register int i;
+
+ (void) printf(" route to: %s\n", routename(&so_dst));
+ if (rtm->rtm_version != RTM_VERSION) {
+ (void)fprintf(stderr,
+ "routing message version %d not understood\n",
+ rtm->rtm_version);
+ return;
+ }
+ if (rtm->rtm_msglen > msglen) {
+ (void)fprintf(stderr,
+ "message length mismatch, in packet %d, returned %d\n",
+ rtm->rtm_msglen, msglen);
+ }
+ if (rtm->rtm_errno) {
+ (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
+ strerror(rtm->rtm_errno), rtm->rtm_errno);
+ return;
+ }
+ cp = ((char *)(rtm + 1));
+ if (rtm->rtm_addrs)
+ for (i = 1; i; i <<= 1)
+ if (i & rtm->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_DST:
+ dst = sa;
+ break;
+ case RTA_GATEWAY:
+ gate = sa;
+ break;
+ case RTA_NETMASK:
+ mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->sdl_nlen)
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ }
+ ADVANCE(cp, sa);
+ }
+ if (dst && mask)
+ mask->sa_family = dst->sa_family; /* XXX */
+ if (dst)
+ (void)printf("destination: %s\n", routename(dst));
+ if (mask) {
+ int savenflag = nflag;
+
+ nflag = 1;
+ (void)printf(" mask: %s\n", routename(mask));
+ nflag = savenflag;
+ }
+ if (gate && rtm->rtm_flags & RTF_GATEWAY)
+ (void)printf(" gateway: %s\n", routename(gate));
+ if (ifp)
+ (void)printf(" interface: %.*s\n",
+ ifp->sdl_nlen, ifp->sdl_data);
+ (void)printf(" flags: ");
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+
+#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
+#define msec(u) (((u) + 500) / 1000) /* usec to msec */
+
+ (void) printf("\n%s\n", "\
+ recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire");
+ printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
+ printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
+ printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
+ printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
+ printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
+ printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
+ printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
+ if (rtm->rtm_rmx.rmx_expire)
+ rtm->rtm_rmx.rmx_expire -= time(0);
+ printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
+#undef lock
+#undef msec
+#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
+ if (verbose)
+ pmsg_common(rtm);
+ else if (rtm->rtm_addrs &~ RTA_IGN) {
+ (void) printf("sockaddrs: ");
+ bprintf(stdout, rtm->rtm_addrs, addrnames);
+ putchar('\n');
+ }
+#undef RTA_IGN
+}
+
+void
+pmsg_common(rtm)
+ register struct rt_msghdr *rtm;
+{
+ (void) printf("\nlocks: ");
+ bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames);
+ (void) printf(" inits: ");
+ bprintf(stdout, rtm->rtm_inits, metricnames);
+ pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
+}
+
+void
+pmsg_addrs(cp, addrs)
+ char *cp;
+ int addrs;
+{
+ register struct sockaddr *sa;
+ int i;
+
+ if (addrs == 0)
+ return;
+ (void) printf("\nsockaddrs: ");
+ bprintf(stdout, addrs, addrnames);
+ (void) putchar('\n');
+ for (i = 1; i; i <<= 1)
+ if (i & addrs) {
+ sa = (struct sockaddr *)cp;
+ (void) printf(" %s", routename(sa));
+ ADVANCE(cp, sa);
+ }
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+void
+bprintf(fp, b, s)
+ register FILE *fp;
+ register int b;
+ register u_char *s;
+{
+ register int i;
+ int gotsome = 0;
+
+ if (b == 0)
+ return;
+ while (i = *s++) {
+ if (b & (1 << (i-1))) {
+ if (gotsome == 0)
+ i = '<';
+ else
+ i = ',';
+ (void) putc(i, fp);
+ gotsome = 1;
+ for (; (i = *s) > 32; s++)
+ (void) putc(i, fp);
+ } else
+ while (*s > 32)
+ s++;
+ }
+ if (gotsome)
+ (void) putc('>', fp);
+}
+
+int
+keyword(cp)
+ char *cp;
+{
+ register struct keytab *kt = keywords;
+
+ while (kt->kt_cp && strcmp(kt->kt_cp, cp))
+ kt++;
+ return kt->kt_i;
+}
+
+void
+sodump(su, which)
+ register sup su;
+ char *which;
+{
+ switch (su->sa.sa_family) {
+ case AF_LINK:
+ (void) printf("%s: link %s; ",
+ which, link_ntoa(&su->sdl));
+ break;
+ case AF_ISO:
+ (void) printf("%s: iso %s; ",
+ which, iso_ntoa(&su->siso.siso_addr));
+ break;
+ case AF_INET:
+ (void) printf("%s: inet %s; ",
+ which, inet_ntoa(su->sin.sin_addr));
+ break;
+ case AF_NS:
+ (void) printf("%s: xns %s; ",
+ which, ns_ntoa(su->sns.sns_addr));
+ break;
+ }
+ (void) fflush(stdout);
+}
+
+/* States*/
+#define VIRGIN 0
+#define GOTONE 1
+#define GOTTWO 2
+/* Inputs */
+#define DIGIT (4*0)
+#define END (4*1)
+#define DELIM (4*2)
+
+void
+sockaddr(addr, sa)
+ register char *addr;
+ register struct sockaddr *sa;
+{
+ register char *cp = (char *)sa;
+ int size = sa->sa_len;
+ char *cplim = cp + size;
+ register int byte = 0, state = VIRGIN, new;
+
+ bzero(cp, size);
+ cp++;
+ do {
+ if ((*addr >= '0') && (*addr <= '9')) {
+ new = *addr - '0';
+ } else if ((*addr >= 'a') && (*addr <= 'f')) {
+ new = *addr - 'a' + 10;
+ } else if ((*addr >= 'A') && (*addr <= 'F')) {
+ new = *addr - 'A' + 10;
+ } else if (*addr == 0)
+ state |= END;
+ else
+ state |= DELIM;
+ addr++;
+ switch (state /* | INPUT */) {
+ case GOTTWO | DIGIT:
+ *cp++ = byte; /*FALLTHROUGH*/
+ case VIRGIN | DIGIT:
+ state = GOTONE; byte = new; continue;
+ case GOTONE | DIGIT:
+ state = GOTTWO; byte = new + (byte << 4); continue;
+ default: /* | DELIM */
+ state = VIRGIN; *cp++ = byte; byte = 0; continue;
+ case GOTONE | END:
+ case GOTTWO | END:
+ *cp++ = byte; /* FALLTHROUGH */
+ case VIRGIN | END:
+ break;
+ }
+ break;
+ } while (cp < cplim);
+ sa->sa_len = cp - (char *)sa;
+}
diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile
new file mode 100644
index 000000000000..965c52c6b4be
--- /dev/null
+++ b/sbin/savecore/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 4/17/94
+
+PROG= savecore
+SRCS= savecore.c zopen.c
+MAN8= savecore.0
+.PATH: ${.CURDIR}/../../usr.bin/compress
+
+.include <bsd.prog.mk>
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
new file mode 100644
index 000000000000..aabee53db700
--- /dev/null
+++ b/sbin/savecore/savecore.8
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)savecore.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt SAVECORE 8
+.Os BSD 4
+.Sh NAME
+.Nm savecore
+.Nd "save a core dump of the operating system"
+.Sh SYNOPSIS
+.Nm savecore
+.Fl c
+.Nm savecore
+.Op Fl fvz
+.Op Fl N Ar system
+.Ar directory
+.Sh DESCRIPTION
+.Nm Savecore
+copies the currently running kernel and its associated core dump into
+.Fa directory ,
+and enters a reboot message and information about the core dump into
+the system log.
+.Pp
+The options are as follows:
+.Bl -tag -width directory
+.It Fl c
+Clears the dump, so that future invocations of
+.Nm savecore
+will ignore it.
+.It Fl f
+Forces a dump to be taken even if the dump doesn't appear correct or there
+is insufficient disk space.
+.It Fl N
+Use
+.Ar system
+as the kernel instead of the default ``/vmunix''.
+.It Fl v
+Prints out some additional debugging information.
+.It Fl z
+Compresses the core dump and kernel (see
+.Xr compress 1 ).
+.El
+.Pp
+.Nm Savecore
+checks the core dump in various ways to make sure that it is current and
+that it corresponds to the currently running system.
+If it passes these checks, it saves the core image in
+.Ar directory Ns Pa /vmcore.#
+and the system in
+.Ar directory Ns Pa /vmunix.#
+The ``#'' is the number from the first line of the file
+.Ar directory Ns Pa /bounds ,
+and it is incremented and stored back into the file each time
+.Nm savecore
+successfully runs.
+.Pp
+.Nm Savecore
+also checks the available disk space before attempting to make the copies.
+If there is insufficient disk space in the filesystem containing
+.Ar directory ,
+or if the file
+.Ar directory Ns Pa /minfree
+exists and the number of free kilobytes (for non-superusers) in the
+filesystem after the copies were made would be less than the number
+in the first line of this file, the copies are not attempted.
+.Pp
+If
+.Nm savecore
+successfully copies the kernel and the core dump, the core dump is cleared
+so that future invocations of
+.Nm savecore
+will ignore it.
+.Pp
+.Nm Savecore
+is meant to be called near the end of the initialization file
+.Pa /etc/rc
+(see
+.Xr rc 8 ) .
+.Sh FILES
+.Bl -tag -width /vmunixxx -compact
+.It Pa /vmunix
+current
+.Tn UNIX
+.El
+.Sh BUGS
+The minfree code does not consider the effect of compression.
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.1 .
diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c
new file mode 100644
index 000000000000..6e34924be3da
--- /dev/null
+++ b/sbin/savecore/savecore.c
@@ -0,0 +1,649 @@
+/*-
+ * Copyright (c) 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1986, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)savecore.c 8.3 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#define ok(number) ((number) - KERNBASE)
+
+struct nlist current_nl[] = { /* Namelist for currently running system. */
+#define X_DUMPDEV 0
+ { "_dumpdev" },
+#define X_DUMPLO 1
+ { "_dumplo" },
+#define X_TIME 2
+ { "_time" },
+#define X_DUMPSIZE 3
+ { "_dumpsize" },
+#define X_VERSION 4
+ { "_version" },
+#define X_PANICSTR 5
+ { "_panicstr" },
+#define X_DUMPMAG 6
+ { "_dumpmag" },
+ { "" },
+};
+int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
+int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
+
+struct nlist dump_nl[] = { /* Name list for dumped system. */
+ { "_dumpdev" }, /* Entries MUST be the same as */
+ { "_dumplo" }, /* those in current_nl[]. */
+ { "_time" },
+ { "_dumpsize" },
+ { "_version" },
+ { "_panicstr" },
+ { "_dumpmag" },
+ { "" },
+};
+
+/* Types match kernel declarations. */
+long dumplo; /* where dump starts on dumpdev */
+int dumpmag; /* magic number in dump */
+int dumpsize; /* amount of memory dumped */
+
+char *vmunix;
+char *dirname; /* directory to save dumps in */
+char *ddname; /* name of dump device */
+dev_t dumpdev; /* dump device */
+int dumpfd; /* read/write descriptor on block dev */
+time_t now; /* current date */
+char panic_mesg[1024];
+int panicstr;
+char vers[1024];
+
+int clear, compress, force, verbose; /* flags */
+
+void check_kmem __P((void));
+int check_space __P((void));
+void clear_dump __P((void));
+int Create __P((char *, int));
+int dump_exists __P((void));
+char *find_dev __P((dev_t, int));
+int get_crashtime __P((void));
+void kmem_setup __P((void));
+void log __P((int, char *, ...));
+void Lseek __P((int, off_t, int));
+int Open __P((char *, int rw));
+int Read __P((int, void *, int));
+char *rawname __P((char *s));
+void save_core __P((void));
+void usage __P((void));
+void Write __P((int, void *, int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+
+ openlog("savecore", LOG_PERROR, LOG_DAEMON);
+
+ while ((ch = getopt(argc, argv, "cdfNvz")) != EOF)
+ switch(ch) {
+ case 'c':
+ clear = 1;
+ break;
+ case 'd': /* Not documented. */
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'N':
+ vmunix = optarg;
+ break;
+ case 'z':
+ compress = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!clear) {
+ if (argc != 1 && argc != 2)
+ usage();
+ dirname = argv[0];
+ }
+ if (argc == 2)
+ vmunix = argv[1];
+
+ (void)time(&now);
+ kmem_setup();
+
+ if (clear) {
+ clear_dump();
+ exit(0);
+ }
+
+ if (!dump_exists() && !force)
+ exit(1);
+
+ check_kmem();
+
+ if (panicstr)
+ syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
+ else
+ syslog(LOG_ALERT, "reboot");
+
+ if ((!get_crashtime() || !check_space()) && !force)
+ exit(1);
+
+ save_core();
+
+ clear_dump();
+ exit(0);
+}
+
+void
+kmem_setup()
+{
+ FILE *fp;
+ int kmem, i;
+ char *dump_sys;
+
+ /*
+ * Some names we need for the currently running system, others for
+ * the system that was running when the dump was made. The values
+ * obtained from the current system are used to look for things in
+ * /dev/kmem that cannot be found in the dump_sys namelist, but are
+ * presumed to be the same (since the disk partitions are probably
+ * the same!)
+ */
+ if ((nlist(_PATH_UNIX, current_nl)) == -1)
+ syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno));
+ for (i = 0; cursyms[i] != -1; i++)
+ if (current_nl[cursyms[i]].n_value == 0) {
+ syslog(LOG_ERR, "%s: %s not in namelist",
+ _PATH_UNIX, current_nl[cursyms[i]].n_name);
+ exit(1);
+ }
+
+ dump_sys = vmunix ? vmunix : _PATH_UNIX;
+ if ((nlist(dump_sys, dump_nl)) == -1)
+ syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno));
+ for (i = 0; dumpsyms[i] != -1; i++)
+ if (dump_nl[dumpsyms[i]].n_value == 0) {
+ syslog(LOG_ERR, "%s: %s not in namelist",
+ dump_sys, dump_nl[dumpsyms[i]].n_name);
+ exit(1);
+ }
+
+ kmem = Open(_PATH_KMEM, O_RDONLY);
+ Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET);
+ (void)Read(kmem, &dumpdev, sizeof(dumpdev));
+ if (dumpdev == NODEV) {
+ syslog(LOG_WARNING, "no core dump (no dumpdev)");
+ exit(1);
+ }
+ Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
+ (void)Read(kmem, &dumplo, sizeof(dumplo));
+ if (verbose)
+ (void)printf("dumplo = %d (%d * %d)\n",
+ dumplo, dumplo/DEV_BSIZE, DEV_BSIZE);
+ Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
+ (void)Read(kmem, &dumpmag, sizeof(dumpmag));
+ dumplo *= DEV_BSIZE;
+ ddname = find_dev(dumpdev, S_IFBLK);
+ dumpfd = Open(ddname, O_RDWR);
+ fp = fdopen(kmem, "r");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
+ exit(1);
+ }
+ if (vmunix)
+ return;
+ (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
+ (void)fgets(vers, sizeof(vers), fp);
+
+ /* Don't fclose(fp), we use dumpfd later. */
+}
+
+void
+check_kmem()
+{
+ register char *cp;
+ FILE *fp;
+ char core_vers[1024];
+
+ fp = fdopen(dumpfd, "r");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "%s: fdopen: %m", ddname);
+ exit(1);
+ }
+ fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
+ fgets(core_vers, sizeof(core_vers), fp);
+ if (strcmp(vers, core_vers) && vmunix == 0)
+ syslog(LOG_WARNING,
+ "warning: %s version mismatch:\n\t%s\nand\t%s\n",
+ _PATH_UNIX, vers, core_vers);
+ (void)fseek(fp,
+ (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
+ (void)fread(&panicstr, sizeof(panicstr), 1, fp);
+ if (panicstr) {
+ (void)fseek(fp, dumplo + ok(panicstr), L_SET);
+ cp = panic_mesg;
+ do
+ *cp = getc(fp);
+ while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
+ }
+ /* Don't fclose(fp), we use dumpfd later. */
+}
+
+void
+clear_dump()
+{
+ long newdumplo;
+
+ newdumplo = 0;
+ Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
+ Write(dumpfd, &newdumplo, sizeof(newdumplo));
+}
+
+int
+dump_exists()
+{
+ int newdumpmag;
+
+ Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
+ (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag));
+ if (newdumpmag != dumpmag) {
+ if (verbose)
+ syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
+ newdumpmag, dumpmag);
+ syslog(LOG_WARNING, "no core dump");
+ return (0);
+ }
+ return (1);
+}
+
+char buf[1024 * 1024];
+
+void
+save_core()
+{
+ register FILE *fp;
+ register int bounds, ifd, nr, nw, ofd;
+ char *rawp, path[MAXPATHLEN];
+
+ /*
+ * Get the current number and update the bounds file. Do the update
+ * now, because may fail later and don't want to overwrite anything.
+ */
+ (void)snprintf(path, sizeof(path), "%s/bounds", dirname);
+ if ((fp = fopen(path, "r")) == NULL)
+ goto err1;
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ if (ferror(fp))
+err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
+ bounds = 0;
+ } else
+ bounds = atoi(buf);
+ if (fp != NULL)
+ (void)fclose(fp);
+ if ((fp = fopen(path, "w")) == NULL)
+ syslog(LOG_ERR, "%s: %m", path);
+ else {
+ (void)fprintf(fp, "%d\n", bounds + 1);
+ (void)fclose(fp);
+ }
+ (void)fclose(fp);
+
+ /* Create the core file. */
+ (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
+ dirname, bounds, compress ? ".Z" : "");
+ if (compress) {
+ if ((fp = zopen(path, "w", 0)) == NULL) {
+ syslog(LOG_ERR, "%s: %s", path, strerror(errno));
+ exit(1);
+ }
+ } else
+ ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ /* Open the raw device. */
+ rawp = rawname(ddname);
+ if ((ifd = open(rawp, O_RDONLY)) == -1) {
+ syslog(LOG_WARNING, "%s: %m; using block device", rawp);
+ ifd = dumpfd;
+ }
+
+ /* Read the dump size. */
+ Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
+ (void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
+
+ /* Seek to the start of the core. */
+ Lseek(ifd, (off_t)dumplo, L_SET);
+
+ /* Copy the core file. */
+ dumpsize *= NBPG;
+ syslog(LOG_NOTICE, "writing %score to %s",
+ compress ? "compressed " : "", path);
+ for (; dumpsize > 0; dumpsize -= nr) {
+ (void)printf("%6dK\r", dumpsize / 1024);
+ (void)fflush(stdout);
+ nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
+ if (nr <= 0) {
+ if (nr == 0)
+ syslog(LOG_WARNING,
+ "WARNING: EOF on dump device");
+ else
+ syslog(LOG_ERR, "%s: %m", rawp);
+ goto err2;
+ }
+ if (compress)
+ nw = fwrite(buf, 1, nr, fp);
+ else
+ nw = write(ofd, buf, nr);
+ if (nw != nr) {
+ syslog(LOG_ERR, "%s: %s",
+ path, strerror(nw == 0 ? EIO : errno));
+err2: syslog(LOG_WARNING,
+ "WARNING: vmcore may be incomplete");
+ (void)printf("\n");
+ exit(1);
+ }
+ }
+ (void)printf("\n");
+ (void)close(ifd);
+ if (compress)
+ (void)fclose(fp);
+ else
+ (void)close(ofd);
+
+ /* Copy the kernel. */
+ ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY);
+ (void)snprintf(path, sizeof(path), "%s/vmunix.%d%s",
+ dirname, bounds, compress ? ".Z" : "");
+ if (compress) {
+ if ((fp = zopen(path, "w", 0)) == NULL) {
+ syslog(LOG_ERR, "%s: %s", path, strerror(errno));
+ exit(1);
+ }
+ } else
+ ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ syslog(LOG_NOTICE, "writing %skernel to %s",
+ compress ? "compressed " : "", path);
+ while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
+ if (compress)
+ nw = fwrite(buf, 1, nr, fp);
+ else
+ nw = write(ofd, buf, nr);
+ if (nw != nr) {
+ syslog(LOG_ERR, "%s: %s",
+ path, strerror(nw == 0 ? EIO : errno));
+ syslog(LOG_WARNING,
+ "WARNING: vmunix may be incomplete");
+ exit(1);
+ }
+ }
+ if (nr < 0) {
+ syslog(LOG_ERR, "%s: %s",
+ vmunix ? vmunix : _PATH_UNIX, strerror(errno));
+ syslog(LOG_WARNING,
+ "WARNING: vmunix may be incomplete");
+ exit(1);
+ }
+ if (compress)
+ (void)fclose(fp);
+ else
+ (void)close(ofd);
+}
+
+char *
+find_dev(dev, type)
+ register dev_t dev;
+ register int type;
+{
+ register DIR *dfd;
+ struct dirent *dir;
+ struct stat sb;
+ char *dp, devname[MAXPATHLEN + 1];
+
+ if ((dfd = opendir(_PATH_DEV)) == NULL) {
+ syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
+ exit(1);
+ }
+ (void)strcpy(devname, _PATH_DEV);
+ while ((dir = readdir(dfd))) {
+ (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
+ if (lstat(devname, &sb)) {
+ syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
+ continue;
+ }
+ if ((sb.st_mode & S_IFMT) != type)
+ continue;
+ if (dev == sb.st_rdev) {
+ closedir(dfd);
+ if ((dp = strdup(devname)) == NULL) {
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(1);
+ }
+ return (dp);
+ }
+ }
+ closedir(dfd);
+ syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
+ exit(1);
+}
+
+char *
+rawname(s)
+ char *s;
+{
+ char *sl, name[MAXPATHLEN];
+
+ if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
+ syslog(LOG_ERR,
+ "can't make raw dump device name from %s", s);
+ return (s);
+ }
+ (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1);
+ if ((sl = strdup(name)) == NULL) {
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(1);
+ }
+ return (sl);
+}
+
+int
+get_crashtime()
+{
+ time_t dumptime; /* Time the dump was taken. */
+
+ Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
+ (void)Read(dumpfd, &dumptime, sizeof(dumptime));
+ if (dumptime == 0) {
+ if (verbose)
+ syslog(LOG_ERR, "dump time is zero");
+ return (0);
+ }
+ (void)printf("savecore: system went down at %s", ctime(&dumptime));
+#define LEEWAY (7 * SECSPERDAY)
+ if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
+ (void)printf("dump time is unreasonable\n");
+ return (0);
+ }
+ return (1);
+}
+
+int
+check_space()
+{
+ register FILE *fp;
+ char *tvmunix;
+ off_t minfree, spacefree, vmunixsize, needed;
+ struct stat st;
+ struct statfs fsbuf;
+ char buf[100], path[MAXPATHLEN];
+
+ tvmunix = vmunix ? vmunix : _PATH_UNIX;
+ if (stat(tvmunix, &st) < 0) {
+ syslog(LOG_ERR, "%s: %m", tvmunix);
+ exit(1);
+ }
+ vmunixsize = st.st_blocks * S_BLKSIZE;
+ if (statfs(dirname, &fsbuf) < 0) {
+ syslog(LOG_ERR, "%s: %m", dirname);
+ exit(1);
+ }
+ spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
+
+ (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
+ if ((fp = fopen(path, "r")) == NULL)
+ minfree = 0;
+ else {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ minfree = 0;
+ else
+ minfree = atoi(buf);
+ (void)fclose(fp);
+ }
+
+ needed = (dumpsize + vmunixsize) / 1024;
+ if (minfree > 0 && spacefree - needed < minfree) {
+ syslog(LOG_WARNING,
+ "no dump, not enough free space on device");
+ return (0);
+ }
+ if (spacefree - needed < minfree)
+ syslog(LOG_WARNING,
+ "dump performed, but free space threshold crossed");
+ return (1);
+}
+
+int
+Open(name, rw)
+ char *name;
+ int rw;
+{
+ int fd;
+
+ if ((fd = open(name, rw, 0)) < 0) {
+ syslog(LOG_ERR, "%s: %m", name);
+ exit(1);
+ }
+ return (fd);
+}
+
+int
+Read(fd, bp, size)
+ int fd, size;
+ void *bp;
+{
+ int nr;
+
+ nr = read(fd, bp, size);
+ if (nr != size) {
+ syslog(LOG_ERR, "read: %m");
+ exit(1);
+ }
+ return (nr);
+}
+
+void
+Lseek(fd, off, flag)
+ int fd, flag;
+ off_t off;
+{
+ off_t ret;
+
+ ret = lseek(fd, off, flag);
+ if (ret == -1) {
+ syslog(LOG_ERR, "lseek: %m");
+ exit(1);
+ }
+}
+
+int
+Create(file, mode)
+ char *file;
+ int mode;
+{
+ register int fd;
+
+ fd = creat(file, mode);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: %m", file);
+ exit(1);
+ }
+ return (fd);
+}
+
+void
+Write(fd, bp, size)
+ int fd, size;
+ void *bp;
+{
+ int n;
+
+ if ((n = write(fd, bp, size)) < size) {
+ syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
+ exit(1);
+ }
+}
+
+void
+usage()
+{
+ (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
+ exit(1);
+}
diff --git a/sbin/scsiformat/Makefile b/sbin/scsiformat/Makefile
new file mode 100644
index 000000000000..e46ee4c0869d
--- /dev/null
+++ b/sbin/scsiformat/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 5.3 (Berkeley) 6/5/93
+
+PROG= scsiformat
+MAN8= scsiformat.0
+CFLAGS+=-I/sys
+
+.include <bsd.prog.mk>
diff --git a/sbin/scsiformat/scsiformat.8 b/sbin/scsiformat/scsiformat.8
new file mode 100644
index 000000000000..ff054bd6a170
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.8
@@ -0,0 +1,82 @@
+.\" Copyright (c) 1993 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)scsiformat.8 5.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt SCSIFORMAT 8
+.Os BSD 4
+.Sh NAME
+.Nm scsiformat
+.Nd format SCSI disks and show SCSI parameters
+.Sh SYNOPSIS
+.Nm scsiformat
+.Op Fl r
+.Op Fl p Ar page-control
+.Ar raw-device-name
+.Sh DESCRIPTION
+The
+.Nm scsiformat
+utility can be used to format SCSI disks
+on systems that support on-line SCSI formatting
+(currently this is limited to the HP300 and SPARC ports).
+It will also show the various parameters set in the SCSI controller.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl p
+Modify how the SCSI mode sense page query is constructed.
+By default,
+.Nm scsiformat
+asks for the current settings.
+The page-control flag is one of:
+.sp
+.Bl -tag -width XXX -compact
+.It Li c
+gets current settings.
+.It Li d
+gets default settings (those provided by the disk's manufacturer).
+.It Li s
+gets saved settings (those that persist across power cycles).
+.It Li v
+shows which parameters are variable.
+.El
+.It Fl r
+Don't format, instead display the disk parameters.
+.El
+.Sh DIAGNOSTICS
+These should mostly be self-explanatory.
+A copy of the SCSI standard (or a more readable derivative)
+may be useful, however.
+.Sh HISTORY
+The
+.Nm scsiformat
+utility first appeared in 4.4BSD.
diff --git a/sbin/scsiformat/scsiformat.c b/sbin/scsiformat/scsiformat.c
new file mode 100644
index 000000000000..a185a8ee154c
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.c
@@ -0,0 +1,664 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ *
+ * @(#)scsiformat.c 5.5 (Berkeley) 4/2/94
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)scsiformat.c 5.5 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <dev/scsi/scsi.h>
+#include <dev/scsi/disk.h>
+#include <dev/scsi/disktape.h>
+#include <dev/scsi/scsi_ioctl.h>
+
+#define COMPAT_HPSCSI
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int fd;
+char *device;
+
+void scsi_str __P((char *, char *, int));
+void do_command __P((int, struct scsi_cdb *, void *, int));
+void do_format __P((void));
+void print_capacity __P((void));
+void print_inquiry __P((void));
+void prflags __P((int, const char *));
+u_char *print_mode_page __P((u_char *));
+void print_mode_sense __P((void));
+void usage __P((void));
+
+#define N2(c, d) (((c) << 8) | (d))
+#define N3(b, c, d) (((b) << 16) | N2(c, d))
+#define N4(a, b, c, d) (((a) << 24) | N3(b, c, d))
+
+int sense_pctl;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ int ch, readonly;
+
+ readonly = 0;
+ sense_pctl = SCSI_MSENSE_PCTL_CUR;
+ while ((ch = getopt(argc, argv, "rp:")) != EOF) {
+ switch(ch) {
+ case 'r':
+ readonly = 1;
+ break;
+ case 'p': /* mode sense page control */
+ switch (*optarg) {
+ case 'c':
+ sense_pctl = SCSI_MSENSE_PCTL_CUR;
+ break;
+ case 'd':
+ sense_pctl = SCSI_MSENSE_PCTL_DFLT;
+ break;
+ case 's':
+ sense_pctl = SCSI_MSENSE_PCTL_SAVED;
+ break;
+ case 'v':
+ (void)printf(
+ "*** note: for variable parameters, 1-bit means ``can write here''\n");
+ sense_pctl = SCSI_MSENSE_PCTL_VAR;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ device = *argv;
+ fd = open(device, readonly ? O_RDONLY : O_RDWR, 0);
+ if (fd < 0) {
+ (void)fprintf(stderr,
+ "scsiformat: %s: %s\n", device, strerror(errno));
+ exit(1);
+ }
+ print_inquiry();
+ print_capacity();
+ print_mode_sense();
+
+ if (!readonly)
+ do_format();
+ exit(0);
+}
+
+/*
+ * Copy a counted string, trimming trailing blanks, and turning the
+ * result into a C-style string.
+ */
+void
+scsi_str(src, dst, len)
+ register char *src, *dst;
+ register int len;
+{
+
+ while (src[len - 1] == ' ') {
+ if (--len == 0) {
+ *dst = 0;
+ return;
+ }
+ }
+ bcopy(src, dst, len);
+ dst[len] = 0;
+}
+
+void
+print_inquiry()
+{
+ register struct scsi_inq_ansi *si;
+ int ver;
+ struct scsi_inquiry inqbuf;
+ char vendor[10], product[17], rev[5];
+ static struct scsi_cdb inq = {
+ CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0
+ };
+
+ do_command(fd, &inq, &inqbuf, sizeof(inqbuf));
+ (void)printf("%s: ", device);
+
+ ver = (inqbuf.si_version >> VER_ANSI_SHIFT) & VER_ANSI_MASK;
+ if (ver != 1 && ver != 2) {
+ (void)printf("type 0x%x, qual 0x%x, ver 0x%x (ansi %d)\n",
+ inqbuf.si_type, inqbuf.si_qual, inqbuf.si_version, ver);
+ return;
+ }
+ si = (struct scsi_inq_ansi *)&inqbuf;
+ switch (si->si_type & TYPE_TYPE_MASK) {
+
+ case TYPE_DAD:
+ (void)printf("(disk)");
+ break;
+
+ case TYPE_WORM:
+ (void)printf("(WORM)");
+ break;
+
+ case TYPE_ROM:
+ (void)printf("(CD-ROM)");
+ break;
+
+ case TYPE_MO:
+ (void)printf("(MO-DISK)");
+ break;
+
+ case TYPE_JUKEBOX:
+ (void)printf("(jukebox)");
+ break;
+
+ default:
+ (void)printf("(??)");
+ break;
+ }
+ scsi_str(si->si_vendor, vendor, sizeof(si->si_vendor));
+ scsi_str(si->si_product, product, sizeof(si->si_product));
+ scsi_str(si->si_rev, rev, sizeof(si->si_rev));
+ (void)printf(" %s %s rev %s:", vendor, product, rev);
+}
+
+void
+print_capacity()
+{
+ struct scsi_rc rc; /* for READ CAPACITY */
+ static struct scsi_cdb cap = { CMD_READ_CAPACITY };
+
+ do_command(fd, &cap, &rc, sizeof(rc));
+ (void)printf(" %d blocks of %d bytes each\n",
+ N4(rc.rc_lbah, rc.rc_lbahm, rc.rc_lbalm, rc.rc_lbal) + 1,
+ N4(rc.rc_blh, rc.rc_blhm, rc.rc_bllm, rc.rc_bll));
+}
+
+void
+print_mode_sense()
+{
+ register u_char *cp, *ep;
+ register struct scsi_ms_bd *bd;
+ register int n, i, l, len, bdlen;
+#ifdef TEN_BYTE_SENSE
+ struct {
+ struct scsi_ms10 ms;
+ u_char p[1023 - sizeof(struct scsi_ms10)];
+ } msbuf;
+ static struct scsi_cdb modesense = {
+ CMD_MODE_SENSE10, SCSI_MSENSE_DBD, 0, 0, 0, 0, 0,
+ sizeof(msbuf) >> 8, sizeof (msbuf), 0
+ };
+
+ CDB10(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL;
+ do_command(fd, &modesense, &msbuf, sizeof(msbuf));
+ len = N2(msbuf.ms.ms_lenh, msbuf.ms.ms_lenl);
+ bdlen = N2(msbuf.ms.ms_bdlh, msbuf.ms.ms_bdll);
+#else
+ struct {
+ struct scsi_ms6 ms;
+ u_char p[255 - sizeof(struct scsi_ms6)];
+ } msbuf;
+ static struct scsi_cdb modesense = {
+ CMD_MODE_SENSE6, 0, 0, 0, sizeof(msbuf), 0
+ };
+
+ CDB6(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL;
+ do_command(fd, &modesense, &msbuf, sizeof(msbuf));
+ len = msbuf.ms.ms_len;
+ bdlen = msbuf.ms.ms_bdl;
+#endif
+ (void)printf("\n%d bytes of mode sense data. ", len);
+ (void)printf("medium type 0x%x, %swrite protected\n",
+ msbuf.ms.ms_mt, msbuf.ms.ms_dsp & SCSI_MS_DSP_WP ? "" : "not ");
+ if ((n = bdlen) != 0) {
+ bd = (struct scsi_ms_bd *)msbuf.p;
+ for (n /= sizeof(*bd); --n >= 0; bd++) {
+ (void)printf("\tdensity code 0x%x, ", bd->bd_dc);
+ i = N3(bd->bd_nbh, bd->bd_nbm, bd->bd_nbl);
+ l = N3(bd->bd_blh, bd->bd_blm, bd->bd_bll);
+ if (i)
+ (void)printf("%d blocks of length %d\n", i, l);
+ else
+ (void)printf("all blocks of length %d\n", l);
+ }
+ }
+ /*
+ * Sense header lengths includes the sense header, while mode page
+ * lengths do not ... let's hear it for consistency!
+ */
+ cp = msbuf.p + bdlen;
+ ep = msbuf.p + len - sizeof(msbuf.ms);
+ while (cp < ep)
+ cp = print_mode_page(cp);
+}
+
+void
+prflags(v, cp)
+ int v;
+ register const char *cp;
+{
+ register const char *np;
+ char f, sep;
+
+ for (sep = '<'; (f = *cp++) != 0; cp = np) {
+ for (np = cp; *np >= ' ';)
+ np++;
+ if ((v & (1 << (f - 1))) == 0)
+ continue;
+ printf("%c%.*s", sep, np - cp, cp);
+ sep = ',';
+ }
+ if (sep != '<')
+ putchar('>');
+}
+
+static char *
+cache_policy(x)
+ int x;
+{
+ static char rsvd[30];
+
+ switch (x) {
+
+ case SCSI_CACHE_DEFAULT:
+ return ("default");
+
+ case SCSI_CACHE_KEEPPF:
+ return ("toss cmd data, save prefetch");
+
+ case SCSI_CACHE_KEEPCMD:
+ return ("toss prefetch data, save cmd");
+
+ default:
+ (void)sprintf(rsvd, "reserved %d", x);
+ return (rsvd);
+ }
+ /* NOTREACHED */
+}
+
+u_char *
+print_mode_page(cp)
+ u_char *cp;
+{
+ register struct scsi_ms_page_hdr *mp;
+ int len, code, i;
+ u_char *tp;
+ const char *s;
+
+ mp = (struct scsi_ms_page_hdr *)cp;
+ code = mp->mp_psc & SCSI_MS_PC_MASK;
+ len = mp->mp_len;
+ (void)printf("\npage type %d%s (%d bytes): ",
+ code, mp->mp_psc & SCSI_MS_MP_SAVEABLE ? " (saveable)" : "", len);
+ switch (code) {
+
+ case SCSI_MS_PC_RWERRREC:
+#define rw ((struct scsi_page_rwerrrec *)(mp + 1))
+ (void)printf("Read/Write Error Recovery parameters.\n");
+ (void)printf("\tflags = 0x%x", rw->rw_flags);
+ prflags(rw->rw_flags,
+ "\10AWRE\7ARRE\6TB\5RC\4EER\3PER\2DTE\1DCR");
+ (void)printf(",\n\t%d read retries, %d correction span bits,\n",
+ rw->rw_read_retry, rw->rw_corr_span);
+ (void)printf("\t%d head offsets, %d data strobe offsets%s\n",
+ rw->rw_hd_off, rw->rw_ds_off, len > 6 ? "," : ".");
+ if (len <= 6)
+ break;
+ (void)printf("\t%d write retries, ", rw->rw_write_retry);
+ i = N2(rw->rw_rtlh, rw->rw_rtll);
+ if (i != 0xffff)
+ (void)printf("%d", i);
+ else
+ (void)printf("no");
+ (void)printf(" recovery time limit.\n");
+ break;
+#undef rw
+
+ case SCSI_MS_PC_DR:
+#define dr ((struct scsi_page_dr *)(mp + 1))
+ (void)printf("Disconnect/Reconnect control.\n");
+ (void)printf("\tbuffer full ratio %d, buffer empty ratio %d,\n",
+ dr->dr_full, dr->dr_empty);
+ (void)printf("\ttime limits: %d bus inactivity, ",
+ N2(dr->dr_inacth, dr->dr_inactl));
+ (void)printf("%d disconnect, %d connect.\n",
+ N2(dr->dr_disconh, dr->dr_disconl),
+ N2(dr->dr_conh, dr->dr_conl));
+ (void)printf("\tmaximum burst size %d,\n",
+ N2(dr->dr_bursth, dr->dr_burstl));
+ switch (dr->dr_dtdc & SCSI_DR_DTDC_MASK) {
+ case SCSI_DR_DTDC_NONE:
+ s = "never";
+ break;
+ case SCSI_DR_DTDC_NOTDATA:
+ s = "during data transfer";
+ break;
+ case SCSI_DR_DTDC_RSVD:
+ s = "???";
+ break;
+ case SCSI_DR_DTDC_NOTD2:
+ s = "during and after data transfer";
+ break;
+ }
+ (void)printf("\tsuppress disconnect %s.\n", s);
+ break;
+#undef dr
+
+ case SCSI_MS_PC_FMT:
+#define fmt ((struct scsi_page_fmt *)(mp + 1))
+ (void)printf("Format parameters.\n");
+ (void)printf("\t%d tracks/zone, %d alt.sect./zone, ",
+ N2(fmt->fmt_tpzh, fmt->fmt_tpzl),
+ N2(fmt->fmt_aspzh, fmt->fmt_aspzl));
+ (void)printf("%d alt.tracks/zone,\n\t%d alt.tracks/vol., ",
+ N2(fmt->fmt_atpzh, fmt->fmt_atpzl),
+ N2(fmt->fmt_atpvh, fmt->fmt_atpvl));
+ (void)printf("%d sectors/track, %d bytes/phys.sector,\n",
+ N2(fmt->fmt_spth, fmt->fmt_sptl),
+ N2(fmt->fmt_dbppsh, fmt->fmt_dbppsl));
+ (void)printf("\tinterleave %d, track skew %d, cyl.skew %d,\n",
+ N2(fmt->fmt_ilh, fmt->fmt_ill),
+ N2(fmt->fmt_tsfh, fmt->fmt_tsfl),
+ N2(fmt->fmt_csfh, fmt->fmt_csfl));
+ (void)printf("\tdrive flags 0x%x", fmt->fmt_flags);
+ prflags(fmt->fmt_flags, "\10SSEC\7HSEC\6RMB\5SURF");
+ (void)printf(".\n");
+ break;
+#undef fmt
+
+ case SCSI_MS_PC_RDGEOM:
+#define rd ((struct scsi_page_rdgeom *)(mp + 1))
+ (void)printf("Disk Geometry parameters.\n");
+ (void)printf("\t%d cylinders, %d heads,\n",
+ N3(rd->rd_ncylh, rd->rd_ncylm, rd->rd_ncyll),
+ rd->rd_nheads);
+ (void)printf("\tstart write precompensation at cyl %d,\n",
+ N3(rd->rd_wpcylh, rd->rd_wpcylm, rd->rd_wpcyll));
+ (void)printf("\tstart reduced write current at cyl %d,\n",
+ N3(rd->rd_rwcylh, rd->rd_rwcylm, rd->rd_rwcyll));
+ (void)printf("\tseek step rate %f us, landing zone cyl %d,\n",
+ N2(rd->rd_steph, rd->rd_stepl) * 0.1,
+ N3(rd->rd_lcylh, rd->rd_lcylm, rd->rd_lcyll));
+ switch (rd->rd_rpl & SCSI_RD_RPL_MASK) {
+ case SCSI_RD_RPL_NONE:
+ s = "disabled or unsupported";
+ break;
+ case SCSI_RD_RPL_SLAVE:
+ s = "slave";
+ break;
+ case SCSI_RD_RPL_MASTER:
+ s = "master";
+ break;
+ case SCSI_RD_RPL_MCONTROL:
+ s = "master control";
+ break;
+ }
+ (void)printf("\trotational synch %s, offset %d/256%s\n",
+ s, rd->rd_roff, len > 18 ? "," : ".");
+ if (len > 18)
+ (void)printf("\trotation %d rpm.\n",
+ N2(rd->rd_rpmh, rd->rd_rpml));
+ break;
+#undef rd
+
+ case SCSI_MS_PC_VERRREC:
+#define v ((struct scsi_page_verrrec *)(mp + 1))
+ (void)printf("Verify Error Recovery parameters.\n");
+ (void)printf("\tflags = 0x%x", v->v_flags);
+ prflags(v->v_flags, "\4EER\3PER\2DTE\1DCR");
+ (void)printf(",\n\t%d verify retries, %d %s span bits,\n\t",
+ v->v_verify_retry, v->v_corr_span, "correction");
+ (void)printf("%d recovery time limit.\n",
+ N2(v->v_rtlh, v->v_rtll));
+ break;
+#undef v
+
+ case SCSI_MS_PC_CACHE:
+#define cache ((struct scsi_page_cache *)(mp + 1))
+ (void)printf("Caching Page.\n");
+ (void)printf("\tflags = 0x%x", cache->cache_flags);
+ prflags(cache->cache_flags, "\3WCE\2MF\1RCD");
+ (void)printf(
+ ",\n\tread retention = %s, write retention = %s,\n",
+ cache_policy(SCSI_CACHE_RDPOLICY(cache->cache_reten)),
+ cache_policy(SCSI_CACHE_WRPOLICY(cache->cache_reten)));
+ (void)printf("\tdisable prefetch transfer length = %d,\n",
+ N2(cache->cache_dptlh, cache->cache_dptll));
+ (void)printf("\tmin prefetch = %d, max prefetch = %d, ",
+ N2(cache->cache_minpfh, cache->cache_minpfl),
+ N2(cache->cache_maxpfh, cache->cache_maxpfl));
+ (void)printf("max prefetch ceiling = %d.\n",
+ N2(cache->cache_mpch, cache->cache_mpcl));
+ break;
+#undef cache
+
+ case SCSI_MS_PC_CTLMODE:
+#define cm ((struct scsi_page_ctlmode *)(mp + 1))
+ (void)printf("Control Mode Page.\n");
+ (void)printf("\t%s report log-activity error conditions,\n",
+ cm->cm_rlec & SCSI_CM_RLEC ? "do" : "do not");
+ (void)printf("\tqueue algorithm modifier = %d, flags = 0x%x",
+ SCSI_CM_QMOD(cm->cm_qctl),
+ cm->cm_qctl & (SCSI_CM_QERR|SCSI_CM_DQUE));
+ prflags(cm->cm_qctl, "\2QERR\1DQUE");
+ (void)printf(",\n\tECA/AEN flags = 0x%x", cm->cm_ecaaen);
+ prflags(cm->cm_ecaaen, "\10ECA\3RAENP\2UUAENP\1EAENP");
+ (void)printf(", AEN holdoff period = %d ms.\n",
+ N2(cm->cm_aenholdh, cm->cm_aenholdl));
+ break;
+#undef cm
+
+ /*
+ * Vendor Unique, but what the heck.
+ */
+ case SCSI_MS_PC_CDCCACHECTL:
+#define ccm ((struct scsi_page_CDCcachectlmode *)(mp + 1))
+ (void)printf("CDC-specific Cache Control Mode Page.\n");
+ (void)printf("\tflags = 0x%x", ccm->ccm_flags);
+ prflags(ccm->ccm_flags, "\7WIE\5ENABLE");
+ (void)printf(", table size = %d, prefetch threshold = %d\n",
+ SCSI_CDC_CCM_TBLSZ(ccm->ccm_flags),
+ ccm->ccm_pfthresh);
+ (void)printf("\tmaximum %s = %d, maximum %s = %d,\n",
+ "threshold", ccm->ccm_maxthresh,
+ "prefetch multiplier", ccm->ccm_maxpfmult);
+ (void)printf("\tminimum %s = %d, minimum %s = %d.\n",
+ "threshold", ccm->ccm_minthresh,
+ "prefetch multiplier", ccm->ccm_minpfmult);
+ break;
+#undef ccm
+
+ default:
+ (void)printf("Unknown page type.");
+ for (tp = cp + sizeof(*mp), i = 0; i < len; ++i) {
+ if ((i & 7) == 0)
+ (void)printf("\n\t%2d: ", i);
+ (void)printf(" %02x", *tp++);
+ }
+ (void)printf(".\n");
+ break;
+ }
+ return (cp + sizeof(*mp) + len);
+}
+
+void
+pr_sense(fd)
+ int fd;
+{
+ static struct scsi_fmt_sense s;
+ register struct scsi_sense *sn;
+
+ if (ioctl(fd, SDIOCSENSE, &s) < 0)
+ (void)fprintf(stderr,
+ "scsiformat: SDIOCSENSE: %s\n", strerror(errno));
+
+ (void)printf("scsi status 0x%x", s.status);
+ if (s.status & STS_CHECKCOND) {
+ sn = (struct scsi_sense *)s.sense;
+
+ (void)printf(" sense class %d, code %d",
+ SENSE_ECLASS(sn), SENSE_ECODE(sn));
+ if (SENSE_ISXSENSE(sn)) {
+ (void)printf(", key %d", XSENSE_KEY(sn));
+ if (XSENSE_IVALID(sn))
+ (void)printf(", blk %d", XSENSE_INFO(sn));
+ }
+ }
+ (void)printf("\n");
+}
+
+void
+do_format()
+{
+ struct {
+ struct scsi_ms6 ms; /* mode select header */
+ struct scsi_ms_bd bd; /* block descriptor */
+ struct scsi_ms_page_hdr mp; /* ctl mode page hdr */
+ struct scsi_page_ctlmode cm; /* ctl mode page */
+ u_char pad[4]; /* ??? */
+ } msel;
+ u_char fmtbuf[128];
+ static struct scsi_cdb modeselect = {
+ CMD_MODE_SELECT6,
+ SCSI_MSEL_SCSI2_DATA | SCSI_MSEL_SAVEPAGES, 0, 0,
+ sizeof(msel), 0
+ };
+ static struct scsi_cdb format = { CMD_FORMAT_UNIT };
+
+ /* want mostly 0s; set them all zero here */
+ bzero(&msel, sizeof(msel));
+
+ /* one block descriptor */
+ msel.ms.ms_bdl = sizeof(struct scsi_ms_bd);
+
+ /* block length = 512 bytes */
+ msel.bd.bd_blm = 512 / 256;
+ msel.bd.bd_bll = 512 % 256;
+
+ /*
+ * In the following, the mystery pad region is copied from
+ * the original driver. I have no idea what it is for.
+ * (Anyone got SCSI-2 documents?)
+ */
+
+ /* mode page parameters: report log-activity exception conditions */
+ msel.mp.mp_psc = SCSI_MS_PC_CTLMODE;
+ msel.mp.mp_len = sizeof(msel.cm) + sizeof(msel.pad);
+ msel.cm.cm_rlec = SCSI_CM_RLEC;
+
+ do_command(fd, &modeselect, &msel, sizeof(msel));
+
+ bzero(fmtbuf, sizeof(fmtbuf));
+ do_command(fd, &format, fmtbuf, sizeof(fmtbuf));
+}
+
+void
+do_command(fd, cdb, buf, len)
+ int fd;
+ struct scsi_cdb *cdb;
+ void *buf;
+ int len;
+{
+ static int on = 1, off = 0;
+ int user, ret;
+
+ bzero(buf, len);
+ if (ioctl(fd, SDIOCSFORMAT, &on) < 0) {
+ (void)fprintf(stderr,
+ "scsiformat: SDIOCSFORMAT (on): %s\n", strerror(errno));
+ if (ioctl(fd, SDIOCGFORMAT, &user) == 0 && user != 0)
+ (void)fprintf(stderr, "scsiformat: pid %d has it\n",
+ user);
+ return;
+ }
+ ret = ioctl(fd, SDIOCSCSICOMMAND, cdb);
+#ifdef COMPAT_HPSCSI
+ if (ret < 0) {
+ static const char scsicmdlen[8] = { 6, 10, 0, 0, 0, 12, 0, 0 };
+#define SCSICMDLEN(cmd) scsicmdlen[(cmd) >> 5]
+ struct scsi_fmt_cdb {
+ int len;
+ u_char cdb[28];
+ } sc;
+#define OSDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb)
+
+ sc.len = SCSICMDLEN(cdb->cdb_bytes[0]);
+ bcopy(cdb->cdb_bytes, sc.cdb, sc.len);
+ ret = ioctl(fd, OSDIOCSCSICOMMAND, &sc);
+ }
+#endif
+ if (ret < 0)
+ (void)fprintf(stderr,
+ "scsiformat: SDIOCSCSICOMMAND: %s\n", strerror(errno));
+ else if (read(fd, buf, len) < 0) {
+ (void)fprintf(stderr,
+ "scsiformat: read: %s\n", strerror(errno));
+ pr_sense(fd);
+ }
+
+ if (ioctl(fd, SDIOCSFORMAT, &off) < 0)
+ (void)fprintf(stderr,
+ "scsiformat: SDIOCSFORMAT (off): %s\n", strerror(errno));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: scsiformat [-r] [-p c|d|s|v] device\n");
+ exit(1);
+}
diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile
new file mode 100644
index 000000000000..19122f21997c
--- /dev/null
+++ b/sbin/shutdown/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= shutdown
+MAN8= shutdown.0
+BINOWN= root
+BINGRP= operator
+BINMODE=4550
+
+.include <bsd.prog.mk>
diff --git a/sbin/shutdown/pathnames.h b/sbin/shutdown/pathnames.h
new file mode 100644
index 000000000000..9d0583848a16
--- /dev/null
+++ b/sbin/shutdown/pathnames.h
@@ -0,0 +1,41 @@
+/*
+ * 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_FASTBOOT "/fastboot"
+#define _PATH_HALT "/sbin/halt"
+#define _PATH_REBOOT "/sbin/reboot"
+#define _PATH_WALL "/usr/bin/wall"
diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8
new file mode 100644
index 000000000000..d5825695dffa
--- /dev/null
+++ b/sbin/shutdown/shutdown.8
@@ -0,0 +1,162 @@
+.\" Copyright (c) 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)shutdown.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt SHUTDOWN 8
+.Os BSD 4
+.Sh NAME
+.Nm shutdown
+.Nd "close down the system at a given time"
+.Sh SYNOPSIS
+.Nm shutdown
+.Op Fl
+.Op Fl fhkrn
+.Ar time
+.Op Ar warning-message ...
+.Sh DESCRIPTION
+.Nm Shutdown
+provides an automated shutdown procedure for super-users
+to nicely notify users when the system is shutting down,
+saving them from system administrators, hackers, and gurus, who
+would otherwise not bother with such niceties.
+.Pp
+Available friendlinesses:
+.Bl -tag -width time
+.It Fl f
+.Nm Shutdown
+arranges, in the manner of
+.Xr fastboot 8 ,
+for the file systems
+.Em not to be
+checked on reboot.
+.It Fl h
+The system is halted at the specified
+.Ar time
+when
+.Nm shutdown
+execs
+.Xr halt 8 .
+.It Fl k
+Kick every body off.
+The
+.Fl k
+option
+does not actually halt the system, but leaves the
+system multi-user with logins disabled (for all but super-user).
+.It Fl n
+Prevent the normal
+.Xr sync 2
+before stopping.
+.It Fl r
+.Nm Shutdown
+execs
+.Xr reboot 8
+at the specified
+.Ar time .
+.It Ar time
+.Ar Time
+is the time at which
+.Nm shutdown
+will bring the system down and
+may be the word
+.Ar now
+(indicating an immediate shutdown) or
+specify a future time in one of two formats:
+.Ar +number ,
+or
+.Ar yymmddhhmm ,
+where the year, month, and day may be defaulted
+to the current system values. The first form brings the system down in
+.Ar number
+minutes and the second at the absolute time specified.
+.It Ar warning-message
+Any other arguments comprise the warning message that is broadcast
+to users currently logged into the system.
+.It Fl
+If
+.Ql Fl
+is supplied as an option, the warning message is read from the standard
+input.
+.El
+.Pp
+At intervals, becoming more frequent as apocalypse approaches
+and starting at ten hours before shutdown, warning messages are displayed
+on the terminals of all users logged in. Five minutes before
+shutdown, or immediately if shutdown is in less than 5 minutes,
+logins are disabled by creating
+.Pa /etc/nologin
+and copying the
+warning message there. If this file exists when a user attempts to
+log in,
+.Xr login 1
+prints its contents and exits. The file is
+removed just before
+.Nm shutdown
+exits.
+.Pp
+At shutdown time a message is written in the system log, containing the
+time of shutdown, who initiated the shutdown and the reason.
+A terminate
+signal is then sent to
+.Xr init
+to bring the system down to single-user state (depending on above
+options).
+The time of the shutdown and the warning message
+are placed in
+.Pa /etc/nologin
+and should be used to
+inform the users about when the system will be back up
+and why it is going down (or anything else).
+.Sh FILES
+.Bl -tag -width /etc/nologin -compact
+.It Pa /etc/nologin
+tells login not to let anyone log in
+.It Pa /fastboot
+tells
+.Xr rc 8
+not to run fsck when rebooting
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr wall 1 ,
+.Xr fastboot 8 ,
+.Xr halt 8 ,
+.Xr reboot 8
+.Sh BACKWARD COMPATIBILITY
+The hours and minutes in the second time format may be separated by
+a colon (``:'') for backward compatibility.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c
new file mode 100644
index 000000000000..c63ba659c66a
--- /dev/null
+++ b/sbin/shutdown/shutdown.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 1988, 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 copyright[] =
+"@(#) Copyright (c) 1988, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syslog.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+#ifdef DEBUG
+#undef _PATH_NOLOGIN
+#define _PATH_NOLOGIN "./nologin"
+#undef _PATH_FASTBOOT
+#define _PATH_FASTBOOT "./fastboot"
+#endif
+
+#define H *60*60
+#define M *60
+#define S *1
+#define NOLOG_TIME 5*60
+struct interval {
+ int timeleft, timetowait;
+} tlist[] = {
+ 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M,
+ 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M,
+ 2 M, 1 M, 1 M, 30 S, 30 S, 30 S,
+ 0, 0,
+};
+#undef H
+#undef M
+#undef S
+
+static time_t offset, shuttime;
+static int dofast, dohalt, doreboot, killflg, mbuflen;
+static char *nosync, *whom, mbuf[BUFSIZ];
+
+void badtime __P((void));
+void die_you_gravy_sucking_pig_dog __P((void));
+void doitfast __P((void));
+void finish __P((int));
+void getoffset __P((char *));
+void loop __P((void));
+void nolog __P((void));
+void timeout __P((int));
+void timewarn __P((int));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ register char *p, *endp;
+ struct passwd *pw;
+ int arglen, ch, len, readstdin;
+
+#ifndef DEBUG
+ if (geteuid()) {
+ (void)fprintf(stderr, "shutdown: NOT super-user\n");
+ exit(1);
+ }
+#endif
+ nosync = NULL;
+ readstdin = 0;
+ while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
+ switch (ch) {
+ case '-':
+ readstdin = 1;
+ break;
+ case 'f':
+ dofast = 1;
+ break;
+ case 'h':
+ dohalt = 1;
+ break;
+ case 'k':
+ killflg = 1;
+ break;
+ case 'n':
+ nosync = "-n";
+ break;
+ case 'r':
+ doreboot = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (dofast && nosync) {
+ (void)fprintf(stderr,
+ "shutdown: incompatible switches -f and -n.\n");
+ usage();
+ }
+ if (doreboot && dohalt) {
+ (void)fprintf(stderr,
+ "shutdown: incompatible switches -h and -r.\n");
+ usage();
+ }
+ getoffset(*argv++);
+
+ if (*argv) {
+ for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
+ arglen = strlen(*argv);
+ if ((len -= arglen) <= 2)
+ break;
+ if (p != mbuf)
+ *p++ = ' ';
+ bcopy(*argv, p, arglen);
+ p += arglen;
+ }
+ *p = '\n';
+ *++p = '\0';
+ }
+
+ if (readstdin) {
+ p = mbuf;
+ endp = mbuf + sizeof(mbuf) - 2;
+ for (;;) {
+ if (!fgets(p, endp - p + 1, stdin))
+ break;
+ for (; *p && p < endp; ++p);
+ if (p == endp) {
+ *p = '\n';
+ *++p = '\0';
+ break;
+ }
+ }
+ }
+ mbuflen = strlen(mbuf);
+
+ if (offset)
+ (void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
+ else
+ (void)printf("Shutdown NOW!\n");
+
+ if (!(whom = getlogin()))
+ whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
+
+#ifdef DEBUG
+ (void)putc('\n', stdout);
+#else
+ (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+ {
+ int forkpid;
+
+ forkpid = fork();
+ if (forkpid == -1) {
+ perror("shutdown: fork");
+ exit(1);
+ }
+ if (forkpid) {
+ (void)printf("shutdown: [pid %d]\n", forkpid);
+ exit(0);
+ }
+ }
+#endif
+ openlog("shutdown", LOG_CONS, LOG_AUTH);
+ loop();
+ /* NOTREACHED */
+}
+
+void
+loop()
+{
+ struct interval *tp;
+ u_int sltime;
+ int logged;
+
+ if (offset <= NOLOG_TIME) {
+ logged = 1;
+ nolog();
+ }
+ else
+ logged = 0;
+ tp = tlist;
+ if (tp->timeleft < offset)
+ (void)sleep((u_int)(offset - tp->timeleft));
+ else {
+ while (offset < tp->timeleft)
+ ++tp;
+ /*
+ * Warn now, if going to sleep more than a fifth of
+ * the next wait time.
+ */
+ if (sltime = offset - tp->timeleft) {
+ if (sltime > tp->timetowait / 5)
+ timewarn(offset);
+ (void)sleep(sltime);
+ }
+ }
+ for (;; ++tp) {
+ timewarn(tp->timeleft);
+ if (!logged && tp->timeleft <= NOLOG_TIME) {
+ logged = 1;
+ nolog();
+ }
+ (void)sleep((u_int)tp->timetowait);
+ if (!tp->timeleft)
+ break;
+ }
+ die_you_gravy_sucking_pig_dog();
+}
+
+static jmp_buf alarmbuf;
+
+void
+timewarn(timeleft)
+ int timeleft;
+{
+ static int first;
+ static char hostname[MAXHOSTNAMELEN + 1];
+ FILE *pf;
+ char wcmd[MAXPATHLEN + 4];
+
+ if (!first++)
+ (void)gethostname(hostname, sizeof(hostname));
+
+ /* undoc -n option to wall suppresses normal wall banner */
+ (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
+ if (!(pf = popen(wcmd, "w"))) {
+ syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
+ return;
+ }
+
+ (void)fprintf(pf,
+ "\007*** %sSystem shutdown message from %s@%s ***\007\n",
+ timeleft ? "": "FINAL ", whom, hostname);
+
+ if (timeleft > 10*60)
+ (void)fprintf(pf, "System going down at %5.5s\n\n",
+ ctime(&shuttime) + 11);
+ else if (timeleft > 59)
+ (void)fprintf(pf, "System going down in %d minute%s\n\n",
+ timeleft / 60, (timeleft > 60) ? "s" : "");
+ else if (timeleft)
+ (void)fprintf(pf, "System going down in 30 seconds\n\n");
+ else
+ (void)fprintf(pf, "System going down IMMEDIATELY\n\n");
+
+ if (mbuflen)
+ (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
+
+ /*
+ * play some games, just in case wall doesn't come back
+ * probably unecessary, given that wall is careful.
+ */
+ if (!setjmp(alarmbuf)) {
+ (void)signal(SIGALRM, timeout);
+ (void)alarm((u_int)30);
+ (void)pclose(pf);
+ (void)alarm((u_int)0);
+ (void)signal(SIGALRM, SIG_DFL);
+ }
+}
+
+void
+timeout(signo)
+ int signo;
+{
+ longjmp(alarmbuf, 1);
+}
+
+void
+die_you_gravy_sucking_pig_dog()
+{
+
+ syslog(LOG_NOTICE, "%s by %s: %s",
+ doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
+ (void)sleep(2);
+
+ (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
+ if (killflg) {
+ (void)printf("\rbut you'll have to do it yourself\r\n");
+ finish(0);
+ }
+ if (dofast)
+ doitfast();
+#ifdef DEBUG
+ if (doreboot)
+ (void)printf("reboot");
+ else if (dohalt)
+ (void)printf("halt");
+ if (nosync)
+ (void)printf(" no sync");
+ if (dofast)
+ (void)printf(" no fsck");
+ (void)printf("\nkill -HUP 1\n");
+#else
+ if (doreboot) {
+ execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
+ syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
+ perror("shutdown");
+ }
+ else if (dohalt) {
+ execle(_PATH_HALT, "halt", "-l", nosync, 0);
+ syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
+ perror("shutdown");
+ }
+ (void)kill(1, SIGTERM); /* to single user */
+#endif
+ finish(0);
+}
+
+#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
+
+void
+getoffset(timearg)
+ register char *timearg;
+{
+ register struct tm *lt;
+ register char *p;
+ time_t now;
+
+ if (!strcasecmp(timearg, "now")) { /* now */
+ offset = 0;
+ return;
+ }
+
+ (void)time(&now);
+ if (*timearg == '+') { /* +minutes */
+ if (!isdigit(*++timearg))
+ badtime();
+ offset = atoi(timearg) * 60;
+ shuttime = now + offset;
+ return;
+ }
+
+ /* handle hh:mm by getting rid of the colon */
+ for (p = timearg; *p; ++p)
+ if (!isascii(*p) || !isdigit(*p))
+ if (*p == ':' && strlen(p) == 3) {
+ p[0] = p[1];
+ p[1] = p[2];
+ p[2] = '\0';
+ }
+ else
+ badtime();
+
+ unsetenv("TZ"); /* OUR timezone */
+ lt = localtime(&now); /* current time val */
+
+ switch(strlen(timearg)) {
+ case 10:
+ lt->tm_year = ATOI2(timearg);
+ /* FALLTHROUGH */
+ case 8:
+ lt->tm_mon = ATOI2(timearg);
+ if (--lt->tm_mon < 0 || lt->tm_mon > 11)
+ badtime();
+ /* FALLTHROUGH */
+ case 6:
+ lt->tm_mday = ATOI2(timearg);
+ if (lt->tm_mday < 1 || lt->tm_mday > 31)
+ badtime();
+ /* FALLTHROUGH */
+ case 4:
+ lt->tm_hour = ATOI2(timearg);
+ if (lt->tm_hour < 0 || lt->tm_hour > 23)
+ badtime();
+ lt->tm_min = ATOI2(timearg);
+ if (lt->tm_min < 0 || lt->tm_min > 59)
+ badtime();
+ lt->tm_sec = 0;
+ if ((shuttime = mktime(lt)) == -1)
+ badtime();
+ if ((offset = shuttime - now) < 0) {
+ (void)fprintf(stderr,
+ "shutdown: that time is already past.\n");
+ exit(1);
+ }
+ break;
+ default:
+ badtime();
+ }
+}
+
+#define FSMSG "fastboot file for fsck\n"
+void
+doitfast()
+{
+ int fastfd;
+
+ if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
+ 0664)) >= 0) {
+ (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
+ (void)close(fastfd);
+ }
+}
+
+#define NOMSG "\n\nNO LOGINS: System going down at "
+void
+nolog()
+{
+ int logfd;
+ char *ct;
+
+ (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */
+ (void)signal(SIGINT, finish);
+ (void)signal(SIGHUP, finish);
+ (void)signal(SIGQUIT, finish);
+ (void)signal(SIGTERM, finish);
+ if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
+ 0664)) >= 0) {
+ (void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
+ ct = ctime(&shuttime);
+ (void)write(logfd, ct + 11, 5);
+ (void)write(logfd, "\n\n", 2);
+ (void)write(logfd, mbuf, strlen(mbuf));
+ (void)close(logfd);
+ }
+}
+
+void
+finish(signo)
+ int signo;
+{
+ (void)unlink(_PATH_NOLOGIN);
+ exit(0);
+}
+
+void
+badtime()
+{
+ (void)fprintf(stderr, "shutdown: bad time format.\n");
+ exit(1);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
+ exit(1);
+}
diff --git a/sbin/slattach/Makefile b/sbin/slattach/Makefile
new file mode 100644
index 000000000000..0a1e759c430c
--- /dev/null
+++ b/sbin/slattach/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= slattach
+MAN8= slattach.0
+MLINKS= slattach.8 slip.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8
new file mode 100644
index 000000000000..217c4817b8f1
--- /dev/null
+++ b/sbin/slattach/slattach.8
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1986, 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.
+.\"
+.\" @(#)slattach.8 8.2 (Berkeley) 4/1/94
+.\"
+.Dd April 1, 1994
+.Dt SLATTACH 8
+.Os BSD 4.3
+.Sh NAME
+.Nm slattach
+.Nd attach serial lines as network interfaces
+.Sh SYNOPSIS
+.Nm Slattach
+.Ar ttyname Op Ar baudrate
+.Sh DESCRIPTION
+.Nm Slattach
+is used to assign a tty line to a network interface,
+and to define the network source and destination addresses.
+The following operands are supported by
+.Nm slattach :
+.Bl -tag -width Ar
+.It Ar ttyname
+Specifies the name of the tty device.
+.Ar Ttyname
+should be a string of the form
+.Ql ttyXX ,
+or
+.Ql /dev/ttyXX .
+.It Ar baudrate
+Specifies the speed of the connection. If not specified, the
+default of 9600 is used.
+.El
+.Pp
+Only the super-user may attach a network interface.
+.Pp
+To detach the interface, use
+.Dq Li ifconfig interface-name down
+after killing off the
+.Nm slattach
+process.
+.Ar Interface-name
+is the name that is shown by
+.Xr netstat 1
+.Sh EXAMPLES
+.Bd -literal -offset indent -compact
+slattach ttyh8
+slattach /dev/tty01 4800
+.Ed
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exit, the
+requested address is unknown, the user is not privileged and
+tried to alter an interface's configuration.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr netintro 4 ,
+.Xr ifconfig 8 ,
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/sbin/slattach/slattach.c b/sbin/slattach/slattach.c
new file mode 100644
index 000000000000..ac81d3352e7c
--- /dev/null
+++ b/sbin/slattach/slattach.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Adams.
+ *
+ * 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) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)slattach.c 8.2 (Berkeley) 1/7/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sgtty.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <paths.h>
+
+#define DEFAULT_BAUD 9600
+int slipdisc = SLIPDISC;
+
+char devname[32];
+char hostname[MAXHOSTNAMELEN];
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int fd;
+ register char *dev = argv[1];
+ struct sgttyb sgtty;
+ int speed;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "usage: %s ttyname [baudrate]\n", argv[0]);
+ exit(1);
+ }
+ speed = argc == 3 ? findspeed(atoi(argv[2])) : findspeed(DEFAULT_BAUD);
+ if (speed == 0) {
+ fprintf(stderr, "unknown speed %s", argv[2]);
+ exit(1);
+ }
+ if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
+ (void)snprintf(devname, sizeof(devname),
+ "%s%s", _PATH_DEV, dev);
+ dev = devname;
+ }
+ if ((fd = open(dev, O_RDWR | O_NDELAY)) < 0) {
+ perror(dev);
+ exit(1);
+ }
+ sgtty.sg_flags = RAW | ANYP;
+ sgtty.sg_ispeed = sgtty.sg_ospeed = speed;
+ if (ioctl(fd, TIOCSETP, &sgtty) < 0) {
+ perror("ioctl(TIOCSETP)");
+ exit(1);
+ }
+ if (ioctl(fd, TIOCSETD, &slipdisc) < 0) {
+ perror("ioctl(TIOCSETD)");
+ exit(1);
+ }
+
+ if (fork() > 0)
+ exit(0);
+ for (;;)
+ sigpause(0L);
+}
+
+struct sg_spds {
+ int sp_val, sp_name;
+} spds[] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+ { 0, 0 }
+};
+
+findspeed(speed)
+ register int speed;
+{
+ register struct sg_spds *sp;
+
+ sp = spds;
+ while (sp->sp_val && sp->sp_val != speed)
+ sp++;
+ return (sp->sp_name);
+}
diff --git a/sbin/startslip/Makefile b/sbin/startslip/Makefile
new file mode 100644
index 000000000000..14c72bc54fb8
--- /dev/null
+++ b/sbin/startslip/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= startslip
+MAN8= startslip.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/startslip/startslip.1 b/sbin/startslip/startslip.1
new file mode 100644
index 000000000000..f12feab06ffd
--- /dev/null
+++ b/sbin/startslip/startslip.1
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1990, 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.
+.\"
+.\" @(#)startslip.1 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt STARTSLIP 1
+.Os BSD 4.4
+.Sh NAME
+.Nm startslip
+.Nd dial up and login to a slip server
+.Sh SYNOPSIS
+.Nm startslip
+.Op Fl d
+.Op Fl s Ar string
+.Op Fl A Ar annexname
+.Op Fl F Ar flowcontrol
+.Ar device user passwd
+.Sh DESCRIPTION
+.Nm Startslip
+opens the specified
+.Ar device .
+.Pp
+Once carrier is asserted
+.Nm startslip
+attempts to login as the specified
+.Ar user
+with the given
+.Ar password .
+If successful, it puts the device into the slip line discipline.
+If carrier drops and a
+.Dv SIGHUP
+is sent to
+.Nm startslip ,
+it closes the device and attempts to repeat the dialup and login sequence.
+.Pp
+Available options:
+.Bl -tag -width Ar
+.It Fl d
+.Nm Startslip
+prints out debugging information about what it is trying to do.
+.It Fl s Ar string
+The optional
+.Ar string
+is written to
+.Ar device .
+For a dialup modem,
+the string is used to specify a dial sequence.
+.It Fl A Ar annexname
+.Nm Startslip
+assumes it is connecting to a Xylogics Annex box and engages in an
+appropriate dialog using the
+.Ar user
+and
+.Ar passwd
+arguments.
+The
+.Ar annexname
+argument is a string that is used to match against the Annex prompt
+to determine when a connection has been established.
+.It Fl F Ar flowcontrol
+Determines the type of flow control used on
+.Ar device .
+Choices for
+.Ar flowcontrol
+are
+``none'' for no flow control (the default),
+``hw'' for hardware RTS/CTS flow control and
+``sw'' for software XON/XOFF flow control.
+.El
+.Sh SEE ALSO
+.Xr sliplogin 8
+.Sh HISTORY
+The
+.Nm startslip
+appeared in
+.Bx 4.4 .
diff --git a/sbin/startslip/startslip.c b/sbin/startslip/startslip.c
new file mode 100644
index 000000000000..d68b24730e34
--- /dev/null
+++ b/sbin/startslip/startslip.c
@@ -0,0 +1,452 @@
+
+/*-
+ * Copyright (c) 1990, 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 copyright[] =
+"@(#) Copyright (c) 1990, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)startslip.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#if BSD >= 199006
+#define POSIX
+#endif
+#ifdef POSIX
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#else
+#include <sgtty.h>
+#endif
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_slvar.h>
+#include <netdb.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+
+#define DEFAULT_BAUD B9600
+int speed = DEFAULT_BAUD;
+#define FC_NONE 0 /* flow control: none */
+#define FC_SW 1 /* flow control: software (XON/XOFF) */
+#define FC_HW 2 /* flow control: hardware (RTS/CTS) */
+int flowcontrol = FC_NONE;
+char *annex;
+int hup;
+int logged_in;
+int wait_time = 60; /* then back off */
+#define MAXTRIES 6 /* w/60 sec and doubling, takes an hour */
+#define PIDFILE "/var/run/startslip.pid"
+
+#ifdef DEBUG
+int debug = 1;
+#undef LOG_ERR
+#undef LOG_INFO
+#define syslog fprintf
+#define LOG_ERR stderr
+#define LOG_INFO stderr
+#else
+int debug = 0;
+#endif
+#define printd if (debug) printf
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ char *cp, **ap;
+ int ch, disc;
+ int fd = -1;
+ void sighup();
+ FILE *wfd = NULL, *pfd;
+ char *dialerstring = 0, buf[BUFSIZ];
+ int first = 1, tries = 0;
+ int pausefirst = 0;
+ int pid;
+#ifdef POSIX
+ struct termios t;
+#else
+ struct sgttyb sgtty;
+#endif
+
+ while ((ch = getopt(argc, argv, "db:s:p:A:F:")) != EOF)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+#ifdef POSIX
+ case 'b':
+ speed = atoi(optarg);
+ break;
+#endif
+ case 'p':
+ pausefirst = atoi(optarg);
+ break;
+ case 's':
+ dialerstring = optarg;
+ break;
+ case 'A':
+ annex = optarg;
+ break;
+ case 'F':
+#ifdef POSIX
+ if (strcmp(optarg, "none") == 0)
+ flowcontrol = FC_NONE;
+ else if (strcmp(optarg, "sw") == 0)
+ flowcontrol = FC_SW;
+ else if (strcmp(optarg, "hw") == 0)
+ flowcontrol = FC_HW;
+ else {
+ (void)fprintf(stderr,
+ "flow control: none, sw, hw\n");
+ exit(1);
+ }
+ break;
+#else
+ (void)fprintf(stderr, "flow control not supported\n");
+ exit(1);
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 3)
+ usage();
+
+ openlog("startslip", LOG_PID, LOG_DAEMON);
+
+#if BSD <= 43
+ if (debug == 0 && (fd = open("/dev/tty", 0)) >= 0) {
+ ioctl(fd, TIOCNOTTY, 0);
+ close(fd);
+ fd = -1;
+ }
+#endif
+
+ if (debug)
+ setbuf(stdout, NULL);
+
+ if (pfd = fopen(PIDFILE, "r")) {
+ pid = 0;
+ fscanf(pfd, "%d", &pid);
+ if (pid > 0)
+ kill(pid, SIGUSR1);
+ fclose(pfd);
+ }
+restart:
+ logged_in = 0;
+ if (++tries > MAXTRIES) {
+ syslog(LOG_ERR, "exiting after %d tries\n", tries);
+ /* ???
+ if (first)
+ */
+ exit(1);
+ }
+
+ /*
+ * We may get a HUP below, when the parent (session leader/
+ * controlling process) exits; ignore HUP until into new session.
+ */
+ signal(SIGHUP, SIG_IGN);
+ hup = 0;
+ if (fork() > 0) {
+ if (pausefirst)
+ sleep(pausefirst);
+ if (first)
+ printd("parent exit\n");
+ exit(0);
+ }
+ pausefirst = 0;
+#ifdef POSIX
+ if (setsid() == -1)
+ perror("setsid");
+#endif
+ pid = getpid();
+ printd("restart: pid %d: ", pid);
+ if (pfd = fopen(PIDFILE, "w")) {
+ fprintf(pfd, "%d\n", pid);
+ fclose(pfd);
+ }
+ if (wfd) {
+ printd("fclose, ");
+ fclose(wfd);
+ wfd == NULL;
+ }
+ if (fd >= 0) {
+ printd("close, ");
+ close(fd);
+ sleep(5);
+ }
+ printd("open");
+ if ((fd = open(argv[0], O_RDWR)) < 0) {
+ perror(argv[0]);
+ syslog(LOG_ERR, "open %s: %m\n", argv[0]);
+ if (first)
+ exit(1);
+ else {
+ sleep(wait_time * tries);
+ goto restart;
+ }
+ }
+ printd(" %d", fd);
+#ifdef TIOCSCTTY
+ if (ioctl(fd, TIOCSCTTY, 0) < 0)
+ perror("ioctl (TIOCSCTTY)");
+#endif
+ signal(SIGHUP, sighup);
+ if (debug) {
+ if (ioctl(fd, TIOCGETD, &disc) < 0)
+ perror("ioctl(TIOCSETD)");
+ printf(" (disc was %d)", disc);
+ }
+ disc = TTYDISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ perror("ioctl(TIOCSETD)");
+ syslog(LOG_ERR, "%s: ioctl (TIOCSETD 0): %m\n",
+ argv[0]);
+ }
+ printd(", ioctl");
+#ifdef POSIX
+ if (tcgetattr(fd, &t) < 0) {
+ perror("tcgetattr");
+ syslog(LOG_ERR, "%s: tcgetattr: %m\n", argv[0]);
+ exit(2);
+ }
+ cfmakeraw(&t);
+ t.c_iflag &= ~IMAXBEL;
+ switch (flowcontrol) {
+ case FC_HW:
+ t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW);
+ break;
+ case FC_SW:
+ t.c_iflag |= (IXON|IXOFF);
+ break;
+ case FC_NONE:
+ t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW);
+ t.c_iflag &= ~(IXON|IXOFF);
+ break;
+ }
+ cfsetspeed(&t, speed);
+ if (tcsetattr(fd, TCSAFLUSH, &t) < 0) {
+ perror("tcsetattr");
+ syslog(LOG_ERR, "%s: tcsetattr: %m\n", argv[0]);
+ if (first)
+ exit(2);
+ else {
+ sleep(wait_time * tries);
+ goto restart;
+ }
+ }
+#else
+ if (ioctl(fd, TIOCGETP, &sgtty) < 0) {
+ perror("ioctl (TIOCGETP)");
+ syslog(LOG_ERR, "%s: ioctl (TIOCGETP): %m\n",
+ argv[0]);
+ exit(2);
+ }
+ sgtty.sg_flags = RAW | ANYP;
+ sgtty.sg_erase = sgtty.sg_kill = 0377;
+ sgtty.sg_ispeed = sgtty.sg_ospeed = speed;
+ if (ioctl(fd, TIOCSETP, &sgtty) < 0) {
+ perror("ioctl (TIOCSETP)");
+ syslog(LOG_ERR, "%s: ioctl (TIOCSETP): %m\n",
+ argv[0]);
+ if (first)
+ exit(2);
+ else {
+ sleep(wait_time * tries);
+ goto restart;
+ }
+ }
+#endif
+ sleep(2); /* wait for flakey line to settle */
+ if (hup)
+ goto restart;
+
+ wfd = fdopen(fd, "w+");
+ if (wfd == NULL) {
+ syslog(LOG_ERR, "can't fdopen slip line\n");
+ exit(10);
+ }
+ setbuf(wfd, (char *)0);
+ if (dialerstring) {
+ printd(", send dialstring");
+ fprintf(wfd, "%s\r", dialerstring);
+ } else
+ putc('\r', wfd);
+ printd("\n");
+
+ /*
+ * Log in
+ */
+ printd("look for login: ");
+ for (;;) {
+ if (getline(buf, BUFSIZ, fd) == 0 || hup) {
+ sleep(wait_time * tries);
+ goto restart;
+ }
+ if (annex) {
+ if (bcmp(buf, annex, strlen(annex)) == 0) {
+ fprintf(wfd, "slip\r");
+ printd("Sent \"slip\"\n");
+ continue;
+ }
+ if (bcmp(&buf[1], "sername:", 8) == 0) {
+ fprintf(wfd, "%s\r", argv[1]);
+ printd("Sent login: %s\n", argv[1]);
+ continue;
+ }
+ if (bcmp(&buf[1], "assword:", 8) == 0) {
+ fprintf(wfd, "%s\r", argv[2]);
+ printd("Sent password: %s\n", argv[2]);
+ break;
+ }
+ } else {
+ if (bcmp(&buf[1], "ogin:", 5) == 0) {
+ fprintf(wfd, "%s\r", argv[1]);
+ printd("Sent login: %s\n", argv[1]);
+ continue;
+ }
+ if (bcmp(&buf[1], "assword:", 8) == 0) {
+ fprintf(wfd, "%s\r", argv[2]);
+ printd("Sent password: %s\n", argv[2]);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Security hack. Do not want private information such as the
+ * password and possible phone number to be left around.
+ * So we clobber the arguments.
+ */
+ for (ap = argv - optind + 1; ap < argv + 3; ap++)
+ for (cp = *ap; *cp != 0; cp++)
+ *cp = '\0';
+
+ /*
+ * Attach
+ */
+ printd("setd");
+ disc = SLIPDISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ perror("ioctl(TIOCSETD)");
+ syslog(LOG_ERR, "%s: ioctl (TIOCSETD SLIP): %m\n",
+ argv[0]);
+ exit(1);
+ }
+ if (first && debug == 0) {
+ close(0);
+ close(1);
+ close(2);
+ (void) open("/dev/null", O_RDWR);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ }
+ (void) system("ifconfig sl0 up");
+ printd(", ready\n");
+ if (!first)
+ syslog(LOG_INFO, "reconnected (%d tries).\n", tries);
+ first = 0;
+ tries = 0;
+ logged_in = 1;
+ while (hup == 0) {
+ sigpause(0L);
+ printd("sigpause return\n");
+ }
+ goto restart;
+}
+
+void
+sighup()
+{
+
+ printd("hup\n");
+ if (hup == 0 && logged_in)
+ syslog(LOG_INFO, "hangup signal\n");
+ hup = 1;
+}
+
+getline(buf, size, fd)
+ char *buf;
+ int size, fd;
+{
+ register int i;
+ int ret;
+
+ size--;
+ for (i = 0; i < size; i++) {
+ if (hup)
+ return (0);
+ if ((ret = read(fd, &buf[i], 1)) == 1) {
+ buf[i] &= 0177;
+ if (buf[i] == '\r' || buf[i] == '\0')
+ buf[i] = '\n';
+ if (buf[i] != '\n' && buf[i] != ':')
+ continue;
+ buf[i + 1] = '\0';
+ printd("Got %d: \"%s\"\n", i + 1, buf);
+ return (i+1);
+ }
+ if (ret <= 0) {
+ if (ret < 0)
+ perror("getline: read");
+ else
+ fprintf(stderr, "read returned 0\n");
+ buf[i] = '\0';
+ printd("returning 0 after %d: \"%s\"\n", i, buf);
+ return (0);
+ }
+ }
+ return (0);
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: startslip [-d] [-b speed] [-s string] [-A annexname] [-F flowcontrol] dev user passwd\n");
+ exit(1);
+}
diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile
new file mode 100644
index 000000000000..23836a100271
--- /dev/null
+++ b/sbin/swapon/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= swapon
+MAN8= swapon.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/swapon/swapon.8 b/sbin/swapon/swapon.8
new file mode 100644
index 000000000000..973ebc2e84c8
--- /dev/null
+++ b/sbin/swapon/swapon.8
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)swapon.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd June 5, 1993
+.Dt SWAPON 8
+.Os BSD 4
+.Sh NAME
+.Nm swapon
+.Nd "specify additional device for paging and swapping"
+.Sh SYNOPSIS
+.Nm swapon
+.Fl a
+.Nm swapon
+.Ar special_file ...
+.Sh DESCRIPTION
+.Nm Swapon
+is used to specify additional devices on which paging and swapping
+are to take place.
+The system begins by swapping and paging on only a single device
+so that only one disk is required at bootstrap time.
+Calls to
+.Nm swapon
+normally occur in the system multi-user initialization file
+.Pa /etc/rc
+making all swap devices available, so that the paging and swapping
+activity is interleaved across several devices.
+.Pp
+Normally, the first form is used:
+.Bl -tag -width Ds
+.It Fl a
+All devices marked as ``sw''
+swap devices in
+.Pa /etc/fstab
+are made available.
+.El
+.Pp
+The second form gives individual block devices as given
+in the system swap configuration table. The call makes only this space
+available to the system for swap allocation.
+.Sh SEE ALSO
+.Xr swapon 2 ,
+.Xr fstab 8
+.Xr init 8
+.Xr rc 8
+.Sh FILES
+.Bl -tag -width /dev/[ru][pk]?b -compact
+.It Pa /dev/[ru][pk]?b
+standard paging devices
+.It Pa /etc/fstab
+ascii filesystem description table
+.El
+.Sh BUGS
+There is no way to stop paging and swapping on a device.
+It is therefore not possible to make use of devices which may be
+dismounted during system operation.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c
new file mode 100644
index 000000000000..577219d4ae58
--- /dev/null
+++ b/sbin/swapon/swapon.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <fstab.h>
+#include <errno.h>
+#include <stdio.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register struct fstab *fsp;
+ register int stat;
+ int ch, doall;
+
+ doall = 0;
+ while ((ch = getopt(argc, argv, "a")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ doall = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+
+ stat = 0;
+ if (doall)
+ while (fsp = getfsent()) {
+ if (strcmp(fsp->fs_type, FSTAB_SW))
+ continue;
+ if (add(fsp->fs_spec, 1))
+ stat = 1;
+ else
+ printf("swapon: adding %s as swap device\n",
+ fsp->fs_spec);
+ }
+ else if (!*argv)
+ usage();
+ for (; *argv; ++argv)
+ stat |= add(*argv, 0);
+ exit(stat);
+}
+
+add(name, ignoreebusy)
+ char *name;
+ int ignoreebusy;
+{
+ extern int errno;
+
+ if (swapon(name) == -1) {
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "swapon: %s: device not configured\n",
+ name);
+ break;
+ case EBUSY:
+ if (!ignoreebusy)
+ fprintf(stderr,
+ "swapon: %s: device already in use\n",
+ name);
+ break;
+ default:
+ fprintf(stderr, "swapon: %s: ", name);
+ perror((char *)NULL);
+ break;
+ }
+ return(1);
+ }
+ return(0);
+}
+
+usage()
+{
+ fprintf(stderr, "usage: swapon [-a] [special_file ...]\n");
+ exit(1);
+}
diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile
new file mode 100644
index 000000000000..92f94d73ea1a
--- /dev/null
+++ b/sbin/tunefs/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= tunefs
+MAN8= tunefs.0
+
+.include <bsd.prog.mk>
diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8
new file mode 100644
index 000000000000..bfe4cb168470
--- /dev/null
+++ b/sbin/tunefs/tunefs.8
@@ -0,0 +1,138 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tunefs.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt TUNEFS 8
+.Os BSD 4.2
+.Sh NAME
+.Nm tunefs
+.Nd tune up an existing file system
+.Sh SYNOPSIS
+.Nm tunefs
+.Op Fl a Ar maxcontig
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl m Ar minfree
+.Bk -words
+.Op Fl o Ar optimize_preference
+.Ek
+.Op Ar special | Ar filesys
+.Sh DESCRIPTION
+.Nm Tunefs
+is designed to change the dynamic parameters of a file system
+which affect the layout policies.
+The parameters which are to be changed are indicated by the flags
+given below:
+.Bl -tag -width Ds
+.It Fl a Ar maxcontig
+This specifies the maximum number of contiguous blocks that will
+be laid out before forcing a rotational delay (see
+.Fl d
+below).
+The default value is one, since most device drivers require
+an interrupt per disk transfer.
+Device drivers that can chain several buffers together in a single
+transfer should set this to the maximum chain length.
+.It Fl d Ar rotdelay
+This specifies the expected time (in milliseconds)
+to service a transfer completion
+interrupt and initiate a new transfer on the same disk.
+It is used to decide how much rotational spacing to place between
+successive blocks in a file.
+.It Fl e Ar maxbpg
+This indicates the maximum number of blocks any single file can
+allocate out of a cylinder group before it is forced to begin
+allocating blocks from another cylinder group.
+Typically this value is set to about one quarter of the total blocks
+in a cylinder group.
+The intent is to prevent any single file from using up all the
+blocks in a single cylinder group,
+thus degrading access times for all files subsequently allocated
+in that cylinder group.
+The effect of this limit is to cause big files to do long seeks
+more frequently than if they were allowed to allocate all the blocks
+in a cylinder group before seeking elsewhere.
+For file systems with exclusively large files,
+this parameter should be set higher.
+.It Fl m Ar minfree
+This value specifies the percentage of space held back
+from normal users; the minimum free space threshold.
+The default value used is 10%.
+This value can be set to zero, however up to a factor of three
+in throughput will be lost over the performance obtained at a 10%
+threshold.
+Note that if the value is raised above the current usage level,
+users will be unable to allocate files until enough files have
+been deleted to get under the higher threshold.
+.It Fl o Ar optimize_preference
+The file system can either try to minimize the time spent
+allocating blocks, or it can attempt to minimize the space
+fragmentation on the disk.
+If the value of minfree (see above) is less than 10%,
+then the file system should optimize for space to avoid
+running out of full sized blocks.
+For values of minfree greater than or equal to 10%,
+fragmentation is unlikely to be problematical, and
+the file system can be optimized for time.
+.El
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr dumpfs 8 ,
+.Xr newfs 8 ,
+.Xr mkfs 8
+.Rs
+.%A M. McKusick
+.%A W. Joy
+.%A S. Leffler
+.%A R. Fabry
+.%T "A Fast File System for UNIX"
+.%J "ACM Transactions on Computer Systems 2"
+.%N 3
+.%P pp 181-197
+.%D August 1984
+.%O "(reprinted in the BSD System Manager's Manual, SMM:5)"
+.Re
+.Sh BUGS
+This program should work on mounted and active file systems.
+Because the super-block is not kept in the buffer cache,
+the changes will only take effect if the program
+is run on dismounted file systems.
+To change the root file system, the system must be rebooted
+after the file system is tuned.
+.Pp
+You can tune a file system, but you can't tune a fish.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c
new file mode 100644
index 000000000000..583dbffdd979
--- /dev/null
+++ b/sbin/tunefs/tunefs.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94";
+#endif /* not lint */
+
+/*
+ * tunefs: change layout parameters to an existing file system.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* the optimization warning string template */
+#define OPTWARN "should optimize for %s with minfree %s %d%%"
+
+union {
+ struct fs sb;
+ char pad[MAXBSIZE];
+} sbun;
+#define sblock sbun.sb
+
+int fi;
+long dev_bsize = 1;
+
+void bwrite(daddr_t, char *, int);
+int bread(daddr_t, char *, int);
+void getsb(struct fs *, char *);
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp, *special, *name;
+ struct stat st;
+ int i;
+ int Aflag = 0;
+ struct fstab *fs;
+ char *chg[2], device[MAXPATHLEN];
+
+ argc--, argv++;
+ if (argc < 2)
+ usage();
+ special = argv[argc - 1];
+ fs = getfsfile(special);
+ if (fs)
+ special = fs->fs_spec;
+again:
+ if (stat(special, &st) < 0) {
+ if (*special != '/') {
+ if (*special == 'r')
+ special++;
+ (void)sprintf(device, "%s/%s", _PATH_DEV, special);
+ special = device;
+ goto again;
+ }
+ err(1, "%s", special);
+ }
+ if ((st.st_mode & S_IFMT) != S_IFBLK &&
+ (st.st_mode & S_IFMT) != S_IFCHR)
+ errx(10, "%s: not a block or character device", special);
+ getsb(&sblock, special);
+ for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
+ for (cp = &argv[0][1]; *cp; cp++)
+ switch (*cp) {
+
+ case 'A':
+ Aflag++;
+ continue;
+
+ case 'a':
+ name = "maximum contiguous block count";
+ if (argc < 1)
+ errx(10, "-a: missing %s", name);
+ argc--, argv++;
+ i = atoi(*argv);
+ if (i < 1)
+ errx(10, "%s must be >= 1 (was %s)",
+ name, *argv);
+ warnx("%s changes from %d to %d",
+ name, sblock.fs_maxcontig, i);
+ sblock.fs_maxcontig = i;
+ continue;
+
+ case 'd':
+ name =
+ "rotational delay between contiguous blocks";
+ if (argc < 1)
+ errx(10, "-d: missing %s", name);
+ argc--, argv++;
+ i = atoi(*argv);
+ warnx("%s changes from %dms to %dms",
+ name, sblock.fs_rotdelay, i);
+ sblock.fs_rotdelay = i;
+ continue;
+
+ case 'e':
+ name =
+ "maximum blocks per file in a cylinder group";
+ if (argc < 1)
+ errx(10, "-e: missing %s", name);
+ argc--, argv++;
+ i = atoi(*argv);
+ if (i < 1)
+ errx(10, "%s must be >= 1 (was %s)",
+ name, *argv);
+ warnx("%s changes from %d to %d",
+ name, sblock.fs_maxbpg, i);
+ sblock.fs_maxbpg = i;
+ continue;
+
+ case 'm':
+ name = "minimum percentage of free space";
+ if (argc < 1)
+ errx(10, "-m: missing %s", name);
+ argc--, argv++;
+ i = atoi(*argv);
+ if (i < 0 || i > 99)
+ errx(10, "bad %s (%s)", name, *argv);
+ warnx("%s changes from %d%% to %d%%",
+ name, sblock.fs_minfree, i);
+ sblock.fs_minfree = i;
+ if (i >= MINFREE &&
+ sblock.fs_optim == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (i < MINFREE &&
+ sblock.fs_optim == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+ continue;
+
+ case 'o':
+ name = "optimization preference";
+ if (argc < 1)
+ errx(10, "-o: missing %s", name);
+ argc--, argv++;
+ chg[FS_OPTSPACE] = "space";
+ chg[FS_OPTTIME] = "time";
+ if (strcmp(*argv, chg[FS_OPTSPACE]) == 0)
+ i = FS_OPTSPACE;
+ else if (strcmp(*argv, chg[FS_OPTTIME]) == 0)
+ i = FS_OPTTIME;
+ else
+ errx(10, "bad %s (options are `space' or `time')",
+ name);
+ if (sblock.fs_optim == i) {
+ warnx("%s remains unchanged as %s",
+ name, chg[i]);
+ continue;
+ }
+ warnx("%s changes from %s to %s",
+ name, chg[sblock.fs_optim], chg[i]);
+ sblock.fs_optim = i;
+ if (sblock.fs_minfree >= MINFREE &&
+ i == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (sblock.fs_minfree < MINFREE &&
+ i == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+ continue;
+
+ default:
+ usage();
+ }
+ }
+ if (argc != 1)
+ usage();
+ bwrite((daddr_t)SBOFF / dev_bsize, (char *)&sblock, SBSIZE);
+ if (Aflag)
+ for (i = 0; i < sblock.fs_ncg; i++)
+ bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)),
+ (char *)&sblock, SBSIZE);
+ close(fi);
+ exit(0);
+}
+
+void
+usage()
+{
+
+ fprintf(stderr, "Usage: tunefs tuneup-options special-device\n");
+ fprintf(stderr, "where tuneup-options are:\n");
+ fprintf(stderr, "\t-a maximum contiguous blocks\n");
+ fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
+ fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
+ fprintf(stderr, "\t-m minimum percentage of free space\n");
+ fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
+ exit(2);
+}
+
+void
+getsb(fs, file)
+ register struct fs *fs;
+ char *file;
+{
+
+ fi = open(file, 2);
+ if (fi < 0)
+ err(3, "cannot open %s", file);
+ if (bread((daddr_t)SBOFF, (char *)fs, SBSIZE))
+ err(4, "%s: bad super block", file);
+ if (fs->fs_magic != FS_MAGIC)
+ err(5, "%s: bad magic number", file);
+ dev_bsize = fs->fs_fsize / fsbtodb(fs, 1);
+}
+
+void
+bwrite(blk, buf, size)
+ daddr_t blk;
+ char *buf;
+ int size;
+{
+
+ if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0)
+ err(6, "FS SEEK");
+ if (write(fi, buf, size) != size)
+ err(7, "FS WRITE");
+}
+
+int
+bread(bno, buf, cnt)
+ daddr_t bno;
+ char *buf;
+ int cnt;
+{
+ int i;
+
+ if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0)
+ return(1);
+ if ((i = read(fi, buf, cnt)) != cnt) {
+ for(i=0; i<sblock.fs_bsize; i++)
+ buf[i] = 0;
+ return (1);
+ }
+ return (0);
+}
diff --git a/sbin/umount/Makefile b/sbin/umount/Makefile
new file mode 100644
index 000000000000..9665a5923db0
--- /dev/null
+++ b/sbin/umount/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.2 (Berkeley) 2/20/94
+
+PROG= umount
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8
new file mode 100644
index 000000000000..234eb8f18384
--- /dev/null
+++ b/sbin/umount/umount.8
@@ -0,0 +1,123 @@
+.\" 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.
+.\"
+.\" @(#)umount.8 8.1 (Berkeley) 2/20/94
+.\"
+.Dd February 20, 1994
+.Dt UMOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm umount
+.Nd unmount file systems
+.Sh SYNOPSIS
+.Nm umount
+.Op Fl fv
+.Ar special | node
+.Nm umount
+.Fl a
+.Op Fl fv
+.Op Fl h Ar host
+.Op Fl t Ar ufs | lfs | external_type
+.Sh DESCRIPTION
+The
+.Nm umount
+command
+calls the
+.Xr unmount 2
+system call to remove a
+.Ar "special device"
+or the remote node (rhost:path) from 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 options are as follows:
+.Bl -tag -width indent
+.It Fl a
+All of the file systems described in
+.Xr fstab 5
+are unmounted.
+.It Fl f
+The file system is forcibly unmounted.
+Active special devices continue to work,
+but all other files return errors if further accesses are attempted.
+The root file system cannot be forcibly unmounted.
+.It Fl h Ar host
+Only filesystems mounted from the specified host will be
+unmounted.
+This option is implies the
+.Fl a
+option and, unless otherwise specified with the
+.Fl t
+option, will only unmount NFS filesystems.
+.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type"
+Is used to indicate 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 umount
+command:
+.Bd -literal -offset indent
+umount -a -t nfs,mfs
+.Ed
+.Pp
+umounts all filesystems of the type
+.Tn NFS
+and
+.Tn MFS .
+.It Fl v
+Verbose, additional information is printed out as each file system
+is unmounted.
+.El
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+file system table
+.El
+.Sh SEE ALSO
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+A
+.Nm umount
+command appeared in
+.At v6 .
diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c
new file mode 100644
index 000000000000..56660dea6840
--- /dev/null
+++ b/sbin/umount/umount.c
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 1980, 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)umount.c 8.3 (Berkeley) 2/20/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <nfs/rpcv2.h>
+
+#include <err.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef enum { MNTON, MNTFROM } mntwhat;
+
+int fake, fflag, vflag, *typelist;
+char *nfshost;
+
+int fsnametotype __P((char *));
+char *getmntname __P((char *, mntwhat, int *));
+void maketypelist __P((char *));
+int selected __P((int));
+int namematch __P((struct hostent *));
+int umountall __P((void));
+int umountfs __P((char *));
+void usage __P((void));
+int xdr_dir __P((XDR *, char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int all, ch, errs;
+
+ /* Start disks transferring immediately. */
+ sync();
+
+ all = 0;
+ while ((ch = getopt(argc, argv, "aFfh:t:v")) != EOF)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'F':
+ fake = 1;
+ break;
+ case 'f':
+ fflag = MNT_FORCE;
+ break;
+ case 'h': /* -h implies -a. */
+ all = 1;
+ nfshost = optarg;
+ break;
+ case 't':
+ maketypelist(optarg);
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 && !all || argc != 0 && all)
+ usage();
+
+ /* -h implies "-t nfs" if no -t flag. */
+ if ((nfshost != NULL) && (typelist == NULL))
+ maketypelist("nfs");
+
+ if (all) {
+ if (setfsent() == 0)
+ err(1, "%s", _PATH_FSTAB);
+ errs = umountall();
+ } else
+ for (errs = 0; *argv != NULL; ++argv)
+ if (umountfs(*argv) == 0)
+ errs = 1;
+ exit(errs);
+}
+
+int
+umountall()
+{
+ struct fstab *fs;
+ int rval, type;
+ char *cp;
+
+ while ((fs = getfsent()) != NULL) {
+ /* Ignore the root. */
+ if (strcmp(fs->fs_file, "/") == 0)
+ continue;
+ /*
+ * !!!
+ * Historic practice: ignore unknown FSTAB_* fields.
+ */
+ if (strcmp(fs->fs_type, FSTAB_RW) &&
+ strcmp(fs->fs_type, FSTAB_RO) &&
+ strcmp(fs->fs_type, FSTAB_RQ))
+ continue;
+ /* If an unknown file system type, complain. */
+ if ((type = fsnametotype(fs->fs_vfstype)) == MOUNT_NONE) {
+ warnx("%s: unknown mount type", fs->fs_vfstype);
+ continue;
+ }
+ if (!selected(type))
+ continue;
+
+ /*
+ * We want to unmount the file systems in the reverse order
+ * that they were mounted. So, we save off the file name
+ * in some allocated memory, and then call recursively.
+ */
+ if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
+ err(1, NULL);
+ (void)strcpy(cp, fs->fs_file);
+ rval = umountall();
+ return (umountfs(cp) || rval);
+ }
+ return (0);
+}
+
+int
+umountfs(name)
+ char *name;
+{
+ enum clnt_stat clnt_stat;
+ struct hostent *hp;
+ struct sockaddr_in saddr;
+ struct stat sb;
+ struct timeval pertry, try;
+ CLIENT *clp;
+ int so, type;
+ char *delimp, *hostp, *mntpt, rname[MAXPATHLEN];
+
+ if (realpath(name, rname) == NULL) {
+ warn("%s", rname);
+ return (0);
+ }
+
+ name = rname;
+
+ if (stat(name, &sb) < 0) {
+ if (((mntpt = getmntname(name, MNTFROM, &type)) == NULL) &&
+ ((mntpt = getmntname(name, MNTON, &type)) == NULL)) {
+ warnx("%s: not currently mounted", name);
+ return (1);
+ }
+ } else if (S_ISBLK(sb.st_mode)) {
+ if ((mntpt = getmntname(name, MNTON, &type)) == NULL) {
+ warnx("%s: not currently mounted", name);
+ return (1);
+ }
+ } else if (S_ISDIR(sb.st_mode)) {
+ mntpt = name;
+ if ((name = getmntname(mntpt, MNTFROM, &type)) == NULL) {
+ warnx("%s: not currently mounted", mntpt);
+ return (1);
+ }
+ } else {
+ warnx("%s: not a directory or special device", name);
+ return (1);
+ }
+
+ if (!selected(type))
+ return (0);
+
+ if ((delimp = strchr(name, '@')) != NULL) {
+ hostp = delimp + 1;
+ *delimp = '\0';
+ hp = gethostbyname(hostp);
+ *delimp = '@';
+ } else if ((delimp = strchr(name, ':')) != NULL) {
+ *delimp = '\0';
+ hostp = name;
+ hp = gethostbyname(hostp);
+ name = delimp + 1;
+ *delimp = ':';
+ } else
+ hp = NULL;
+ if (!namematch(hp))
+ return (0);
+
+ if (vflag)
+ (void)printf("%s: unmount from %s\n", name, mntpt);
+ if (fake)
+ return (0);
+
+ if (unmount(mntpt, fflag) < 0) {
+ warn("%s", mntpt);
+ return (1);
+ }
+
+ if ((hp != NULL) && !(fflag & MNT_FORCE)) {
+ *delimp = '\0';
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = 0;
+ memmove(&saddr.sin_addr, hp->h_addr, hp->h_length);
+ pertry.tv_sec = 3;
+ pertry.tv_usec = 0;
+ so = RPC_ANYSOCK;
+ if ((clp = clntudp_create(&saddr,
+ RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) {
+ clnt_pcreateerror("Cannot MNT PRC");
+ return (1);
+ }
+ clp->cl_auth = authunix_create_default();
+ try.tv_sec = 20;
+ try.tv_usec = 0;
+ clnt_stat = clnt_call(clp,
+ RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS) {
+ clnt_perror(clp, "Bad MNT RPC");
+ return (1);
+ }
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ }
+ return (0);
+}
+
+char *
+getmntname(name, what, type)
+ char *name;
+ mntwhat what;
+ int *type;
+{
+ struct statfs *mntbuf;
+ int i, mntsize;
+
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
+ warn("getmntinfo");
+ return (NULL);
+ }
+ for (i = 0; i < mntsize; i++) {
+ if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) {
+ if (type)
+ *type = mntbuf[i].f_type;
+ return (mntbuf[i].f_mntonname);
+ }
+ if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) {
+ if (type)
+ *type = mntbuf[i].f_type;
+ return (mntbuf[i].f_mntfromname);
+ }
+ }
+ return (NULL);
+}
+
+static enum { IN_LIST, NOT_IN_LIST } which;
+
+int
+selected(type)
+ int type;
+{
+ /* If no type specified, it's always selected. */
+ if (typelist == NULL)
+ return (1);
+ for (; *typelist != MOUNT_NONE; ++typelist)
+ if (type == *typelist)
+ return (which == IN_LIST ? 1 : 0);
+ return (which == IN_LIST ? 0 : 1);
+}
+
+void
+maketypelist(fslist)
+ char *fslist;
+{
+ int *av, i;
+ char *nextcp;
+
+ if ((fslist == NULL) || (fslist[0] == '\0'))
+ errx(1, "empty type list");
+
+ /*
+ * XXX
+ * Note: the syntax is "noxxx,yyy" for no xxx's and
+ * no yyy's, not the more intuitive "noyyy,noyyy".
+ */
+ if (fslist[0] == 'n' && fslist[1] == 'o') {
+ fslist += 2;
+ which = NOT_IN_LIST;
+ } else
+ which = IN_LIST;
+
+ /* Count the number of types. */
+ for (i = 0, nextcp = fslist; *nextcp != NULL; ++nextcp)
+ if (*nextcp == ',')
+ i++;
+
+ /* Build an array of that many types. */
+ if ((av = typelist = malloc((i + 2) * sizeof(int))) == NULL)
+ err(1, NULL);
+ for (i = 0; fslist != NULL; fslist = nextcp, ++i) {
+ if ((nextcp = strchr(fslist, ',')) != NULL)
+ *nextcp++ = '\0';
+ av[i] = fsnametotype(fslist);
+ if (av[i] == MOUNT_NONE)
+ errx(1, "%s: unknown mount type", fslist);
+ }
+ /* Terminate the array. */
+ av[i++] = MOUNT_NONE;
+}
+
+int
+fsnametotype(name)
+ char *name;
+{
+ static char const *namelist[] = INITMOUNTNAMES;
+ char const **cp;
+
+ for (cp = namelist; *cp; ++cp)
+ if (strcmp(name, *cp) == 0)
+ return (cp - namelist);
+ return (MOUNT_NONE);
+}
+
+int
+namematch(hp)
+ struct hostent *hp;
+{
+ char *cp, **np;
+
+ if ((hp == NULL) || (nfshost == NULL))
+ return (1);
+
+ if (strcasecmp(nfshost, hp->h_name) == 0)
+ return (1);
+
+ if ((cp = strchr(hp->h_name, '.')) != NULL) {
+ *cp = '\0';
+ if (strcasecmp(nfshost, hp->h_name) == 0)
+ return (1);
+ }
+ for (np = hp->h_aliases; *np; np++) {
+ if (strcasecmp(nfshost, *np) == 0)
+ return (1);
+ if ((cp = strchr(*np, '.')) != NULL) {
+ *cp = '\0';
+ if (strcasecmp(nfshost, *np) == 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: %s\n %s\n",
+ "umount [-fv] [-t fstypelist] special | node",
+ "umount -a[fv] [-h host] [-t fstypelist]");
+ exit(1);
+}
diff --git a/usr.sbin/mount_portalfs/Makefile b/usr.sbin/mount_portalfs/Makefile
new file mode 100644
index 000000000000..1f17d245ed20
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/activate.c b/usr.sbin/mount_portalfs/activate.c
new file mode 100644
index 000000000000..33617988f7df
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/conf.c b/usr.sbin/mount_portalfs/conf.c
new file mode 100644
index 000000000000..18833b608093
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/mount_portalfs.8 b/usr.sbin/mount_portalfs/mount_portalfs.8
new file mode 100644
index 000000000000..0df65316c2ad
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/mount_portalfs.c b/usr.sbin/mount_portalfs/mount_portalfs.c
new file mode 100644
index 000000000000..ae5345d3049f
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/pathnames.h b/usr.sbin/mount_portalfs/pathnames.h
new file mode 100644
index 000000000000..25321145d990
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/portal.conf b/usr.sbin/mount_portalfs/portal.conf
new file mode 100644
index 000000000000..5b5a773eaa3c
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/portald.h b/usr.sbin/mount_portalfs/portald.h
new file mode 100644
index 000000000000..fbe111b1a7ad
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/pt_conf.c b/usr.sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 000000000000..d1eba94ea3fe
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/pt_exec.c b/usr.sbin/mount_portalfs/pt_exec.c
new file mode 100644
index 000000000000..06e3382da85b
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/pt_file.c b/usr.sbin/mount_portalfs/pt_file.c
new file mode 100644
index 000000000000..ace35c04fe59
--- /dev/null
+++ b/usr.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/usr.sbin/mount_portalfs/pt_tcp.c b/usr.sbin/mount_portalfs/pt_tcp.c
new file mode 100644
index 000000000000..18a53ce26aba
--- /dev/null
+++ b/usr.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/usr.sbin/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 000000000000..36e2a0572406
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.3 (Berkeley) 1/25/94
+
+PROG= mountd
+CFLAGS+=-DNFS -DMFS -DCD9660
+MAN5= exports.0 netgroup.0
+MAN8= mountd.0
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
new file mode 100644
index 000000000000..d32527f71766
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,250 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)exports.5 8.2 (Berkeley) 1/28/94
+.\"
+.Dd January 28, 1994
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd define remote mount points for
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm exports
+.Sh DESCRIPTION
+The
+.Nm exports
+file specifies remote mount points for the
+.Tn NFS
+mount protocol per the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification \\*(tNRFC\\*(sP 1094, Appendix A" .
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the mount point(s) and export flags within one local server
+filesystem for one or more hosts.
+A host may be specified only once for each local filesystem on the
+server and there may be only one default entry for each server
+filesystem that applies to all other hosts.
+The latter exports the filesystem to the ``world'' and should
+be used only when the filesystem contains public information.
+.Pp
+In a mount entry,
+the first field(s) specify the directory path(s) within a server filesystem
+that can be mounted on by the corresponding client(s).
+There are two forms of this specification.
+The first is to list all mount points as absolute
+directory paths separated by whitespace.
+The second is to specify the pathname of the root of the filesystem
+followed by the
+.Fl alldirs
+flag;
+this form allows the host(s) to mount any directory within the filesystem.
+The pathnames must not have any symbolic links in them and should not have
+any "." or ".." components.
+Mount points for a filesystem may appear on multiple lines each with
+different sets of hosts and export options.
+.Pp
+The second component of a line specifies how the filesystem is to be
+exported to the host set.
+The option flags specify whether the filesystem
+is exported read-only or read-write and how the client uid is mapped to
+user credentials on the server.
+.Pp
+Export options are specified as follows:
+.Pp
+.Sm off
+.Fl maproot No = Sy user
+.Sm on
+The credential of the specified user is used for remote access by root.
+The credential includes all the groups to which the user is a member
+on the local machine (see
+.Xr id 1 ).
+The user may be specified by name or number.
+.Pp
+.Sm off
+.Fl maproot No = Sy user:group1:group2:...
+.Sm on
+The colon separated list is used to specify the precise credential
+to be used for remote access by root.
+The elements of the list may be either names or numbers.
+Note that user: should be used to distinguish a credential containing
+no groups from a complete credential for that user.
+.Pp
+.Sm off
+.Fl mapall No = Sy user
+.Sm on
+or
+.Sm off
+.Fl mapall No = Sy user:group1:group2:...
+.Sm on
+specifies a mapping for all client uids (including root)
+using the same semantics as
+.Fl maproot .
+.Pp
+The option
+.Fl r
+is a synonym for
+.Fl maproot
+in an effort to be backward compatible with older export file formats.
+.Pp
+In the absence of
+.Fl maproot
+and
+.Fl mapall
+options, remote accesses by root will result in using a credential of -2:-2.
+All other users will be mapped to their remote credential.
+If a
+.Fl maproot
+option is given,
+remote access by root will be mapped to that credential instead of -2:-2.
+If a
+.Fl mapall
+option is given,
+all users (including root) will be mapped to that credential in
+place of their own.
+.Pp
+The
+.Fl kerb
+option specifies that the Kerberos authentication server should be
+used to authenticate and map client credentials.
+(Note that this is NOT Sun NFS compatible and
+is supported for TCP transport only.)
+.Pp
+The
+.Fl ro
+option specifies that the filesystem should be exported read-only
+(default read/write).
+The option
+.Fl o
+is a synonym for
+.Fl ro
+in an effort to be backward compatible with older export file formats.
+.Pp
+The third component of a line specifies the host set to which the line applies.
+The set may be specified in three ways.
+The first way is to list the host name(s) separated by white space.
+(Standard internet ``dot'' addresses may be used in place of names.)
+The second way is to specify a ``netgroup'' as defined in the netgroup file (see
+.Xr netgroup 5 ).
+The third way is to specify an internet subnetwork using a network and
+network mask that is defined as the set of all hosts with addresses within
+the subnetwork.
+This latter approach requires less overhead within the
+kernel and is recommended for cases where the export line refers to a
+large number of clients within an administrative subnet.
+.Pp
+The first two cases are specified by simply listing the name(s) separated
+by whitespace.
+All names are checked to see if they are ``netgroup'' names
+first and are assumed to be hostnames otherwise.
+Using the full domain specification for a hostname can normally
+circumvent the problem of a host that has the same name as a netgroup.
+The third case is specified by the flag
+.Sm off
+.Fl network No = Sy netname
+.Sm on
+and optionally
+.Sm off
+.Fl mask No = Sy netmask .
+.Sm on
+If the mask is not specified, it will default to the mask for that network
+class (A, B or C; see
+.Xr inet 5 ).
+.Pp
+For example:
+.Bd -literal -offset indent
+/usr /usr/local -maproot=0:10 friends
+/usr -maproot=daemon grumpy.cis.uoguelph.ca 131.104.48.16
+/usr -ro -mapall=nobody
+/u -maproot=bin: -network 131.104.48 -mask 255.255.255.0
+/u2 -maproot=root friends
+/u2 -alldirs -kerb -network cis-net -mask cis-mask
+.Ed
+.Pp
+Given that
+.Sy /usr ,
+.Sy /u
+and
+.Sy /u2
+are
+local filesystem mount points, the above example specifies the following:
+.Sy /usr
+is exported to hosts
+.Em friends
+where friends is specified in the netgroup file
+with users mapped to their remote credentials and
+root mapped to uid 0 and group 10.
+It is exported read-write and the hosts in ``friends'' can mount either /usr
+or /usr/local.
+It is exported to
+.Em 131.104.48.16
+and
+.Em grumpy.cis.uoguelph.ca
+with users mapped to their remote credentials and
+root mapped to the user and groups associated with ``daemon'';
+it is exported to the rest of the world as read-only with
+all users mapped to the user and groups associated with ``nobody''.
+.Pp
+.Sy /u
+is exported to all hosts on the subnetwork
+.Em 131.104.48
+with root mapped to the uid for ``bin'' and with no group access.
+.Pp
+.Sy /u2
+is exported to the hosts in ``friends'' with root mapped to uid and groups
+associated with ``root'';
+it is exported to all hosts on network ``cis-net'' allowing mounts at any
+directory within /u2 and mapping all uids to credentials for the principal
+that is authenticated by a Kerberos ticket.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+The default remote mount-point file.
+.El
+.Sh SEE ALSO
+.Xr netgroup 5 ,
+.Xr mountd 8 ,
+.Xr nfsd 8 ,
+.Xr showmount 8
+.Sh BUGS
+The export options are tied to the local mount points in the kernel and
+must be non-contradictory for any exported subdirectory of the local
+server mount point.
+It is recommended that all exported directories within the same server
+filesystem be specified on adjacent lines going down the tree.
+You cannot specify a hostname that is also the name of a netgroup.
+Specifying the full domain specification for a hostname can normally
+circumvent the problem.
diff --git a/usr.sbin/mountd/mountd.8 b/usr.sbin/mountd/mountd.8
new file mode 100644
index 000000000000..47a6cfc5ebea
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,100 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)mountd.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm /sbin/mountd
+.Op Fl n
+.Op Ar exportsfile
+.Sh DESCRIPTION
+.Xr Mountd
+is the server for
+.Tn NFS
+mount requests from other client machines.
+.Xr Mountd
+listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094.
+.Pp
+Options and operands available for
+.Nm mountd :
+.Bl -tag -width Ds
+.It Fl n
+The
+.Fl n
+option allows non-root mount requests to be served.
+This should only be specified if there are clients such as PC's,
+that require it.
+.It Ar exportsfile
+The
+.Ar exportsfile
+argument specifies an alternate location
+for the exports file.
+.El
+.Pp
+When mountd is started,
+it loads the export host addresses and options into the kernel
+using the mount(2) system call.
+After changing the exports file,
+a hangup signal should be sent to the mountd daemon
+to get it to reload the export information.
+After sending the SIGHUP
+(kill -HUP `cat /var/run/mountd.pid`),
+check the syslog output to see if mountd logged any parsing
+errors in the exports file.
+.Sh FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+the list of exported filesystems
+.It Pa /var/run/mountd.pid
+the pid of the currently running mountd
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr exports 5 ,
+.Xr nfsd 8 ,
+.Xr portmap 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm mountd
+utility first appeared in 4.4BSD.
diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
new file mode 100644
index 000000000000..d7c31dfb4ea1
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * 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) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)mountd.c 8.8 (Berkeley) 2/20/94";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+/*
+ * Structures for keeping the mount list and export list
+ */
+struct mountlist {
+ struct mountlist *ml_next;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct dirlist {
+ struct dirlist *dp_left;
+ struct dirlist *dp_right;
+ int dp_flag;
+ struct hostlist *dp_hosts; /* List of hosts this dir exported to */
+ char dp_dirp[1]; /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define DP_DEFSET 0x1
+
+struct exportlist {
+ struct exportlist *ex_next;
+ struct dirlist *ex_dirl;
+ struct dirlist *ex_defdir;
+ int ex_flag;
+ fsid_t ex_fs;
+ char *ex_fsdir;
+};
+/* ex_flag bits */
+#define EX_LINKED 0x1
+
+struct netmsk {
+ u_long nt_net;
+ u_long nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct hostent *gt_hostent;
+ struct netmsk gt_net;
+#ifdef ISO
+ struct sockaddr_iso *gt_isoaddr;
+#endif
+};
+
+struct grouplist {
+ int gr_type;
+ union grouptypes gr_ptr;
+ struct grouplist *gr_next;
+};
+/* Group types */
+#define GT_NULL 0x0
+#define GT_HOST 0x1
+#define GT_NET 0x2
+#define GT_ISO 0x4
+
+struct hostlist {
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+/* Global defs */
+char *add_expdir __P((struct dirlist **, char *, int));
+void add_dlist __P((struct dirlist **, struct dirlist *,
+ struct grouplist *));
+void add_mlist __P((char *, char *));
+int check_dirpath __P((char *));
+int check_options __P((struct dirlist *));
+int chk_host __P((struct dirlist *, u_long, int *));
+void del_mlist __P((char *, char *));
+struct dirlist *dirp_search __P((struct dirlist *, char *));
+int do_mount __P((struct exportlist *, struct grouplist *, int,
+ struct ucred *, char *, int, struct statfs *));
+int do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct ucred *));
+struct exportlist *ex_search __P((fsid_t *));
+struct exportlist *get_exp __P((void));
+void free_dir __P((struct dirlist *));
+void free_exp __P((struct exportlist *));
+void free_grp __P((struct grouplist *));
+void free_host __P((struct hostlist *));
+void get_exportlist __P((void));
+int get_host __P((char *, struct grouplist *));
+struct hostlist *get_ht __P((void));
+int get_line __P((void));
+void get_mountlist __P((void));
+int get_net __P((char *, struct netmsk *, int));
+void getexp_err __P((struct exportlist *, struct grouplist *));
+struct grouplist *get_grp __P((void));
+void hang_dirp __P((struct dirlist *, struct grouplist *,
+ struct exportlist *, int));
+void mntsrv __P((struct svc_req *, SVCXPRT *));
+void nextfield __P((char **, char **));
+void out_of_mem __P((void));
+void parsecred __P((char *, struct ucred *));
+int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
+int scan_tree __P((struct dirlist *, u_long));
+void send_umntall __P((void));
+int umntall_each __P((caddr_t, struct sockaddr_in *));
+int xdr_dir __P((XDR *, char *));
+int xdr_explist __P((XDR *, caddr_t));
+int xdr_fhs __P((XDR *, nfsv2fh_t *));
+int xdr_mlist __P((XDR *, caddr_t));
+
+/* C library */
+int getnetgrent();
+void endnetgrent();
+void setnetgrent();
+
+#ifdef ISO
+struct iso_addr *iso_addr();
+#endif
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char exname[MAXPATHLEN];
+struct ucred def_anon = {
+ 1,
+ (uid_t) -2,
+ 1,
+ { (gid_t) -2 }
+};
+int root_only = 1;
+int opt_flags;
+/* Bits for above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+#define OP_KERB 0x04
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ISO 0x20
+#define OP_ALLDIRS 0x40
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG __P((int, const char *, ...));
+#define syslog SYSLOG
+#else
+int debug = 0;
+#endif
+
+/*
+ * Mountd server for NFS mount protocol as described in:
+ * NFS: Network File System Protocol Specification, RFC1094, Appendix A
+ * The optional arguments are the exports file name
+ * default: _PATH_EXPORTS
+ * and "-n" to allow nonroot mount.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *transp;
+ int c;
+
+ while ((c = getopt(argc, argv, "n")) != EOF)
+ switch (c) {
+ case 'n':
+ root_only = 0;
+ break;
+ default:
+ fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
+ exit(1);
+ };
+ argc -= optind;
+ argv += optind;
+ grphead = (struct grouplist *)NULL;
+ exphead = (struct exportlist *)NULL;
+ mlhead = (struct mountlist *)NULL;
+ if (argc == 1) {
+ strncpy(exname, *argv, MAXPATHLEN-1);
+ exname[MAXPATHLEN-1] = '\0';
+ } else
+ strcpy(exname, _PATH_EXPORTS);
+ openlog("mountd", LOG_PID, LOG_DAEMON);
+ if (debug)
+ fprintf(stderr,"Getting export list.\n");
+ get_exportlist();
+ if (debug)
+ fprintf(stderr,"Getting mount list.\n");
+ get_mountlist();
+ if (debug)
+ fprintf(stderr,"Here we go.\n");
+ if (debug == 0) {
+ daemon(0, 0);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ }
+ signal(SIGHUP, (void (*) __P((int))) get_exportlist);
+ signal(SIGTERM, (void (*) __P((int))) send_umntall);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+ if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
+ syslog(LOG_ERR, "Can't create socket");
+ exit(1);
+ }
+ pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
+ if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,
+ IPPROTO_UDP)) {
+ syslog(LOG_ERR, "Can't register mount");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "Mountd died");
+ exit(1);
+}
+
+/*
+ * The mount rpc service
+ */
+void
+mntsrv(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct exportlist *ep;
+ struct dirlist *dp;
+ nfsv2fh_t nfh;
+ struct authunix_parms *ucr;
+ struct stat stb;
+ struct statfs fsb;
+ struct hostent *hp;
+ u_long saddr;
+ char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];
+ int bad = ENOENT, omask, defset;
+ uid_t uid = -2;
+
+ /* Get authorization */
+ switch (rqstp->rq_cred.oa_flavor) {
+ case AUTH_UNIX:
+ ucr = (struct authunix_parms *)rqstp->rq_clntcred;
+ uid = ucr->aup_uid;
+ break;
+ case AUTH_NULL:
+ default:
+ break;
+ }
+
+ saddr = transp->xp_raddr.sin_addr.s_addr;
+ hp = (struct hostent *)NULL;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ case RPCMNT_MOUNT:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+
+ /*
+ * Get the real pathname and make sure it is a directory
+ * that exists.
+ */
+ if (realpath(rpcpath, dirpath) == 0 ||
+ stat(dirpath, &stb) < 0 ||
+ (stb.st_mode & S_IFMT) != S_IFDIR ||
+ statfs(dirpath, &fsb) < 0) {
+ chdir("/"); /* Just in case realpath doesn't */
+ if (debug)
+ fprintf(stderr, "stat failed on %s\n", dirpath);
+ if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ }
+
+ /* Check in the exports list */
+ omask = sigblock(sigmask(SIGHUP));
+ ep = ex_search(&fsb.f_fsid);
+ defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ /* Get the file handle */
+ bzero((caddr_t)&nfh, sizeof(nfh));
+ if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
+ bad = errno;
+ syslog(LOG_ERR, "Can't get fh for %s", dirpath);
+ if (!svc_sendreply(transp, xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ sigsetmask(omask);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
+ syslog(LOG_ERR, "Can't send reply");
+ if (hp == NULL)
+ hp = gethostbyaddr((caddr_t)&saddr,
+ sizeof(saddr), AF_INET);
+ if (hp)
+ add_mlist(hp->h_name, dirpath);
+ else
+ add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
+ dirpath);
+ if (debug)
+ fprintf(stderr,"Mount successfull.\n");
+ } else {
+ bad = EACCES;
+ if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ }
+ sigsetmask(omask);
+ return;
+ case RPCMNT_DUMP:
+ if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ case RPCMNT_UMOUNT:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, dirpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, dirpath);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if ((uid != 0 && root_only) || uid == -2) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, (char *)NULL);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
+ return;
+ case RPCMNT_EXPORT:
+ if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+}
+
+/*
+ * Xdr conversion for a dirpath string
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+/*
+ * Xdr routine to generate fhstatus
+ */
+int
+xdr_fhs(xdrsp, nfh)
+ XDR *xdrsp;
+ nfsv2fh_t *nfh;
+{
+ int ok = 0;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
+}
+
+int
+xdr_mlist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct mountlist *mlp;
+ int true = 1;
+ int false = 0;
+ char *strp;
+
+ mlp = mlhead;
+ while (mlp) {
+ if (!xdr_bool(xdrsp, &true))
+ return (0);
+ strp = &mlp->ml_host[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = &mlp->ml_dirp[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ mlp = mlp->ml_next;
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+}
+
+/*
+ * Xdr conversion for export list
+ */
+int
+xdr_explist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct exportlist *ep;
+ int false = 0;
+ int omask, putdef;
+
+ omask = sigblock(sigmask(SIGHUP));
+ ep = exphead;
+ while (ep) {
+ putdef = 0;
+ if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
+ goto errout;
+ if (ep->ex_defdir && putdef == 0 &&
+ put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+ &putdef))
+ goto errout;
+ ep = ep->ex_next;
+ }
+ sigsetmask(omask);
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+errout:
+ sigsetmask(omask);
+ return (0);
+}
+
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+int
+put_exlist(dp, xdrsp, adp, putdefp)
+ struct dirlist *dp;
+ XDR *xdrsp;
+ struct dirlist *adp;
+ int *putdefp;
+{
+ struct grouplist *grp;
+ struct hostlist *hp;
+ int true = 1;
+ int false = 0;
+ int gotalldir = 0;
+ char *strp;
+
+ if (dp) {
+ if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
+ return (1);
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = dp->dp_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (1);
+ if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
+ gotalldir = 1;
+ *putdefp = 1;
+ }
+ if ((dp->dp_flag & DP_DEFSET) == 0 &&
+ (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ if (grp->gr_type == GT_HOST) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_hostent->h_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ } else if (grp->gr_type == GT_NET) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_net.nt_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ }
+ hp = hp->ht_next;
+ if (gotalldir && hp == (struct hostlist *)NULL) {
+ hp = adp->dp_hosts;
+ gotalldir = 0;
+ }
+ }
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (1);
+ if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
+ return (1);
+ }
+ return (0);
+}
+
+#define LINESIZ 10240
+char line[LINESIZ];
+FILE *exp_file;
+
+/*
+ * Get the export list
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb, *fsp;
+ struct hostent *hpe;
+ struct ucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ /*
+ * First, get rid of the old list
+ */
+ ep = exphead;
+ while (ep) {
+ ep2 = ep;
+ ep = ep->ex_next;
+ free_exp(ep2);
+ }
+ exphead = (struct exportlist *)NULL;
+
+ grp = grphead;
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+ grphead = (struct grouplist *)NULL;
+
+ /*
+ * And delete exports that are in the kernel for all local
+ * file systems.
+ * XXX: Should know how to handle all local exportable file systems
+ * instead of just MOUNT_UFS.
+ */
+ num = getmntinfo(&fsp, MNT_NOWAIT);
+ for (i = 0; i < num; i++) {
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+ } targs;
+
+ switch (fsp->f_type) {
+ case MOUNT_MFS:
+ case MOUNT_UFS:
+ case MOUNT_CD9660:
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_type, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE,
+ (caddr_t)&targs) < 0)
+ syslog(LOG_ERR, "Can't delete exports for %s",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", exname);
+ exit(2);
+ }
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ fprintf(stderr,"Got line %s\n",line);
+ cp = line;
+ nextfield(&cp, &endcp);
+ if (*cp == '#')
+ goto nextline;
+
+ /*
+ * Set defaults.
+ */
+ has_host = FALSE;
+ anon = def_anon;
+ exflags = MNT_EXPORTED;
+ got_nondir = 0;
+ opt_flags = 0;
+ ep = (struct exportlist *)NULL;
+
+ /*
+ * Create new exports list entry
+ */
+ len = endcp-cp;
+ tgrp = grp = get_grp();
+ while (len > 0) {
+ if (len > RPCMNT_NAMELEN) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (*cp == '-') {
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (debug)
+ fprintf(stderr, "doing opt %s\n", cp);
+ got_nondir = 1;
+ if (do_opt(&cp, &endcp, ep, grp, &has_host,
+ &exflags, &anon)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (*cp == '/') {
+ savedc = *endcp;
+ *endcp = '\0';
+ if (check_dirpath(cp) &&
+ statfs(cp, &fsb) >= 0) {
+ if (got_nondir) {
+ syslog(LOG_ERR, "Dirs must be first");
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (ep) {
+ if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+ ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else {
+ /*
+ * See if this directory is already
+ * in the list.
+ */
+ ep = ex_search(&fsb.f_fsid);
+ if (ep == (struct exportlist *)NULL) {
+ ep = get_exp();
+ ep->ex_fs = fsb.f_fsid;
+ ep->ex_fsdir = (char *)
+ malloc(strlen(fsb.f_mntonname) + 1);
+ if (ep->ex_fsdir)
+ strcpy(ep->ex_fsdir,
+ fsb.f_mntonname);
+ else
+ out_of_mem();
+ if (debug)
+ fprintf(stderr,
+ "Making new ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ fprintf(stderr,
+ "Found ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ }
+
+ /*
+ * Add dirpath to export mount point.
+ */
+ dirp = add_expdir(&dirhead, cp, len);
+ dirplen = len;
+ } else {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ *endcp = savedc;
+ } else {
+ savedc = *endcp;
+ *endcp = '\0';
+ got_nondir = 1;
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Get the host or netgroup.
+ */
+ setnetgrent(cp);
+ netgrp = getnetgrent(&hst, &usr, &dom);
+ do {
+ if (has_host) {
+ grp->gr_next = get_grp();
+ grp = grp->gr_next;
+ }
+ if (netgrp) {
+ if (get_host(hst, grp)) {
+ syslog(LOG_ERR, "Bad netgroup %s", cp);
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (get_host(cp, grp)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ has_host = TRUE;
+ } while (netgrp && getnetgrent(&hst, &usr, &dom));
+ endnetgrent();
+ *endcp = savedc;
+ }
+ cp = endcp;
+ nextfield(&cp, &endcp);
+ len = endcp - cp;
+ }
+ if (check_options(dirhead)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (!has_host) {
+ grp->gr_type = GT_HOST;
+ if (debug)
+ fprintf(stderr,"Adding a default entry\n");
+ /* add a default group and make the grp list NULL */
+ hpe = (struct hostent *)malloc(sizeof(struct hostent));
+ if (hpe == (struct hostent *)NULL)
+ out_of_mem();
+ hpe->h_name = "Default";
+ hpe->h_addrtype = AF_INET;
+ hpe->h_length = sizeof (u_long);
+ hpe->h_addr_list = (char **)NULL;
+ grp->gr_ptr.gt_hostent = hpe;
+
+ /*
+ * Don't allow a network export coincide with a list of
+ * host(s) on the same line.
+ */
+ } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Loop through hosts, pushing the exports into the kernel.
+ * After loop, tgrp points to the start of the list and
+ * grp points to the last entry in the list.
+ */
+ grp = tgrp;
+ do {
+ if (do_mount(ep, grp, exflags, &anon, dirp,
+ dirplen, &fsb)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } while (grp->gr_next && (grp = grp->gr_next));
+
+ /*
+ * Success. Update the data structures.
+ */
+ if (has_host) {
+ hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS));
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ (opt_flags & OP_ALLDIRS));
+ free_grp(grp);
+ }
+ dirhead = (struct dirlist *)NULL;
+ if ((ep->ex_flag & EX_LINKED) == 0) {
+ ep2 = exphead;
+ epp = &exphead;
+
+ /*
+ * Insert in the list in alphabetical order.
+ */
+ while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+ epp = &ep2->ex_next;
+ ep2 = ep2->ex_next;
+ }
+ if (ep2)
+ ep->ex_next = ep2;
+ *epp = ep;
+ ep->ex_flag |= EX_LINKED;
+ }
+nextline:
+ if (dirhead) {
+ free_dir(dirhead);
+ dirhead = (struct dirlist *)NULL;
+ }
+ }
+ fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+ struct exportlist *ep;
+
+ ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+ if (ep == (struct exportlist *)NULL)
+ out_of_mem();
+ bzero((caddr_t)ep, sizeof (struct exportlist));
+ return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+ struct grouplist *gp;
+
+ gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+ if (gp == (struct grouplist *)NULL)
+ out_of_mem();
+ bzero((caddr_t)gp, sizeof (struct grouplist));
+ return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+ struct exportlist *ep;
+ struct grouplist *grp;
+{
+ struct grouplist *tgrp;
+
+ syslog(LOG_ERR, "Bad exports list line %s", line);
+ if (ep && (ep->ex_flag & EX_LINKED) == 0)
+ free_exp(ep);
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+ fsid_t *fsid;
+{
+ struct exportlist *ep;
+
+ ep = exphead;
+ while (ep) {
+ if (ep->ex_fs.val[0] == fsid->val[0] &&
+ ep->ex_fs.val[1] == fsid->val[1])
+ return (ep);
+ ep = ep->ex_next;
+ }
+ return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+ struct dirlist **dpp;
+ char *cp;
+ int len;
+{
+ struct dirlist *dp;
+
+ dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+ dp->dp_left = *dpp;
+ dp->dp_right = (struct dirlist *)NULL;
+ dp->dp_flag = 0;
+ dp->dp_hosts = (struct hostlist *)NULL;
+ strcpy(dp->dp_dirp, cp);
+ *dpp = dp;
+ return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, alldirs)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int alldirs;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (alldirs) {
+ if (ep->ex_defdir)
+ free((caddr_t)dp);
+ else
+ ep->ex_defdir = dp;
+ if (grp == (struct grouplist *)NULL)
+ ep->ex_defdir->dp_flag |= DP_DEFSET;
+ else while (grp) {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = ep->ex_defdir->dp_hosts;
+ ep->ex_defdir->dp_hosts = hp;
+ grp = grp->gr_next;
+ }
+ } else {
+
+ /*
+ * Loop throught the directories adding them to the tree.
+ */
+ while (dp) {
+ dp2 = dp->dp_left;
+ add_dlist(&ep->ex_dirl, dp, grp);
+ dp = dp2;
+ }
+ }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+{
+ struct dirlist *dp;
+ struct hostlist *hp;
+ int cmp;
+
+ dp = *dpp;
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+ if (cmp > 0) {
+ add_dlist(&dp->dp_left, newdp, grp);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp);
+ return;
+ } else
+ free((caddr_t)newdp);
+ } else {
+ dp = newdp;
+ dp->dp_left = (struct dirlist *)NULL;
+ *dpp = dp;
+ }
+ if (grp) {
+
+ /*
+ * Hang all of the host(s) off of the directory point.
+ */
+ do {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = dp->dp_hosts;
+ dp->dp_hosts = hp;
+ grp = grp->gr_next;
+ } while (grp);
+ } else
+ dp->dp_flag |= DP_DEFSET;
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirpath)
+ struct dirlist *dp;
+ char *dirpath;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirpath);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirpath));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirpath));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp)
+ struct dirlist *dp;
+ u_long saddr;
+ int *defsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ u_long **addrp;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = 1;
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ switch (grp->gr_type) {
+ case GT_HOST:
+ addrp = (u_long **)
+ grp->gr_ptr.gt_hostent->h_addr_list;
+ while (*addrp) {
+ if (**addrp == saddr)
+ return (1);
+ addrp++;
+ }
+ break;
+ case GT_NET:
+ if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+ grp->gr_ptr.gt_net.nt_net)
+ return (1);
+ break;
+ };
+ hp = hp->ht_next;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+ struct dirlist *dp;
+ u_long saddr;
+{
+ int defset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset))
+ return (1);
+ if (scan_tree(dp->dp_right, saddr))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+ struct dirlist *dp;
+{
+
+ if (dp) {
+ free_dir(dp->dp_left);
+ free_dir(dp->dp_right);
+ free_host(dp->dp_hosts);
+ free((caddr_t)dp);
+ }
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+ char **cpp, **endcpp;
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int *has_hostp;
+ int *exflagsp;
+ struct ucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ cpopt = *cpp;
+ cpopt++;
+ cp = *endcpp;
+ savedc = *cp;
+ *cp = '\0';
+ while (cpopt && *cpopt) {
+ allflag = 1;
+ usedarg = -2;
+ if (cpoptend = index(cpopt, ',')) {
+ *cpoptend++ = '\0';
+ if (cpoptarg = index(cpopt, '='))
+ *cpoptarg++ = '\0';
+ } else {
+ if (cpoptarg = index(cpopt, '='))
+ *cpoptarg++ = '\0';
+ else {
+ *cp = savedc;
+ nextfield(&cp, &endcp);
+ **endcpp = '\0';
+ if (endcp > cp && *cp != '-') {
+ cpoptarg = cp;
+ savedc2 = *endcp;
+ *endcp = '\0';
+ usedarg = 0;
+ }
+ }
+ }
+ if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ *exflagsp |= MNT_EXRDONLY;
+ } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+ !(allflag = strcmp(cpopt, "mapall")) ||
+ !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+ usedarg++;
+ parsecred(cpoptarg, cr);
+ if (allflag == 0) {
+ *exflagsp |= MNT_EXPORTANON;
+ opt_flags |= OP_MAPALL;
+ } else
+ opt_flags |= OP_MAPROOT;
+ } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
+ *exflagsp |= MNT_EXKERB;
+ opt_flags |= OP_KERB;
+ } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+ !strcmp(cpopt, "m"))) {
+ if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+ syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
+ return (1);
+ }
+ usedarg++;
+ opt_flags |= OP_MASK;
+ } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+ !strcmp(cpopt, "n"))) {
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "Network/host conflict");
+ return (1);
+ } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+ syslog(LOG_ERR, "Bad net: %s", cpoptarg);
+ return (1);
+ }
+ grp->gr_type = GT_NET;
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_NET;
+ } else if (!strcmp(cpopt, "alldirs")) {
+ opt_flags |= OP_ALLDIRS;
+#ifdef ISO
+ } else if (cpoptarg && !strcmp(cpopt, "iso")) {
+ if (get_isoaddr(cpoptarg, grp)) {
+ syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
+ return (1);
+ }
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_ISO;
+#endif /* ISO */
+ } else {
+ syslog(LOG_ERR, "Bad opt %s", cpopt);
+ return (1);
+ }
+ if (usedarg >= 0) {
+ *endcp = savedc2;
+ **endcpp = savedc;
+ if (usedarg > 0) {
+ *cpp = cp;
+ *endcpp = endcp;
+ }
+ return (0);
+ }
+ cpopt = cpoptend;
+ }
+ **endcpp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct hostent *hp, *nhp;
+ char **addrp, **naddrp;
+ struct hostent t_host;
+ int i;
+ u_long saddr;
+ char *aptr[2];
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((hp = gethostbyname(cp)) == NULL) {
+ if (isdigit(*cp)) {
+ saddr = inet_addr(cp);
+ if (saddr == -1) {
+ syslog(LOG_ERR, "Inet_addr failed");
+ return (1);
+ }
+ if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
+ AF_INET)) == NULL) {
+ hp = &t_host;
+ hp->h_name = cp;
+ hp->h_addrtype = AF_INET;
+ hp->h_length = sizeof (u_long);
+ hp->h_addr_list = aptr;
+ aptr[0] = (char *)&saddr;
+ aptr[1] = (char *)NULL;
+ }
+ } else {
+ syslog(LOG_ERR, "Gethostbyname failed");
+ return (1);
+ }
+ }
+ grp->gr_type = GT_HOST;
+ nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+ malloc(sizeof(struct hostent));
+ if (nhp == (struct hostent *)NULL)
+ out_of_mem();
+ bcopy((caddr_t)hp, (caddr_t)nhp,
+ sizeof(struct hostent));
+ i = strlen(hp->h_name)+1;
+ nhp->h_name = (char *)malloc(i);
+ if (nhp->h_name == (char *)NULL)
+ out_of_mem();
+ bcopy(hp->h_name, nhp->h_name, i);
+ addrp = hp->h_addr_list;
+ i = 1;
+ while (*addrp++)
+ i++;
+ naddrp = nhp->h_addr_list = (char **)
+ malloc(i*sizeof(char *));
+ if (naddrp == (char **)NULL)
+ out_of_mem();
+ addrp = hp->h_addr_list;
+ while (*addrp) {
+ *naddrp = (char *)
+ malloc(hp->h_length);
+ if (*naddrp == (char *)NULL)
+ out_of_mem();
+ bcopy(*addrp, *naddrp,
+ hp->h_length);
+ addrp++;
+ naddrp++;
+ }
+ *naddrp = (char *)NULL;
+ if (debug)
+ fprintf(stderr, "got host %s\n", hp->h_name);
+ return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+ struct exportlist *ep;
+{
+
+ if (ep->ex_defdir) {
+ free_host(ep->ex_defdir->dp_hosts);
+ free((caddr_t)ep->ex_defdir);
+ }
+ if (ep->ex_fsdir)
+ free(ep->ex_fsdir);
+ free_dir(ep->ex_dirl);
+ free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+ struct hostlist *hp;
+{
+ struct hostlist *hp2;
+
+ while (hp) {
+ hp2 = hp;
+ hp = hp->ht_next;
+ free((caddr_t)hp2);
+ }
+}
+
+struct hostlist *
+get_ht()
+{
+ struct hostlist *hp;
+
+ hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+ if (hp == (struct hostlist *)NULL)
+ out_of_mem();
+ hp->ht_next = (struct hostlist *)NULL;
+ return (hp);
+}
+
+#ifdef ISO
+/*
+ * Translate an iso address.
+ */
+get_isoaddr(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct iso_addr *isop;
+ struct sockaddr_iso *isoaddr;
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((isop = iso_addr(cp)) == NULL) {
+ syslog(LOG_ERR,
+ "iso_addr failed, ignored");
+ return (1);
+ }
+ isoaddr = (struct sockaddr_iso *)
+ malloc(sizeof (struct sockaddr_iso));
+ if (isoaddr == (struct sockaddr_iso *)NULL)
+ out_of_mem();
+ bzero((caddr_t)isoaddr, sizeof (struct sockaddr_iso));
+ bcopy((caddr_t)isop, (caddr_t)&isoaddr->siso_addr,
+ sizeof (struct iso_addr));
+ isoaddr->siso_len = sizeof (struct sockaddr_iso);
+ isoaddr->siso_family = AF_ISO;
+ grp->gr_type = GT_ISO;
+ grp->gr_ptr.gt_isoaddr = isoaddr;
+ return (0);
+}
+#endif /* ISO */
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "Out of memory");
+ exit(2);
+}
+
+/*
+ * Do the mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int exflags;
+ struct ucred *anoncrp;
+ char *dirp;
+ int dirplen;
+ struct statfs *fsb;
+{
+ char *cp = (char *)NULL;
+ u_long **addrp;
+ int done;
+ char savedc = '\0';
+ struct sockaddr_in sin, imask;
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+ } args;
+ u_long net;
+
+ args.ua.fspec = 0;
+ args.ua.export.ex_flags = exflags;
+ args.ua.export.ex_anon = *anoncrp;
+ bzero((char *)&sin, sizeof(sin));
+ bzero((char *)&imask, sizeof(imask));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ imask.sin_family = AF_INET;
+ imask.sin_len = sizeof(sin);
+ if (grp->gr_type == GT_HOST)
+ addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
+ else
+ addrp = (u_long **)NULL;
+ done = FALSE;
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (addrp) {
+ sin.sin_addr.s_addr = **addrp;
+ args.ua.export.ex_addrlen = sizeof(sin);
+ } else
+ args.ua.export.ex_addrlen = 0;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_mask)
+ imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
+ else {
+ net = ntohl(grp->gr_ptr.gt_net.nt_net);
+ if (IN_CLASSA(net))
+ imask.sin_addr.s_addr = inet_addr("255.0.0.0");
+ else if (IN_CLASSB(net))
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.0.0");
+ else
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.255.0");
+ grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
+ }
+ sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_addrlen = sizeof (sin);
+ args.ua.export.ex_mask = (struct sockaddr *)&imask;
+ args.ua.export.ex_masklen = sizeof (imask);
+ break;
+#ifdef ISO
+ case GT_ISO:
+ args.ua.export.ex_addr =
+ (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+ args.ua.export.ex_addrlen =
+ sizeof(struct sockaddr_iso);
+ args.ua.export.ex_masklen = 0;
+ break;
+#endif /* ISO */
+ default:
+ syslog(LOG_ERR, "Bad grouptype");
+ if (cp)
+ *cp = savedc;
+ return (1);
+ };
+
+ /*
+ * XXX:
+ * Maybe I should just use the fsb->f_mntonname path instead
+ * of looping back up the dirp to the mount point??
+ * Also, needs to know how to export all types of local
+ * exportable file systems and not just MOUNT_UFS.
+ */
+ while (mount(fsb->f_type, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (errno == EPERM) {
+ syslog(LOG_ERR,
+ "Can't change attributes for %s.\n", dirp);
+ return (1);
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ syslog(LOG_ERR, "Not root dir");
+ return (1);
+ }
+ /* back up over the last component */
+ while (*cp == '/' && cp > dirp)
+ cp--;
+ while (*(cp - 1) != '/' && cp > dirp)
+ cp--;
+ if (cp == dirp) {
+ if (debug)
+ fprintf(stderr,"mnt unsucc\n");
+ syslog(LOG_ERR, "Can't export %s", dirp);
+ return (1);
+ }
+ savedc = *cp;
+ *cp = '\0';
+ }
+ if (addrp) {
+ ++addrp;
+ if (*addrp == (u_long *)NULL)
+ done = TRUE;
+ } else
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np;
+ long netaddr;
+ struct in_addr inetaddr, inetaddr2;
+ char *name;
+
+ if (np = getnetbyname(cp))
+ inetaddr = inet_makeaddr(np->n_net, 0);
+ else if (isdigit(*cp)) {
+ if ((netaddr = inet_network(cp)) == -1)
+ return (1);
+ inetaddr = inet_makeaddr(netaddr, 0);
+ /*
+ * Due to arbritrary subnet masks, you don't know how many
+ * bits to shift the address to make it into a network,
+ * however you do know how to make a network address into
+ * a host with host == 0 and then compare them.
+ * (What a pest)
+ */
+ if (!maskflg) {
+ setnetent(0);
+ while (np = getnetent()) {
+ inetaddr2 = inet_makeaddr(np->n_net, 0);
+ if (inetaddr2.s_addr == inetaddr.s_addr)
+ break;
+ }
+ endnetent();
+ }
+ } else
+ return (1);
+ if (maskflg)
+ net->nt_mask = inetaddr.s_addr;
+ else {
+ if (np)
+ name = np->n_name;
+ else
+ name = inet_ntoa(inetaddr);
+ net->nt_name = (char *)malloc(strlen(name) + 1);
+ if (net->nt_name == (char *)NULL)
+ out_of_mem();
+ strcpy(net->nt_name, name);
+ net->nt_net = inetaddr.s_addr;
+ }
+ return (0);
+}
+
+/*
+ * Parse out the next white space separated field
+ */
+void
+nextfield(cp, endcp)
+ char **cp;
+ char **endcp;
+{
+ char *p;
+
+ p = *cp;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ *cp = *endcp = p;
+ else {
+ *cp = p++;
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ p++;
+ *endcp = p;
+ }
+}
+
+/*
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
+ */
+int
+get_line()
+{
+ char *p, *cp;
+ int len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
+ return (0);
+ len = strlen(p);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ *++cp = '\0';
+ if (len > 0) {
+ totlen += len;
+ if (totlen >= LINESIZ) {
+ syslog(LOG_ERR, "Exports line too long");
+ exit(2);
+ }
+ p = cp;
+ }
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct ucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ int ngroups, groups[NGROUPS + 1];
+
+ /*
+ * Set up the unpriviledged user.
+ */
+ cr->cr_ref = 1;
+ cr->cr_uid = -2;
+ cr->cr_groups[0] = -2;
+ cr->cr_ngroups = 1;
+ /*
+ * Get the user's password table entry.
+ */
+ names = strsep(&namelist, " \t\n");
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-')
+ pw = getpwuid(atoi(name));
+ else
+ pw = getpwnam(name);
+ /*
+ * Credentials specified as those of a user.
+ */
+ if (names == NULL) {
+ if (pw == NULL) {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_uid = pw->pw_uid;
+ ngroups = NGROUPS + 1;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+ syslog(LOG_ERR, "Too many groups");
+ /*
+ * Convert from int's to gid_t's and compress out duplicate
+ */
+ cr->cr_ngroups = ngroups - 1;
+ cr->cr_groups[0] = groups[0];
+ for (cnt = 2; cnt < ngroups; cnt++)
+ cr->cr_groups[cnt - 1] = groups[cnt];
+ return;
+ }
+ /*
+ * Explicit credential specified as a colon separated list:
+ * uid:gid:gid:...
+ */
+ if (pw != NULL)
+ cr->cr_uid = pw->pw_uid;
+ else if (isdigit(*name) || *name == '-')
+ cr->cr_uid = atoi(name);
+ else {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_ngroups = 0;
+ while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-') {
+ cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+ } else {
+ if ((gr = getgrnam(name)) == NULL) {
+ syslog(LOG_ERR, "Unknown group: %s", name);
+ continue;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+ }
+ }
+ if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+ syslog(LOG_ERR, "Too many groups");
+}
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
+/*
+ * Routines that maintain the remote mounttab
+ */
+void
+get_mountlist()
+{
+ struct mountlist *mlp, **mlpp;
+ char *eos, *dirp;
+ int len;
+ char str[STRSIZ];
+ FILE *mlfile;
+
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlpp = &mlhead;
+ while (fgets(str, STRSIZ, mlfile) != NULL) {
+ if ((dirp = index(str, '\t')) == NULL &&
+ (dirp = index(str, ' ')) == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ len = dirp-str;
+ if (len > RPCMNT_NAMELEN)
+ len = RPCMNT_NAMELEN;
+ bcopy(str, mlp->ml_host, len);
+ mlp->ml_host[len] = '\0';
+ while (*dirp == '\t' || *dirp == ' ')
+ dirp++;
+ if ((eos = index(dirp, '\t')) == NULL &&
+ (eos = index(dirp, ' ')) == NULL &&
+ (eos = index(dirp, '\n')) == NULL)
+ len = strlen(dirp);
+ else
+ len = eos-dirp;
+ if (len > RPCMNT_PATHLEN)
+ len = RPCMNT_PATHLEN;
+ bcopy(dirp, mlp->ml_dirp, len);
+ mlp->ml_dirp[len] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ mlpp = &mlp->ml_next;
+ }
+ fclose(mlfile);
+}
+
+void
+del_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ struct mountlist *mlp2;
+ FILE *mlfile;
+ int fnd = 0;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) &&
+ (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
+ fnd = 1;
+ mlp2 = mlp;
+ *mlpp = mlp = mlp->ml_next;
+ free((caddr_t)mlp2);
+ } else {
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ }
+ if (fnd) {
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
+ syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlp = mlhead;
+ while (mlp) {
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ mlp = mlp->ml_next;
+ }
+ fclose(mlfile);
+ }
+}
+
+void
+add_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ FILE *mlfile;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
+ return;
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
+ syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ fclose(mlfile);
+}
+
+/*
+ * This function is called via. SIGTERM when the system is going down.
+ * It sends a broadcast RPCMNT_UMNTALL.
+ */
+void
+send_umntall()
+{
+ (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
+ xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
+ exit(0);
+}
+
+int
+umntall_each(resultsp, raddr)
+ caddr_t resultsp;
+ struct sockaddr_in *raddr;
+{
+ return (1);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ char **addrp;
+
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_hostent->h_name) {
+ addrp = grp->gr_ptr.gt_hostent->h_addr_list;
+ while (addrp && *addrp)
+ free(*addrp++);
+ free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
+ free(grp->gr_ptr.gt_hostent->h_name);
+ }
+ free((caddr_t)grp->gr_ptr.gt_hostent);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+#ifdef ISO
+ else if (grp->gr_type == GT_ISO)
+ free((caddr_t)grp->gr_ptr.gt_isoaddr);
+#endif
+ free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+ struct dirlist *dp;
+{
+
+ if (dp == (struct dirlist *)NULL)
+ return (1);
+ if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
+ (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
+ (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
+ syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -net");
+ return (1);
+ }
+ if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
+ syslog(LOG_ERR, "-net and -iso mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldir has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ * if no symbolic links are found.
+ */
+int
+check_dirpath(dirp)
+ char *dirp;
+{
+ char *cp;
+ int ret = 1;
+ struct stat sb;
+
+ cp = dirp + 1;
+ while (*cp && ret) {
+ if (*cp == '/') {
+ *cp = '\0';
+ if (lstat(dirp, &sb) < 0 ||
+ (sb.st_mode & S_IFMT) != S_IFDIR)
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 ||
+ (sb.st_mode & S_IFMT) != S_IFDIR)
+ ret = 0;
+ return (ret);
+}
diff --git a/usr.sbin/mountd/netgroup.5 b/usr.sbin/mountd/netgroup.5
new file mode 100644
index 000000000000..9ad8c48258a7
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm netgroup
+.Sh DESCRIPTION
+The
+.Nm netgroup
+file
+specifies ``netgroups'', which are sets of
+.Sy (host, user, domain)
+tuples that are to be given similar network access.
+.Pp
+Each line in the file
+consists of a netgroup name followed by a list of the members of the
+netgroup.
+Each member can be either the name of another netgroup or a specification
+of a tuple as follows:
+.Bd -literal -offset indent
+(host, user, domain)
+.Ed
+where the
+.Sy host ,
+.Sy user ,
+and
+.Sy domain
+are character string names for the corresponding component.
+Any of the comma separated fields may be empty to specify a ``wildcard'' value
+or may consist of the string ``-'' to specify ``no valid value''.
+The members of the list may be separated by whitespace and/or commas;
+the ``\e'' character may be used at the end of a line to specify
+line continuation.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm netgroup
+database.
+.Pp
+Lines that begin with a # are treated as comments.
+.Sh FILES
+.Bl -tag -width /etc/netgroup -compact
+.It Pa /etc/netgroup
+the netgroup database.
+.El
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr exports 5
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.Sh BUGS
+The interpretation of access restrictions based on the member tuples of a
+netgroup is left up to the various network applications.
+Also, it is not obvious how the domain specification
+applies to the BSD environment.
diff --git a/usr.sbin/mountd/pathnames.h b/usr.sbin/mountd/pathnames.h
new file mode 100644
index 000000000000..aa1c55523103
--- /dev/null
+++ b/usr.sbin/mountd/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * 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_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile
new file mode 100644
index 000000000000..35d0d0e21413
--- /dev/null
+++ b/usr.sbin/nfsd/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+=-DNFS
+MAN8= nfsd.0
+DPADD= ${LIBRPC}
+LDADD= -lrpc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 000000000000..4ee6c4b21277
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.8
@@ -0,0 +1,114 @@
+.\" Copyright (c) 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.
+.\"
+.\" @(#)nfsd.8 8.3 (Berkeley) 2/22/94
+.\"
+.Dd February 22, 1994
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm nfsd
+.Op Fl rut
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+.Nm Nfsd
+runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm nfsd
+must be running for a machine to operate as a server.
+.Pp
+Unless otherwise specified, four servers for
+.Tn UDP
+transport are started.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl r
+Register the
+.Tn NFS
+service with
+.Xr portmap 8
+without creating any servers.
+This option can be used along with the
+.Fl u
+or
+.Fl t
+options to re-register NFS if the portmap server is restarted.
+.It Fl n
+Specifies how many servers to create.
+.It Fl t
+Serve
+.Tn TCP NFS
+clients.
+.It Fl u
+Serve
+.Tn UDP NFS
+clients.
+.El
+.Pp
+For example,
+.Dq Li "nfsd -u -t 6"
+serves
+.Tn UDP
+and
+.Tn TCP
+transports using six daemons.
+.Pp
+A server should run enough daemons to handle
+the maximum level of concurrency from its clients,
+typically four to six.
+.Pp
+.Nm Nfsd
+listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094.
+.Pp
+The
+.Nm nfsd
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsd
+utility first appeared in 4.4BSD.
diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c
new file mode 100644
index 000000000000..63f4f005c2fb
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)nfsd.c 8.7 (Berkeley) 2/22/94";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s) fprintf(stderr,(s))
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArg = NULL; /* end of argv */
+
+#ifdef KERBEROS
+char lnam[ANAME_SZ];
+KTEXT_ST kt;
+AUTH_DAT auth;
+char inst[INST_SZ];
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+void setproctitle __P((char *));
+void usage __P((void));
+
+/*
+ * Nfs server daemon mostly just a user context for nfssvc()
+ *
+ * 1 - do file descriptor and signal cleanup
+ * 2 - fork the nfsd(s)
+ * 3 - create server socket(s)
+ * 4 - register socket with portmap
+ *
+ * For connectionless protocols, just pass the socket into the kernel via.
+ * nfssvc().
+ * For connection based sockets, loop doing accepts. When you get a new
+ * socket from accept, pass the msgsock into the kernel via. nfssvc().
+ * The arguments are:
+ * -c - support iso cltp clients
+ * -r - reregister with portmapper
+ * -t - support tcp nfs clients
+ * -u - support udp nfs clients
+ * followed by "n" which is the number of nfsds' to fork off
+ */
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ extern int optind;
+ struct group *grp;
+ struct nfsd_args nfsdargs;
+ struct passwd *pwd;
+ struct ucred *cr;
+ struct sockaddr_in inetaddr, inetpeer;
+#ifdef ISO
+ struct sockaddr_iso isoaddr, isopeer;
+#endif
+ fd_set ready, sockbits;
+ int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock;
+ int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock;
+ int tp4cnt, tp4flag, tp4sock, tpipcnt, tpipflag, tpipsock, udpflag;
+ char *cp, **cpp;
+
+ /* Save start and extent of argv for setproctitle. */
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 4
+ nfsdcnt = DEFNFSDCNT;
+ cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0;
+ tpipflag = udpflag = 0;
+#ifdef ISO
+#define GETOPT "cn:rtu"
+#define USAGE "[-crtu] [-n num_servers]"
+#else
+#define GETOPT "n:rtu"
+#define USAGE "[-rtu] [-n num_servers]"
+#endif
+ while ((ch = getopt(argc, argv, GETOPT)) != EOF)
+ switch (ch) {
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+#ifdef ISO
+ case 'c':
+ cltpflag = 1;
+ break;
+#ifdef notyet
+ case 'i':
+ tp4cnt = 1;
+ break;
+ case 'p':
+ tpipcnt = 1;
+ break;
+#endif /* notyet */
+#endif /* ISO */
+ default:
+ case '?':
+ usage();
+ };
+ argv += optind;
+ argc -= optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ nfsdcnt = atoi(argv[0]);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ }
+
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGTERM, SIG_IGN);
+ }
+ (void)signal(SIGCHLD, reapchild);
+
+ if (reregister) {
+ if (udpflag &&
+ !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT))
+ err(1, "can't register with portmap for UDP.");
+ if (tcpflag &&
+ !pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT))
+ err(1, "can't register with portmap for TCP.");
+ exit(0);
+ }
+ openlog("nfsd:", LOG_PID, LOG_DAEMON);
+
+ for (i = 0; i < nfsdcnt; i++) {
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit (1);
+ case 0:
+ break;
+ default:
+ continue;
+ }
+
+ setproctitle("nfsd-srv");
+ nfssvc_flag = NFSSVC_NFSD;
+ nsd.nsd_nfsd = NULL;
+#ifdef KERBEROS
+ nsd.nsd_authstr = (char *)kt.dat;
+#endif
+ while (nfssvc(nfssvc_flag, &nsd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit(1);
+ }
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
+#ifdef KERBEROS
+ kt.length = nsd.nsd_authlen;
+ kt.mbz = 0;
+ (void)strcpy(inst, "*");
+ if (krb_rd_req(&kt, "rcmd",
+ inst, nsd.nsd_haddr, &auth, "") == RD_AP_OK &&
+ krb_kntoln(&auth, lnam) == KSUCCESS &&
+ (pwd = getpwnam(lnam)) != NULL) {
+ cr = &nsd.nsd_cr;
+ cr->cr_uid = pwd->pw_uid;
+ cr->cr_groups[0] = pwd->pw_gid;
+ cr->cr_ngroups = 1;
+ setgrent();
+ while ((grp = getgrent()) != NULL) {
+ if (grp->gr_gid == cr->cr_groups[0])
+ continue;
+ for (cpp = grp->gr_mem;
+ *cpp != NULL; ++cpp)
+ if (!strcmp(*cpp, lnam))
+ break;
+ if (*cpp == NULL)
+ continue;
+ cr->cr_groups[cr->cr_ngroups++]
+ = grp->gr_gid;
+ if (cr->cr_ngroups == NGROUPS)
+ break;
+ }
+ endgrent();
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
+ }
+#endif /* KERBEROS */
+ }
+ exit(0);
+ }
+
+ /* If we are serving udp, set up the socket. */
+ if (udpflag) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create udp socket");
+ exit(1);
+ }
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(sock,
+ (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind udp addr");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ exit(1);
+ }
+ (void)close(sock);
+ }
+
+#ifdef ISO
+ /* If we are serving cltp, set up the socket. */
+ if (cltpflag) {
+ if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create cltp socket");
+ exit(1);
+ }
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(sock,
+ (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind cltp addr");
+ exit(1);
+ }
+#ifdef notyet
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+#endif /* notyet */
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't add UDP socket");
+ exit(1);
+ }
+ close(sock);
+ }
+#endif /* ISO */
+
+ /* Now set up the master server socket waiting for tcp connections. */
+ on = 1;
+ FD_ZERO(&sockbits);
+ connect_type_cnt = 0;
+ if (tcpflag) {
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tcp socket");
+ exit(1);
+ }
+ if (setsockopt(tcpsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tcpsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tcpsock, &sockbits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+
+#ifdef notyet
+ /* Now set up the master server socket waiting for tp4 connections. */
+ if (tp4flag) {
+ if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tp4 socket");
+ exit(1);
+ }
+ if (setsockopt(tp4sock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(tp4sock,
+ (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tp4 addr");
+ exit(1);
+ }
+ if (listen(tp4sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tp4sock, &sockbits);
+ maxsock = tp4sock;
+ connect_type_cnt++;
+ }
+
+ /* Now set up the master server socket waiting for tpip connections. */
+ if (tpipflag) {
+ if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tpip socket");
+ exit(1);
+ }
+ if (setsockopt(tpipsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tpipsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tpipsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tpipsock, &sockbits);
+ maxsock = tpipsock;
+ connect_type_cnt++;
+ }
+#endif /* notyet */
+
+ if (connect_type_cnt == 0)
+ exit(0);
+
+ setproctitle("nfsd-master");
+
+ /*
+ * Loop forever accepting connections and passing the sockets
+ * into the kernel for the mounts.
+ */
+ for (;;) {
+ ready = sockbits;
+ if (connect_type_cnt > 1) {
+ if (select(maxsock + 1,
+ &ready, NULL, NULL, NULL) < 1) {
+ syslog(LOG_ERR, "select failed: %m");
+ exit(1);
+ }
+ }
+ if (tcpflag && FD_ISSET(tcpsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero));
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = sizeof(inetpeer);
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#ifdef notyet
+ if (tp4flag && FD_ISSET(tp4sock, &ready)) {
+ len = sizeof(isopeer);
+ if ((msgsock = accept(tp4sock,
+ (struct sockaddr *)&isopeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&isopeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+ if (tpipflag && FD_ISSET(tpipsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tpipsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "Accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#endif /* notyet */
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "nfsd %s\n", USAGE);
+ exit(1);
+}
+
+void
+nonfs(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available.");
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+
+ while (wait3(NULL, WNOHANG, NULL));
+}
+
+void
+setproctitle(a)
+ char *a;
+{
+ register char *cp;
+ char buf[80];
+
+ cp = Argv[0];
+ (void)snprintf(buf, sizeof(buf), "%s", a);
+ (void)strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 000000000000..84e9f0c3b152
--- /dev/null
+++ b/usr.sbin/nologin/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+MAN8= nologin.0
+
+nologin clean depend lint tags:
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/nologin.sh ${DESTDIR}/sbin/nologin
+
+cleandir:
+ rm -f nologin.0
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nologin/nologin.8 b/usr.sbin/nologin/nologin.8
new file mode 100644
index 000000000000..32a7e73070b8
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os BSD 4.4
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm nologin
+.Sh DESCRIPTION
+.Nm Nologin
+displays a message that an account is not available and
+exits non-zero.
+It is intended as a replacement shell field for accounts that
+have been disabled.
+.Sh SEE ALSO
+.Xr login 1
+.Sh HISTORY
+The
+.Nm nologin
+command appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nologin/nologin.sh b/usr.sbin/nologin/nologin.sh
new file mode 100644
index 000000000000..8ad87fb8d872
--- /dev/null
+++ b/usr.sbin/nologin/nologin.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)nologin.sh 8.1 (Berkeley) 6/5/93
+#
+
+echo 'This account is currently not available.'
+exit 1