summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile17
-rw-r--r--sbin/Makefile.inc20
-rw-r--r--sbin/adjkerntz/Makefile5
-rw-r--r--sbin/adjkerntz/adjkerntz.8179
-rw-r--r--sbin/adjkerntz/adjkerntz.c350
-rw-r--r--sbin/adjkerntz/pathnames.h29
-rw-r--r--sbin/badsect/Makefile6
-rw-r--r--sbin/badsect/badsect.8132
-rw-r--r--sbin/badsect/badsect.c230
-rw-r--r--sbin/bsdlabel/Makefile8
-rw-r--r--sbin/bsdlabel/bsdlabel.5384
-rw-r--r--sbin/bsdlabel/bsdlabel.8384
-rw-r--r--sbin/bsdlabel/bsdlabel.c1455
-rw-r--r--sbin/bsdlabel/dkcksum.c53
-rw-r--r--sbin/bsdlabel/pathnames.h40
-rw-r--r--sbin/ccdconfig/Makefile11
-rw-r--r--sbin/ccdconfig/ccdconfig.8156
-rw-r--r--sbin/ccdconfig/ccdconfig.c714
-rw-r--r--sbin/ccdconfig/pathnames.h35
-rw-r--r--sbin/clri/Makefile6
-rw-r--r--sbin/clri/clri.877
-rw-r--r--sbin/clri/clri.c140
-rw-r--r--sbin/comcontrol/Makefile6
-rw-r--r--sbin/comcontrol/comcontrol.863
-rw-r--r--sbin/comcontrol/comcontrol.c108
-rw-r--r--sbin/cxconfig/Makefile4
-rw-r--r--sbin/cxconfig/cxconfig.8297
-rw-r--r--sbin/cxconfig/cxconfig.c766
-rw-r--r--sbin/disklabel/Makefile8
-rw-r--r--sbin/disklabel/disklabel.5384
-rw-r--r--sbin/disklabel/disklabel.8384
-rw-r--r--sbin/disklabel/disklabel.c1455
-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.868
-rw-r--r--sbin/dmesg/dmesg.c168
-rw-r--r--sbin/dset/Makefile7
-rw-r--r--sbin/dset/dset.878
-rw-r--r--sbin/dset/dset.c305
-rw-r--r--sbin/dump/Makefile25
-rw-r--r--sbin/dump/dump.8354
-rw-r--r--sbin/dump/dump.h213
-rw-r--r--sbin/dump/dumprmt.c409
-rw-r--r--sbin/dump/itime.c271
-rw-r--r--sbin/dump/main.c578
-rw-r--r--sbin/dump/optr.c530
-rw-r--r--sbin/dump/pathnames.h42
-rw-r--r--sbin/dump/tape.c859
-rw-r--r--sbin/dump/traverse.c615
-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.c322
-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/dumpon/Makefile6
-rw-r--r--sbin/dumpon/dumpon.8100
-rw-r--r--sbin/dumpon/dumpon.c129
-rw-r--r--sbin/fdisk/Makefile9
-rw-r--r--sbin/fdisk/fdisk.8409
-rw-r--r--sbin/fdisk/fdisk.c1316
-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.t452
-rw-r--r--sbin/fsck/SMM.doc/4.t1424
-rw-r--r--sbin/fsck/SMM.doc/Makefile7
-rw-r--r--sbin/fsck/dir.c720
-rw-r--r--sbin/fsck/fsck.8298
-rw-r--r--sbin/fsck/fsck.h293
-rw-r--r--sbin/fsck/inode.c619
-rw-r--r--sbin/fsck/main.c342
-rw-r--r--sbin/fsck/pass1.c319
-rw-r--r--sbin/fsck/pass1b.c101
-rw-r--r--sbin/fsck/pass2.c434
-rw-r--r--sbin/fsck/pass3.c72
-rw-r--r--sbin/fsck/pass4.c135
-rw-r--r--sbin/fsck/pass5.c338
-rw-r--r--sbin/fsck/preen.c383
-rw-r--r--sbin/fsck/setup.c478
-rw-r--r--sbin/fsck/utilities.c595
-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.t452
-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.c720
-rw-r--r--sbin/fsck_ffs/fsck.h293
-rw-r--r--sbin/fsck_ffs/fsck_ffs.8298
-rw-r--r--sbin/fsck_ffs/inode.c619
-rw-r--r--sbin/fsck_ffs/main.c342
-rw-r--r--sbin/fsck_ffs/pass1.c319
-rw-r--r--sbin/fsck_ffs/pass1b.c101
-rw-r--r--sbin/fsck_ffs/pass2.c434
-rw-r--r--sbin/fsck_ffs/pass3.c72
-rw-r--r--sbin/fsck_ffs/pass4.c135
-rw-r--r--sbin/fsck_ffs/pass5.c338
-rw-r--r--sbin/fsck_ffs/preen.c383
-rw-r--r--sbin/fsck_ffs/setup.c478
-rw-r--r--sbin/fsck_ffs/utilities.c595
-rw-r--r--sbin/fsck_ifs/Makefile9
-rw-r--r--sbin/fsck_ifs/dir.c720
-rw-r--r--sbin/fsck_ifs/fsck.h293
-rw-r--r--sbin/fsck_ifs/fsck_ifs.8298
-rw-r--r--sbin/fsck_ifs/inode.c619
-rw-r--r--sbin/fsck_ifs/main.c342
-rw-r--r--sbin/fsck_ifs/pass1.c319
-rw-r--r--sbin/fsck_ifs/pass1b.c101
-rw-r--r--sbin/fsck_ifs/pass2.c434
-rw-r--r--sbin/fsck_ifs/pass3.c72
-rw-r--r--sbin/fsck_ifs/pass4.c135
-rw-r--r--sbin/fsck_ifs/pass5.c338
-rw-r--r--sbin/fsck_ifs/preen.c383
-rw-r--r--sbin/fsck_ifs/setup.c478
-rw-r--r--sbin/fsck_ifs/utilities.c595
-rw-r--r--sbin/fsdb/Makefile15
-rw-r--r--sbin/fsdb/fsdb.8235
-rw-r--r--sbin/fsdb/fsdb.c895
-rw-r--r--sbin/fsdb/fsdb.h56
-rw-r--r--sbin/fsdb/fsdbutil.c204
-rw-r--r--sbin/i386/Makefile6
-rw-r--r--sbin/i386/Makefile.inc3
-rw-r--r--sbin/i386/comcontrol/Makefile6
-rw-r--r--sbin/i386/comcontrol/comcontrol.863
-rw-r--r--sbin/i386/comcontrol/comcontrol.c108
-rw-r--r--sbin/i386/cxconfig/Makefile4
-rw-r--r--sbin/i386/cxconfig/cxconfig.8297
-rw-r--r--sbin/i386/cxconfig/cxconfig.c766
-rw-r--r--sbin/i386/fdisk/Makefile9
-rw-r--r--sbin/i386/fdisk/fdisk.8409
-rw-r--r--sbin/i386/fdisk/fdisk.c1316
-rw-r--r--sbin/i386/ft/Makefile7
-rw-r--r--sbin/i386/ft/ft.886
-rw-r--r--sbin/i386/ft/ft.c517
-rw-r--r--sbin/i386/ft/ftecc.c479
-rw-r--r--sbin/i386/mount_msdos/Makefile16
-rw-r--r--sbin/i386/mount_msdos/mount_msdos.8119
-rw-r--r--sbin/i386/mount_msdos/mount_msdos.c212
-rw-r--r--sbin/i386/nextboot/Makefile9
-rw-r--r--sbin/i386/nextboot/nextboot.888
-rw-r--r--sbin/i386/nextboot/nextboot.c231
-rw-r--r--sbin/ifconfig/Makefile11
-rw-r--r--sbin/ifconfig/ifconfig.8305
-rw-r--r--sbin/ifconfig/ifconfig.c1169
-rw-r--r--sbin/init/Makefile19
-rw-r--r--sbin/init/NOTES112
-rw-r--r--sbin/init/init.8301
-rw-r--r--sbin/init/init.c1436
-rw-r--r--sbin/init/pathnames.h42
-rw-r--r--sbin/ipfw/Makefile5
-rw-r--r--sbin/ipfw/ipfw.8358
-rw-r--r--sbin/ipfw/ipfw.c962
-rw-r--r--sbin/ldconfig/Makefile13
-rw-r--r--sbin/ldconfig/ldconfig.8131
-rw-r--r--sbin/ldconfig/ldconfig.c521
-rw-r--r--sbin/md5/Makefile9
-rw-r--r--sbin/md5/global.h30
-rw-r--r--sbin/md5/md5.146
-rw-r--r--sbin/md5/md5.c177
-rw-r--r--sbin/mknod/Makefile6
-rw-r--r--sbin/mknod/mknod.8108
-rw-r--r--sbin/mknod/mknod.c118
-rw-r--r--sbin/modload/Makefile42
-rw-r--r--sbin/modload/modload.8123
-rw-r--r--sbin/modload/modload.c402
-rw-r--r--sbin/modload/pathnames.h5
-rw-r--r--sbin/modunload/Makefile42
-rw-r--r--sbin/modunload/modunload.876
-rw-r--r--sbin/modunload/modunload.c125
-rw-r--r--sbin/modunload/pathnames.h3
-rw-r--r--sbin/mount/Makefile8
-rw-r--r--sbin/mount/getmntopts.3174
-rw-r--r--sbin/mount/getmntopts.c98
-rw-r--r--sbin/mount/mntopts.h82
-rw-r--r--sbin/mount/mount.8304
-rw-r--r--sbin/mount/mount.c600
-rw-r--r--sbin/mount/mount_ufs.c149
-rw-r--r--sbin/mount/pathnames.h38
-rw-r--r--sbin/mount_cd9660/Makefile11
-rw-r--r--sbin/mount_cd9660/mount_cd9660.897
-rw-r--r--sbin/mount_cd9660/mount_cd9660.c151
-rw-r--r--sbin/mount_ext2fs/Makefile11
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.875
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.c127
-rw-r--r--sbin/mount_ifs/Makefile8
-rw-r--r--sbin/mount_ifs/getmntopts.3174
-rw-r--r--sbin/mount_ifs/getmntopts.c98
-rw-r--r--sbin/mount_ifs/mntopts.h82
-rw-r--r--sbin/mount_ifs/mount.8304
-rw-r--r--sbin/mount_ifs/mount.c600
-rw-r--r--sbin/mount_ifs/mount_ufs.c149
-rw-r--r--sbin/mount_ifs/pathnames.h38
-rw-r--r--sbin/mount_lfs/Makefile11
-rw-r--r--sbin/mount_lfs/mount_lfs.8129
-rw-r--r--sbin/mount_lfs/mount_lfs.c165
-rw-r--r--sbin/mount_lfs/pathnames.h36
-rw-r--r--sbin/mount_msdos/Makefile16
-rw-r--r--sbin/mount_msdos/mount_msdos.8119
-rw-r--r--sbin/mount_msdos/mount_msdos.c212
-rw-r--r--sbin/mount_msdosfs/Makefile16
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.8119
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.c212
-rw-r--r--sbin/mount_nfs/Makefile19
-rw-r--r--sbin/mount_nfs/mount_nfs.8294
-rw-r--r--sbin/mount_nfs/mount_nfs.c788
-rw-r--r--sbin/mount_null/Makefile11
-rw-r--r--sbin/mount_null/mount_null.8221
-rw-r--r--sbin/mount_null/mount_null.c145
-rw-r--r--sbin/mount_nullfs/Makefile11
-rw-r--r--sbin/mount_nullfs/mount_nullfs.8221
-rw-r--r--sbin/mount_nullfs/mount_nullfs.c145
-rw-r--r--sbin/mount_portal/Makefile13
-rw-r--r--sbin/mount_portal/activate.c215
-rw-r--r--sbin/mount_portal/conf.c337
-rw-r--r--sbin/mount_portal/mount_portal.8137
-rw-r--r--sbin/mount_portal/mount_portal.c291
-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.c108
-rw-r--r--sbin/mount_portal/pt_tcp.c164
-rw-r--r--sbin/mount_portalfs/Makefile13
-rw-r--r--sbin/mount_portalfs/activate.c215
-rw-r--r--sbin/mount_portalfs/conf.c337
-rw-r--r--sbin/mount_portalfs/mount_portalfs.8137
-rw-r--r--sbin/mount_portalfs/mount_portalfs.c291
-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.c108
-rw-r--r--sbin/mount_portalfs/pt_tcp.c164
-rw-r--r--sbin/mount_std/Makefile16
-rw-r--r--sbin/mount_std/mount_devfs.891
-rw-r--r--sbin/mount_std/mount_fdesc.8172
-rw-r--r--sbin/mount_std/mount_kernfs.8132
-rw-r--r--sbin/mount_std/mount_procfs.8254
-rw-r--r--sbin/mount_std/mount_std.8137
-rw-r--r--sbin/mount_std/mount_std.c132
-rw-r--r--sbin/mount_umap/Makefile11
-rw-r--r--sbin/mount_umap/mount_umap.8131
-rw-r--r--sbin/mount_umap/mount_umap.c249
-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.c249
-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/Makefile11
-rw-r--r--sbin/mount_union/mount_union.8201
-rw-r--r--sbin/mount_union/mount_union.c152
-rw-r--r--sbin/mount_unionfs/Makefile11
-rw-r--r--sbin/mount_unionfs/mount_unionfs.8201
-rw-r--r--sbin/mount_unionfs/mount_unionfs.c152
-rw-r--r--sbin/mountd/Makefile9
-rw-r--r--sbin/mountd/exports.5252
-rw-r--r--sbin/mountd/mountd.8132
-rw-r--r--sbin/mountd/mountd.c2139
-rw-r--r--sbin/mountd/netgroup.5160
-rw-r--r--sbin/mountd/pathnames.h39
-rw-r--r--sbin/newfs/Makefile14
-rw-r--r--sbin/newfs/mkfs.c1301
-rw-r--r--sbin/newfs/newfs.8299
-rw-r--r--sbin/newfs/newfs.c691
-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.c483
-rw-r--r--sbin/newlfs/newlfs.898
-rw-r--r--sbin/nextboot/Makefile9
-rw-r--r--sbin/nextboot/nextboot.888
-rw-r--r--sbin/nextboot/nextboot.c231
-rw-r--r--sbin/nfsd/Makefile17
-rw-r--r--sbin/nfsd/nfsd.8134
-rw-r--r--sbin/nfsd/nfsd.c673
-rw-r--r--sbin/nfsiod/Makefile7
-rw-r--r--sbin/nfsiod/nfsiod.893
-rw-r--r--sbin/nfsiod/nfsiod.c185
-rw-r--r--sbin/nologin/Makefile12
-rw-r--r--sbin/nologin/nologin.562
-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.8346
-rw-r--r--sbin/ping/ping.c1090
-rw-r--r--sbin/quotacheck/Makefile8
-rw-r--r--sbin/quotacheck/preen.c383
-rw-r--r--sbin/quotacheck/quotacheck.8157
-rw-r--r--sbin/quotacheck/quotacheck.c650
-rw-r--r--sbin/reboot/Makefile18
-rw-r--r--sbin/reboot/boot_i386.8169
-rw-r--r--sbin/reboot/reboot.8110
-rw-r--r--sbin/reboot/reboot.c208
-rw-r--r--sbin/restore/Makefile15
-rw-r--r--sbin/restore/dirs.c751
-rw-r--r--sbin/restore/extern.h108
-rw-r--r--sbin/restore/interactive.c755
-rw-r--r--sbin/restore/main.c347
-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.c1361
-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/keywords46
-rw-r--r--sbin/route/route.8332
-rw-r--r--sbin/route/route.c1580
-rw-r--r--sbin/routed/Makefile13
-rw-r--r--sbin/routed/Makefile.inc1
-rw-r--r--sbin/routed/defs.h65
-rw-r--r--sbin/routed/main.c89
-rw-r--r--sbin/routed/md5.c325
-rw-r--r--sbin/routed/pathnames.h4
-rw-r--r--sbin/routed/routed.828
-rw-r--r--sbin/routed/routed.h174
-rw-r--r--sbin/routed/rtquery/Makefile10
-rw-r--r--sbin/routed/rtquery/md5.c325
-rw-r--r--sbin/routed/rtquery/rtquery.c14
-rw-r--r--sbin/routed/table.c54
-rw-r--r--sbin/savecore/Makefile11
-rw-r--r--sbin/savecore/savecore.8121
-rw-r--r--sbin/savecore/savecore.c664
-rw-r--r--sbin/scsi/Makefile8
-rw-r--r--sbin/scsi/scsi.8251
-rw-r--r--sbin/scsi/scsi.c974
-rw-r--r--sbin/scsiformat/Makefile9
-rw-r--r--sbin/scsiformat/scsiformat.8102
-rw-r--r--sbin/scsiformat/scsiformat.sh143
-rw-r--r--sbin/shutdown/Makefile9
-rw-r--r--sbin/shutdown/pathnames.h41
-rw-r--r--sbin/shutdown/shutdown.8150
-rw-r--r--sbin/shutdown/shutdown.c468
-rw-r--r--sbin/slattach/Makefile14
-rw-r--r--sbin/slattach/slattach.8263
-rw-r--r--sbin/slattach/slattach.c601
-rw-r--r--sbin/startslip/Makefile6
-rw-r--r--sbin/startslip/startslip.1200
-rw-r--r--sbin/startslip/startslip.c590
-rw-r--r--sbin/startslip/uucplock.c161
-rw-r--r--sbin/swapon/Makefile6
-rw-r--r--sbin/swapon/swapon.891
-rw-r--r--sbin/swapon/swapon.c122
-rw-r--r--sbin/sysctl/Makefile6
-rw-r--r--sbin/sysctl/pathconf.c229
-rw-r--r--sbin/sysctl/sysctl.8242
-rw-r--r--sbin/sysctl/sysctl.c463
-rw-r--r--sbin/tunefs/Makefile6
-rw-r--r--sbin/tunefs/tunefs.8150
-rw-r--r--sbin/tunefs/tunefs.c311
-rw-r--r--sbin/umount/Makefile6
-rw-r--r--sbin/umount/umount.8145
-rw-r--r--sbin/umount/umount.c424
370 files changed, 87307 insertions, 987 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
new file mode 100644
index 0000000..036b4b2
--- /dev/null
+++ b/sbin/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.5 (Berkeley) 3/31/94
+
+# XXX MISSING: icheck ncheck
+
+SUBDIR= adjkerntz badsect ccdconfig clri disklabel dmesg dset dump dumpfs \
+ dumplfs dumpon fsck fsdb ifconfig init ipfw md5 mknod modload \
+ modunload mount mount_cd9660 mount_ext2fs \
+ mount_lfs mount_nfs mount_null mount_portal mount_std \
+ mount_umap mount_union mountd newfs newlfs nfsd nfsiod \
+ nologin ping quotacheck reboot restore route routed savecore scsi \
+ scsiformat shutdown slattach startslip swapon tunefs umount
+
+.if exists(${.CURDIR}/${MACHINE})
+SUBDIR+= ${MACHINE}
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc
new file mode 100644
index 0000000..71c9438
--- /dev/null
+++ b/sbin/Makefile.inc
@@ -0,0 +1,20 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93
+
+BINDIR?= /sbin
+NOSHARED?= YES
+
+.if exists(${.CURDIR}/../../lib/libcrypt/obj)
+SCRYPTOBJDIR= ${.CURDIR}/../../lib/libcrypt/obj
+.else
+SCRYPTOBJDIR= ${.CURDIR}/../../lib/libcrypt
+.endif
+
+.if exists (${.CURDIR}/../../secure)
+
+.if exists(${.CURDIR}/../../secure/lib/libcrypt/obj)
+DESCRYPTOBJDIR= ${.CURDIR}/../../secure/lib/libcrypt/obj
+.else
+DESCRYPTOBJDIR= ${.CURDIR}/../../secure/lib/libcrypt
+.endif
+
+.endif
diff --git a/sbin/adjkerntz/Makefile b/sbin/adjkerntz/Makefile
new file mode 100644
index 0000000..ee6e5c1
--- /dev/null
+++ b/sbin/adjkerntz/Makefile
@@ -0,0 +1,5 @@
+PROG= adjkerntz
+MAN8= adjkerntz.8
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/sbin/adjkerntz/adjkerntz.8 b/sbin/adjkerntz/adjkerntz.8
new file mode 100644
index 0000000..eb72fc6
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.8
@@ -0,0 +1,179 @@
+.\" Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 4, 1996
+.Dt ADJKERNTZ 8
+.Os FreeBSD
+.Sh NAME
+.Nm adjkerntz
+.Nd "adjust local time CMOS clock to reflect time zone changes and keep current timezone offset for the kernel"
+.Sh SYNOPSIS
+.Nm adjkerntz
+.Fl i
+.Nm adjkerntz
+.Fl a Op Fl s
+.Sh DESCRIPTION
+.Nm Adjkerntz
+maintains the proper relationship between the kernel clock, which
+is always set to UTC, and the CMOS clock, which may be set to local
+time.
+.Nm Adjkerntz
+also informs the kernel about machine timezone shifts to
+maintain proper timestamps for local time file systems such as the MS-DOS
+file system. The main purpose of this thing is not general fixing of
+initially broken MS-DOS file timestamp idea but keeping
+the same timestamps between FreeBSD MS-DOS file system
+and MS-DOS operating system installed on the same
+machine.
+If the file
+.Pa /etc/wall_cmos_clock
+exists, it means that CMOS clock keeps local time (MS-DOS and MS-Windows
+compatible mode).
+If that file does not exist, it means that the CMOS clock keeps UTC time.
+.Nm Adjkerntz
+passes this state to the
+.Pa machdep.wall_cmos_clock
+kernel variable.
+.Pp
+Adjustments may be needed at system startup and shutdown, and
+whenever a time zone change occurs.
+To handle these different situations,
+.Nm adjkerntz
+is invoked in two ways:
+.Bl -tag -width 4n
+.It Cm Fl i
+This form handles system startups and shutdowns.
+.Nm Adjkerntz
+is invoked with this option from
+.Pa /etc/rc
+on entry to multi-user mode, before any other daemons have been started.
+.Nm Adjkerntz
+puts itself into the background.
+Then, for a local time CMOS clock,
+.Nm adjkerntz
+reads the local time from it
+and sets the kernel clock to the corresponding UTC time.
+.Nm Adjkerntz
+also stores the local time zone offset into the
+.Pa machdep.adjkerntz
+kernel variable, for use by subsequent invocations of
+.Nm "'adjkerntz -a'"
+and by local time file systems.
+.Pp
+For a local time CMOS clock
+.Nm "'adjkerntz -i'"
+pauses, and remains inactive as a background daemon until it
+receives a SIGTERM.
+The SIGTERM will normally be sent by
+.Xr init 8
+when the system leaves multi-user mode (usually, because the system
+is being shut down).
+After receiving the SIGTERM,
+.Nm adjkerntz
+reads the UTC kernel clock and updates the CMOS clock, if necessary,
+to ensure that it reflects the current local time zone.
+Then
+.Nm adjkerntz
+exits.
+.It Cm Fl a Op Fl s
+This form is used to update the local time CMOS clock and kernel
+.Pa machdep.adjkerntz
+variable when time zone changes occur,
+e.g., when entering or leaving daylight savings time.
+.Nm Adjkerntz
+uses the kernel clock's UTC time,
+the previously stored
+time zone offset, and the changed time zone rule to
+calculate a new time zone offset.
+It stores the new offset into the
+.Pa machdep.adjkerntz
+kernel variable, and updates the wall CMOS clock to the new local time.
+If
+.Nm "'adjkerntz -a'"
+was started at a nonexistent time (during a timezone change), it exits
+with a warning diagnostic unless the
+.Fl s
+option was used, in which case
+.Nm adjkerntz
+sleeps 30 minutes and tries again.
+.Pp
+This form should be invoked from root's
+.Xr crontab 5
+every half hour between midnight and 4am, when most modern time
+zone changes occur.
+Warning: don't use the
+.Fl s
+option in a
+.Xr crontab 5
+command line, or multiple
+.Nm "'adjkerntz -a'"
+instances could conflict with each other.
+.El
+.Pp
+.Nm Adjkerntz
+clears the kernel timezone structure and makes the kernel clock run
+in the UTC time zone.
+Super-user privileges are required for all operations.
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev TZ
+Time zone change rule, see
+.Xr tzset 3 ;
+not needed when
+.Xr tzsetup 8
+or
+.Xr zic 8
+is used.
+.Sh FILES
+.Bl -tag -width /etc/wall_cmos_clock -compact
+.It Pa /etc/localtime
+Current zoneinfo file, see
+.Xr tzsetup 8
+and
+.Xr zic 8 .
+.It Pa /etc/wall_cmos_clock
+Empty file.
+Its presence indicates that the machine's CMOS clock is set to local
+time, while its absence indicates a UTC CMOS clock.
+.Sh SEE ALSO
+.Xr tzset 3 ,
+.Xr crontab 5 ,
+.Xr rc 8 ,
+.Xr mount_msdos 8 ,
+.Xr sysctl 8 ,
+.Xr tzsetup 8 ,
+.Xr zic 8 .
+.Sh DIAGNOSTICS
+No diagnostics.
+If an error occurs,
+.Nm adjkerntz
+logs an error message via
+.Xr syslog 3
+and exits with a nonzero return code.
+.Sh AUTHOR
+Andrey A. Chernov <ache@astral.msk.su>
+.Sh HISTORY
+The
+.Nm adjkerntz
+command appeared in FreeBSD 1.0.1.
diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c
new file mode 100644
index 0000000..8cd3ff9
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993
+ *
+ * Fix kernel time value if machine run wall CMOS clock
+ * (and /etc/wall_cmos_clock file present)
+ * using zoneinfo rules or direct TZ environment variable set.
+ * Use Joerg Wunsch idea for seconds accurate offset calculation
+ * with Garrett Wollman and Bruce Evans fixes.
+ *
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <machine/cpu.h>
+#include <sys/sysctl.h>
+
+#include "pathnames.h"
+
+/*#define DEBUG*/
+
+#define True (1)
+#define False (0)
+#define Unknown (-1)
+
+#define REPORT_PERIOD (30*60)
+
+void fake() {}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct tm local, utc;
+ struct timeval tv, *stv;
+ struct timezone tz, *stz;
+ int kern_offset, wall_clock, disrtcset;
+ size_t len;
+ int mib[2];
+ /* Avoid time_t here, can be unsigned long or worse */
+ long offset, utcsec, localsec, diff;
+ time_t initial_sec, final_sec;
+ int ch;
+ int initial_isdst = -1, final_isdst;
+ int need_restore = False, sleep_mode = False, looping,
+ init = Unknown;
+ sigset_t mask, emask;
+
+ while ((ch = getopt(argc, argv, "ais")) != EOF)
+ switch((char)ch) {
+ case 'i': /* initial call, save offset */
+ if (init != Unknown)
+ goto usage;
+ init = True;
+ break;
+ case 'a': /* adjustment call, use saved offset */
+ if (init != Unknown)
+ goto usage;
+ init = False;
+ break;
+ case 's':
+ sleep_mode = True;
+ break;
+ default:
+ usage:
+ fprintf(stderr, "Usage:\n\
+\tadjkerntz -i\t\t(initial call from /etc/rc)\n\
+\tadjkerntz -a [-s]\t(adjustment call, -s for sleep/retry mode)\n");
+ return 2;
+ }
+ if (init == Unknown)
+ goto usage;
+ if (init)
+ sleep_mode = True;
+
+ sigemptyset(&mask);
+ sigemptyset(&emask);
+ sigaddset(&mask, SIGTERM);
+
+ openlog("adjkerntz", LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+ (void) signal(SIGHUP, SIG_IGN);
+
+ if (init && daemon(0, 1)) {
+ syslog(LOG_ERR, "daemon: %m");
+ return 1;
+ }
+
+again:
+ (void) sigprocmask(SIG_BLOCK, &mask, NULL);
+ (void) signal(SIGTERM, fake);
+
+ diff = 0;
+ stv = NULL;
+ stz = NULL;
+ looping = False;
+
+ wall_clock = (access(_PATH_CLOCK, F_OK) == 0);
+ if (init && !sleep_mode) {
+ init = False;
+ if (!wall_clock)
+ return 0;
+ }
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_ADJKERNTZ;
+ len = sizeof(kern_offset);
+ if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl(get_offset): %m");
+ return 1;
+ }
+
+/****** Critical section, do all things as fast as possible ******/
+
+ /* get local CMOS clock and possible kernel offset */
+ if (gettimeofday(&tv, &tz)) {
+ syslog(LOG_ERR, "gettimeofday: %m");
+ return 1;
+ }
+
+ /* get the actual local timezone difference */
+ initial_sec = tv.tv_sec;
+
+recalculate:
+ local = *localtime(&initial_sec);
+ if (diff == 0)
+ initial_isdst = local.tm_isdst;
+ utc = *gmtime(&initial_sec);
+ local.tm_isdst = utc.tm_isdst = initial_isdst;
+
+ /* calculate local CMOS diff from GMT */
+
+ utcsec = mktime(&utc);
+ localsec = mktime(&local);
+ if (utcsec == -1 || localsec == -1) {
+ /*
+ * XXX user can only control local time, and it is
+ * unacceptable to fail here for init. 2:30 am in the
+ * middle of the nonexistent hour means 3:30 am.
+ */
+ syslog(LOG_WARNING,
+ "Warning: nonexistent %s time.",
+ utcsec == -1 && localsec == -1 ? "UTC time and local" :
+ utcsec == -1 ? "UTC" : "local");
+ if (!sleep_mode) {
+ syslog(LOG_WARNING, "Giving up.");
+ return 1;
+ }
+ syslog(LOG_WARNING, "Will retry after %d minutes.",
+ REPORT_PERIOD / 60);
+ (void) signal(SIGTERM, SIG_DFL);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ (void) sleep(REPORT_PERIOD);
+ goto again;
+ }
+ offset = utcsec - localsec;
+#ifdef DEBUG
+ fprintf(stderr, "Initial offset: %ld secs\n", offset);
+#endif
+
+ /* correct the kerneltime for this diffs */
+ /* subtract kernel offset, if present, old offset too */
+
+ diff = offset - tz.tz_minuteswest * 60 - kern_offset;
+
+ if (diff != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Initial diff: %ld secs\n", diff);
+#endif
+ /* Yet one step for final time */
+
+ final_sec = initial_sec + diff;
+
+ /* get the actual local timezone difference */
+ local = *localtime(&final_sec);
+ final_isdst = diff < 0 ? initial_isdst : local.tm_isdst;
+ if (diff > 0 && initial_isdst != final_isdst) {
+ if (looping)
+ goto bad_final;
+ looping = True;
+ initial_isdst = final_isdst;
+ goto recalculate;
+ }
+ utc = *gmtime(&final_sec);
+ local.tm_isdst = utc.tm_isdst = final_isdst;
+
+ utcsec = mktime(&utc);
+ localsec = mktime(&local);
+ if (utcsec == -1 || localsec == -1) {
+ bad_final:
+ /*
+ * XXX as above. The user has even less control,
+ * but perhaps we never get here.
+ */
+ syslog(LOG_WARNING,
+ "Warning: nonexistent final %s time.",
+ utcsec == -1 && localsec == -1 ? "UTC time and local" :
+ utcsec == -1 ? "UTC" : "local");
+ if (!sleep_mode) {
+ syslog(LOG_WARNING, "Giving up.");
+ return 1;
+ }
+ syslog(LOG_WARNING, "Will retry after %d minutes.",
+ REPORT_PERIOD / 60);
+ (void) signal(SIGTERM, SIG_DFL);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ (void) sleep(REPORT_PERIOD);
+ goto again;
+ }
+ offset = utcsec - localsec;
+#ifdef DEBUG
+ fprintf(stderr, "Final offset: %ld secs\n", offset);
+#endif
+
+ /* correct the kerneltime for this diffs */
+ /* subtract kernel offset, if present, old offset too */
+
+ diff = offset - tz.tz_minuteswest * 60 - kern_offset;
+
+ if (diff != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Final diff: %ld secs\n", diff);
+#endif
+ tv.tv_sec += diff;
+ tv.tv_usec = 0; /* we are restarting here... */
+ stv = &tv;
+ }
+ }
+
+ if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) {
+ tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */
+ stz = &tz;
+ }
+ if (!wall_clock && stz == NULL)
+ stv = NULL;
+
+ /* if init or UTC clock and offset/date will be changed, */
+ /* disable RTC modification for a while. */
+
+ if ( (init && stv != NULL)
+ || ((init || !wall_clock) && kern_offset != offset)
+ ) {
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_DISRTCSET;
+ len = sizeof(disrtcset);
+ if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl(get_disrtcset): %m");
+ return 1;
+ }
+ if (disrtcset == 0) {
+ disrtcset = 1;
+ need_restore = True;
+ if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(set_disrtcset): %m");
+ return 1;
+ }
+ }
+ }
+
+ if ( ( (init && (stv != NULL || stz != NULL))
+ || (stz != NULL && stv == NULL)
+ )
+ && settimeofday(stv, stz)
+ ) {
+ syslog(LOG_ERR, "settimeofday: %m");
+ return 1;
+ }
+
+ /* setting CPU_ADJKERNTZ have a side effect: resettodr(), which */
+ /* can be disabled by CPU_DISRTCSET, so if init or UTC clock */
+ /* -- don't write RTC, else write RTC. */
+
+ if (kern_offset != offset) {
+ kern_offset = offset;
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_ADJKERNTZ;
+ len = sizeof(kern_offset);
+ if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(update_offset): %m");
+ return 1;
+ }
+ }
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_WALLCLOCK;
+ len = sizeof(wall_clock);
+ if (sysctl(mib, 2, NULL, NULL, &wall_clock, len) == -1) {
+ syslog(LOG_ERR, "sysctl(put_wallclock): %m");
+ return 1;
+ }
+
+ if (need_restore) {
+ need_restore = False;
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_DISRTCSET;
+ disrtcset = 0;
+ len = sizeof(disrtcset);
+ if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) {
+ syslog(LOG_ERR, "sysctl(restore_disrtcset): %m");
+ return 1;
+ }
+ }
+
+/****** End of critical section ******/
+
+ if (init && wall_clock) {
+ sleep_mode = False;
+ /* wait for signals and acts like -a */
+ (void) sigsuspend(&emask);
+ goto again;
+ }
+
+ return 0;
+}
diff --git a/sbin/adjkerntz/pathnames.h b/sbin/adjkerntz/pathnames.h
new file mode 100644
index 0000000..c0e7526
--- /dev/null
+++ b/sbin/adjkerntz/pathnames.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <paths.h>
+
+#define _PATH_CLOCK "/etc/wall_cmos_clock"
diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile
new file mode 100644
index 0000000..9ddb3f7
--- /dev/null
+++ b/sbin/badsect/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= badsect
+MAN8= badsect.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/badsect/badsect.8 b/sbin/badsect/badsect.8
new file mode 100644
index 0000000..840011d
--- /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 0000000..4526c1b
--- /dev/null
+++ b/sbin/badsect/badsect.c
@@ -0,0 +1,230 @@
+/*
+ * 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 const 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 const 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/stat.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+
+#include <dirent.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 diskbn;
+ daddr_t number;
+ struct stat stbuf, devstat;
+ register struct dirent *dp;
+ DIR *dirp;
+ char name[2 * MAXPATHLEN];
+ char *name_dir_end;
+
+ 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);
+ }
+ name_dir_end = name + strlen(name);
+ while ((dp = readdir(dirp)) != NULL) {
+ strcpy(name_dir_end, dp->d_name);
+ if (lstat(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%lo corresponding to %s\n",
+ stbuf.st_rdev, argv[1]);
+ exit(5);
+ }
+ /*
+ * Opening of a mounted on device is not allowed.
+ * Attempt to open the raw device instead.
+ */
+ memcpy(name_dir_end + 1, name_dir_end, strlen(name_dir_end) + 1);
+ *name_dir_end = 'r';
+ if ((fsi = open(name, O_RDONLY)) < 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 = atol(*argv);
+ if (chkuse(number, 1))
+ continue;
+ /*
+ * Print a warning if converting the block number to a dev_t
+ * will truncate it. badsect was not very useful in versions
+ * of BSD before 4.4 because dev_t was 16 bits and another
+ * bit was lost by bogus sign extensions.
+ */
+ diskbn = dbtofsb(fs, number);
+ if ((dev_t)diskbn != diskbn) {
+ printf("sector %ld cannot be represented as a dev_t\n",
+ number);
+ errs++;
+ }
+ else if (mknod(*argv, IFMT|0600, (dev_t)diskbn) < 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 %ld 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 %ld in non-data area: cannot attach\n",
+ blkno);
+ return (1);
+ }
+ } else {
+ if ((fsbn+cnt) > cgbase(fs, cg+1)) {
+ printf("block %ld 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 %ld 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 0000000..bf77cff
--- /dev/null
+++ b/sbin/bsdlabel/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 3/17/94
+
+PROG= disklabel
+SRCS= disklabel.c dkcksum.c
+MAN8= disklabel.8
+MAN5= disklabel.5
+
+.include <bsd.prog.mk>
diff --git a/sbin/bsdlabel/bsdlabel.5 b/sbin/bsdlabel/bsdlabel.5
new file mode 100644
index 0000000..9062e82
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.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 2 ;
+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 2 .
+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 2
+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/bsdlabel.8 b/sbin/bsdlabel/bsdlabel.8
new file mode 100644
index 0000000..ce1b168
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.8
@@ -0,0 +1,384 @@
+.\" 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
+For a virgin disk that is not known to
+.Xr disktab 5 ,
+.Ar disktype
+can be specified as
+.Dq auto .
+In this case, the driver is requested to produce a virgin label for the
+disk. This might or might not be successful, depending on whether the
+driver for the disk is able to get the required data without reading
+anything from the disk at all. It will likely succeed for all SCSI
+disks, most IDE disks, and vnode devices. Writing a label to the
+disk is the only supported operation, and the
+.Ar disk
+itself must be provided as the canonical name, i.e. not as a full
+path name.
+.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 -w sd0 auto
+.Pp
+Try to auto-detect the required information from sd0, and write a new
+label to the disk. Use another disklabel -e command to edit the
+partitioning and file system information.
+.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.
+.Pp
+For the i386 architecture, the primary bootstrap sector contains
+an embedded
+.Em fdisk
+table.
+.Nm Disklabel
+takes care to not clobber it when installing a bootstrap only
+.Pq Fl B ,
+or when editing an existing label
+.Pq Fl e ,
+but it unconditionally writes the primary bootstrap program onto
+the disk for
+.Fl w
+or
+.Fl R ,
+thus replacing the
+.Em fdisk
+table by the dummy one in the bootstrap program. This is only of
+concern if the disk is fully dedicated, so that the BSD disklabel
+starts at absolute block 0 on the disk.
diff --git a/sbin/bsdlabel/bsdlabel.c b/sbin/bsdlabel/bsdlabel.c
new file mode 100644
index 0000000..5fcd1cd
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.c
@@ -0,0 +1,1455 @@
+/*
+ * 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>
+#include <sys/wait.h>
+#define DKTYPENAMES
+#include <sys/disklabel.h>
+#include <ufs/ffs/fs.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdarg.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
+
+void makelabel __P((char *, char *, struct disklabel *));
+int writelabel __P((int, char *, struct disklabel *));
+void l_perror __P((char *));
+struct disklabel * readlabel __P((int));
+struct disklabel * makebootarea __P((char *, struct disklabel *, int));
+void display __P((FILE *, struct disklabel *));
+int edit __P((struct disklabel *, int));
+int editit __P((void));
+char * skip __P((char *));
+char * word __P((char *));
+int getasciilabel __P((FILE *, struct disklabel *));
+int checklabel __P((struct disklabel *));
+void setbootflag __P((struct disklabel *));
+void Warning (char *, ...);
+void Perror __P((char *));
+void usage __P((void));
+extern u_short dkcksum __P((struct disklabel *));
+struct disklabel * getvirginlabel __P((void));
+
+#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
+
+int
+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.
+ */
+void
+makelabel(type, name, lp)
+ char *type, *name;
+ register struct disklabel *lp;
+{
+ register struct disklabel *dp;
+
+ if (strcmp(type, "auto") == 0)
+ dp = getvirginlabel();
+ else
+ 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));
+}
+
+int
+writelabel(f, boot, lp)
+ int f;
+ char *boot;
+ register struct disklabel *lp;
+{
+#ifdef vax
+ register int i;
+#endif
+ 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);
+}
+
+void
+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;
+#if NUMBOOT == 1
+ struct stat sb;
+#endif
+#ifdef __i386__
+ char *tmpbuf;
+ int i, found;
+#endif /* i386 */
+#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
+#ifdef __i386__
+ /*
+ * XXX Botch alert.
+ * The i386 has the so-called fdisk table embedded into the
+ * primary bootstrap. We take care to not clobber it, but
+ * only if it does already contain some data. (Otherwise,
+ * the xxboot provides a template.)
+ */
+ if ((tmpbuf = (char *)malloc((int)dp->d_secsize)) == 0)
+ Perror(xxboot);
+ memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
+#endif /* i386 */
+ if (read(b, boot, (int)dp->d_secsize) < 0)
+ Perror(xxboot);
+ (void)close(b);
+#ifdef __i386__
+ for (i = DOSPARTOFF, found = 0;
+ !found && i < DOSPARTOFF + NDOSPART*sizeof(struct dos_partition);
+ i++)
+ found = tmpbuf[i] != 0;
+ if (found)
+ memcpy((void *)&boot[DOSPARTOFF],
+ (void *)&tmpbuf[DOSPARTOFF],
+ NDOSPART * sizeof(struct dos_partition));
+ free(tmpbuf);
+#endif /* i386 */
+ 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);
+}
+
+void
+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", (int)sizeof(lp->d_typename),
+ lp->d_typename);
+ fprintf(f, "label: %.*s\n", (int)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: %ld\n", lp->d_secsize);
+ fprintf(f, "sectors/track: %ld\n", lp->d_nsectors);
+ fprintf(f, "tracks/cylinder: %ld\n", lp->d_ntracks);
+ fprintf(f, "sectors/cylinder: %ld\n", lp->d_secpercyl);
+ fprintf(f, "cylinders: %ld\n", lp->d_ncylinders);
+ fprintf(f, "sectors/unit: %ld\n", lp->d_secperunit);
+ 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: %ld\t\t# milliseconds\n", lp->d_headswitch);
+ fprintf(f, "track-to-track seek: %ld\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, "%ld ", lp->d_drivedata[j]);
+ fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
+ fprintf(f,
+ "# size offset fstype [fsize bsize bps/cpg]\n");
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ fprintf(f, " %c: %8ld %8ld ", '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, " %5ld %5ld %5.5s ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag, "");
+ break;
+
+ case FS_BSDFFS:
+ fprintf(f, " %5ld %5ld %5d ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ case FS_BSDLFS:
+ fprintf(f, " %5ld %5ld %5d",
+ pp->p_fsize, pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ default:
+ fprintf(f, "%20.20s", "");
+ break;
+ }
+ fprintf(f, "\t# (Cyl. %4ld",
+ pp->p_offset / lp->d_secpercyl);
+ if (pp->p_offset % lp->d_secpercyl)
+ putc('*', f);
+ else
+ putc(' ', f);
+ fprintf(f, "- %ld",
+ (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);
+}
+
+int
+edit(lp, f)
+ struct disklabel *lp;
+ int f;
+{
+ register int c;
+ struct disklabel label;
+ FILE *fd;
+
+ (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);
+}
+
+int
+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.
+ */
+int
+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')) != 0)
+ *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, "sectors/unit")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_secperunit = 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) { \
+ if (tp == NULL) { \
+ fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+ errors++; \
+ break; \
+ } else { \
+ 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;
+
+ case FS_BSDLFS:
+ 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.
+ */
+int
+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 %ld\n", lp->d_secsize);
+ return (1);
+ }
+ if (lp->d_nsectors == 0) {
+ fprintf(stderr, "sectors/track %ld\n", lp->d_nsectors);
+ return (1);
+ }
+ if (lp->d_ntracks == 0) {
+ fprintf(stderr, "tracks/cylinder %ld\n", lp->d_ntracks);
+ return (1);
+ }
+ if (lp->d_ncylinders == 0) {
+ fprintf(stderr, "cylinders/unit %ld\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 %ld\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 %ld\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);
+}
+
+/*
+ * When operating on a "virgin" disk, try getting an initial label
+ * from the associated device driver. This might work for all device
+ * drivers that are able to fetch some initial device parameters
+ * without even having access to a (BSD) disklabel, like SCSI disks,
+ * most IDE drives, or vn devices.
+ *
+ * The device name must be given in its "canonical" form.
+ */
+struct disklabel *
+getvirginlabel(void)
+{
+ static struct disklabel lab;
+ char namebuf[BBSIZE];
+ int f;
+
+ if (dkname[0] == '/') {
+ fprintf(stderr,
+ "\"auto\" requires the usage of a canonical disk name.\n");
+ return (NULL);
+ }
+ (void)snprintf(namebuf, BBSIZE, "%sr%s", _PATH_DEV, dkname);
+ if ((f = open(namebuf, O_RDONLY, 0)) == -1) {
+ Perror("open()");
+ return (NULL);
+ }
+ if (ioctl(f, DIOCGDINFO, &lab) < 0) {
+ Perror("ioctl DIOCGDINFO");
+ close(f);
+ return (NULL);
+ }
+ close(f);
+ return (&lab);
+}
+
+
+/*
+ * 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.
+ */
+void
+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*/
+void
+Warning(char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "Warning, ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void
+Perror(str)
+ char *str;
+{
+ fputs("disklabel: ", stderr); perror(str);
+ exit(4);
+}
+
+void
+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/dkcksum.c b/sbin/bsdlabel/dkcksum.c
new file mode 100644
index 0000000..f7a1e49
--- /dev/null
+++ b/sbin/bsdlabel/dkcksum.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+
+u_short
+dkcksum(lp)
+ register struct disklabel *lp;
+{
+ register u_short *start, *end;
+ register u_short sum = 0;
+
+ start = (u_short *)lp;
+ end = (u_short *)&lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return (sum);
+}
diff --git a/sbin/bsdlabel/pathnames.h b/sbin/bsdlabel/pathnames.h
new file mode 100644
index 0000000..def3297
--- /dev/null
+++ b/sbin/bsdlabel/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ */
+
+#include <paths.h>
+
+#define _PATH_BOOTDIR "/usr/mdec"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/EdDk.aXXXXXX"
diff --git a/sbin/ccdconfig/Makefile b/sbin/ccdconfig/Makefile
new file mode 100644
index 0000000..411f90b
--- /dev/null
+++ b/sbin/ccdconfig/Makefile
@@ -0,0 +1,11 @@
+PROG= ccdconfig
+SRCS= ccdconfig.c
+MAN8= ccdconfig.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+LDADD+= -lkvm
+DPADD+= ${LIBKVM}
+BINGRP= kmem
+BINMODE= 2555
+
+.include <bsd.prog.mk>
diff --git a/sbin/ccdconfig/ccdconfig.8 b/sbin/ccdconfig/ccdconfig.8
new file mode 100644
index 0000000..3dfe787
--- /dev/null
+++ b/sbin/ccdconfig/ccdconfig.8
@@ -0,0 +1,156 @@
+.\" $NetBSD: ccdconfig.8,v 1.1.2.1 1995/11/11 02:43:33 thorpej Exp $
+.\"
+.\" Copyright (c) 1995 Jason R. Thorpe.
+.\" 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 for the NetBSD Project
+.\" by Jason R. Thorpe.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 17, 1995
+.Dt CCDCONFIG 8
+.Os FreeBSD
+.Sh NAME
+.Nm ccdconfig
+.Nd configuration utility for the concatenated disk driver
+.Sh SYNOPSIS
+.Nm ccdconfig
+.Op Fl cv
+.Ar ccd
+.Ar ileave
+.Op Ar flags
+.Ar dev
+.Op Ar ...
+.Nm ccdconfig
+.Fl C
+.Op Fl v
+.Op Fl f Ar config_file
+.Nm ccdconfig
+.Fl u
+.Op Fl v
+.Ar ccd
+.Op Ar ...
+.Nm ccdconfig
+.Fl U
+.Op Fl v
+.Op Fl f Ar config_file
+.Nm ccdconfig
+.Fl g
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Oo
+.Ar ccd Oo ...
+.Oc
+.Oc
+.Sh DESCRIPTION
+.Nm Ccdconfig
+is used to dynamically configure and unconfigure concatenated disk
+devices, or ccds. For more information about the ccd, see
+.Xr ccd 4 .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Configure a ccd. This is the default behavior of
+.Nm ccdconfig .
+.It Fl C
+Configure all ccd devices listed in the ccd configuration file.
+.It Fl f Ar config_file
+When configuring or unconfiguring all devices, read the file
+.Pa config_file
+instead of the default
+.Pa /etc/ccd.conf .
+.It Fl g
+Dump the current ccd configuration in a format suitable for use as the
+ccd configuration file. If no arguments are specified, every configured
+ccd is dumped. Otherwise, the configuration of each listed ccd is dumped.
+.It Fl M Ar core
+Extract values associated with the name list from
+.Pa core
+instead of the default
+.Pa /dev/mem .
+.It Fl N Ar system
+Use
+.Ar system
+as the kernel instead of the running kernel (as determined from
+.Xr getbootfile 3 ).
+.It Fl u
+Unconfigure a ccd.
+.It Fl U
+Unconfigure all ccd devices listed the ccd configuration file.
+.It Fl v
+Causes
+.Nm ccdconfig
+to be verbose.
+.El
+.Pp
+A ccd is described on the command line and in the ccd configuration
+file by the name of the ccd, the interleave factor, the ccd configuration
+flags, and a list of one or more devices. The flags may be represented
+as a decimal number, a hexadecimal number, a comma-separated list
+of strings, or the word
+.Dq none .
+The flags are as follows:
+.Bd -unfilled -offset indent
+CCDF_SWAP 0x01 Interleave should be dmmax
+CCDF_UNIFORM 0x02 Use uniform interleave
+CCDF_MIRROR 0x04 Support mirroring
+CCDF_PARITY 0x08 Support parity (not implemented yet)
+.Ed
+.Pp
+The format in the
+configuration file appears exactly as if it were entered on the command line.
+Note that on the command line and in the configuration file, the
+.Pa flags
+argument is optional.
+.Bd -unfilled -offset indent
+#
+# /etc/ccd.conf
+# Configuration file for concatenated disk devices
+#
+.Pp
+# ccd ileave flags component devices
+ccd0 16 none /dev/sd2e /dev/sd3e
+.Ed
+.Pp
+.Sh EXAMPLE
+The following command, executed from the command line, would configure ccd0
+with 4 components (/dev/sd2e, /dev/sd3e, /dev/sd4e, /dev/sd5e), and an
+interleave factor of 32 blocks.
+.Bd -unfilled -offset indent
+# ccdconfig ccd0 32 0 /dev/sd2e /dev/sd3e /dev/sd4e /dev/sd5e
+.Ed
+.Pp
+.Sh FILES
+/etc/ccd.conf - default ccd configuration file.
+.Sh SEE ALSO
+.Xr ccd 4 ,
+.Xr rc 8 .
+.Sh HISTORY
+The
+.Nm ccdconfig
+command first appeared in
+.Nx 1.0a .
diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c
new file mode 100644
index 0000000..2de876e
--- /dev/null
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -0,0 +1,714 @@
+/* $Id: ccdconfig.c,v 1.3 1995/12/28 00:22:16 asami Exp $ */
+
+/* $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 Jason R. Thorpe.
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ccdvar.h>
+
+#include "pathnames.h"
+
+extern char *__progname;
+
+static int lineno = 0;
+static int verbose = 0;
+static char *ccdconf = _PATH_CCDCONF;
+
+static char *core = NULL;
+static char *kernel = NULL;
+
+struct flagval {
+ char *fv_flag;
+ int fv_val;
+} flagvaltab[] = {
+ { "CCDF_SWAP", CCDF_SWAP },
+ { "CCDF_UNIFORM", CCDF_UNIFORM },
+ { "CCDF_MIRROR", CCDF_MIRROR },
+ { "CCDF_PARITY", CCDF_PARITY },
+ { NULL, 0 },
+};
+
+static struct nlist nl[] = {
+ { "_ccd_softc" },
+#define SYM_CCDSOFTC 0
+ { "_numccd" },
+#define SYM_NUMCCD 1
+ { NULL },
+};
+
+#define CCD_CONFIG 0 /* configure a device */
+#define CCD_CONFIGALL 1 /* configure all devices */
+#define CCD_UNCONFIG 2 /* unconfigure a device */
+#define CCD_UNCONFIGALL 3 /* unconfigure all devices */
+#define CCD_DUMP 4 /* dump a ccd's configuration */
+
+static int checkdev __P((char *));
+static int do_io __P((char *, u_long, struct ccd_ioctl *));
+static int do_single __P((int, char **, int));
+static int do_all __P((int));
+static int dump_ccd __P((int, char **));
+static int getmaxpartitions __P((void));
+static int getrawpartition __P((void));
+static int flags_to_val __P((char *));
+static int pathtodevt __P((char *, dev_t *));
+static void print_ccd_info __P((struct ccd_softc *, kvm_t *));
+static char *resolve_ccdname __P((char *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, options = 0, action = CCD_CONFIG;
+
+ while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) {
+ switch (ch) {
+ case 'c':
+ action = CCD_CONFIG;
+ ++options;
+ break;
+
+ case 'C':
+ action = CCD_CONFIGALL;
+ ++options;
+ break;
+
+ case 'f':
+ ccdconf = optarg;
+ break;
+
+ case 'g':
+ action = CCD_DUMP;
+ break;
+
+ case 'M':
+ core = optarg;
+ break;
+
+ case 'N':
+ kernel = optarg;
+ break;
+
+ case 'u':
+ action = CCD_UNCONFIG;
+ ++options;
+ break;
+
+ case 'U':
+ action = CCD_UNCONFIGALL;
+ ++options;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options > 1)
+ usage();
+
+ switch (action) {
+ case CCD_CONFIG:
+ case CCD_UNCONFIG:
+ exit(do_single(argc, argv, action));
+ /* NOTREACHED */
+
+ case CCD_CONFIGALL:
+ case CCD_UNCONFIGALL:
+ exit(do_all(action));
+ /* NOTREACHED */
+
+ case CCD_DUMP:
+ exit(dump_ccd(argc, argv));
+ /* NOTREACHED */
+ }
+ /* NOTREACHED */
+}
+
+static int
+do_single(argc, argv, action)
+ int argc;
+ char **argv;
+ int action;
+{
+ struct ccd_ioctl ccio;
+ char *ccd, *cp, *cp2, **disks;
+ int noflags = 0, i, ileave, flags, j, error;
+
+ bzero(&ccio, sizeof(ccio));
+
+ /*
+ * If unconfiguring, all arguments are treated as ccds.
+ */
+ if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
+ for (i = 0; argc != 0; ) {
+ cp = *argv++; --argc;
+ if ((ccd = resolve_ccdname(cp)) == NULL) {
+ warnx("invalid ccd name: %s", cp);
+ i = 1;
+ continue;
+ }
+ if (do_io(ccd, CCDIOCCLR, &ccio))
+ i = 1;
+ else
+ if (verbose)
+ printf("%s unconfigured\n", cp);
+ }
+ return (i);
+ }
+
+ /* Make sure there are enough arguments. */
+ if (argc < 4)
+ if (argc == 3) {
+ /* Assume that no flags are specified. */
+ noflags = 1;
+ } else {
+ if (action == CCD_CONFIGALL) {
+ warnx("%s: bad line: %d", ccdconf, lineno);
+ return (1);
+ } else
+ usage();
+ }
+
+ /* First argument is the ccd to configure. */
+ cp = *argv++; --argc;
+ if ((ccd = resolve_ccdname(cp)) == NULL) {
+ warnx("invalid ccd name: %s", cp);
+ return (1);
+ }
+
+ /* Next argument is the interleave factor. */
+ cp = *argv++; --argc;
+ errno = 0; /* to check for ERANGE */
+ ileave = (int)strtol(cp, &cp2, 10);
+ if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
+ warnx("invalid interleave factor: %s", cp);
+ return (1);
+ }
+
+ if (noflags == 0) {
+ /* Next argument is the ccd configuration flags. */
+ cp = *argv++; --argc;
+ if ((flags = flags_to_val(cp)) < 0) {
+ warnx("invalid flags argument: %s", cp);
+ return (1);
+ }
+ }
+
+ /* Next is the list of disks to make the ccd from. */
+ disks = malloc(argc * sizeof(char *));
+ if (disks == NULL) {
+ warnx("no memory to configure ccd");
+ return (1);
+ }
+ for (i = 0; argc != 0; ) {
+ cp = *argv++; --argc;
+ if ((j = checkdev(cp)) == 0)
+ disks[i++] = cp;
+ else {
+ warnx("%s: %s", cp, strerror(j));
+ return (1);
+ }
+ }
+
+ /* Fill in the ccio. */
+ ccio.ccio_disks = disks;
+ ccio.ccio_ndisks = i;
+ ccio.ccio_ileave = ileave;
+ ccio.ccio_flags = flags;
+
+ if (do_io(ccd, CCDIOCSET, &ccio)) {
+ free(disks);
+ return (1);
+ }
+
+ if (verbose) {
+ printf("ccd%d: %d components ", ccio.ccio_unit,
+ ccio.ccio_ndisks);
+ for (i = 0; i < ccio.ccio_ndisks; ++i) {
+ if ((cp2 = strrchr(disks[i], '/')) != NULL)
+ ++cp2;
+ else
+ cp2 = disks[i];
+ printf("%c%s%c",
+ i == 0 ? '(' : ' ', cp2,
+ i == ccio.ccio_ndisks - 1 ? ')' : ',');
+ }
+ printf(", %d blocks ", ccio.ccio_size);
+ if (ccio.ccio_ileave != 0)
+ printf("interleaved at %d blocks\n", ccio.ccio_ileave);
+ else
+ printf("concatenated\n");
+ }
+
+ free(disks);
+ return (0);
+}
+
+static int
+do_all(action)
+ int action;
+{
+ FILE *f;
+ char line[_POSIX2_LINE_MAX];
+ char *cp, **argv;
+ int argc, rval;
+
+ if ((f = fopen(ccdconf, "r")) == NULL) {
+ warn("fopen: %s", ccdconf);
+ return (1);
+ }
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+ argc = 0;
+ argv = NULL;
+ ++lineno;
+ if ((cp = strrchr(line, '\n')) != NULL)
+ *cp = '\0';
+
+ /* Break up the line and pass it's contents to do_single(). */
+ if (line[0] == '\0')
+ goto end_of_line;
+ for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
+ if (*cp == '#')
+ break;
+ if ((argv = realloc(argv,
+ sizeof(char *) * ++argc)) == NULL) {
+ warnx("no memory to configure ccds");
+ return (1);
+ }
+ argv[argc - 1] = cp;
+ /*
+ * If our action is to unconfigure all, then pass
+ * just the first token to do_single() and ignore
+ * the rest. Since this will be encountered on
+ * our first pass through the line, the Right
+ * Thing will happen.
+ */
+ if (action == CCD_UNCONFIGALL) {
+ if (do_single(argc, argv, action))
+ rval = 1;
+ goto end_of_line;
+ }
+ }
+ if (argc != 0)
+ if (do_single(argc, argv, action))
+ rval = 1;
+
+ end_of_line:
+ if (argv != NULL)
+ free(argv);
+ }
+
+ (void)fclose(f);
+ return (rval);
+}
+
+static int
+checkdev(path)
+ char *path;
+{
+ struct stat st;
+
+ if (stat(path, &st) != 0)
+ return (errno);
+
+ if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+ return (EINVAL);
+
+ return (0);
+}
+
+static int
+pathtounit(path, unitp)
+ char *path;
+ int *unitp;
+{
+ struct stat st;
+ dev_t dev;
+ int maxpartitions;
+
+ if (stat(path, &st) != 0)
+ return (errno);
+
+ if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+ return (EINVAL);
+
+ if ((maxpartitions = getmaxpartitions()) < 0)
+ return (errno);
+
+ *unitp = minor(st.st_rdev) / maxpartitions;
+
+ return (0);
+}
+
+static char *
+resolve_ccdname(name)
+ char *name;
+{
+ char c, *cp, *path;
+ size_t len, newlen;
+ int rawpart;
+
+ if (name[0] == '/' || name[0] == '.') {
+ /* Assume they gave the correct pathname. */
+ return (strdup(name));
+ }
+
+ len = strlen(name);
+ c = name[len - 1];
+
+ newlen = len + 8;
+ if ((path = malloc(newlen)) == NULL)
+ return (NULL);
+ bzero(path, newlen);
+
+ if (isdigit(c)) {
+ if ((rawpart = getrawpartition()) < 0) {
+ free(path);
+ return (NULL);
+ }
+ (void)sprintf(path, "/dev/%s%c", name, 'a' + rawpart);
+ } else
+ (void)sprintf(path, "/dev/%s", name);
+
+ return (path);
+}
+
+static int
+do_io(path, cmd, cciop)
+ char *path;
+ u_long cmd;
+ struct ccd_ioctl *cciop;
+{
+ int fd;
+ char *cp;
+
+ if ((fd = open(path, O_RDWR, 0640)) < 0) {
+ warn("open: %s", path);
+ return (1);
+ }
+
+ if (ioctl(fd, cmd, cciop) < 0) {
+ switch (cmd) {
+ case CCDIOCSET:
+ cp = "CCDIOCSET";
+ break;
+
+ case CCDIOCCLR:
+ cp = "CCDIOCCLR";
+ break;
+
+ default:
+ cp = "unknown";
+ }
+ warn("ioctl (%s): %s", cp, path);
+ return (1);
+ }
+
+ return (0);
+}
+
+#define KVM_ABORT(kd, str) { \
+ (void)kvm_close((kd)); \
+ warnx((str)); \
+ warnx(kvm_geterr((kd))); \
+ return (1); \
+}
+
+static int
+dump_ccd(argc, argv)
+ int argc;
+ char **argv;
+{
+ char errbuf[_POSIX2_LINE_MAX], *ccd, *cp;
+ struct ccd_softc *cs, *kcs;
+ size_t readsize;
+ int i, error, numccd, numconfiged = 0;
+ kvm_t *kd;
+
+ bzero(errbuf, sizeof(errbuf));
+
+ if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY,
+ errbuf)) == NULL) {
+ warnx("can't open kvm: %s", errbuf);
+ return (1);
+ }
+
+ if (kvm_nlist(kd, nl))
+ KVM_ABORT(kd, "ccd-related symbols not available");
+
+ /* Check to see how many ccds are currently configured. */
+ if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd,
+ sizeof(numccd)) != sizeof(numccd))
+ KVM_ABORT(kd, "can't determine number of configured ccds");
+
+ if (numccd == 0) {
+ printf("ccd driver in kernel, but is uninitialized\n");
+ goto done;
+ }
+
+ /* Allocate space for the configuration data. */
+ readsize = numccd * sizeof(struct ccd_softc);
+ if ((cs = malloc(readsize)) == NULL) {
+ warnx("no memory for configuration data");
+ goto bad;
+ }
+ bzero(cs, readsize);
+
+ /*
+ * Read the ccd configuration data from the kernel and dump
+ * it to stdout.
+ */
+ if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs,
+ sizeof(kcs)) != sizeof(kcs)) {
+ free(cs);
+ KVM_ABORT(kd, "can't find pointer to configuration data");
+ }
+ if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) {
+ free(cs);
+ KVM_ABORT(kd, "can't read configuration data");
+ }
+
+ if (argc == 0) {
+ for (i = 0; i < numccd; ++i)
+ if (cs[i].sc_flags & CCDF_INITED) {
+ ++numconfiged;
+ print_ccd_info(&cs[i], kd);
+ }
+
+ if (numconfiged == 0)
+ printf("no concatenated disks configured\n");
+ } else {
+ while (argc) {
+ cp = *argv++; --argc;
+ if ((ccd = resolve_ccdname(cp)) == NULL) {
+ warnx("invalid ccd name: %s", cp);
+ continue;
+ }
+ if ((error = pathtounit(ccd, &i)) != 0) {
+ warnx("%s: %s", ccd, strerror(error));
+ continue;
+ }
+ if (i >= numccd) {
+ warnx("ccd%d not configured", i);
+ continue;
+ }
+ if (cs[i].sc_flags & CCDF_INITED)
+ print_ccd_info(&cs[i], kd);
+ else
+ printf("ccd%d not configured\n", i);
+ }
+ }
+
+ free(cs);
+
+ done:
+ (void)kvm_close(kd);
+ return (0);
+
+ bad:
+ (void)kvm_close(kd);
+ return (1);
+}
+
+static void
+print_ccd_info(cs, kd)
+ struct ccd_softc *cs;
+ kvm_t *kd;
+{
+ static int header_printed = 0;
+ struct ccdcinfo *cip;
+ size_t readsize;
+ char path[MAXPATHLEN];
+ int i;
+
+ if (header_printed == 0 && verbose) {
+ printf("# ccd\t\tileave\tflags\tcompnent devices\n");
+ header_printed = 1;
+ }
+
+ readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo);
+ if ((cip = malloc(readsize)) == NULL) {
+ warn("ccd%d: can't allocate memory for component info",
+ cs->sc_unit);
+ return;
+ }
+ bzero(cip, readsize);
+
+ /* Dump out softc information. */
+ printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
+ cs->sc_cflags & CCDF_USERMASK);
+ fflush(stdout);
+
+ /* Read in the component info. */
+ if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip,
+ readsize) != readsize) {
+ printf("\n");
+ warnx("can't read component info");
+ warnx(kvm_geterr(kd));
+ goto done;
+ }
+
+ /* Read component pathname and display component info. */
+ for (i = 0; i < cs->sc_nccdisks; ++i) {
+ if (kvm_read(kd, (u_long)cip[i].ci_path, (char *)path,
+ cip[i].ci_pathlen) != cip[i].ci_pathlen) {
+ printf("\n");
+ warnx("can't read component pathname");
+ warnx(kvm_geterr(kd));
+ goto done;
+ }
+ printf((i + 1 < cs->sc_nccdisks) ? "%s " : "%s\n", path);
+ fflush(stdout);
+ }
+
+ done:
+ free(cip);
+}
+
+static int
+getmaxpartitions()
+{
+ return (MAXPARTITIONS);
+}
+
+static int
+getrawpartition()
+{
+ return (RAW_PART);
+}
+
+static int
+flags_to_val(flags)
+ char *flags;
+{
+ char *cp, *tok;
+ int i, tmp, val = ~CCDF_USERMASK;
+ size_t flagslen;
+
+ /*
+ * The most common case is that of NIL flags, so check for
+ * those first.
+ */
+ if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
+ strcmp("0", flags) == 0)
+ return (0);
+
+ flagslen = strlen(flags);
+
+ /* Check for values represented by strings. */
+ if ((cp = strdup(flags)) == NULL)
+ err(1, "no memory to parse flags");
+ tmp = 0;
+ for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
+ for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
+ if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
+ break;
+ if (flagvaltab[i].fv_flag == NULL) {
+ free(cp);
+ goto bad_string;
+ }
+ tmp |= flagvaltab[i].fv_val;
+ }
+
+ /* If we get here, the string was ok. */
+ free(cp);
+ val = tmp;
+ goto out;
+
+ bad_string:
+
+ /* Check for values represented in hex. */
+ if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
+ errno = 0; /* to check for ERANGE */
+ val = (int)strtol(&flags[2], &cp, 16);
+ if ((errno == ERANGE) || (*cp != '\0'))
+ return (-1);
+ goto out;
+ }
+
+ /* Check for values represented in decimal. */
+ errno = 0; /* to check for ERANGE */
+ val = (int)strtol(flags, &cp, 10);
+ if ((errno == ERANGE) || (*cp != '\0'))
+ return (-1);
+
+ out:
+ return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
+}
+
+static void
+usage()
+{
+
+ fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] %s\n", __progname,
+ "dev [...]");
+ fprintf(stderr, " %s -C [-v] [-f config_file]\n", __progname);
+ fprintf(stderr, " %s -u [-v] ccd [...]\n", __progname);
+ fprintf(stderr, " %s -U [-v] [-f config_file]\n", __progname);
+ fprintf(stderr, " %s -g [-M core] [-N system] %s\n", __progname,
+ "[ccd [...]]");
+ exit(1);
+}
+
+/* Local Variables: */
+/* c-argdecl-indent: 8 */
+/* c-indent-level: 8 */
+/* End: */
diff --git a/sbin/ccdconfig/pathnames.h b/sbin/ccdconfig/pathnames.h
new file mode 100644
index 0000000..f8da9e0
--- /dev/null
+++ b/sbin/ccdconfig/pathnames.h
@@ -0,0 +1,35 @@
+/* $NetBSD: pathnames.h,v 1.1 1995/08/17 16:37:20 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 Jason R. Thorpe.
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _PATH_CCDCONF "/etc/ccd.conf"
diff --git a/sbin/clri/Makefile b/sbin/clri/Makefile
new file mode 100644
index 0000000..2447d7a
--- /dev/null
+++ b/sbin/clri/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= clri
+MAN8= clri.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8
new file mode 100644
index 0000000..9ee8ffe
--- /dev/null
+++ b/sbin/clri/clri.8
@@ -0,0 +1,77 @@
+.\" 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
+.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 0000000..c2bdccc
--- /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/comcontrol/Makefile b/sbin/comcontrol/Makefile
new file mode 100644
index 0000000..abc8d0a
--- /dev/null
+++ b/sbin/comcontrol/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 5.4 (Berkeley) 6/5/91
+
+PROG= comcontrol
+MAN8= comcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/comcontrol/comcontrol.8 b/sbin/comcontrol/comcontrol.8
new file mode 100644
index 0000000..574386b
--- /dev/null
+++ b/sbin/comcontrol/comcontrol.8
@@ -0,0 +1,63 @@
+.Dd May 15, 1994
+.Dt COMCONTROL 8
+.Os FreeBSD
+.Sh NAME
+.Nm comcontrol
+.Nd control an sio device.
+.Sh SYNOPSIS
+.Nm comcontrol
+.Ar sio_special_device
+.Op options
+.Sh DESCRIPTION
+.Nm Comcontrol
+is used to examine and modify some of the special characteristics
+of the specified sio device.
+If no arguments other than the device are specified,
+it prints the settings of all controllable characteristics.
+This usage requires only read access on the device.
+Only the superuser can change the settings.
+.Pp
+The following options are available:
+.Bl -tag -width Fl
+.It Cm dtrwait Ar number
+Set the time to wait after dropping DTR
+to the given number.
+The units are hundredths of a second.
+The default is 300 hundredths, i.e., 3 seconds.
+This option needed mainly to set proper recover time after
+modem reset.
+.El
+.Bl -tag -width Fl
+.It Cm drainwait Ar number
+Set the time to wait for output drain
+to the given number.
+The units are seconds.
+The default is 0, i.e. wait forever.
+This option needed mainly to specify upper limit of minutes
+to prevent modem hanging.
+.El
+.Pp
+The standard way to use
+.Nm comcontrol
+is to put invocations of it in the
+.Ar /etc/rc.serial
+startup script.
+.Sh SEE ALSO
+.Xr sio 4 ,
+.Xr stty 1 .
+.Sh FILES
+.Bl -tag -width Pa
+.It Pa /dev/ttyd?
+dialin devices, hardwired terminals
+.It Pa /dev/cuaa?
+dialout devices.
+.Sh AUTHOR
+Christopher G. Demetriou
+.Sh BUGS
+.Nm comcontrol
+should be named
+.Nm siocontrol .
+.Sh HISTORY
+Originally part of cgd's com package patches, version 0.2.1, to 386BSD 0.1.
+Once controlled bidirectional capabilities. Little is left to control now
+that these capabilities are standard.
diff --git a/sbin/comcontrol/comcontrol.c b/sbin/comcontrol/comcontrol.c
new file mode 100644
index 0000000..17c2882
--- /dev/null
+++ b/sbin/comcontrol/comcontrol.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1992 Christopher G. Demetriou
+ * 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. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* comcontrol.c */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+
+void usage(char *progname)
+{
+ fprintf(stderr, "usage: %s <filename> [dtrwait <n>] [drainwait <n>]\n", progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int res = 0;
+ int dtrwait = -1, drainwait = -1;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ fd = open(argv[1], O_RDONLY|O_NONBLOCK, 0);
+ if (fd < 0) {
+ perror("open");
+ fprintf(stderr, "%s: couldn't open file %s\n", argv[0], argv[1]);
+ return 1;
+ }
+
+ if (argc == 2) {
+ if (ioctl(fd, TIOCMGDTRWAIT, &dtrwait) < 0) {
+ res = 1;
+ perror("TIOCMGDTRWAIT");
+ }
+ if (ioctl(fd, TIOCGDRAINWAIT, &drainwait) < 0) {
+ res = 1;
+ perror("TIOCGDRAINWAIT");
+ }
+ printf("dtrwait %d drainwait %d\n", dtrwait, drainwait);
+ } else {
+ char *prg = argv[0];
+
+ while (argv[2] != NULL) {
+ if (!strcmp(argv[2],"dtrwait")) {
+ if (dtrwait >= 0)
+ usage(prg);
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage(prg);
+ dtrwait = atoi(argv[3]);
+ argv += 2;
+ } else if (!strcmp(argv[2],"drainwait")) {
+ if (drainwait >= 0)
+ usage(prg);
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage(prg);
+ drainwait = atoi(argv[3]);
+ argv += 2;
+ } else
+ usage(prg);
+ }
+ if (dtrwait >= 0) {
+ if (ioctl(fd, TIOCMSDTRWAIT, &dtrwait) < 0) {
+ res = 1;
+ perror("TIOCMSDTRWAIT");
+ }
+ }
+ if (drainwait >= 0) {
+ if (ioctl(fd, TIOCSDRAINWAIT, &drainwait) < 0) {
+ res = 1;
+ perror("TIOCSDRAINWAIT");
+ }
+ }
+ }
+
+ close(fd);
+ return res;
+}
diff --git a/sbin/cxconfig/Makefile b/sbin/cxconfig/Makefile
new file mode 100644
index 0000000..59c828d
--- /dev/null
+++ b/sbin/cxconfig/Makefile
@@ -0,0 +1,4 @@
+PROG = cxconfig
+MAN8 = cxconfig.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/cxconfig/cxconfig.8 b/sbin/cxconfig/cxconfig.8
new file mode 100644
index 0000000..9907435
--- /dev/null
+++ b/sbin/cxconfig/cxconfig.8
@@ -0,0 +1,297 @@
+.TH Cronyx-Sigma 1
+.rm ES
+.rm EE
+.de ES
+.PP
+.nf
+.in +0.5i
+..
+.de EE
+.in -0.5i
+.fi
+..
+.na
+.SH NAME
+.B cxconfig
+\- channel options management utility for Cronyx-Sigma adapter
+.SH DESCRIPTION
+.PP
+The \fBcxconfig\fP utility is used for configuring the channel options of
+the Cronyx-Sigma adapter.
+.PP
+To change cha options the channel should be free: the corresponding
+network interface in ``down'' state, the asynchronous terminal device /dev/tty*
+closed.
+Generally, the channel options are set up during the operating
+system startup, for example from the \fI/etc/rc\fP file.
+.PP
+Note, that not all options have a sense for every particular
+case, and an attempt to set some of them can hang up the channel or
+the whole adapter.
+.SH "Usage"
+.IP "cxconfig"
+The brief information about all channels.
+.IP "cxconfig -a"
+The full information about all channels.
+.IP "cxconfig <channel>"
+The brief information about the channel.
+.IP "cxconfig -a <channel>"
+The full information about the channel.
+.IP "cxconfig <channel> <option>..."
+Setting the channel options.
+.SH "Channel options"
+.IP ispeed=#
+Set the receiver baud rate to the number given.
+The maximal value is 256000 bits/sec.
+In the synchronous mode the receiver baud rate is significant
+only when DPLL mode is used.
+.IP ospeed=#
+Set the transmitter baud rate to the number given.
+The maximal value is 256000 bits/sec.
+In the synchronous mode the transmitter baud rate is significant
+only in the case of the internal clock source.
+If receiver and transmitter have equal data rate, then it could
+be set by specifying only the numerical argument.
+.IP async
+Set the asynchronous channel mode.
+.IP "hdlc, bisync, bsc, x.21, x21
+Set the synchronous channel mode: HDLC, Bisync (BSC) or X.21.
+.IP ppp
+Set the link-level protocol: PPP/HDLC. The built-in simplified synchronous PPP
+implementation is used (see RFC-1548, RFC-1549).
+.IP cisco
+Set the link-level protocol: Cisco/HDLC (see RFC-1547).
+This protocol is intended for compatibility with old models of Cisco routers,
+and with early versions of BSD/386 drivers.
+The extensive usage of this protocol is not recommended.
+.IP ext
+Use the external link-level protocol suite (for BSD/386 only).
+.IP "+keepalive, -keepalive"
+Enable the automatic line state control sub-protocol.
+This setting is not significant when the external link-level protocol is used.
+.IP "+autorts, -autorts"
+Enable the automatic RTS signal control.
+When enabled, the RTS signal goes up only when both halves of
+the receiver ring buffer are free and ready for receive,
+and goes down when one or both buffers are busy.
+.IP "port=rs232, port=rs449, port=v35
+Set the zero channel hardware interface type.
+.SH "Common options"
+.IP "nrz, nrzi, manchester
+Set the data line signal encoding.
+In the case of \fINRZ\fP encoding the zero bit is transmitted by the zero signal
+level, the one bit - by the positive signal level.
+In the case of \fINRZI\fP encoding the zero bit is transmitted by the change of
+the signal level, the one bit - by the constant signal level.
+In the case of \fIManchester\fP encoding the zero bit is encoded as 01 value,
+the one bit - as 10 value.
+.IP "+dpll, -dpll"
+Enable the digital phase locked loop mode (DPLL).
+When enabled, the receiver timing clock signal
+is derived from the received data.
+.IP "+lloop, -lloop"
+Set the local loopback mode.
+.IP "+extclock, -extclock"
+Set the timing clock source of synchronous channels. There are
+two possible variants: \fIexternal clock\fP source or \fIinternal clock\fP
+generation.
+.br
+\fIExternal clock\fP mode is the most common method for connecting
+external modem hardware. In this mode the external timing
+signal is received on TXCIN pin of the connector, and it is
+used as a synchronization clock for transmitting data (TXD).
+.br
+In the case of \fIinternal clock\fP mode the transmitted data (TXD)
+are synchronized using the internal on-board timing generator,
+the internally generated timing signal is driven on the TXCOUT
+pin, and the signal on the TXCIN pin is ignored. This mode
+is used for direct terminal-to-terminal communication,
+e.g. for connecting two computers together in a synchronous mode
+via relatively short cable. This method should also be used
+for testing channels with an external loopback connector.
+.IP fifo=#
+FIFO threshold level setup for receiver and transmitter.
+.IP rfifo=#
+Hardware RTS/CTS flow control FIFO threshold setup.
+.IP "+ctsup, -ctsup"
+Enable/disable interrupts on CTS (Clear To Send) signal setup (0 to 1 transition).
+.IP "+ctsdown, -ctsdown"
+Enable/disable interrupts on CTS (Clear To Send) signal clear (1 to 0 transition).
+.IP "+cdup, -cdup"
+Enable/disable interrupts on CD (Carrier Detect) signal setup (0 to 1 transition).
+.IP "+cddown, -cddown"
+Enable/disable interrupts on CD (Carrier Detect) signal clear (1 to 0 transition).
+.IP "+dsrup, -dsrup"
+Enable/disable interrupts on DSR (Data Set Ready) signal setup (0 to 1 transition).
+.IP "+dsrdown, -dsrdown"
+Enable/disable interrupts on DSR (Data Set Ready) signal clear (1 to 0 transition).
+.SH "Asynchronous mode options"
+.IP cs#
+Select character size: 5, 6, 7 or 8 bits.
+.IP "parodd, pareven
+Parity mode: odd or even.
+.IP "+ignpar, -ignpar
+Disable/enable parity detection.
+.IP nopar
+Disable parity bit generation.
+.IP forcepar
+Force parity: even - 0, odd - 1.
+.IP "stopb1, stopb1.5, stopb2
+Use 1 or 1.5 or 2 stop bits per character.
+.IP "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.IP "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.IP "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.IP "+rloop, -rloop"
+Set the remote loopback mode.
+.IP "+etc, -etc"
+Enable the embedded transmit commands mode.
+.IP "+ixon, -ixon"
+Enable the hardware XON/XOFF flow control support.
+.IP "+ixany, -ixany"
+Use the hardware IXANY mode support.
+.IP "+sdt, -sdt"
+Detect the spec. characters SCHR1 and SCHR2 in the receive data.
+.IP "+flowct, -flowct"
+Receive the flow control spec. characters as data.
+.IP "+rdt, -rdt"
+Detect the spec. characters in range SCRL..SCRH in the receive data.
+.IP "+exdt, -exdt"
+Detect the spec. characters SCHR3 and SCHR4 in the receive data.
+.IP "parintr, parnull, parign, pardisc, parffnull
+Action on parity errors:
+.ES
+ Mode Action
+ -----------------------------------------------------
+ parintr Generate the receiver error interrupt
+ parnull Input the NULL character
+ parign Ignore the error, receive as good data
+ pardisc Ignore the character
+ parffnull Input the sequence <0xFF, NULL, character>
+.EE
+.IP "brkintr, brknull, brkdisc
+Line break state action:
+.ES
+ Mode Action
+ ---------------------------------------------------
+ brkintr Generate the receiver error interrupt
+ brknull Input the NULL character
+ brkdisc Ignore the line break state
+.EE
+.IP "+inlcr, -inlcr"
+Translate received NL characters to CR.
+.IP "+icrnl, -icrnl"
+Translate received CR characters to NL.
+.IP "+igncr, -igncr"
+Ignore received CR characters.
+.IP "+ocrnl, -ocrnl"
+Translate transmitted CR characters to NL.
+.IP "+onlcr, -onlcr"
+Translate transmitted NL characters to CR.
+.IP "+fcerr, -fcerr"
+Process (don't process) the characters, received with errors,
+for special character/flow control matching.
+.IP "+lnext, -lnext"
+Enable the LNEXT character option: the character following
+the LNEXT character is not processed for special character/flow
+control matching.
+.IP "+istrip, -istrip"
+Strip input characters to seven bits.
+.IP schr1=#
+The XON flow control character value.
+.IP schr2=#
+The XOFF flow control character value.
+.IP schr3=#
+The SCHR3 spec. character value.
+.IP schr4=#
+The SCHR4 spec. character value.
+.IP "scrl=#, scrh=#
+The spec. character range (inclusive).
+.IP lnext=#
+The LNEXT spec. character value.
+.SH "HDLC mode options"
+.IP if#
+The minimum number of flags transmitted before a frame is started.
+.IP noaddr
+No frame address recognition.
+.IP "addrlen1, addrlen2"
+Address field length: 1 or 2 bytes.
+.IP "addr1, addr2"
+Addressing mode: 4x1 bytes or 2x2 bytes.
+Registers RFAR1..RFAR4 should contain the address to be matched.
+.IP "+clrdet, -clrdet"
+Enable/disable clear detect for X.21 protocol support.
+.IP "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.IP "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.IP "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.IP "+fcs, -fcs"
+Enable/disable the frame checksum generation and checking.
+.IP "crc-16, crc-v.41
+Select the CRC polynomial: CRC-16 (x^16+x^15+x^2+1)
+or CRC V.41 (x^16+x^12+x^5+1).
+.IP "fcs-crc-16, fcs-v.41
+Frame checksum preset: all zeros (CRC-16) or all ones (CRC V.41).
+.IP "+crcinv, -crcinv"
+Invert (ie. CRC V.41) or don't invert (ie. CRC-16) the transmitted frame checksum.
+.IP "+fcsapd, -fcsapd"
+Pass the received CRC to the host at the end of receiver data buffer.
+.IP "idlemark, idleflag
+Idle mode: idle in mark (transmit all ones) or idle in flag (transmit flag).
+.IP "+syn, -syn"
+Enable/disable sending pad characters before sending flag when coming out
+of the idle mode.
+.IP pad#
+The number of synchronous characters sent (0..4).
+.IP "syn=0xaa, syn=0x00
+Send sync pattern.
+.IP "rfar1=#, rfar2=#, rfar3=#, rfar4=#
+Frame address registers for address recognition.
+.SH EXAMPLES
+.PP
+Set up the channel 7 of the adapter Sigma-400 under FreeBSD.
+Physical 4-wire leased line with Zelax+ M115 short-range modems.
+Synchronous mode, 128000 bits/sec, interface RS-232,
+protocol PPP/HDLC without keepalive support, NRZI encoding,
+DPLL mode, no flow control:
+.ES
+cxconfig cx7 128000 hdlc ppp -keepalive nrzi -cts +dpll -extclock
+ifconfig cx7 158.250.244.2 158.250.244.1 up
+.EE
+.PP
+Set up the channel 0 of the adapter Sigma-100 under FreeBSD.
+Attachment to the near computer by short cable, internal clock source.
+Synchronous mode, 256000 bits/sec, interface RS-232,
+protocol Cisco/HDLC with keepalive support:
+.ES
+cxconfig cx0 hdlc 256000 cisco +keepalive -extclock
+ifconfig cx0 200.1.1.1 200.1.1.2 up
+.EE
+.PP
+Set up the channel 1 of the adapter Sigma-840 under BSD/386.
+Synchronous 64 kbit/sec leased line, external clock source.
+Synchronous mode, interface V.35, external protocol suite:
+.ES
+cxconfig cx1 hdlc ext
+ifconfig cx1 193.124.254.50 193.124.254.49 multicast up
+.EE
+.PP
+Set up the channel 0 of the adapter Sigma-840 under FreeBSD.
+Attachment to the Cisco-4000 router by null-modem cable, internal clock source.
+Synchronous mode, 64000 bits/sec, interface RS-232,
+protocol PPP/HDLC with keepalive support and flow control,
+LCP and IPCP protocols (see RFC-1548 and RFC-1332) debug tracing enabled:
+.ES
+cxconfig cx0 hdlc 64000 port=rs232 ppp +keepalive -extclock +cts
+ifconfig cx0 100.0.0.2 100.0.0.1 debug up
+.EE
+.SH FILES
+.IP /dev/cronyx
+The special device file for adapter options management.
+.SH SEE ALSO
+.PP
+cx(4)
diff --git a/sbin/cxconfig/cxconfig.c b/sbin/cxconfig/cxconfig.c
new file mode 100644
index 0000000..4e2400e
--- /dev/null
+++ b/sbin/cxconfig/cxconfig.c
@@ -0,0 +1,766 @@
+/*
+ * Cronyx-Sigma adapter configuration utility for Unix.
+ *
+ * Copyright (C) 1994 Cronyx Ltd.
+ * Author: Serge Vakulenko, <vak@zebub.msk.su>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
+ *
+ * Usage:
+ * cxconfig [-a]
+ * -- print status of all channels
+ * cxconfig [-a] <channel>
+ * -- print status of the channel
+ * cxconfig <channel> <option>...
+ * -- set channel options
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <machine/cronyx.h>
+#include <net/if.h>
+#include <stdio.h>
+
+#define NBRD 3
+#define CXDEV "/dev/cronyx"
+#define atoi(a) strtol((a), (char**)0, 0)
+
+cx_options_t o;
+cx_stat_t st;
+int aflag;
+int sflag;
+
+char *symbol (unsigned char sym)
+{
+ static char buf[40];
+
+ if (sym < ' ')
+ sprintf (buf, "^%c", sym+0100);
+ else if (sym == '\\')
+ strcat (buf, "\\\\");
+ else if (sym < 127)
+ sprintf (buf, "%c", sym);
+ else
+ sprintf (buf, "\\%03o", sym);
+ return (buf);
+}
+
+unsigned char atosym (char *s)
+{
+ unsigned char c;
+
+ if (*s == '^')
+ return (*++s & 037);
+ if (*s == '\\')
+ return (strtol (++s, 0, 8));
+ return (*s);
+}
+
+void usage ()
+{
+ printf ("Cronyx-Sigma Adapter Configuration Utility, Version 1.0\n");
+ printf ("Copyright (C) 1994 Cronyx Ltd.\n");
+ printf ("Usage:\n");
+ printf ("\tcxconfig [-a]\n");
+ printf ("\t\t-- print status of all channels\n");
+ printf ("\tcxconfig [-a] <channel>\n");
+ printf ("\t\t-- print status of the channel\n");
+ printf ("\tcxconfig <channel> [async | hdlc | bisync | x.21] [ispeed #] [ospeed #]\n");
+ printf ("\t\t[+cts | -cts]\n");
+ printf ("\t\t-- set channel options\n");
+ exit (1);
+}
+
+char *chantype (int type)
+{
+ switch (type) {
+ case T_NONE: return ("none");
+ case T_ASYNC: return ("RS-232");
+ case T_UNIV_RS232: return ("RS-232");
+ case T_UNIV_RS449: return ("RS-232/RS-449");
+ case T_UNIV_V35: return ("RS-232/V.35");
+ case T_SYNC_RS232: return ("RS-232");
+ case T_SYNC_V35: return ("V.35");
+ case T_SYNC_RS449: return ("RS-449");
+ }
+}
+
+char *chanmode (int mode)
+{
+ switch (mode) {
+ case M_ASYNC: return ("Async");
+ case M_HDLC: return ("HDLC");
+ case M_BISYNC: return ("Bisync");
+ case M_X21: return ("X.21");
+ default: return ("???");
+ }
+}
+
+void getchan (int channel)
+{
+ int s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ o.board = channel/NCHAN;
+ o.channel = channel%NCHAN;
+ if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCGETMODE");
+ exit (1);
+ }
+ close (s);
+ if (o.type == T_NONE) {
+ fprintf (stderr, "cx%d: channel %d not configured\n", o.board,
+ o.channel);
+ exit (1);
+ }
+}
+
+int printstats (int channel, int hflag)
+{
+ int s, res;
+
+ s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ st.board = channel/NCHAN;
+ st.channel = channel%NCHAN;
+ res = ioctl (s, CXIOCGETSTAT, (caddr_t)&st);
+ close (s);
+ if (res < 0)
+ return (-1);
+
+ if (hflag)
+ printf ("Chan Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n");
+ printf ("cx%-2d %7ld %7ld %7ld %8ld %7ld %7ld %8ld %7ld %7ld\n",
+ channel, st.rintr, st.tintr, st.mintr, st.ibytes, st.ipkts,
+ st.ierrs, st.obytes, st.opkts, st.oerrs);
+ return (0);
+}
+
+void printallstats ()
+{
+ int b, c;
+
+ printf ("Chan Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n");
+ for (b=0; b<NBRD; ++b)
+ for (c=0; c<NCHAN; ++c)
+ printstats (b*NCHAN + c, 0);
+}
+
+void setchan (int channel)
+{
+ int s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ o.board = channel/NCHAN;
+ o.channel = channel%NCHAN;
+ if (ioctl (s, CXIOCSETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCSETMODE");
+ exit (1);
+ }
+ close (s);
+}
+
+void printopt ()
+{
+ /* Common channel options */
+ /* channel option register 4 */
+ printf ("\t");
+ printf ("fifo=%d ", o.opt.cor4.thr); /* FIFO threshold */
+ printf ("%cctsdown ", o.opt.cor4.cts_zd ? '+' : '-'); /* detect 1 to 0 transition on the CTS */
+ printf ("%ccddown ", o.opt.cor4.cd_zd ? '+' : '-'); /* detect 1 to 0 transition on the CD */
+ printf ("%cdsrdown ", o.opt.cor4.dsr_zd ? '+' : '-'); /* detect 1 to 0 transition on the DSR */
+ printf ("\n");
+
+ /* channel option register 5 */
+ printf ("\t");
+ printf ("rfifo=%d ", o.opt.cor5.rx_thr); /* receive flow control FIFO threshold */
+ printf ("%cctsup ", o.opt.cor5.cts_od ? '+' : '-'); /* detect 0 to 1 transition on the CTS */
+ printf ("%ccdup ", o.opt.cor5.cd_od ? '+' : '-'); /* detect 0 to 1 transition on the CD */
+ printf ("%cdsrup ", o.opt.cor5.dsr_od ? '+' : '-'); /* detect 0 to 1 transition on the DSR */
+ printf ("\n");
+
+ /* receive clock option register */
+ printf ("\t");
+ printf ("%s ", o.opt.rcor.encod == ENCOD_NRZ ? "nrz" : /* signal encoding */
+ o.opt.rcor.encod == ENCOD_NRZI ? "nrzi" :
+ o.opt.rcor.encod == ENCOD_MANCHESTER ? "manchester" : "???");
+ printf ("%cdpll ", o.opt.rcor.dpll ? '+' : '-'); /* DPLL enable */
+
+ /* transmit clock option register */
+ printf ("%clloop ", o.opt.tcor.llm ? '+' : '-'); /* local loopback mode */
+ printf ("%cextclock ", o.opt.tcor.ext1x ? '+' : '-'); /* external 1x clock mode */
+ printf ("\n");
+
+ switch (o.mode) {
+ case M_ASYNC: /* async mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.aopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.aopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.aopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.aopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.aopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.aopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("%cdsr ", o.aopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */
+ printf ("%ccts ", o.aopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */
+ printf ("%crts ", o.aopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */
+ printf ("%crloop ", o.aopt.cor2.rlm ? '+' : '-'); /* remote loopback mode enable */
+ printf ("%cetc ", o.aopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */
+ printf ("%cxon ", o.aopt.cor2.ixon ? '+' : '-'); /* in-band XON/XOFF enable */
+ printf ("%cxany ", o.aopt.cor2.ixany ? '+' : '-'); /* XON on any character */
+ printf ("\n");
+
+ /* option register 3 */
+ printf ("\t");
+ printf ("%s ", o.aopt.cor3.stopb == STOPB_1 ? "stopb1" : /* stop bit length */
+ o.aopt.cor3.stopb == STOPB_15 ? "stopb1.5" :
+ o.aopt.cor3.stopb == STOPB_2 ? "stopb2" : "???");
+ printf ("%csdt ", o.aopt.cor3.scde ? '+' : '-'); /* special char detection enable */
+ printf ("%cflowct ", o.aopt.cor3.flowct ? '+' : '-'); /* flow control transparency mode */
+ printf ("%crdt ", o.aopt.cor3.rngde ? '+' : '-'); /* range detect enable */
+ printf ("%cexdt ", o.aopt.cor3.escde ? '+' : '-'); /* extended spec. char detect enable */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("%s ", o.aopt.cor6.parerr == PERR_INTR ? "parintr" : /* parity/framing error actions */
+ o.aopt.cor6.parerr == PERR_NULL ? "parnull" :
+ o.aopt.cor6.parerr == PERR_IGNORE ? "parign" :
+ o.aopt.cor6.parerr == PERR_DISCARD ? "pardisc" :
+ o.aopt.cor6.parerr == PERR_FFNULL ? "parffnull" : "???");
+ printf ("%s ", o.aopt.cor6.brk == BRK_INTR ? "brkintr" : /* action on break condition */
+ o.aopt.cor6.brk == BRK_NULL ? "brknull" :
+ o.aopt.cor6.brk == BRK_DISCARD ? "brkdisc" : "???");
+ printf ("%cinlcr ", o.aopt.cor6.inlcr ? '+' : '-'); /* translate NL to CR on input */
+ printf ("%cicrnl ", o.aopt.cor6.icrnl ? '+' : '-'); /* translate CR to NL on input */
+ printf ("%cigncr ", o.aopt.cor6.igncr ? '+' : '-'); /* discard CR on input */
+ printf ("\n");
+
+ /* channel option register 7 */
+ printf ("\t");
+ printf ("%cocrnl ", o.aopt.cor7.ocrnl ? '+' : '-'); /* translate CR to NL on output */
+ printf ("%conlcr ", o.aopt.cor7.onlcr ? '+' : '-'); /* translate NL to CR on output */
+ printf ("%cfcerr ", o.aopt.cor7.fcerr ? '+' : '-'); /* process flow ctl err chars enable */
+ printf ("%clnext ", o.aopt.cor7.lnext ? '+' : '-'); /* LNext option enable */
+ printf ("%cistrip ", o.aopt.cor7.istrip ? '+' : '-'); /* strip 8-bit on input */
+ printf ("\n");
+
+ printf ("\t");
+ printf ("schr1=%s ", symbol (o.aopt.schr1)); /* special character register 1 (XON) */
+ printf ("schr2=%s ", symbol (o.aopt.schr2)); /* special character register 2 (XOFF) */
+ printf ("schr3=%s ", symbol (o.aopt.schr3)); /* special character register 3 */
+ printf ("schr4=%s ", symbol (o.aopt.schr4)); /* special character register 4 */
+ printf ("scrl=%s ", symbol (o.aopt.scrl)); /* special character range low */
+ printf ("scrh=%s ", symbol (o.aopt.scrh)); /* special character range high */
+ printf ("lnext=%s ", symbol (o.aopt.lnxt)); /* LNext character */
+ printf ("\n");
+ break;
+
+ case M_HDLC: /* hdlc mode options */
+ /* hdlc channel option register 1 */
+ printf ("\t");
+ printf ("if%d ", o.hopt.cor1.ifflags); /* number of inter-frame flags sent */
+ printf ("%s ", o.hopt.cor1.admode == ADMODE_NOADDR ? "noaddr" : /* addressing mode */
+ o.hopt.cor1.admode == ADMODE_4_1 ? "addr1" :
+ o.hopt.cor1.admode == ADMODE_2_2 ? "addr2" : "???");
+ printf ("%cclrdet ", o.hopt.cor1.clrdet ? '+' : '-'); /* clear detect for X.21 data transfer phase */
+ printf ("addrlen%d ", o.hopt.cor1.aflo + 1); /* address field length option */
+ printf ("\n");
+
+ /* hdlc channel option register 2 */
+ printf ("\t");
+ printf ("%cdsr ", o.hopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */
+ printf ("%ccts ", o.hopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */
+ printf ("%crts ", o.hopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */
+ printf ("%ccrcinv ", o.hopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */
+ printf ("%cfcsapd ", o.hopt.cor2.fcsapd ? '+' : '-'); /* FCS append */
+ printf ("\n");
+
+ /* hdlc channel option register 3 */
+ printf ("\t");
+ printf ("pad%d ", o.hopt.cor3.padcnt); /* pad character count */
+ printf ("idle%s ", o.hopt.cor3.idle ? "mark" : "flag"); /* idle mode */
+ printf ("%cfcs ", o.hopt.cor3.nofcs ? '-' : '+'); /* FCS disable */
+ printf ("fcs-%s ", o.hopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */
+ printf ("syn=%s ", o.hopt.cor3.syncpat ? "0xAA" : "0x00"); /* send sync pattern */
+ printf ("%csyn ", o.hopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */
+ printf ("\n");
+
+ printf ("\t");
+ printf ("rfar1=0x%02x ", o.hopt.rfar1); /* receive frame address register 1 */
+ printf ("rfar2=0x%02x ", o.hopt.rfar2); /* receive frame address register 2 */
+ printf ("rfar3=0x%02x ", o.hopt.rfar3); /* receive frame address register 3 */
+ printf ("rfar4=0x%02x ", o.hopt.rfar4); /* receive frame address register 4 */
+ printf ("crc-%s ", o.hopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */
+ printf ("\n");
+ break;
+
+ case M_BISYNC: /* bisync mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.bopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.bopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.bopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.bopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.bopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.bopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("syn%d ", o.bopt.cor2.syns+2); /* number of extra SYN chars before a frame */
+ printf ("%ccrcinv ", o.bopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */
+ printf ("%s ", o.bopt.cor2.ebcdic ? "ebcdic" : "ascii"); /* use EBCDIC as char set (instead of ASCII) */
+ printf ("%cbccapd ", o.bopt.cor2.bcc ? '+' : '-'); /* BCC append enable */
+ printf ("%s ", o.bopt.cor2.lrc ? "lrc" : "crc-16"); /* longitudinal redundancy check */
+ printf ("\n");
+
+ /* channel option register 3 */
+ printf ("\t");
+ printf ("pad%d ", o.bopt.cor3.padcnt); /* pad character count */
+ printf ("idle%s ", o.bopt.cor3.idle ? "mark" : "syn"); /* idle mode */
+ printf ("%cfcs ", o.bopt.cor3.nofcs ? '-' : '+'); /* FCS disable */
+ printf ("fcs-%s ", o.bopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */
+ printf ("syn=%s ", o.bopt.cor3.padpat ? "0x55" : "0xAA"); /* send sync pattern */
+ printf ("%csyn ", o.bopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("specterm=%s ", symbol (o.bopt.cor6.specterm)); /* special termination character */
+
+ printf ("crc-%s ", o.bopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */
+ printf ("\n");
+ break;
+
+ case M_X21: /* x.21 mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.xopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.xopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.xopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.xopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.xopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.xopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("%cetc ", o.xopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */
+
+ /* channel option register 3 */
+ printf ("%csdt ", o.xopt.cor3.scde ? '+' : '-'); /* special char detection enable */
+ printf ("%cstripsyn ", o.xopt.cor3.stripsyn ? '+' : '-'); /* treat SYN chars as special condition */
+ printf ("%cssdt ", o.xopt.cor3.ssde ? '+' : '-'); /* steady state detect enable */
+ printf ("syn%c ", o.xopt.cor3.syn ? '1' : '2'); /* the number of SYN chars on receive */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("syn=%s ", symbol (o.xopt.cor6.synchar)); /* syn character */
+
+ printf ("schr1=%s ", symbol (o.xopt.schr1)); /* special character register 1 */
+ printf ("schr2=%s ", symbol (o.xopt.schr2)); /* special character register 2 */
+ printf ("schr3=%s ", symbol (o.xopt.schr3)); /* special character register 3 */
+ printf ("\n");
+ break;
+ }
+}
+
+void printchan (int channel)
+{
+ printf ("cx%d (%s) %s", channel, chantype (o.type), chanmode (o.mode));
+ if (o.txbaud == o.rxbaud)
+ printf (" %d", o.rxbaud);
+ else
+ printf (" ospeed=%d ispeed=%d", o.txbaud, o.rxbaud);
+ if ((o.channel == 0 || o.channel == 8) &&
+ (o.type == T_UNIV_V35 || o.type == T_UNIV_RS449))
+ printf (" port=%s", o.iftype ? (o.type == T_UNIV_V35 ?
+ "v35" : "rs449") : "rs232");
+ printf (o.sopt.ext ? " ext" : o.sopt.cisco ? " cisco" : " ppp");
+ printf (" %ckeepalive", o.sopt.keepalive ? '+' : '-');
+ printf (" %cautorts", o.sopt.norts ? '-' : '+');
+ if (*o.master)
+ printf (" master=%s", o.master);
+ printf ("\n");
+ if (aflag)
+ printopt ();
+}
+
+void printall ()
+{
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char buf[BUFSIZ], *cp;
+ int s, c;
+
+ s = socket (AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror ("cxconfig: socket");
+ exit (1);
+ }
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (s, SIOCGIFCONF, (caddr_t)&ifc) < 0) {
+ perror ("cxconfig: SIOCGIFCONF");
+ exit (1);
+ }
+ close (s);
+ s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+
+ ifr = ifc.ifc_req;
+#define max(a,b) ((a)>(b) ? (a) : (b))
+#define size(p) max((p).sa_len, sizeof(p))
+ for (cp=buf; cp<buf+ifc.ifc_len; cp+=sizeof(ifr->ifr_name)+size(ifr->ifr_addr)) {
+ ifr = (struct ifreq*) cp;
+ if (ifr->ifr_addr.sa_family != AF_LINK)
+ continue;
+ if (strncmp (ifr->ifr_name, "cx", 2) != 0)
+ continue;
+ c = atoi (ifr->ifr_name + 2);
+ o.board = c/NCHAN;
+ o.channel = c%NCHAN;
+ if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCGETMODE");
+ exit (1);
+ }
+ printchan (c);
+ }
+ close (s);
+}
+
+void set_interface_type (char *type)
+{
+ if (o.channel != 0 && o.channel != 8) {
+ printf ("interface option is applicable only for channels 0 and 8\n");
+ exit (1);
+ }
+ if (o.type != T_UNIV_V35 && o.type != T_UNIV_RS449) {
+ printf ("interface option is applicable only for universal channels\n");
+ exit (1);
+ }
+ if (! strcasecmp (type, "port=rs232"))
+ o.iftype = 0;
+ else
+ o.iftype = 1;
+}
+
+void set_master (char *ifname)
+{
+ if (o.type == T_ASYNC) {
+ printf ("master option is not applicable for async channels\n");
+ exit (1);
+ }
+ strcpy (o.master, ifname);
+}
+
+void set_async_opt (char *opt)
+{
+ /* channel option register 1 */
+ if (! strncasecmp (opt, "cs", 2)) o.aopt.cor1.charlen = atoi (opt + 2) - 1;
+ else if (! strcasecmp (opt, "parodd")) o.aopt.cor1.parity = 1;
+ else if (! strcasecmp (opt, "pareven")) o.aopt.cor1.parity = 0;
+ else if (! strcasecmp (opt, "-ignpar")) o.aopt.cor1.ignpar = 0;
+ else if (! strcasecmp (opt, "+ignpar")) o.aopt.cor1.ignpar = 1;
+ else if (! strcasecmp (opt, "nopar")) o.aopt.cor1.parmode = PARM_NOPAR;
+ else if (! strcasecmp (opt, "forcepar")) o.aopt.cor1.parmode = PARM_FORCE;
+
+ /* channel option register 2 */
+ else if (! strcasecmp (opt, "-dsr")) o.aopt.cor2.dsrae = 0;
+ else if (! strcasecmp (opt, "+dsr")) o.aopt.cor2.dsrae = 1;
+ else if (! strcasecmp (opt, "-cts")) o.aopt.cor2.ctsae = 0;
+ else if (! strcasecmp (opt, "+cts")) o.aopt.cor2.ctsae = 1;
+ else if (! strcasecmp (opt, "-rts")) o.aopt.cor2.rtsao = 0;
+ else if (! strcasecmp (opt, "+rts")) o.aopt.cor2.rtsao = 1;
+ else if (! strcasecmp (opt, "-rloop")) o.aopt.cor2.rlm = 0;
+ else if (! strcasecmp (opt, "+rloop")) o.aopt.cor2.rlm = 1;
+ else if (! strcasecmp (opt, "-etc")) o.aopt.cor2.etc = 0;
+ else if (! strcasecmp (opt, "+etc")) o.aopt.cor2.etc = 1;
+ else if (! strcasecmp (opt, "-ixon")) o.aopt.cor2.ixon = 0;
+ else if (! strcasecmp (opt, "+ixon")) o.aopt.cor2.ixon = 1;
+ else if (! strcasecmp (opt, "-ixany")) o.aopt.cor2.ixany = 0;
+ else if (! strcasecmp (opt, "+ixany")) o.aopt.cor2.ixany = 1;
+
+ /* option register 3 */
+ else if (! strcasecmp (opt, "stopb1")) o.aopt.cor3.stopb = STOPB_1;
+ else if (! strcasecmp (opt, "stopb1.5")) o.aopt.cor3.stopb = STOPB_15;
+ else if (! strcasecmp (opt, "stopb2")) o.aopt.cor3.stopb = STOPB_2;
+ else if (! strcasecmp (opt, "-sdt")) o.aopt.cor3.scde = 0;
+ else if (! strcasecmp (opt, "+sdt")) o.aopt.cor3.scde = 1;
+ else if (! strcasecmp (opt, "-flowct")) o.aopt.cor3.flowct = 0;
+ else if (! strcasecmp (opt, "+flowct")) o.aopt.cor3.flowct = 1;
+ else if (! strcasecmp (opt, "-rdt")) o.aopt.cor3.rngde = 0;
+ else if (! strcasecmp (opt, "+rdt")) o.aopt.cor3.rngde = 1;
+ else if (! strcasecmp (opt, "-exdt")) o.aopt.cor3.escde = 0;
+ else if (! strcasecmp (opt, "+exdt")) o.aopt.cor3.escde = 1;
+
+ /* channel option register 6 */
+ else if (! strcasecmp (opt, "parintr")) o.aopt.cor6.parerr = PERR_INTR;
+ else if (! strcasecmp (opt, "parnull")) o.aopt.cor6.parerr = PERR_NULL;
+ else if (! strcasecmp (opt, "parign")) o.aopt.cor6.parerr = PERR_IGNORE;
+ else if (! strcasecmp (opt, "pardisc")) o.aopt.cor6.parerr = PERR_DISCARD;
+ else if (! strcasecmp (opt, "parffnull")) o.aopt.cor6.parerr = PERR_FFNULL;
+ else if (! strcasecmp (opt, "brkintr")) o.aopt.cor6.brk = BRK_INTR;
+ else if (! strcasecmp (opt, "brknull")) o.aopt.cor6.brk = BRK_NULL;
+ else if (! strcasecmp (opt, "brkdisc")) o.aopt.cor6.brk = BRK_DISCARD;
+ else if (! strcasecmp (opt, "-inlcr")) o.aopt.cor6.inlcr = 0;
+ else if (! strcasecmp (opt, "+inlcr")) o.aopt.cor6.inlcr = 1;
+ else if (! strcasecmp (opt, "-icrnl")) o.aopt.cor6.icrnl = 0;
+ else if (! strcasecmp (opt, "+icrnl")) o.aopt.cor6.icrnl = 1;
+ else if (! strcasecmp (opt, "-igncr")) o.aopt.cor6.igncr = 0;
+ else if (! strcasecmp (opt, "+igncr")) o.aopt.cor6.igncr = 1;
+
+ /* channel option register 7 */
+ else if (! strcasecmp (opt, "-ocrnl")) o.aopt.cor7.ocrnl = 0;
+ else if (! strcasecmp (opt, "+ocrnl")) o.aopt.cor7.ocrnl = 1;
+ else if (! strcasecmp (opt, "-onlcr")) o.aopt.cor7.onlcr = 0;
+ else if (! strcasecmp (opt, "+onlcr")) o.aopt.cor7.onlcr = 1;
+ else if (! strcasecmp (opt, "-fcerr")) o.aopt.cor7.fcerr = 0;
+ else if (! strcasecmp (opt, "+fcerr")) o.aopt.cor7.fcerr = 1;
+ else if (! strcasecmp (opt, "-lnext")) o.aopt.cor7.lnext = 0;
+ else if (! strcasecmp (opt, "+lnext")) o.aopt.cor7.lnext = 1;
+ else if (! strcasecmp (opt, "-istrip")) o.aopt.cor7.istrip = 0;
+ else if (! strcasecmp (opt, "+istrip")) o.aopt.cor7.istrip = 1;
+
+ else if (! strncasecmp (opt, "schr1=", 6)) o.aopt.schr1 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr2=", 6)) o.aopt.schr2 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr3=", 6)) o.aopt.schr3 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr4=", 6)) o.aopt.schr4 = atosym (opt+6);
+ else if (! strncasecmp (opt, "scrl=", 5)) o.aopt.scrl = atosym (opt+5);
+ else if (! strncasecmp (opt, "scrh=", 5)) o.aopt.scrh = atosym (opt+5);
+ else if (! strncasecmp (opt, "lnext=", 6)) o.aopt.lnxt = atosym (opt+6);
+ else
+ usage ();
+}
+
+void set_hdlc_opt (char *opt)
+{
+ /* hdlc channel option register 1 */
+ if (! strncasecmp (opt, "if", 2)) o.hopt.cor1.ifflags = atoi (opt + 2);
+ else if (! strcasecmp (opt, "noaddr")) o.hopt.cor1.admode = ADMODE_NOADDR;
+ else if (! strcasecmp (opt, "addr1")) o.hopt.cor1.admode = ADMODE_4_1;
+ else if (! strcasecmp (opt, "addr2")) o.hopt.cor1.admode = ADMODE_2_2;
+ else if (! strcasecmp (opt, "-clrdet")) o.hopt.cor1.clrdet = 0;
+ else if (! strcasecmp (opt, "+clrdet")) o.hopt.cor1.clrdet = 1;
+ else if (! strcasecmp (opt, "addrlen1")) o.hopt.cor1.aflo = 0;
+ else if (! strcasecmp (opt, "addrlen2")) o.hopt.cor1.aflo = 1;
+
+ /* hdlc channel option register 2 */
+ else if (! strcasecmp (opt, "-dsr")) o.hopt.cor2.dsrae = 0;
+ else if (! strcasecmp (opt, "+dsr")) o.hopt.cor2.dsrae = 1;
+ else if (! strcasecmp (opt, "-cts")) o.hopt.cor2.ctsae = 0;
+ else if (! strcasecmp (opt, "+cts")) o.hopt.cor2.ctsae = 1;
+ else if (! strcasecmp (opt, "-rts")) o.hopt.cor2.rtsao = 0;
+ else if (! strcasecmp (opt, "+rts")) o.hopt.cor2.rtsao = 1;
+ else if (! strcasecmp (opt, "-fcsapd")) o.hopt.cor2.fcsapd = 0;
+ else if (! strcasecmp (opt, "+fcsapd")) o.hopt.cor2.fcsapd = 1;
+ else if (! strcasecmp (opt, "-crcinv")) o.hopt.cor2.crcninv = 1;
+ else if (! strcasecmp (opt, "+crcinv")) o.hopt.cor2.crcninv = 0;
+
+ /* hdlc channel option register 3 */
+ else if (! strncasecmp (opt, "pad", 3)) o.hopt.cor3.padcnt = atoi (opt + 3);
+ else if (! strcasecmp (opt, "idlemark")) o.hopt.cor3.idle = 1;
+ else if (! strcasecmp (opt, "idleflag")) o.hopt.cor3.idle = 0;
+ else if (! strcasecmp (opt, "-fcs")) o.hopt.cor3.nofcs = 1;
+ else if (! strcasecmp (opt, "+fcs")) o.hopt.cor3.nofcs = 0;
+ else if (! strcasecmp (opt, "fcs-crc-16")) o.hopt.cor3.fcspre = 1;
+ else if (! strcasecmp (opt, "fcs-v.41")) o.hopt.cor3.fcspre = 0;
+ else if (! strcasecmp (opt, "syn=0xaa")) o.hopt.cor3.syncpat = 1;
+ else if (! strcasecmp (opt, "syn=0x00")) o.hopt.cor3.syncpat = 0;
+ else if (! strcasecmp (opt, "-syn")) o.hopt.cor3.sndpad = 0;
+ else if (! strcasecmp (opt, "+syn")) o.hopt.cor3.sndpad = 1;
+
+ else if (! strncasecmp (opt, "rfar1=", 6)) o.hopt.rfar1 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar2=", 6)) o.hopt.rfar2 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar3=", 6)) o.hopt.rfar3 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar4=", 6)) o.hopt.rfar4 = atoi (opt + 6);
+ else if (! strcasecmp (opt, "crc-16")) o.hopt.cpsr = 1;
+ else if (! strcasecmp (opt, "crc-v.41")) o.hopt.cpsr = 0;
+ else usage ();
+}
+
+void set_bisync_opt (char *opt)
+{
+ usage ();
+}
+
+void set_x21_opt (char *opt)
+{
+ usage ();
+}
+
+int main (int argc, char **argv)
+{
+ int channel;
+
+ for (--argc, ++argv; argc>0 && **argv=='-'; --argc, ++argv)
+ if (! strcasecmp (*argv, "-a"))
+ ++aflag;
+ else if (! strcasecmp (*argv, "-s"))
+ ++sflag;
+ else
+ usage ();
+
+ if (argc <= 0) {
+ if (sflag)
+ printallstats ();
+ else
+ printall ();
+ return (0);
+ }
+
+ if (argv[0][0]=='c' && argv[0][1]=='x')
+ *argv += 2;
+ if (**argv<'0' || **argv>'9')
+ usage ();
+ channel = atoi (*argv);
+ --argc, ++argv;
+
+ if (sflag) {
+ if (printstats (channel, 1) < 0)
+ printf ("channel cx%d not available\n", channel);
+ return (0);
+ }
+
+ getchan (channel);
+
+ if (argc <= 0) {
+ printchan (channel);
+ return (0);
+ }
+
+ for (; argc>0; --argc, ++argv)
+ if (**argv == '(')
+ continue;
+ else if (! strncasecmp (*argv, "ispeed=", 7))
+ o.rxbaud = atoi (*argv+7);
+ else if (! strncasecmp (*argv, "ospeed=", 7))
+ o.txbaud = atoi (*argv+7);
+ else if (! strcasecmp (*argv, "async"))
+ o.mode = M_ASYNC;
+ else if (! strcasecmp (*argv, "hdlc"))
+ o.mode = M_HDLC;
+ else if (! strcasecmp (*argv, "bisync") ||
+ ! strcasecmp (*argv, "bsc"))
+ o.mode = M_BISYNC;
+ else if (! strcasecmp (*argv, "x.21") ||
+ ! strcasecmp (*argv, "x21"))
+ o.mode = M_X21;
+ else if (**argv>='0' && **argv<='9')
+ o.txbaud = o.rxbaud = atoi (*argv);
+ else if (! strcasecmp (*argv, "cisco")) {
+ o.sopt.cisco = 1;
+ o.sopt.ext = 0;
+ } else if (! strcasecmp (*argv, "ppp")) {
+ o.sopt.cisco = 0;
+ o.sopt.ext = 0;
+ } else if (! strcasecmp (*argv, "ext"))
+ o.sopt.ext = 1;
+ else if (! strcasecmp (*argv, "+keepalive"))
+ o.sopt.keepalive = 1;
+ else if (! strcasecmp (*argv, "-keepalive"))
+ o.sopt.keepalive = 0;
+ else if (! strcasecmp (*argv, "+autorts"))
+ o.sopt.norts = 0;
+ else if (! strcasecmp (*argv, "-autorts"))
+ o.sopt.norts = 1;
+ else if (! strcasecmp (*argv, "port=rs232") ||
+ ! strcasecmp (*argv, "port=rs449") ||
+ ! strcasecmp (*argv, "port=v35"))
+ set_interface_type (*argv);
+ else if (! strncasecmp (*argv, "master=",7))
+ set_master (*argv+7);
+
+ /*
+ * Common channel options
+ */
+ /* channel option register 4 */
+ else if (! strcasecmp (*argv, "-ctsdown"))
+ o.opt.cor4.cts_zd = 0;
+ else if (! strcasecmp (*argv, "+ctsdown"))
+ o.opt.cor4.cts_zd = 1;
+ else if (! strcasecmp (*argv, "-cddown"))
+ o.opt.cor4.cd_zd = 0;
+ else if (! strcasecmp (*argv, "+cddown"))
+ o.opt.cor4.cd_zd = 1;
+ else if (! strcasecmp (*argv, "-dsrdown"))
+ o.opt.cor4.dsr_zd = 0;
+ else if (! strcasecmp (*argv, "+dsrdown"))
+ o.opt.cor4.dsr_zd = 1;
+ else if (! strncasecmp (*argv, "fifo=", 5))
+ o.opt.cor4.thr = atoi (*argv + 5);
+
+ /* channel option register 5 */
+ else if (! strcasecmp (*argv, "-ctsup"))
+ o.opt.cor5.cts_od = 0;
+ else if (! strcasecmp (*argv, "+ctsup"))
+ o.opt.cor5.cts_od = 1;
+ else if (! strcasecmp (*argv, "-cdup"))
+ o.opt.cor5.cd_od = 0;
+ else if (! strcasecmp (*argv, "+cdup"))
+ o.opt.cor5.cd_od = 1;
+ else if (! strcasecmp (*argv, "-dsrup"))
+ o.opt.cor5.dsr_od = 0;
+ else if (! strcasecmp (*argv, "+dsrup"))
+ o.opt.cor5.dsr_od = 1;
+ else if (! strncasecmp (*argv, "rfifo=", 6))
+ o.opt.cor5.rx_thr = atoi (*argv + 6);
+
+ /* receive clock option register */
+ else if (! strcasecmp (*argv, "nrz"))
+ o.opt.rcor.encod = ENCOD_NRZ;
+ else if (! strcasecmp (*argv, "nrzi"))
+ o.opt.rcor.encod = ENCOD_NRZI;
+ else if (! strcasecmp (*argv, "manchester"))
+ o.opt.rcor.encod = ENCOD_MANCHESTER;
+ else if (! strcasecmp (*argv, "-dpll"))
+ o.opt.rcor.dpll = 0;
+ else if (! strcasecmp (*argv, "+dpll"))
+ o.opt.rcor.dpll = 1;
+
+ /* transmit clock option register */
+ else if (! strcasecmp (*argv, "-lloop"))
+ o.opt.tcor.llm = 0;
+ else if (! strcasecmp (*argv, "+lloop"))
+ o.opt.tcor.llm = 1;
+ else if (! strcasecmp (*argv, "-extclock"))
+ o.opt.tcor.ext1x = 0;
+ else if (! strcasecmp (*argv, "+extclock"))
+ o.opt.tcor.ext1x = 1;
+
+ /*
+ * Mode dependent channel options
+ */
+ else switch (o.mode) {
+ case M_ASYNC: set_async_opt (*argv); break;
+ case M_HDLC: set_hdlc_opt (*argv); break;
+ case M_BISYNC: set_bisync_opt (*argv); break;
+ case M_X21: set_x21_opt (*argv); break;
+ }
+
+ setchan (channel);
+ return (0);
+}
diff --git a/sbin/disklabel/Makefile b/sbin/disklabel/Makefile
new file mode 100644
index 0000000..bf77cff
--- /dev/null
+++ b/sbin/disklabel/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 3/17/94
+
+PROG= disklabel
+SRCS= disklabel.c dkcksum.c
+MAN8= disklabel.8
+MAN5= disklabel.5
+
+.include <bsd.prog.mk>
diff --git a/sbin/disklabel/disklabel.5 b/sbin/disklabel/disklabel.5
new file mode 100644
index 0000000..9062e82
--- /dev/null
+++ b/sbin/disklabel/disklabel.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 2 ;
+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 2 .
+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 2
+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 0000000..ce1b168
--- /dev/null
+++ b/sbin/disklabel/disklabel.8
@@ -0,0 +1,384 @@
+.\" 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
+For a virgin disk that is not known to
+.Xr disktab 5 ,
+.Ar disktype
+can be specified as
+.Dq auto .
+In this case, the driver is requested to produce a virgin label for the
+disk. This might or might not be successful, depending on whether the
+driver for the disk is able to get the required data without reading
+anything from the disk at all. It will likely succeed for all SCSI
+disks, most IDE disks, and vnode devices. Writing a label to the
+disk is the only supported operation, and the
+.Ar disk
+itself must be provided as the canonical name, i.e. not as a full
+path name.
+.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 -w sd0 auto
+.Pp
+Try to auto-detect the required information from sd0, and write a new
+label to the disk. Use another disklabel -e command to edit the
+partitioning and file system information.
+.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.
+.Pp
+For the i386 architecture, the primary bootstrap sector contains
+an embedded
+.Em fdisk
+table.
+.Nm Disklabel
+takes care to not clobber it when installing a bootstrap only
+.Pq Fl B ,
+or when editing an existing label
+.Pq Fl e ,
+but it unconditionally writes the primary bootstrap program onto
+the disk for
+.Fl w
+or
+.Fl R ,
+thus replacing the
+.Em fdisk
+table by the dummy one in the bootstrap program. This is only of
+concern if the disk is fully dedicated, so that the BSD disklabel
+starts at absolute block 0 on the disk.
diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c
new file mode 100644
index 0000000..5fcd1cd
--- /dev/null
+++ b/sbin/disklabel/disklabel.c
@@ -0,0 +1,1455 @@
+/*
+ * 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>
+#include <sys/wait.h>
+#define DKTYPENAMES
+#include <sys/disklabel.h>
+#include <ufs/ffs/fs.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdarg.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
+
+void makelabel __P((char *, char *, struct disklabel *));
+int writelabel __P((int, char *, struct disklabel *));
+void l_perror __P((char *));
+struct disklabel * readlabel __P((int));
+struct disklabel * makebootarea __P((char *, struct disklabel *, int));
+void display __P((FILE *, struct disklabel *));
+int edit __P((struct disklabel *, int));
+int editit __P((void));
+char * skip __P((char *));
+char * word __P((char *));
+int getasciilabel __P((FILE *, struct disklabel *));
+int checklabel __P((struct disklabel *));
+void setbootflag __P((struct disklabel *));
+void Warning (char *, ...);
+void Perror __P((char *));
+void usage __P((void));
+extern u_short dkcksum __P((struct disklabel *));
+struct disklabel * getvirginlabel __P((void));
+
+#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
+
+int
+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.
+ */
+void
+makelabel(type, name, lp)
+ char *type, *name;
+ register struct disklabel *lp;
+{
+ register struct disklabel *dp;
+
+ if (strcmp(type, "auto") == 0)
+ dp = getvirginlabel();
+ else
+ 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));
+}
+
+int
+writelabel(f, boot, lp)
+ int f;
+ char *boot;
+ register struct disklabel *lp;
+{
+#ifdef vax
+ register int i;
+#endif
+ 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);
+}
+
+void
+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;
+#if NUMBOOT == 1
+ struct stat sb;
+#endif
+#ifdef __i386__
+ char *tmpbuf;
+ int i, found;
+#endif /* i386 */
+#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
+#ifdef __i386__
+ /*
+ * XXX Botch alert.
+ * The i386 has the so-called fdisk table embedded into the
+ * primary bootstrap. We take care to not clobber it, but
+ * only if it does already contain some data. (Otherwise,
+ * the xxboot provides a template.)
+ */
+ if ((tmpbuf = (char *)malloc((int)dp->d_secsize)) == 0)
+ Perror(xxboot);
+ memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
+#endif /* i386 */
+ if (read(b, boot, (int)dp->d_secsize) < 0)
+ Perror(xxboot);
+ (void)close(b);
+#ifdef __i386__
+ for (i = DOSPARTOFF, found = 0;
+ !found && i < DOSPARTOFF + NDOSPART*sizeof(struct dos_partition);
+ i++)
+ found = tmpbuf[i] != 0;
+ if (found)
+ memcpy((void *)&boot[DOSPARTOFF],
+ (void *)&tmpbuf[DOSPARTOFF],
+ NDOSPART * sizeof(struct dos_partition));
+ free(tmpbuf);
+#endif /* i386 */
+ 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);
+}
+
+void
+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", (int)sizeof(lp->d_typename),
+ lp->d_typename);
+ fprintf(f, "label: %.*s\n", (int)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: %ld\n", lp->d_secsize);
+ fprintf(f, "sectors/track: %ld\n", lp->d_nsectors);
+ fprintf(f, "tracks/cylinder: %ld\n", lp->d_ntracks);
+ fprintf(f, "sectors/cylinder: %ld\n", lp->d_secpercyl);
+ fprintf(f, "cylinders: %ld\n", lp->d_ncylinders);
+ fprintf(f, "sectors/unit: %ld\n", lp->d_secperunit);
+ 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: %ld\t\t# milliseconds\n", lp->d_headswitch);
+ fprintf(f, "track-to-track seek: %ld\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, "%ld ", lp->d_drivedata[j]);
+ fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions);
+ fprintf(f,
+ "# size offset fstype [fsize bsize bps/cpg]\n");
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ fprintf(f, " %c: %8ld %8ld ", '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, " %5ld %5ld %5.5s ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag, "");
+ break;
+
+ case FS_BSDFFS:
+ fprintf(f, " %5ld %5ld %5d ",
+ pp->p_fsize, pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ case FS_BSDLFS:
+ fprintf(f, " %5ld %5ld %5d",
+ pp->p_fsize, pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ default:
+ fprintf(f, "%20.20s", "");
+ break;
+ }
+ fprintf(f, "\t# (Cyl. %4ld",
+ pp->p_offset / lp->d_secpercyl);
+ if (pp->p_offset % lp->d_secpercyl)
+ putc('*', f);
+ else
+ putc(' ', f);
+ fprintf(f, "- %ld",
+ (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);
+}
+
+int
+edit(lp, f)
+ struct disklabel *lp;
+ int f;
+{
+ register int c;
+ struct disklabel label;
+ FILE *fd;
+
+ (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);
+}
+
+int
+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.
+ */
+int
+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')) != 0)
+ *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, "sectors/unit")) {
+ v = atoi(tp);
+ if (v <= 0) {
+ fprintf(stderr, "line %d: %s: bad %s\n",
+ lineno, tp, cp);
+ errors++;
+ } else
+ lp->d_secperunit = 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) { \
+ if (tp == NULL) { \
+ fprintf(stderr, "line %d: too few numeric fields\n", lineno); \
+ errors++; \
+ break; \
+ } else { \
+ 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;
+
+ case FS_BSDLFS:
+ 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.
+ */
+int
+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 %ld\n", lp->d_secsize);
+ return (1);
+ }
+ if (lp->d_nsectors == 0) {
+ fprintf(stderr, "sectors/track %ld\n", lp->d_nsectors);
+ return (1);
+ }
+ if (lp->d_ntracks == 0) {
+ fprintf(stderr, "tracks/cylinder %ld\n", lp->d_ntracks);
+ return (1);
+ }
+ if (lp->d_ncylinders == 0) {
+ fprintf(stderr, "cylinders/unit %ld\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 %ld\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 %ld\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);
+}
+
+/*
+ * When operating on a "virgin" disk, try getting an initial label
+ * from the associated device driver. This might work for all device
+ * drivers that are able to fetch some initial device parameters
+ * without even having access to a (BSD) disklabel, like SCSI disks,
+ * most IDE drives, or vn devices.
+ *
+ * The device name must be given in its "canonical" form.
+ */
+struct disklabel *
+getvirginlabel(void)
+{
+ static struct disklabel lab;
+ char namebuf[BBSIZE];
+ int f;
+
+ if (dkname[0] == '/') {
+ fprintf(stderr,
+ "\"auto\" requires the usage of a canonical disk name.\n");
+ return (NULL);
+ }
+ (void)snprintf(namebuf, BBSIZE, "%sr%s", _PATH_DEV, dkname);
+ if ((f = open(namebuf, O_RDONLY, 0)) == -1) {
+ Perror("open()");
+ return (NULL);
+ }
+ if (ioctl(f, DIOCGDINFO, &lab) < 0) {
+ Perror("ioctl DIOCGDINFO");
+ close(f);
+ return (NULL);
+ }
+ close(f);
+ return (&lab);
+}
+
+
+/*
+ * 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.
+ */
+void
+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*/
+void
+Warning(char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "Warning, ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void
+Perror(str)
+ char *str;
+{
+ fputs("disklabel: ", stderr); perror(str);
+ exit(4);
+}
+
+void
+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 0000000..f7a1e49
--- /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 0000000..def3297
--- /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 0000000..83a194c
--- /dev/null
+++ b/sbin/dmesg/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= dmesg
+MAN8= dmesg.8
+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 0000000..b3648fc
--- /dev/null
+++ b/sbin/dmesg/dmesg.8
@@ -0,0 +1,68 @@
+.\" 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
+``/kernel''.
+.El
+.Sh SEE ALSO
+.Xr syslogd 8
+.Sh FILES
+/dev/mem
+/dev/kmem
+/dev/drum
+/kernel
+.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 0000000..b68a1d9
--- /dev/null
+++ b/sbin/dmesg/dmesg.c
@@ -0,0 +1,168 @@
+/*-
+ * 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 const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/msgbuf.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <locale.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];
+
+ (void) setlocale(LC_CTYPE, "");
+ 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. If the buffer has wrapped, the
+ * write pointer points to the oldest data. Otherwise, the write
+ * pointer points to \0's following the data. Read the entire
+ * buffer starting at the write pointer and ignore nulls so that
+ * we effectively start at the oldest data.
+ */
+ p = cur.msg_bufc + cur.msg_bufx;
+ ep = (cur.msg_bufx == 0 ? cur.msg_bufc + MSG_BSIZE : p);
+ newl = skip = 0;
+ do {
+ 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);
+ } while (++p != ep);
+ 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/dset/Makefile b/sbin/dset/Makefile
new file mode 100644
index 0000000..ce15a61
--- /dev/null
+++ b/sbin/dset/Makefile
@@ -0,0 +1,7 @@
+PROG= dset
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+CFLAGS+=-I${.CURDIR}/../../sys/
+MAN8= dset.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dset/dset.8 b/sbin/dset/dset.8
new file mode 100644
index 0000000..aeec2ed
--- /dev/null
+++ b/sbin/dset/dset.8
@@ -0,0 +1,78 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: dset.8,v 1.3 1996/10/01 14:42:35 jkh Exp $
+.\" "
+.Dd May 2, 1996
+.Dt DSET 8
+.Os FreeBSD
+.Sh NAME
+.Nm dset
+.Nd "update the boot file to the current configuration"
+.Sh SYNOPSIS
+.Nm dset
+.Op Fl vqt
+.Sh DESCRIPTION
+.Nm Dset
+records the configuration changes for ISA devices that have been made
+from
+.Em UserConfig
+after booting with the
+.Fl c
+option. The changes (if any) are recorded to the file name returned by
+.Xr getbootfile 3 .
+.Nm Dset
+is typically called from within
+.Pa /etc/rc .
+.Pp
+.Nm Dset
+understands the following options:
+.Bl -tag -width indent -compact
+.It Fl v
+Verbose; tell what is being done.
+.It Fl q
+Quiet; don't print any error messages.
+.It Fl t
+Test only; don't actually record any changes to the boot file, print
+only what would have been done. This implies
+.Fl v .
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with status 0 on success, or with status 1 if any
+problem occured. Unless
+.Fl q
+has been specified, a short message hinting to the cause of the problem
+will be printed to standard error output.
+.Sh SEE ALSO
+.Xr getbootfile 3 ,
+.Xr boot_i386 8 ,
+.Xr rc 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.0.5 .
diff --git a/sbin/dset/dset.c b/sbin/dset/dset.c
new file mode 100644
index 0000000..3961d55
--- /dev/null
+++ b/sbin/dset/dset.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 1995 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * Device configuration to kernel image saving utility.
+ */
+
+#include <stdio.h>
+#include <nlist.h>
+#include <paths.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <kvm.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <machine/param.h>
+#include "i386/isa/isa_device.h"
+
+#define TRUE 1
+#define FALSE 0
+
+extern int errno;
+
+struct nlist nl[] = {
+#define N_TABTTY 0
+ {"_isa_devtab_tty"},
+#define N_TABBIO 1
+ {"_isa_devtab_bio"},
+#define N_TABNET 2
+ {"_isa_devtab_net"},
+#define N_TABNULL 3
+ {"_isa_devtab_null"},
+ "",
+};
+#define N_TABLAST N_TABNULL
+
+struct nlist nlk[] = {
+ {"_isa_devlist"},
+ "",
+};
+
+int quiet = FALSE;
+
+void
+fatal(name, str)
+ char *name, *str;
+{
+ if (quiet)
+ exit(1);
+ if (str)
+ fprintf(stderr, "%s : %s\n", name, str);
+ else
+ perror(name);
+ exit(1);
+}
+
+void
+error(name, str)
+ char *name, *str;
+{
+ if (quiet)
+ return;
+ if (str)
+ fprintf(stderr, "%s : %s\n", name, str);
+ else
+ perror(name);
+}
+
+void
+usage(char *title)
+{
+ fprintf(stderr, "usage: %s [-qtv]\n", title);
+}
+
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int f, res, s;
+ int modified,dev_found;
+ int sym;
+ u_long pos, entry, pos1, pos_t;
+ u_long flags;
+ struct isa_device buf, buf1;
+ struct isa_driver dbuf;
+ char nbuf[5];
+ struct stat fst;
+ struct exec es;
+ kvm_t *kd;
+ static char errb[_POSIX2_LINE_MAX];
+ const char *kernel = NULL;
+
+ extern char *optarg;
+ char ch;
+ int testonly = FALSE;
+ int verbose = FALSE;
+
+ while ((ch = getopt(ac, av, "qtv")) != EOF)
+ switch (ch) {
+ case 'q':
+ quiet = TRUE;
+ break;
+ case 't':
+ testonly = TRUE;
+ /* In test mode we want to be verbose */
+
+ case 'v':
+ verbose = TRUE;
+ break;
+
+ case '?':
+ default:
+ usage(av[0]);
+ exit(1);
+ }
+
+
+ kernel = getbootfile();
+ if (verbose)
+ printf("Boot image: %s\n", kernel);
+
+ if (!(kd = kvm_open(NULL, NULL, NULL, O_RDONLY, errb)))
+ fatal("kvm_open", NULL);
+
+ if (kvm_nlist(kd, nlk) != 0)
+ fatal("kvm_nlist", NULL);
+
+ if (nlk[0].n_type == 0)
+ fatal("kvm_nlist", "bad symbol type");
+
+ if (nlist(kernel, nl) != 0)
+ fatal("nlist", NULL);
+
+ if (nl[0].n_type == 0)
+ fatal("nlist", "bad symbol type");
+
+ if (stat(kernel, &fst) < 0)
+ fatal("stat", NULL);
+
+ flags = fst.st_flags;
+
+ if (chflags(kernel, (u_long) 0) < 0)
+ fatal("chflags", NULL);
+
+ if ((f = open(kernel, O_RDWR)) <= 0)
+ fatal("open", NULL);
+
+ if (read(f, &es, sizeof(struct exec)) <= 0)
+ fatal("read header", NULL);
+
+ entry = es.a_entry;
+
+ for (sym = 0; sym <= N_TABLAST; sym++) {
+ if (verbose)
+ printf("\nTable: %s\n", nl[sym].n_name);
+ pos = nl[sym].n_value + getpagesize() - entry;
+
+ pos1 = nlk[0].n_value;
+
+ if (lseek(f, pos, SEEK_SET) != pos)
+ fatal("seek", NULL);
+
+ if (verbose)
+ printf("----------------------------------------------------\n");
+
+ do {
+ if ((res = read(f, (char *) &buf, sizeof(struct isa_device)))
+ <= 0)
+ fatal("read", NULL);
+
+
+
+ if (kvm_read(kd, pos1, &pos_t, sizeof(u_long)) < 0)
+ fatal("kvmread", NULL);
+ dev_found = 0;
+
+ while(pos_t!=NULL) {
+ if (kvm_read(kd, pos_t, &buf1, sizeof(struct isa_device)) < 0)
+ fatal("kvmread", NULL);
+
+ if (buf1.id_id != buf.id_id) {
+ pos_t = (u_long)(buf1.id_next);
+ continue;
+ } else
+ dev_found=1;
+
+ if (buf1.id_driver)
+ if (kvm_read(kd, (u_long) buf1.id_driver,
+ &dbuf, sizeof(struct isa_driver)) < 0) {
+ error("kvm_read", "no driver");
+ } else {
+ if (kvm_read(kd, (u_long) dbuf.name,
+ nbuf, sizeof(nbuf)) < 0) {
+ error("kvm_read", NULL);
+ } else {
+ nbuf[sizeof(nbuf) - 1] = 0;
+ if (verbose)
+ printf("Device: %s%d\n", nbuf, buf1.id_unit);
+ }
+ }
+ else
+ error("kvm_read", "no driver");
+ break;
+
+ };
+
+ if (!dev_found)
+ continue;
+
+ if (buf1.id_id != 0)
+ if (verbose)
+ printf(
+ "kernel: id=%u io=%X irq=%d drq=%d maddr=%X msize=%d flags=%X enabled=%X \n",
+ buf1.id_id, buf1.id_iobase, buf1.id_irq, buf1.id_drq,
+ buf1.id_maddr, buf1.id_msize, buf1.id_flags, buf1.id_enabled);
+
+ if (buf.id_id != 0)
+ if (verbose)
+ printf(
+ "file: id=%u io=%X irq=%d drq=%d maddr=%X msize=%d flags=%X enabled=%X \n",
+ buf.id_id, buf.id_iobase, buf.id_irq, buf.id_drq,
+ buf.id_maddr, buf.id_msize, buf.id_flags, buf.id_enabled);
+
+
+ /*
+ * OK,now we'd compare values and set'em from kernel.
+ */
+ modified = FALSE;
+
+ if (buf.id_iobase != -1 && buf.id_iobase !=
+ buf1.id_iobase) {
+ if (verbose)
+ printf("Setting IO addr\n");
+ buf.id_iobase = buf1.id_iobase;
+ modified = TRUE;
+ }
+ if (buf.id_irq != (u_short)-1 && buf.id_irq != buf1.id_irq) {
+ if (verbose)
+ printf("Setting IRQ\n");
+ buf.id_irq = buf1.id_irq;
+ modified = TRUE;
+ }
+ if (buf.id_drq != -1 && buf.id_drq != buf1.id_drq) {
+ if (verbose)
+ printf("Setting DRQ\n");
+ buf.id_drq = buf1.id_drq;
+ modified = TRUE;
+ }
+ if (buf.id_maddr != (caddr_t)-1 && buf.id_maddr != buf1.id_maddr) {
+ if (verbose)
+ printf("Setting memory addres\n");
+ buf.id_maddr = buf1.id_maddr;
+ modified = TRUE;
+ }
+ if (buf.id_msize != buf1.id_msize) {
+ if (verbose)
+ printf("Setting msize\n");
+ buf.id_msize = buf1.id_msize;
+ modified = TRUE;
+ }
+ if (buf.id_flags != buf1.id_flags) {
+ if (verbose)
+ printf("Setting flags\n");
+ buf.id_flags = buf1.id_flags;
+ modified = TRUE;
+ }
+ if (buf.id_enabled != buf1.id_enabled) {
+ if (verbose)
+ printf("Setting device enable/disable\n");
+ buf.id_enabled = buf1.id_enabled;
+ modified = TRUE;
+ }
+ if (verbose)
+ printf("----------------------------------------------------\n");
+ if (modified && !testonly) {
+
+ res = lseek(f, -(off_t) sizeof(struct isa_device),
+ SEEK_CUR);
+ if (write(f, &buf, sizeof(struct isa_device)) <= 0)
+ fatal("write", NULL);
+
+ }
+ } while (buf.id_id != 0 && buf1.id_id != 0);
+ }
+
+ if (chflags(kernel, flags) < 0)
+ fatal("chflags restore", NULL);
+
+ kvm_close(kd);
+ close(f);
+}
diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile
new file mode 100644
index 0000000..d9aba3d
--- /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.8
+MLINKS+=dump.8 rdump.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8
new file mode 100644
index 0000000..3eb067b
--- /dev/null
+++ b/sbin/dump/dump.8
@@ -0,0 +1,354 @@
+.\" 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/rst0
+(a tape drive),
+.Pa /dev/rfd1
+(a floppy 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 .
+The default path name of the remote
+.Xr rmt 8
+program is
+.Pa /etc/rmt ;
+this can be overridden by the environment variable
+.Ev RMT .
+.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
+/sbin/dump 0uf /dev/nrst0 /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 ENVIRONMENT
+The environment variable
+.Ev RMT
+will be used to determine the pathname of the remote
+.Xr rmt 8
+program.
+.Sh FILES
+.Bl -tag -width /etc/dumpdates -compact
+.It Pa /dev/rst0
+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 ft 8 ,
+.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
+restore(8) is currently unable to restore dumps that were created
+with a blocksize larger than 32 on some tape drives. This is likely
+a bug in the tape driver. Workaround for safety reasons:
+dump aborts with an error message when choosing a blocksize > 32.
+.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
+.At v6 .
diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h
new file mode 100644
index 0000000..4b2330e
--- /dev/null
+++ b/sbin/dump/dump.h
@@ -0,0 +1,213 @@
+/*-
+ * 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 */
+time_t tend_writing; /* after writing the last 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));
+
+void Exit __P((int status)) __dead2;
+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 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 0000000..d7d59a4
--- /dev/null
+++ b/sbin/dump/dumprmt.c
@@ -0,0 +1,409 @@
+/*-
+ * 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/in_systm.h>
+#include <netinet/ip.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 *));
+
+static int errfd = -1;
+
+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()
+{
+ msg("Lost connection to remote host.\n");
+ if (errfd != -1) {
+ fd_set r;
+ struct timeval t;
+
+ FD_ZERO(&r);
+ FD_SET(errfd, &r);
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ if (select(errfd + 1, &r, NULL, NULL, &t)) {
+ int i;
+ char buf[2048];
+
+ if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) {
+ buf[i] = '\0';
+ msg("on %s: %s%s", rmtpeer, buf,
+ buf[i - 1] == '\n' ? "" : "\n");
+ }
+ }
+ }
+
+ exit(X_ABORT);
+}
+
+void
+rmtgetconn()
+{
+ register char *cp;
+ register const char *rmt;
+ static struct servent *sp = NULL;
+ static struct passwd *pwd = NULL;
+#ifdef notdef
+ static int on = 1;
+#endif
+ char *tuser;
+ int size;
+ int throughput;
+
+ if (sp == NULL) {
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL) {
+ msg("shell/tcp: unknown service\n");
+ exit(X_ABORT);
+ }
+ pwd = getpwuid(getuid());
+ if (pwd == NULL) {
+ msg("who are you?\n");
+ exit(X_ABORT);
+ }
+ }
+ if ((cp = index(rmtpeer, '@')) != NULL) {
+ tuser = rmtpeer;
+ *cp = '\0';
+ if (!okname(tuser))
+ exit(X_ABORT);
+ rmtpeer = ++cp;
+ } else
+ tuser = pwd->pw_name;
+ if ((rmt = getenv("RMT")) == NULL)
+ rmt = _PATH_RMT;
+ msg("");
+ rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser,
+ rmt, &errfd);
+ if (rmtape < 0) {
+ msg("login to %s as %s failed.\n", rmtpeer, tuser);
+ return;
+ }
+ (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer);
+ 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));
+ throughput = IPTOS_THROUGHPUT;
+ if (setsockopt(rmtape, IPPROTO_IP, IP_TOS,
+ &throughput, sizeof(throughput)) < 0)
+ perror("IP_TOS:IPTOS_THROUGHPUT 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 == '-')) {
+ msg("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 0000000..94656ac
--- /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 0000000..408043a
--- /dev/null
+++ b/sbin/dump/main.c
@@ -0,0 +1,578 @@
+/*-
+ * 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" " <- this is for hilit19 */
+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 void missingarg __P((int, char *)) __dead2;
+
+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);
+ /* XXX restore is unable to restore dumps that
+ were created with a blocksize larger than 32K.
+ Possibly a bug in the scsi tape driver. */
+ if ( ntrec > 32 ) {
+ msg("please choose a blocksize <= 32\n");
+ exit(X_ABORT);
+ }
+ 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)
+ * hilit19 hits again: "
+ */
+ 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);
+ }
+
+ (void)time((time_t *)&(tend_writing));
+ 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);
+
+ /* report dump performance, avoid division through zero */
+ if (tend_writing - tstart_writing == 0)
+ msg("finished in less than a second\n");
+ else
+ msg("finished in %d seconds, throughput %d KBytes/sec\n",
+ tend_writing - tstart_writing,
+ spcl.c_tapea / (tend_writing - tstart_writing));
+
+ 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 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)strncpy(rawbuf, cp, MAXPATHLEN - 1);
+ *dp = '/';
+ (void)strncat(rawbuf, "/r", MAXPATHLEN-1 - strlen(rawbuf));
+ (void)strncat(rawbuf, dp + 1, MAXPATHLEN-1 - strlen(rawbuf));
+ 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 0000000..3c464e4
--- /dev/null
+++ b/sbin/dump/optr.c
@@ -0,0 +1,530 @@
+/*-
+ * 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>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#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 * 86400));
+ 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 0000000..a9c3f82
--- /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/rst0"
+#define _PATH_DTMP "/etc/dtmp"
+#define _PATH_DUMPDATES "/etc/dumpdates"
+#define _PATH_LOCK "/tmp/dumplockXXXXXX"
+#define _PATH_RMT "/etc/rmt"
diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c
new file mode 100644
index 0000000..cc7cc13
--- /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);
+}
+
+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 0000000..32575ab
--- /dev/null
+++ b/sbin/dump/traverse.c
@@ -0,0 +1,615 @@
+/*-
+ * 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.tv_sec >= (t) || (dp)->di_ctime.tv_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 (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
+ ((off_t)blkno << dev_bshift))
+ 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 (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
+ ((off_t)blkno << dev_bshift))
+ 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 0000000..7b3fd4e
--- /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 0000000..851a733
--- /dev/null
+++ b/sbin/dumpfs/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= dumpfs
+MAN8= dumpfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8
new file mode 100644
index 0000000..17263d0
--- /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 0000000..bcde08a
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.c
@@ -0,0 +1,322 @@
+/*
+ * 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_magic != FS_MAGIC) {
+ warnx("%s: superblock has bad magic number, skipping.",
+ name);
+ (void) close(fd);
+ return (1);
+ }
+
+ 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\tclean\t%d\n",
+ afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean);
+ if (afs.fs_cpc != 0)
+ printf("blocks available in each of %d rotational positions",
+ afs.fs_nrpos);
+ else
+ printf("(no rotational position table)\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))) *
+ (off_t)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))) *
+ (off_t)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 0000000..97c4c05
--- /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: ${.CURDIR}/../../sys/ufs/lfs
+MAN8= dumplfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumplfs/dumplfs.8 b/sbin/dumplfs/dumplfs.8
new file mode 100644
index 0000000..de0c680
--- /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 0000000..38de196
--- /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.tv_sec),
+ "mtime ", ctime(&dip->di_mtime.tv_sec),
+ "ctime ", ctime(&dip->di_ctime.tv_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;
+ daddr_t *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 0000000..82e87b2
--- /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 0000000..861d0fd
--- /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/dumpon/Makefile b/sbin/dumpon/Makefile
new file mode 100644
index 0000000..da6f365
--- /dev/null
+++ b/sbin/dumpon/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+PROG= dumpon
+MAN8= dumpon.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8
new file mode 100644
index 0000000..6d050ce
--- /dev/null
+++ b/sbin/dumpon/dumpon.8
@@ -0,0 +1,100 @@
+.\" 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.
+.\"
+.\" From: @(#)swapon.8 8.1 (Berkeley) 6/5/93
+.\" $Id: dumpon.8,v 1.2 1996/02/12 01:20:09 mpp Exp $
+.\"
+.Dd May 12, 1995
+.Dt DUMPON 8
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm dumpon
+.Nd "specify a device for crash dumps"
+.Sh SYNOPSIS
+.Nm dumpon
+.Op Fl v
+.Ar special_file
+.Sh DESCRIPTION
+.Nm Dumpon
+is used to specify a device where the kernel can save a crash dump in
+the case of a panic.
+The system begins with no dump area configured for safety, unless a
+.Dq dumps on
+clause was specified in the input to
+.Xr config 8 .
+The dump device must be one of the swap areas
+specified in the configuration file, and it must be at least the size
+of physical memory.
+Calls to
+.Nm dumpon
+normally occur in the system multi-user initialization file
+.Pa /etc/rc ,
+before the
+.Xr savecore 8
+program is run.
+The
+.Fl v
+flag can be specified to cause the
+.Nm
+program to be verbose about its activity.
+.Pp
+The
+.Nm
+program operates by setting the
+.Xr sysctl 3
+MIB variable
+.Dq kern.dumpdev
+to the device number of the designated
+.Ar special_file
+or to
+.Dv NODEV
+(meaning that no dumps are to be taken) if
+.Ar special_file
+is the text string
+.Dq Li off .
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr init 8 ,
+.Xr rc 8 ,
+.Xr savecore 8
+.Sh FILES
+.Bl -tag -width /dev/[ws]d?b -compact
+.It Pa /dev/[ws]d?b
+standard paging devices
+.El
+.Sh BUGS
+Because the filesystem layer is already dead by the time a crash dump
+is taken, it is not possible to send crash dumps directly to a file.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
new file mode 100644
index 0000000..dc185e9
--- /dev/null
+++ b/sbin/dumpon/dumpon.c
@@ -0,0 +1,129 @@
+/*
+ * 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[] = "From: @(#)swapon.c 8.1 (Berkeley) 6/5/93";*/
+static const char rcsid[] =
+ "$Id: dumpon.c,v 1.1 1995/05/12 19:10:09 wollman Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sysexits.h>
+#include <err.h>
+
+void usage __P((void)) __dead2;
+static char *whoami;
+
+int
+main(int argc, char **argv)
+{
+ extern char *optarg;
+ extern int optind;
+ int ch, verbose, rv;
+ struct stat stab;
+ int mib[2];
+
+ verbose = rv = 0;
+ whoami = argv[0];
+ while ((ch = getopt(argc, argv, "v")) != EOF)
+ switch((char)ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+
+ if (!argv[0] || argv[1])
+ usage();
+
+ if (strcmp(argv[0], "off")) {
+ rv = stat(argv[0], &stab);
+ if (rv) {
+ err(EX_OSFILE, "%s", argv[0]);
+ }
+
+ if (!S_ISBLK(stab.st_mode)) {
+ errx(EX_USAGE, "%s: must specify a block device",
+ argv[0]);
+ }
+ } else {
+ stab.st_rdev = NODEV;
+ }
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_DUMPDEV;
+
+ rv = sysctl(mib, 2, (void *)0, (size_t *)0, &stab.st_rdev,
+ sizeof stab.st_rdev);
+ if (rv) {
+ err(EX_OSERR, "sysctl: kern.dumpdev");
+ }
+
+ if (verbose) {
+ if (stab.st_rdev == NODEV) {
+ printf("%s: crash dumps disabled\n", whoami);
+ } else {
+ printf("%s: crash dumps to %s (%lu, %lu)\n",
+ whoami, argv[0],
+ (unsigned long)major(stab.st_rdev),
+ (unsigned long)minor(stab.st_rdev));
+ }
+ }
+
+ return 0;
+}
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: %s [-v] special_file\n"
+ " %s [-v] off\n", whoami, whoami);
+ exit(EX_USAGE);
+}
diff --git a/sbin/fdisk/Makefile b/sbin/fdisk/Makefile
new file mode 100644
index 0000000..5efa9c5
--- /dev/null
+++ b/sbin/fdisk/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.1 (Julian Elischer) 3/28/93
+#
+#
+
+PROG= fdisk
+SRCS= fdisk.c
+MAN8= fdisk.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8
new file mode 100644
index 0000000..a43a85a
--- /dev/null
+++ b/sbin/fdisk/fdisk.8
@@ -0,0 +1,409 @@
+.Dd October 4, 1996
+.Dt FDISK 8
+.\".Os BSD 4
+.Sh NAME
+.Nm fdisk
+.Nd DOS partition maintenance program
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl u
+.Op Fl a
+.Op Fl 0123
+.Op Ar disk
+.Bl -tag -width time
+.Nm fdisk
+.Op Fl f Ar configfile
+.Op Fl i
+.Op Fl v
+.Op Fl t
+.Op Ar disk
+.Sh PROLOGUE
+In order for the BIOS to boot the kernel,
+certain conventions must be adhered to.
+Sector 0 of the disk must contain boot code,
+a partition table,
+and a magic number.
+BIOS partitions can be used to break the disk up into several pieces.
+The BIOS brings in sector 0
+(does it really use the code?)
+and verifies the magic number.
+It then searches the 4 BIOS partitions described by sector 0
+to determine which of them is
+.Em active.
+This boot then brings in the secondary boot block from the
+.Em active
+partition and runs it.
+Under DOS,
+you could have one or more partitions with one
+.Em active.
+The DOS
+.Nm
+program can be used to divide space on the disk into partitions and set one
+.Em active.
+.Sh DESCRIPTION
+.Pp
+The FreeBSD program
+.Nm
+serves a similar purpose to the DOS program. The first form is used to
+display partition information or to interactively edit the partition
+table. The second is used to write a partition table using a
+.Ar configfile
+and is designed to be used by other scripts/programs.
+.Pp
+Options are:
+.It Fl u
+Is used for updating (editing) sector 0 of the disk. Ignored if
+.Fl f
+is given.
+.It Fl i
+Initializes sector 0 of the disk. This implies
+.Fl u ,
+unless
+.Fl f
+is given.
+.It Fl a
+Change the active partition only. Ignored if
+.Fl f
+is given.
+.It Fl 0123
+Operate on a single fdisk entry only. Ignored if
+.Fl f
+is given.
+.It Fl f Ar configfile
+Set partition values using the file
+.Ar configfile .
+The
+.Ar configfile
+always modifies existing partitions, unless
+.Fl i
+is also given, in which case all existing partitions are deleted (marked
+as "unused") before the
+.Ar configfile
+is read. The
+.Ar configfile
+can be "-", in which case
+.Ar stdin
+is read. See
+.Em CONFIGURATION FILE ,
+below, for file syntax.
+.Pp
+.Em WARNING:
+when
+.Fl f
+is used, you are not asked if you really want to write the partition
+table (as you are in the interactive mode). Use with caution!
+.It Fl t
+Test mode; do not write partition values. Generally used with the
+.Fl f
+option to see what would be written to the partition table. Implies
+.Fl v .
+.It Fl v
+Be verbose. When
+.Fl f
+is used,
+.Nm
+prints out the partition table that is written to the disk.
+.El
+.Pp
+The final disk name can be provided as a
+.Sq bare
+disk name only, e.g.
+.Ql sd0 ,
+or as a fully qualified device node under
+.Pa /dev .
+If omitted, the disks
+.Ql wd0 ,
+.Ql sd0 ,
+and
+.Ql od0
+are being searched in that order, until one is
+being found responding.
+.Pp
+When called with no arguments, it prints the sector 0 partition table.
+An example follows:
+
+.Bd -literal
+ ******* Working on device /dev/rwd0 *******
+ parameters extracted from in-core disklabel are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ parameters to be used for BIOS calculations are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ Warning: BIOS sector numbering starts with sector 1
+ Information from DOS bootblock is:
+ The data for partition 0 is:
+ sysid 165,(FreeBSD/NetBSD/386BSD)
+ start 495, size 380160 (185 Meg), flag 0
+ beg: cyl 1/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 1 is:
+ sysid 164,(unknown)
+ start 378180, size 2475 (1 Meg), flag 0
+ beg: cyl 764/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 2 is:
+ <UNUSED>
+ The data for partition 3 is:
+ sysid 99,(ISC UNIX, other System V/386, GNU HURD or Mach)
+ start 380656, size 224234 (109 Meg), flag 80
+ beg: cyl 769/ sector 2/ head 0;
+ end: cyl 197/ sector 33/ head 14
+.Ed
+.Pp
+The disk is divided into three partitions that happen to fill the disk.
+The second partition overlaps the end of the first.
+(Used for debugging purposes)
+.Bl -tag -width "cyl, sector and head"
+.It Em "sysid"
+is used to label the partition. FreeBSD reserves the
+magic number 165 decimal (A5 in hex).
+.It Em "start and size"
+fields provide the start address
+and size of a partition in sectors.
+.It Em "flag 80"
+specifies that this is the active partition.
+.It Em "cyl, sector and head"
+fields are used to specify the beginning address
+and end address for the partition.
+.It Em "Note:"
+these numbers are calculated using BIOS's understanding of the disk geometry
+and saved in the bootblock.
+.El
+.Pp
+The flags
+.Fl i
+or
+.Fl u
+are used to indicate that the partition data is to be updated, unless the
+.Fl f
+option is used. If the
+.Fl f
+option is not used, the
+.Nm
+program will enter a conversational mode.
+This mode is designed not to change any data unless you explicitly tell it to.
+.Nm
+selects defaults for its questions to guarantee the above behavior.
+.Pp
+It displays each partition
+and ask if you want to edit it.
+If you say yes,
+it will step through each field showing the old value
+and asking for a new one.
+When you are done with a partition,
+.Nm
+will display it and ask if it is correct.
+.Nm
+will then proceed to the next entry.
+.Pp
+Getting the
+.Em cyl, sector,
+and
+.Em head
+fields correct is tricky.
+So by default,
+they will be calculated for you;
+you can specify them if you choose.
+.Pp
+After all the partitions are processed,
+you are given the option to change the
+.Em active
+partition.
+Finally,
+when the all the data for the first sector has been accumulated,
+you are asked if you really want to rewrite sector 0.
+Only if you answer yes,
+will the data be written to disk.
+.Pp
+The difference between the
+.Fl u
+flag and
+.Fl i
+flag is that
+the
+.Fl u
+flag just edits the fields as they appear on the disk.
+While the
+.Fl i
+flag is used to "initialize" sector 0;
+it will setup the last BIOS partition to use the whole disk for FreeBSD;
+and make it active.
+.Sh NOTES
+.Pp
+The automatic calculation of starting cylinder etc. uses
+a set of figures that represent what the BIOS thinks is the
+geometry of the drive.
+These figures are by default taken from the incore disklabel,
+but the program initially gives you an opportunity to change them.
+This allows the user to create a bootblock that can work with drives
+that use geometry translation under the BIOS.
+.Pp
+If you hand craft your disk layout,
+please make sure that the FreeBSD partition starts on a cylinder boundary.
+A number of decisions made later may assume this.
+(This might not be necessary later.)
+.Pp
+Editing an existing partition will most likely cause you to
+lose all the data in that partition.
+.Pp
+You should run this program interactively once or twice to see how it
+works. This is completely safe as long as you answer the last question
+in the negative. There are subtleties that the program detects that are
+not fully explained in this manual page.
+.Sh CONFIGURATION FILE
+.Pp
+When the
+.Fl f
+option is given, a disk's partition table can be written using values
+from a
+.Ar configfile .
+The syntax of this file is very simple. Each line is either a comment or
+a specification, and whitespace (except for newlines) are ignored:
+.Bl -tag -width Ds
+.It Xo
+.Ic #
+.No Ar comment ...
+.Xc
+Lines beginning with a "#" are comments and are ignored.
+.It Xo
+.Ic g
+.No Ar spec1
+.No Ar spec2
+.No Ar spec3
+.Xc
+Set the BIOS geometry used in partition calculations. There must be
+three values specfied, with a letter preceding each number:
+.Bl -tag -width Ds
+.Sm off
+.It Cm c No Ar num
+.Sm on
+Set the number of cylinders to
+.Ar num .
+.Sm off
+.It Cm h No Ar num
+.Sm on
+Set the number of heads to
+.Ar num .
+.Sm off
+.It Cm s No Ar num
+.Sm on
+Set the number of sectors/track to
+.Ar num .
+.El
+.Pp
+These specs can occur in any order, as the leading letter determines
+which value is which; however, all three must be specified.
+.Pp
+This line must occur before any lines that specify partition
+information.
+.Pp
+It is an error if the following is not true:
+.Pp
+.nf
+ 1 <= number of cylinders
+ 1 <= number of heads <= 256
+ 1 <= number of sectors/track < 64
+.fi
+.Pp
+The number of cylinders should be less than or equal to 1024, but this
+is not enforced, although a warning will be output. Note that bootable
+FreeBSD partitions (the "/" filesystem) must lie completely within the
+first 1024 cylinders; if this is not true, booting may fail.
+Non-bootable partitions do not have this restriction.
+.Pp
+Example (all of these are equivalent), for a disk with 1019 cylinders,
+39 heads, and 63 sectors:
+.Pp
+.nf
+ g c1019 h39 s63
+ g h39 c1019 s63
+ g s63 h39 c1019
+.fi
+.It Xo
+.Ic p
+.No Ar partition
+.No Ar type
+.No Ar start
+.No Ar length
+.Xc
+Set the partition given by
+.Ar partition
+(0-3) to type
+.Ar type ,
+starting at sector
+.Ar start
+for
+.Ar length
+sectors.
+.Pp
+Only those partitions explicitly mentioned by these lines are modified;
+any partition not referenced by a "p" line will not be modified.
+However, if an invalid partition table is present, or the
+.Fl i
+option is specified, all existing partition entries will be cleared
+(marked as unused), and these "p" lines will have to be used to
+explicitly set partition information. If multiple partitions need to be
+set, multiple "p" lines must be specified; one for each partition.
+.Pp
+These partition lines must occur after any geometry specification lines,
+if one is present.
+.Pp
+The
+.Ar type
+is 165 for FreeBSD partitions. Specifying a partition type of zero is
+the same as clearing the partition and marking it as unused; however,
+dummy values (such as "0") must still be specified for
+.Ar start
+and
+.Ar length .
+.Pp
+Note: the start offset will be rounded upwards to a head boundary if
+necessary, and the end offset will be rounded downwards to a cylinder
+boundary if necessary.
+.Pp
+Example: to clear partition 3 and mark it as unused:
+.Pp
+.nf
+ p 3 0 0 0
+.fi
+.Pp
+Example: to set partition 0 to a FreeBSD partition, starting at sector 1
+for 2503871 sectors (note: these numbers will be rounded upwards and
+downwards to correspond to head and cylinder boundaries):
+.Pp
+.nf
+ p 0 165 1 2503871
+.fi
+.It Xo
+.Ic a
+.No Ar partition
+.Xc
+Make
+.Ar partition
+the active partition. Can occur anywhere in the config file, but only
+one must be present.
+.Pp
+Example: to make partition 0 the active partition:
+.Pp
+.nf
+ a 0
+.fi
+
+.El
+.Pp
+.Sh SEE ALSO
+.Xr disklabel 8
+.Sh BUGS
+The entire program should be made more user-friendly.
+.Pp
+Throughout this man page, the term
+.Sq partition
+is used where it should actually be
+.Sq slice ,
+in order to conform with the terms used elsewhere.
+.Pp
+You cannot use this command to completely dedicate a disk to FreeBSD. The
+.Xr disklabel 8
+command must be used for this.
diff --git a/sbin/fdisk/fdisk.c b/sbin/fdisk/fdisk.c
new file mode 100644
index 0000000..38506b5
--- /dev/null
+++ b/sbin/fdisk/fdisk.c
@@ -0,0 +1,1316 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int iotest;
+
+#define LBUF 100
+static char lbuf[LBUF];
+
+/*
+ *
+ * Ported to 386bsd by Julian Elischer Thu Oct 15 20:26:46 PDT 1992
+ *
+ * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University
+ * Copyright (c) 1989 Robert. V. Baron
+ * Created.
+ */
+
+#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
+#define Hex(str, ans, tmp) if (hex(str, &tmp, ans)) ans = tmp
+#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
+
+#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
+
+#define MAX_SEC_SIZE 2048 /* maximum section size that is supported */
+#define MIN_SEC_SIZE 512 /* the sector size to start sensing at */
+int secsize = 0; /* the sensed sector size */
+
+const char *disk;
+const char *disks[] =
+{
+ "/dev/rwd0", "/dev/rsd0", "/dev/rod0", 0
+};
+
+char *name;
+
+struct disklabel disklabel; /* disk parameters */
+
+int cyls, sectors, heads, cylsecs, disksecs;
+
+struct mboot
+{
+ unsigned char padding[2]; /* force the longs to be long alligned */
+ unsigned char bootinst[DOSPARTOFF];
+ struct dos_partition parts[4];
+ unsigned short int signature;
+ /* room to read in MBRs that are bigger then DEV_BSIZE */
+ unsigned char large_sector_overflow[MAX_SEC_SIZE-MIN_SEC_SIZE];
+};
+struct mboot mboot;
+
+#define ACTIVE 0x80
+#define BOOT_MAGIC 0xAA55
+
+int dos_cyls;
+int dos_heads;
+int dos_sectors;
+int dos_cylsecs;
+
+#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
+#define DOSCYL(c) (c & 0xff)
+static int partition = -1;
+
+
+#define MAX_ARGS 10
+
+static int current_line_number;
+
+static int geom_processed = 0;
+static int part_processed = 0;
+static int active_processed = 0;
+
+
+typedef struct cmd {
+ char cmd;
+ int n_args;
+ struct arg {
+ char argtype;
+ int arg_val;
+ } args[MAX_ARGS];
+} CMD;
+
+
+static int a_flag = 0; /* set active partition */
+static int i_flag = 0; /* replace partition data */
+static int u_flag = 0; /* update partition data */
+static int t_flag = 0; /* test only, if f_flag is given */
+static char *f_flag = NULL; /* Read config info from file */
+static int v_flag = 0; /* Be verbose */
+
+static unsigned char bootcode[] = {
+0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
+0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
+0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
+0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
+0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
+0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
+0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
+0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
+0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
+'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
+ 'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
+ 'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
+ 'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
+'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
+ 'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+struct part_type
+{
+ unsigned char type;
+ char *name;
+}part_types[] =
+{
+ {0x00, "unused"}
+ ,{0x01, "Primary DOS with 12 bit FAT"}
+ ,{0x02, "XENIX / filesystem"}
+ ,{0x03, "XENIX /usr filesystem"}
+ ,{0x04, "Primary DOS with 16 bit FAT"}
+ ,{0x05, "Extended DOS"}
+ ,{0x06, "Primary 'big' DOS (> 32MB)"}
+ ,{0x07, "OS/2 HPFS, QNX or Advanced UNIX"}
+ ,{0x08, "AIX filesystem"}
+ ,{0x09, "AIX boot partition or Coherent"}
+ ,{0x0A, "OS/2 Boot Manager or OPUS"}
+ ,{0x10, "OPUS"}
+ ,{0x40, "VENIX 286"}
+ ,{0x50, "DM"}
+ ,{0x51, "DM"}
+ ,{0x52, "CP/M or Microport SysV/AT"}
+ ,{0x56, "GB"}
+ ,{0x61, "Speed"}
+ ,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
+ ,{0x64, "Novell Netware 2.xx"}
+ ,{0x65, "Novell Netware 3.xx"}
+ ,{0x75, "PCIX"}
+ ,{0x80, "Minix 1.1 ... 1.4a"}
+ ,{0x81, "Minix 1.4b ... 1.5.10"}
+ ,{0x82, "Linux swap"}
+ ,{0x83, "Linux filesystem"}
+ ,{0x93, "Amoeba filesystem"}
+ ,{0x94, "Amoeba bad block table"}
+ ,{0xA5, "FreeBSD/NetBSD/386BSD"}
+ ,{0xA7, "NEXTSTEP"}
+ ,{0xB7, "BSDI BSD/386 filesystem"}
+ ,{0xB8, "BSDI BSD/386 swap"}
+ ,{0xDB, "Concurrent CPM or C.DOS or CTOS"}
+ ,{0xE1, "Speed"}
+ ,{0xE3, "Speed"}
+ ,{0xE4, "Speed"}
+ ,{0xF1, "Speed"}
+ ,{0xF2, "DOS 3.3+ Secondary"}
+ ,{0xF4, "Speed"}
+ ,{0xFF, "BBT (Bad Blocks Table)"}
+};
+
+static void print_s0(int which);
+static void print_part(int i);
+static void init_sector0(unsigned long start);
+static void init_boot(void);
+static void change_part(int i);
+static void print_params();
+static void change_active(int which);
+static void get_params_to_use();
+static void dos(int sec, int size, unsigned char *c, unsigned char *s,
+ unsigned char *h);
+static int open_disk(int u_flag);
+static ssize_t read_disk(off_t sector, void *buf);
+static ssize_t write_disk(off_t sector, void *buf);
+static int get_params();
+static int read_s0();
+static int write_s0();
+static int ok(char *str);
+static int decimal(char *str, int *num, int deflt);
+static char *get_type(int type);
+static int read_config(char *config_file);
+static void reset_boot(void);
+#if 0
+static int hex(char *str, int *num, int deflt);
+static int string(char *str, char **ans);
+#endif
+
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ name = *argv;
+ {register char *cp = name;
+ while (*cp) if (*cp++ == '/') name = cp;
+ }
+
+ for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
+ if (*token++ != '-' || !*token)
+ break;
+ else { register int flag;
+ for ( ; (flag = *token++) ; ) {
+ switch (flag) {
+ case '0':
+ partition = 0;
+ break;
+ case '1':
+ partition = 1;
+ break;
+ case '2':
+ partition = 2;
+ break;
+ case '3':
+ partition = 3;
+ break;
+ case 'a':
+ a_flag = 1;
+ break;
+ case 'f':
+ if (*token)
+ {
+ f_flag = token;
+ token = "";
+ }
+ else
+ {
+ if (argc == 1)
+ {
+ goto usage;
+ }
+ --argc;
+ f_flag = *++argv;
+ }
+ /*
+ * u_flag is needed, because we're
+ * writing to the disk.
+ */
+ u_flag = 1;
+ break;
+ case 'i':
+ i_flag = 1;
+ case 'u':
+ u_flag = 1;
+ break;
+ case 't':
+ t_flag = 1;
+ case 'v':
+ v_flag = 1;
+ break;
+ default:
+ goto usage;
+ }
+ }
+ }
+ }
+
+ if (argc > 0)
+ {
+ static char realname[12];
+
+ if(strncmp(argv[0], "/dev", 4) == 0)
+ disk = argv[0];
+ else
+ {
+ snprintf(realname, 12, "/dev/r%s", argv[0]);
+ disk = realname;
+ }
+
+ if (open_disk(u_flag) < 0)
+ {
+ fprintf(stderr, "Cannot open disk %s (%s)\n",
+ disk, sys_errlist[errno]);
+ exit(1);
+ }
+ }
+ else
+ {
+ int i, rv = 0;
+
+ for(i = 0; disks[i]; i++)
+ {
+ disk = disks[i];
+ rv = open_disk(u_flag);
+ if(rv != -2) break;
+ }
+ if(rv < 0)
+ {
+ fprintf(stderr, "Cannot open any disk (%s)\n",
+ sys_errlist[errno]);
+ exit(1);
+ }
+ }
+
+ printf("******* Working on device %s *******\n",disk);
+
+ if (f_flag)
+ {
+ if (read_s0() || i_flag)
+ {
+ reset_boot();
+ }
+
+ if (!read_config(f_flag))
+ {
+ exit(1);
+ }
+ if (v_flag)
+ {
+ print_s0(-1);
+ }
+ if (!t_flag)
+ {
+ write_s0();
+ }
+ }
+ else
+ {
+ if(u_flag)
+ {
+ get_params_to_use();
+ }
+ else
+ {
+ print_params();
+ }
+
+ if (read_s0())
+ init_sector0(1);
+
+ printf("Media sector size is %d\n", secsize);
+ printf("Warning: BIOS sector numbering starts with sector 1\n");
+ printf("Information from DOS bootblock is:\n");
+ if (partition == -1)
+ for (i = 0; i < NDOSPART; i++)
+ change_part(i);
+ else
+ change_part(partition);
+
+ if (u_flag || a_flag)
+ change_active(partition);
+
+ if (u_flag || a_flag) {
+ if (!t_flag)
+ {
+ printf("\nWe haven't changed the partition table yet. ");
+ printf("This is your last chance.\n");
+ }
+ print_s0(-1);
+ if (!t_flag)
+ {
+ if (ok("Should we write new partition table?"))
+ write_s0();
+ }
+ else
+ {
+ printf("\n-t flag specified -- partition table not written.\n");
+ }
+ }
+ }
+
+ exit(0);
+
+usage:
+ printf("fdisk {-a|-i|-u} [-f <config file> [-t] [-v]] [-{0,1,2,3}] [disk]\n");
+ return(1);
+}
+
+static void
+print_s0(int which)
+{
+int i;
+
+ print_params();
+ printf("Information from DOS bootblock is:\n");
+ if (which == -1)
+ for (i = 0; i < NDOSPART; i++)
+ printf("%d: ", i), print_part(i);
+ else
+ print_part(which);
+}
+
+static struct dos_partition mtpart = { 0 };
+
+static void
+print_part(int i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+
+ if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
+ printf(" start %ld, size %ld (%ld Meg), flag %x\n",
+ partp->dp_start,
+ partp->dp_size, partp->dp_size * secsize / (1024 * 1024),
+ partp->dp_flag);
+ printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
+ ,DPCYL(partp->dp_scyl, partp->dp_ssect)
+ ,DPSECT(partp->dp_ssect)
+ ,partp->dp_shd
+ ,DPCYL(partp->dp_ecyl, partp->dp_esect)
+ ,DPSECT(partp->dp_esect)
+ ,partp->dp_ehd);
+}
+
+
+static void
+init_boot(void)
+{
+ memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
+ mboot.signature = BOOT_MAGIC;
+}
+
+
+static void
+init_sector0(unsigned long start)
+{
+struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
+unsigned long size = disksecs - start;
+
+ init_boot();
+
+ partp->dp_typ = DOSPTYP_386BSD;
+ partp->dp_flag = ACTIVE;
+ partp->dp_start = start;
+ partp->dp_size = size;
+
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+}
+
+static void
+change_part(int i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+ printf("The data for partition %d is:\n", i);
+ print_part(i);
+
+ if (u_flag && ok("Do you want to change it?")) {
+ int tmp;
+
+ if (i_flag) {
+ bzero((char *)partp, sizeof (struct dos_partition));
+ if (i == 3) {
+ init_sector0(1);
+ printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
+ print_part(i);
+ }
+ }
+
+ do {
+ Decimal("sysid", partp->dp_typ, tmp);
+ Decimal("start", partp->dp_start, tmp);
+ Decimal("size", partp->dp_size, tmp);
+
+ if (ok("Explicitly specifiy beg/end address ?"))
+ {
+ int tsec,tcyl,thd;
+ tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
+ thd = partp->dp_shd;
+ tsec = DPSECT(partp->dp_ssect);
+ Decimal("beginning cylinder", tcyl, tmp);
+ Decimal("beginning head", thd, tmp);
+ Decimal("beginning sector", tsec, tmp);
+ partp->dp_scyl = DOSCYL(tcyl);
+ partp->dp_ssect = DOSSECT(tsec,tcyl);
+ partp->dp_shd = thd;
+
+ tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
+ thd = partp->dp_ehd;
+ tsec = DPSECT(partp->dp_esect);
+ Decimal("ending cylinder", tcyl, tmp);
+ Decimal("ending head", thd, tmp);
+ Decimal("ending sector", tsec, tmp);
+ partp->dp_ecyl = DOSCYL(tcyl);
+ partp->dp_esect = DOSSECT(tsec,tcyl);
+ partp->dp_ehd = thd;
+ } else {
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+ }
+
+ print_part(i);
+ } while (!ok("Are we happy with this entry?"));
+ }
+}
+
+static void
+print_params()
+{
+ printf("parameters extracted from in-core disklabel are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,cyls,heads,sectors,cylsecs);
+ if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
+ printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
+ printf("parameters to be used for BIOS calculations are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
+}
+
+static void
+change_active(int which)
+{
+int i;
+int active = 3, tmp;
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
+
+ if (a_flag && which != -1)
+ active = which;
+ if (!ok("Do you want to change the active partition?"))
+ return;
+ do
+ Decimal("active partition", active, tmp);
+ while (!ok("Are you happy with this choice"));
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ if (active >= 0 && active < NDOSPART)
+ partp[active].dp_flag = ACTIVE;
+}
+
+void
+get_params_to_use()
+{
+ int tmp;
+ print_params();
+ if (ok("Do you want to change our idea of what BIOS thinks ?"))
+ {
+ do
+ {
+ Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
+ Decimal("BIOS's idea of #heads", dos_heads, tmp);
+ Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
+ dos_cylsecs = dos_heads * dos_sectors;
+ print_params();
+ }
+ while(!ok("Are you happy with this choice"));
+ }
+}
+
+
+/***********************************************\
+* Change real numbers into strange dos numbers *
+\***********************************************/
+static void
+dos(sec, size, c, s, h)
+int sec, size;
+unsigned char *c, *s, *h;
+{
+int cy;
+int hd;
+
+ if (sec == 0 && size == 0) {
+ *s = *c = *h = 0;
+ return;
+ }
+
+ cy = sec / ( dos_cylsecs );
+ sec = sec - cy * ( dos_cylsecs );
+
+ hd = sec / dos_sectors;
+ sec = (sec - hd * dos_sectors) + 1;
+
+ *h = hd;
+ *c = cy & 0xff;
+ *s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
+}
+
+int fd;
+
+ /* Getting device status */
+
+static int
+open_disk(int u_flag)
+{
+struct stat st;
+
+ if (stat(disk, &st) == -1) {
+ fprintf(stderr, "%s: Can't get file status of %s\n",
+ name, disk);
+ return -1;
+ }
+ if ( !(st.st_mode & S_IFCHR) )
+ fprintf(stderr,"%s: Device %s is not character special\n",
+ name, disk);
+ if ((fd = open(disk, a_flag || u_flag ? O_RDWR : O_RDONLY)) == -1) {
+ if(errno == ENXIO)
+ return -2;
+ fprintf(stderr,"%s: Can't open device %s\n", name, disk);
+ return -1;
+ }
+ if (get_params(0) == -1) {
+ fprintf(stderr, "%s: Can't get disk parameters on %s\n",
+ name, disk);
+ return -1;
+ }
+ return fd;
+}
+
+static ssize_t
+read_disk(off_t sector, void *buf)
+{
+ lseek(fd,(sector * 512), 0);
+ if( secsize == 0 )
+ for( secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2 )
+ {
+ /* try the read */
+ int size = read(fd, buf, secsize);
+ if( size == secsize )
+ /* it worked so return */
+ return secsize;
+ }
+ else
+ return read( fd, buf, secsize );
+
+ /* we failed to read at any of the sizes */
+ return -1;
+}
+
+static ssize_t
+write_disk(off_t sector, void *buf)
+{
+ lseek(fd,(sector * 512), 0);
+ /* write out in the size that the read_disk found worked */
+ return write(fd, buf, secsize);
+}
+
+static int
+get_params()
+{
+
+ if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
+ fprintf(stderr,
+ "%s: Can't get disk parameters on %s; supplying dummy ones\n",
+ name, disk);
+ dos_cyls = cyls = 1;
+ dos_heads = heads = 1;
+ dos_sectors = sectors = 1;
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+ return disksecs;
+ }
+
+ dos_cyls = cyls = disklabel.d_ncylinders;
+ dos_heads = heads = disklabel.d_ntracks;
+ dos_sectors = sectors = disklabel.d_nsectors;
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+
+ return (disksecs);
+}
+
+
+static int
+read_s0()
+{
+ if (read_disk(0, (char *) mboot.bootinst) == -1) {
+ fprintf(stderr, "%s: Can't read fdisk partition table\n", name);
+ return -1;
+ }
+ if (mboot.signature != BOOT_MAGIC) {
+ fprintf(stderr, "%s: Invalid fdisk partition table found\n",
+ name);
+ /* So should we initialize things */
+ return -1;
+ }
+ return 0;
+}
+
+static int
+write_s0()
+{
+ int flag;
+ if (iotest) {
+ print_s0(-1);
+ return 0;
+ }
+ /*
+ * write enable label sector before write (if necessary),
+ * disable after writing.
+ * needed if the disklabel protected area also protects
+ * sector 0. (e.g. empty disk)
+ */
+ flag = 1;
+#ifdef NOT_NOW
+ if (ioctl(fd, DIOCWLABEL, &flag) < 0)
+ perror("ioctl DIOCWLABEL");
+#endif
+ if (write_disk(0, (char *) mboot.bootinst) == -1) {
+ fprintf(stderr, "%s: Can't write fdisk partition table\n",
+ name);
+ return -1;
+ flag = 0;
+#ifdef NOT_NOW
+ (void) ioctl(fd, DIOCWLABEL, &flag);
+#endif
+ }
+ return(0);
+}
+
+
+static int
+ok(str)
+char *str;
+{
+ printf("%s [n] ", str);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (*lbuf &&
+ (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
+ !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
+ return 1;
+ else
+ return 0;
+}
+
+static int
+decimal(char *str, int *num, int deflt)
+{
+int acc = 0, c;
+char *cp;
+
+ while (1) {
+ printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = acc * 10 + c - '0';
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid decimal number. Try again\n",
+ lbuf);
+ }
+
+}
+
+#if 0
+static int
+hex(char *str, int *num, int deflt)
+{
+int acc = 0, c;
+char *cp;
+
+ while (1) {
+ printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = (acc << 4) + c - '0';
+ else if (c <= 'f' && c >= 'a')
+ acc = (acc << 4) + c - 'a' + 10;
+ else if (c <= 'F' && c >= 'A')
+ acc = (acc << 4) + c - 'A' + 10;
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid hex number. Try again\n",
+ lbuf);
+ }
+
+}
+
+static int
+string(char *str, char **ans)
+{
+int c;
+char *cp = lbuf;
+
+ while (1) {
+ printf("Supply a string value for \"%s\" [%s] ", str, *ans);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (c == '"') {
+ c = *++cp;
+ *ans = cp;
+ while ((c = *cp) && c != '"') cp++;
+ } else {
+ *ans = cp;
+ while ((c = *cp) && c != ' ' && c != '\t') cp++;
+ }
+
+ if (c)
+ *cp = 0;
+ return 1;
+ }
+}
+#endif
+
+static char *
+get_type(int type)
+{
+ int numentries = (sizeof(part_types)/sizeof(struct part_type));
+ int counter = 0;
+ struct part_type *ptr = part_types;
+
+
+ while(counter < numentries)
+ {
+ if(ptr->type == type)
+ {
+ return(ptr->name);
+ }
+ ptr++;
+ counter++;
+ }
+ return("unknown");
+}
+
+
+static void
+parse_config_line(line, command)
+ char *line;
+ CMD *command;
+{
+ char *cp, *end;
+
+ cp = line;
+ while (1) /* dirty trick used to insure one exit point for this
+ function */
+ {
+ memset(command, 0, sizeof(*command));
+
+ while (isspace(*cp)) ++cp;
+ if (*cp == '\0' || *cp == '#')
+ {
+ break;
+ }
+ command->cmd = *cp++;
+
+ /*
+ * Parse args
+ */
+ while (1)
+ {
+ while (isspace(*cp)) ++cp;
+ if (*cp == '#')
+ {
+ break; /* found comment */
+ }
+ if (isalpha(*cp))
+ {
+ command->args[command->n_args].argtype = *cp++;
+ }
+ if (!isdigit(*cp))
+ {
+ break; /* assume end of line */
+ }
+ end = NULL;
+ command->args[command->n_args].arg_val = strtol(cp, &end, 0);
+ if (cp == end)
+ {
+ break; /* couldn't parse number */
+ }
+ cp = end;
+ command->n_args++;
+ }
+ break;
+ }
+}
+
+
+static int
+process_geometry(command)
+ CMD *command;
+{
+ int status = 1, i;
+
+ while (1)
+ {
+ geom_processed = 1;
+ if (part_processed)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: the geometry specification line must occur before\n\
+ all partition specifications.\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ if (command->n_args != 3)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of geometry args\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ dos_cyls = -1;
+ dos_heads = -1;
+ dos_sectors = -1;
+ for (i = 0; i < 3; ++i)
+ {
+ switch (command->args[i].argtype)
+ {
+ case 'c':
+ dos_cyls = command->args[i].arg_val;
+ break;
+ case 'h':
+ dos_heads = command->args[i].arg_val;
+ break;
+ case 's':
+ dos_sectors = command->args[i].arg_val;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: ERROR line %d: unknown geometry arg type: '%c' (0x%02x)\n",
+ name, current_line_number, command->args[i].argtype,
+ command->args[i].argtype);
+ status = 0;
+ break;
+ }
+ }
+ if (status == 0)
+ {
+ break;
+ }
+
+ dos_cylsecs = dos_heads * dos_sectors;
+
+ /*
+ * Do sanity checks on parameter values
+ */
+ if (dos_cyls < 0)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of cylinders not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ if (dos_cyls == 0 || dos_cyls > 1024)
+ {
+ fprintf(stderr,
+ "%s: WARNING line %d: number of cylinders (%d) may be out-of-range\n\
+ (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
+ is dedicated to FreeBSD).\n",
+ name, current_line_number, dos_cyls);
+ }
+
+ if (dos_heads < 0)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of heads not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ else if (dos_heads < 1 || dos_heads > 256)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of heads must be within (1-256)\n",
+ name, current_line_number);
+ status = 0;
+ }
+
+ if (dos_sectors < 0)
+ {
+ fprintf(stderr, "%s: ERROR line %d: number of sectors not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ else if (dos_sectors < 1 || dos_sectors > 63)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of sectors must be within (1-63)\n",
+ name, current_line_number);
+ status = 0;
+ }
+
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_partition(command)
+ CMD *command;
+{
+ int status = 0, partition;
+ unsigned long chunks, adj_size, max_end;
+ struct dos_partition *partp;
+
+ while (1)
+ {
+ part_processed = 1;
+ if (command->n_args != 4)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of partition args\n",
+ name, current_line_number);
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 0 || partition > 3)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ partp = ((struct dos_partition *) &mboot.parts) + partition;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ partp->dp_typ = command->args[1].arg_val;
+ partp->dp_start = command->args[2].arg_val;
+ partp->dp_size = command->args[3].arg_val;
+ max_end = partp->dp_start + partp->dp_size;
+
+ if (partp->dp_typ == 0)
+ {
+ /*
+ * Get out, the partition is marked as unused.
+ */
+ /*
+ * Insure that it's unused.
+ */
+ bzero((char *)partp, sizeof (struct dos_partition));
+ status = 1;
+ break;
+ }
+
+ /*
+ * Adjust start upwards, if necessary, to fall on an head boundary.
+ */
+ if (partp->dp_start % dos_sectors != 0)
+ {
+ adj_size =
+ (partp->dp_start / dos_sectors + 1) * dos_sectors;
+ if (adj_size > max_end)
+ {
+ /*
+ * Can't go past end of partition
+ */
+ fprintf(stderr,
+ "%s: ERROR line %d: unable to adjust start of partition %d to fall on\n\
+ a cylinder boundary.\n",
+ name, current_line_number, partition);
+ break;
+ }
+ fprintf(stderr,
+ "%s: WARNING: adjusting start offset of partition '%d' from %d\n\
+ to %d, to round to an head boundary.\n",
+ name, partition, partp->dp_start, adj_size);
+ partp->dp_start = adj_size;
+ }
+
+ /*
+ * Adjust size downwards, if necessary, to fall on a cylinder
+ * boundary.
+ */
+ chunks =
+ ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
+ adj_size = chunks - partp->dp_start;
+ if (adj_size != partp->dp_size)
+ {
+ fprintf(stderr,
+ "%s: WARNING: adjusting size of partition '%d' from %d to %d,\n\
+ to round to a cylinder boundary.\n",
+ name, partition, partp->dp_size, adj_size);
+ if (chunks > 0)
+ {
+ partp->dp_size = adj_size;
+ }
+ else
+ {
+ partp->dp_size = 0;
+ }
+ }
+ if (partp->dp_size < 1)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: size for partition '%d' is zero.\n",
+ name, current_line_number, partition);
+ break;
+ }
+
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start+partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_active(command)
+ CMD *command;
+{
+ int status = 0, partition, i;
+ struct dos_partition *partp;
+
+ while (1)
+ {
+ active_processed = 1;
+ if (command->n_args != 1)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of active args\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 0 || partition > 3)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ /*
+ * Reset active partition
+ */
+ partp = ((struct dos_partition *) &mboot.parts);
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ partp[partition].dp_flag = ACTIVE;
+
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_line(line)
+ char *line;
+{
+ CMD command;
+ int status = 1;
+
+ while (1)
+ {
+ parse_config_line(line, &command);
+ switch (command.cmd)
+ {
+ case 0:
+ /*
+ * Comment or blank line
+ */
+ break;
+ case 'g':
+ /*
+ * Set geometry
+ */
+ status = process_geometry(&command);
+ break;
+ case 'p':
+ status = process_partition(&command);
+ break;
+ case 'a':
+ status = process_active(&command);
+ break;
+ default:
+ status = 0;
+ break;
+ }
+ break;
+ }
+ return (status);
+}
+
+
+static int
+read_config(config_file)
+ char *config_file;
+{
+ FILE *fp = NULL;
+ int status = 1;
+ char buf[1010];
+
+ while (1) /* dirty trick used to insure one exit point for this
+ function */
+ {
+ if (strcmp(config_file, "-") != 0)
+ {
+ /*
+ * We're not reading from stdin
+ */
+ if ((fp = fopen(config_file, "r")) == NULL)
+ {
+ status = 0;
+ break;
+ }
+ }
+ else
+ {
+ fp = stdin;
+ }
+ current_line_number = 0;
+ while (!feof(fp))
+ {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ {
+ break;
+ }
+ ++current_line_number;
+ status = process_line(buf);
+ if (status == 0)
+ {
+ break;
+ }
+ }
+ break;
+ }
+ if (fp)
+ {
+ /*
+ * It doesn't matter if we're reading from stdin, as we've reached EOF
+ */
+ fclose(fp);
+ }
+ return (status);
+}
+
+
+static void
+reset_boot(void)
+{
+ int i;
+ struct dos_partition *partp;
+
+ init_boot();
+ for (i = 0; i < 4; ++i)
+ {
+ partp = ((struct dos_partition *) &mboot.parts) + i;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ }
+}
diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile
new file mode 100644
index 0000000..e31e992
--- /dev/null
+++ b/sbin/fsck/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck
+MAN8= fsck.8
+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 0000000..528dd96
--- /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 October 7, 1996
+.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 0000000..4d2f535
--- /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 0000000..7d00cea
--- /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 0000000..bb6f05b
--- /dev/null
+++ b/sbin/fsck/SMM.doc/3.t
@@ -0,0 +1,452 @@
+.\" 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
+.I Fsck
+also checks for directories with unallocated blocks (holes).
+Such directories should never be created.
+When found,
+.I fsck
+will prompt the user to adjust the length of the offending directory
+which is done by shortening the size of the directory to the end of the
+last allocated block preceeding the hole.
+Unfortunately, this means that another Phase 1 run has to be done.
+.I Fsck
+will remind the user to rerun fsck after repairing a
+directory containing an unallocated block.
+.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 0000000..5ea8179
--- /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 0000000..26823bc
--- /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 0000000..9a2b923
--- /dev/null
+++ b/sbin/fsck/dir.c
@@ -0,0 +1,720 @@
+/*
+ * 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 const 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 <stdio.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, ".."
+};
+
+
+static int chgino __P((struct inodesc *idesc));
+static int dircheck __P((struct inodesc *idesc, struct direct *dp));
+static int expanddir __P((struct dinode *dp, char *name));
+static void freedir __P((ino_t ino, ino_t parent));
+static struct direct * fsck_readdir __P((struct inodesc *idesc));
+static struct bufarea * getdirblk __P((daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *idesc));
+
+/*
+ * Propagate connected state through the tree.
+ */
+void
+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.
+ */
+int
+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.
+ */
+int
+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);
+}
+
+void
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+void
+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);
+}
+
+void
+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();
+ }
+ }
+}
+
+int
+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];
+ dirp->d_namlen = newent.d_namlen;
+ } else {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ dirp->d_type = newent.d_namlen;
+ dirp->d_namlen = 0;
+# else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+# endif
+ }
+ dirp->d_reclen = newent.d_reclen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+int
+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);
+}
+
+int
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+
+ 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);
+ /*
+ * The parent directory, because of the ordering
+ * guarantees, has had the link count incremented
+ * for the child, but no entry was made. This
+ * fixes the parent link count so that fsck does
+ * not need to be rerun.
+ */
+ lncntp[parentdir]++;
+
+ }
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+int
+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
+ */
+int
+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
+ */
+int
+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
+ */
+ino_t
+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
+ */
+static void
+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.
+ */
+int
+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 0000000..c8ae6fa
--- /dev/null
+++ b/sbin/fsck/fsck.8
@@ -0,0 +1,298 @@
+.\" 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 f
+.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 clean flag of each filesystem's superblock is examined and only those filesystems that
+are not marked clean are checked. If the
+.Fl f
+option is specified, the filesystems
+will be checked regardless of the state of their clean flag.
+.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
+Holes in directories.
+.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 newfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h
new file mode 100644
index 0000000..57cb17a
--- /dev/null
+++ b/sbin/fsck/fsck.h
@@ -0,0 +1,293 @@
+/*
+ * 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 fflag; /* force fs check (ignore clean flag) */
+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 */
+int returntosingle; /* return to single user mode */
+int rerun; /* rerun fsck. Only used in non-preen mode */
+
+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 */
+unsigned 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
+
+/* dir.c */
+void adjust __P((struct inodesc *idesc, short lcnt));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *idesc));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void propagate __P((void));
+
+/* ffs_subr.c */
+void ffs_fragacct __P((struct fs *fs, int fragmap, long *fraglist, int cnt));
+
+/* inode.c */
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, daddr_t blk));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+int chkrange __P((daddr_t blk, int cnt));
+int ckinode __P((struct dinode *dp, struct inodesc *idesc));
+void clri __P((struct inodesc *idesc, char *type, int flag));
+int findino __P((struct inodesc *idesc));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+struct dinode * ginode __P((ino_t inumber));
+struct inoinfo * getinoinfo __P((ino_t inumber));
+struct dinode * getnextinode __P((ino_t inumber));
+void inodirty __P((void));
+void inocleanup __P((void));
+void pinode __P((ino_t ino));
+void resetinodebuf __P((void));
+int findname __P((struct inodesc *idesc));
+
+/* pass1.c */
+void pass1 __P((void));
+int pass1check __P((struct inodesc *idesc));
+
+/* pass1b.c */
+void pass1b __P((void));
+
+/* pass2.c */
+void pass2 __P((void));
+
+/* pass3.c */
+void pass3 __P((void));
+
+/* pass4.c */
+void pass4 __P((void));
+int pass4check __P((struct inodesc *idesc));
+
+/* pass5.c */
+void pass5 __P((void));
+
+/* preen.c */
+char *blockcheck __P((char *name));
+int checkfstab __P((int preen, int maxrun,int (*docheck)(), int (*chkit)()));
+
+/* setup.c */
+int setup __P((char *dev));
+
+/* utilities.c */
+int allocblk __P((long frags));
+int bread __P((int fd, char *buf, daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, daddr_t blk, long size));
+void catch __P((int));
+void catchquit __P((int));
+void ckfini __P((void));
+int dofix __P((struct inodesc *idesc, char *msg));
+void errexit __P((const char *s1, ...)) __dead2;
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((daddr_t blkno, long frags));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, daddr_t blk, long size));
+struct bufarea * getdatablk __P((daddr_t blkno, long size));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+void panic __P((const char *, ...)) __dead2;
+void pfatal __P((const char *s1, ...));
+void pwarn __P((const char *s1, ...));
+int reply __P((char *question));
+void voidquit __P((int));
diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c
new file mode 100644
index 0000000..5adac75
--- /dev/null
+++ b/sbin/fsck/inode.c
@@ -0,0 +1,619 @@
+/*
+ * 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 const char sccsid[] = "@(#)inode.c 8.5 (Berkeley) 2/8/95";
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *idesc, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ int ret;
+ long n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+ char pathbuf[MAXPATHLEN + 1];
+
+ 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 || dp->di_blocks == 0)))
+ 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) {
+ if (idesc->id_type == DATA && ndb >= 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size = (ap - &dino.di_db[0]) *
+ sblock.fs_bsize;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+
+ }
+ }
+ 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);
+ } else {
+ if (idesc->id_type == DATA && remsize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= remsize;
+ remsize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ break;
+ }
+ }
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+static int
+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];
+ char pathbuf[MAXPATHLEN + 1];
+ struct dinode *dp;
+
+ 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);
+ }
+ } else {
+ if (idesc->id_type == DATA && isize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= isize;
+ isize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ bp->b_flags &= ~B_INUSE;
+ return(STOP);
+ }
+ }
+ }
+ 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.
+ */
+int
+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++);
+}
+
+void
+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);
+}
+
+void
+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.
+ */
+void
+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;
+ if (inumber == ROOTINO)
+ inp->i_parent = ROOTINO;
+ else
+ 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.
+ */
+void
+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;
+}
+
+void
+inodirty()
+{
+ dirty(pbp);
+}
+
+void
+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();
+ }
+}
+
+int
+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);
+}
+
+int
+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);
+}
+
+void
+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.tv_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+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.tv_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
+ */
+void
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ 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 0000000..363bba7
--- /dev/null
+++ b/sbin/fsck/main.c
@@ -0,0 +1,342 @@
+/*
+ * 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 const 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 const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include "fsck.h"
+static int argtoi __P((int flag, char *req, char *str, int base));
+static int docheck __P((struct fstab *fsp));
+static int checkfilesys __P((char *filesys, char *mntpt, long auxdata,
+ int child));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern char *optarg;
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb: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 'f':
+ fflag++;
+ 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);
+}
+
+int
+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.
+ */
+int
+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 */
+int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ 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);
+ }
+
+ if (preen && sblock.fs_clean && !fflag) {
+ pwarn("clean, %ld free ", sblock.fs_cstotal.cs_nffree +
+ sblock.fs_frag * sblock.fs_cstotal.cs_nbfree);
+ printf("(%ld frags, %ld blocks, %.1f%% fragmentation)\n",
+ sblock.fs_cstotal.cs_nffree,
+ sblock.fs_cstotal.cs_nbfree,
+ (float)(sblock.fs_cstotal.cs_nffree * 100) /
+ sblock.fs_dsize);
+ 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, %ld.%ld%% 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 (rerun)
+ printf("\n***** PLEASE RERUN FSCK *****\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 0000000..3fec63a
--- /dev/null
+++ b/sbin/fsck/pass1.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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *idesc));
+
+void
+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();
+}
+
+void
+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;
+
+ 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) {
+ symbuf = alloca(secsize);
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)secsize) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %ld(%s) of size %ld\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) || dp->di_blocks == 0) {
+ 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();
+ }
+}
+
+int
+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 0000000..1450bd8
--- /dev/null
+++ b/sbin/fsck/pass1b.c
@@ -0,0 +1,101 @@
+/*
+ * 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 const 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;
+
+void
+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;
+ }
+ }
+}
+
+int
+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 0000000..382207e
--- /dev/null
+++ b/sbin/fsck/pass2.c
@@ -0,0 +1,434 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+void
+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();
+}
+
+int
+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.
+ */
+int
+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 0000000..963c41a
--- /dev/null
+++ b/sbin/fsck/pass3.c
@@ -0,0 +1,72 @@
+/*
+ * 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 const 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"
+
+void
+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 0000000..df8f722
--- /dev/null
+++ b/sbin/fsck/pass4.c
@@ -0,0 +1,135 @@
+/*
+ * 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 const 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();
+
+void
+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);
+ }
+ }
+}
+
+int
+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 0000000..a6ed6a1
--- /dev/null
+++ b/sbin/fsck/pass5.c
@@ -0,0 +1,338 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <string.h>
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0;
+ 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;
+ sbdirty();
+ }
+ if (fs->fs_fmod != 0) {
+ pwarn("MODIFIED FLAG SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+ }
+ if (fs->fs_clean == 0) {
+ pwarn("CLEAN FLAG NOT SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_clean = 1;
+ sbdirty();
+ }
+ }
+}
diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c
new file mode 100644
index 0000000..b560861
--- /dev/null
+++ b/sbin/fsck/preen.c
@@ -0,0 +1,383 @@
+/*
+ * 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 const 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 <ufs/ufs/dinode.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fstab.h>
+#include "fsck.h"
+
+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;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static int startdisk __P((struct disk *dk, int (*checkit)()));
+static struct disk *finddisk __P((char *name));
+static char *unrawname __P((char *name));
+static char *rawname __P((char *name));
+
+int nrun, ndisks;
+char hotroot;
+
+int
+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 || (passno == 1 && fsp->fs_passno == 1)) {
+ name = blockcheck(fsp->fs_spec);
+ if (name) {
+ sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0);
+ if (sumstatus)
+ 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)) != 0 &&
+ 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)) != 0
+ && 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)) != 0
+ && 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 = 0;
+
+ 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);
+}
+
+void
+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;
+}
+
+int
+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;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ 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;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(name) - 1;
+ if (l > 0 && name[l] == '/')
+ /* remove trailing slash */
+ name[l] = '\0';
+ if(!(fsinfo=getfsfile(name))) {
+ printf("Can't resolve %s to character special device",
+ name);
+ return (0);
+ }
+ name = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ printf("Warning: Can't find blockdevice corresponding to name %s\n",
+ name);
+ return (name);
+}
+
+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 0000000..5fb5697
--- /dev/null
+++ b/sbin/fsck/setup.c
@@ -0,0 +1,478 @@
+/*
+ * 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 const 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 <stdio.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)
+
+static int readsb __P((int listerr));
+static void badsb __P((int listerr, char *s));
+static int calcsb __P((char *dev, int devfd, struct fs *fs));
+static struct disklabel * getdisklabel __P((char *s, int fd));
+
+
+int
+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");
+ lp = getdisklabel((char *)NULL, fsreadfd);
+ if (lp)
+ 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).");
+ bflag = 0;
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ bflag = 0;
+ }
+ 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.
+ */
+static int
+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);
+}
+
+static void
+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!
+ */
+int
+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 0000000..d0c91fa
--- /dev/null
+++ b/sbin/fsck/utilities.c
@@ -0,0 +1,595 @@
+/*
+ * 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 const 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 <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, daddr_t blk));
+
+int
+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);
+ }
+}
+
+int
+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.
+ */
+void
+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;
+ }
+}
+
+void
+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);
+ }
+}
+
+void
+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");
+}
+
+void
+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);
+}
+
+int
+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);
+}
+
+void
+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
+ */
+int
+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
+ */
+void
+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
+ */
+void
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+int
+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);
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+void
+errexit(const char *s1, ...)
+{
+ va_list ap;
+ va_start(ap,s1);
+ vfprintf(stdout, s1, ap);
+ va_end(ap);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+void
+pfatal(const char *s, ...)
+{
+
+ va_list ap;
+ va_start(ap,s);
+ if (preen) {
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+void
+pwarn(const char *s, ...)
+{
+ va_list ap;
+ va_start(ap,s);
+ if (preen)
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+void
+#ifdef __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+#endif
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(fmt);
+}
+#endif
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile
new file mode 100644
index 0000000..e31e992
--- /dev/null
+++ b/sbin/fsck_ffs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck
+MAN8= fsck.8
+SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ffs/SMM.doc/0.t b/sbin/fsck_ffs/SMM.doc/0.t
new file mode 100644
index 0000000..528dd96
--- /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 October 7, 1996
+.AE
+.LP
+.bp
+.ce
+.B "TABLE OF CONTENTS"
+.LP
+.sp 1
+.nf
+.B "1. Introduction"
+.LP
+.sp .5v
+.nf
+.B "2. Overview of the file system
+2.1. Superblock
+2.2. Summary Information
+2.3. Cylinder groups
+2.4. Fragments
+2.5. Updates to the file system
+.LP
+.sp .5v
+.nf
+.B "3. Fixing corrupted file systems
+3.1. Detecting and correcting corruption
+3.2. Super block checking
+3.3. Free block checking
+3.4. Checking the inode state
+3.5. Inode links
+3.6. Inode data size
+3.7. Checking the data associated with an inode
+3.8. File system connectivity
+.LP
+.sp .5v
+.nf
+.B Acknowledgements
+.LP
+.sp .5v
+.nf
+.B References
+.LP
+.sp .5v
+.nf
+.B "4. Appendix A
+4.1. Conventions
+4.2. Initialization
+4.3. Phase 1 - Check Blocks and Sizes
+4.4. Phase 1b - Rescan for more Dups
+4.5. Phase 2 - Check Pathnames
+4.6. Phase 3 - Check Connectivity
+4.7. Phase 4 - Check Reference Counts
+4.8. Phase 5 - Check Cyl groups
+4.9. Cleanup
+.ds RH Introduction
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/1.t b/sbin/fsck_ffs/SMM.doc/1.t
new file mode 100644
index 0000000..4d2f535
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/1.t
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Introduction
+.NH
+Introduction
+.PP
+This document reflects the use of
+.I fsck
+with the 4.2BSD and 4.3BSD file system organization. This
+is a revision of the
+original paper written by
+T. J. Kowalski.
+.PP
+When a UNIX
+operating system is brought up, a consistency
+check of the file systems should always be performed.
+This precautionary measure helps to insure
+a reliable environment for file storage on disk.
+If an inconsistency is discovered,
+corrective action must be taken.
+.I Fsck
+runs in two modes.
+Normally it is run non-interactively by the system after
+a normal boot.
+When running in this mode,
+it will only make changes to the file system that are known
+to always be correct.
+If an unexpected inconsistency is found
+.I fsck
+will exit with a non-zero exit status,
+leaving the system running single-user.
+Typically the operator then runs
+.I fsck
+interactively.
+When running in this mode,
+each problem is listed followed by a suggested corrective action.
+The operator must decide whether or not the suggested correction
+should be made.
+.PP
+The purpose of this memo is to dispel the
+mystique surrounding
+file system inconsistencies.
+It first describes the updating of the file system
+(the calm before the storm) and
+then describes file system corruption (the storm).
+Finally,
+the set of deterministic corrective actions
+used by
+.I fsck
+(the Coast Guard
+to the rescue) is presented.
+.ds RH Overview of the File System
diff --git a/sbin/fsck_ffs/SMM.doc/2.t b/sbin/fsck_ffs/SMM.doc/2.t
new file mode 100644
index 0000000..7d00cea
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/2.t
@@ -0,0 +1,265 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Overview of the file system
+.NH
+Overview of the file system
+.PP
+The file system is discussed in detail in [Mckusick84];
+this section gives a brief overview.
+.NH 2
+Superblock
+.PP
+A file system is described by its
+.I "super-block" .
+The super-block is built when the file system is created (\c
+.I newfs (8))
+and never changes.
+The super-block
+contains the basic parameters of the file system,
+such as the number of data blocks it contains
+and a count of the maximum number of files.
+Because the super-block contains critical data,
+.I newfs
+replicates it to protect against catastrophic loss.
+The
+.I "default super block"
+always resides at a fixed offset from the beginning
+of the file system's disk partition.
+The
+.I "redundant super blocks"
+are not referenced unless a head crash
+or other hard disk error causes the default super-block
+to be unusable.
+The redundant blocks are sprinkled throughout the disk partition.
+.PP
+Within the file system are files.
+Certain files are distinguished as directories and contain collections
+of pointers to files that may themselves be directories.
+Every file has a descriptor associated with it called an
+.I "inode".
+The inode contains information describing ownership of the file,
+time stamps indicating modification and access times for the file,
+and an array of indices pointing to the data blocks for the file.
+In this section,
+we assume that the first 12 blocks
+of the file are directly referenced by values stored
+in the inode structure itself\(dg.
+.FS
+\(dgThe actual number may vary from system to system, but is usually in
+the range 5-13.
+.FE
+The inode structure may also contain references to indirect blocks
+containing further data block indices.
+In a file system with a 4096 byte block size, a singly indirect
+block contains 1024 further block addresses,
+a doubly indirect block contains 1024 addresses of further single indirect
+blocks,
+and a triply indirect block contains 1024 addresses of further doubly indirect
+blocks (the triple indirect block is never needed in practice).
+.PP
+In order to create files with up to
+2\(ua32 bytes,
+using only two levels of indirection,
+the minimum size of a file system block is 4096 bytes.
+The size of file system blocks can be any power of two
+greater than or equal to 4096.
+The block size of the file system is maintained in the super-block,
+so it is possible for file systems of different block sizes
+to be accessible simultaneously on the same system.
+The block size must be decided when
+.I newfs
+creates the file system;
+the block size cannot be subsequently
+changed without rebuilding the file system.
+.NH 2
+Summary information
+.PP
+Associated with the super block is non replicated
+.I "summary information" .
+The summary information changes
+as the file system is modified.
+The summary information contains
+the number of blocks, fragments, inodes and directories in the file system.
+.NH 2
+Cylinder groups
+.PP
+The file system partitions the disk into one or more areas called
+.I "cylinder groups".
+A cylinder group is comprised of one or more consecutive
+cylinders on a disk.
+Each cylinder group includes inode slots for files, a
+.I "block map"
+describing available blocks in the cylinder group,
+and summary information describing the usage of data blocks
+within the cylinder group.
+A fixed number of inodes is allocated for each cylinder group
+when the file system is created.
+The current policy is to allocate one inode for each 2048
+bytes of disk space;
+this is expected to be far more inodes than will ever be needed.
+.PP
+All the cylinder group bookkeeping information could be
+placed at the beginning of each cylinder group.
+However if this approach were used,
+all the redundant information would be on the top platter.
+A single hardware failure that destroyed the top platter
+could cause the loss of all copies of the redundant super-blocks.
+Thus the cylinder group bookkeeping information
+begins at a floating offset from the beginning of the cylinder group.
+The offset for
+the
+.I "i+1" st
+cylinder group is about one track further
+from the beginning of the cylinder group
+than it was for the
+.I "i" th
+cylinder group.
+In this way,
+the redundant
+information spirals down into the pack;
+any single track, cylinder,
+or platter can be lost without losing all copies of the super-blocks.
+Except for the first cylinder group,
+the space between the beginning of the cylinder group
+and the beginning of the cylinder group information stores data.
+.NH 2
+Fragments
+.PP
+To avoid waste in storing small files,
+the file system space allocator divides a single
+file system block into one or more
+.I "fragments".
+The fragmentation of the file system is specified
+when the file system is created;
+each file system block can be optionally broken into
+2, 4, or 8 addressable fragments.
+The lower bound on the size of these fragments is constrained
+by the disk sector size;
+typically 512 bytes is the lower bound on fragment size.
+The block map associated with each cylinder group
+records the space availability at the fragment level.
+Aligned fragments are examined
+to determine block availability.
+.PP
+On a file system with a block size of 4096 bytes
+and a fragment size of 1024 bytes,
+a file is represented by zero or more 4096 byte blocks of data,
+and possibly a single fragmented block.
+If a file system block must be fragmented to obtain
+space for a small amount of data,
+the remainder of the block is made available for allocation
+to other files.
+For example,
+consider an 11000 byte file stored on
+a 4096/1024 byte file system.
+This file uses two full size blocks and a 3072 byte fragment.
+If no fragments with at least 3072 bytes
+are available when the file is created,
+a full size block is split yielding the necessary 3072 byte
+fragment and an unused 1024 byte fragment.
+This remaining fragment can be allocated to another file, as needed.
+.NH 2
+Updates to the file system
+.PP
+Every working day hundreds of files
+are created, modified, and removed.
+Every time a file is modified,
+the operating system performs a
+series of file system updates.
+These updates, when written on disk, yield a consistent file system.
+The file system stages
+all modifications of critical information;
+modification can
+either be completed or cleanly backed out after a crash.
+Knowing the information that is first written to the file system,
+deterministic procedures can be developed to
+repair a corrupted file system.
+To understand this process,
+the order that the update
+requests were being honored must first be understood.
+.PP
+When a user program does an operation to change the file system,
+such as a
+.I write ,
+the data to be written is copied into an internal
+.I "in-core"
+buffer in the kernel.
+Normally, the disk update is handled asynchronously;
+the user process is allowed to proceed even though
+the data has not yet been written to the disk.
+The data,
+along with the inode information reflecting the change,
+is eventually written out to disk.
+The real disk write may not happen until long after the
+.I write
+system call has returned.
+Thus at any given time, the file system,
+as it resides on the disk,
+lags the state of the file system represented by the in-core information.
+.PP
+The disk information is updated to reflect the in-core information
+when the buffer is required for another use,
+when a
+.I sync (2)
+is done (at 30 second intervals) by
+.I "/etc/update" "(8),"
+or by manual operator intervention with the
+.I sync (8)
+command.
+If the system is halted without writing out the in-core information,
+the file system on the disk will be in an inconsistent state.
+.PP
+If all updates are done asynchronously, several serious
+inconsistencies can arise.
+One inconsistency is that a block may be claimed by two inodes.
+Such an inconsistency can occur when the system is halted before
+the pointer to the block in the old inode has been cleared
+in the copy of the old inode on the disk,
+and after the pointer to the block in the new inode has been written out
+to the copy of the new inode on the disk.
+Here,
+there is no deterministic method for deciding
+which inode should really claim the block.
+A similar problem can arise with a multiply claimed inode.
+.PP
+The problem with asynchronous inode updates
+can be avoided by doing all inode deallocations synchronously.
+Consequently,
+inodes and indirect blocks are written to the disk synchronously
+(\fIi.e.\fP the process blocks until the information is
+really written to disk)
+when they are being deallocated.
+Similarly inodes are kept consistent by synchronously
+deleting, adding, or changing directory entries.
+.ds RH Fixing corrupted file systems
diff --git a/sbin/fsck_ffs/SMM.doc/3.t b/sbin/fsck_ffs/SMM.doc/3.t
new file mode 100644
index 0000000..bb6f05b
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/3.t
@@ -0,0 +1,452 @@
+.\" 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
+.I Fsck
+also checks for directories with unallocated blocks (holes).
+Such directories should never be created.
+When found,
+.I fsck
+will prompt the user to adjust the length of the offending directory
+which is done by shortening the size of the directory to the end of the
+last allocated block preceeding the hole.
+Unfortunately, this means that another Phase 1 run has to be done.
+.I Fsck
+will remind the user to rerun fsck after repairing a
+directory containing an unallocated block.
+.PP
+If a directory entry inode number references
+outside the inode list, then
+.I fsck
+will remove that directory entry.
+This condition occurs if bad data is written into a directory data block.
+.PP
+The directory inode number entry for ``\fB.\fP''
+must be the first entry in the directory data block.
+The inode number for ``\fB.\fP''
+must reference itself;
+e.g., it must equal the inode number
+for the directory data block.
+The directory inode number entry
+for ``\fB..\fP'' must be
+the second entry in the directory data block.
+Its value must equal the inode number for the
+parent of the directory entry
+(or the inode number of the directory
+data block if the directory is the
+root directory).
+If the directory inode numbers are
+incorrect,
+.I fsck
+will replace them with the correct values.
+If there are multiple hard links to a directory,
+the first one encountered is considered the real parent
+to which ``\fB..\fP'' should point;
+\fIfsck\fP recommends deletion for the subsequently discovered names.
+.NH 2
+File system connectivity
+.PP
+.I Fsck
+checks the general connectivity of the file system.
+If directories are not linked into the file system, then
+.I fsck
+links the directory back into the file system in the
+.I lost+found
+directory.
+This condition only occurs when there has been a hardware failure.
+.ds RH "References"
+.SH
+\s+2Acknowledgements\s0
+.PP
+I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie
+for their suggestions and help in implementing the new file system.
+Thanks also to Robert Henry for his editorial input to
+get this document together.
+Finally we thank our sponsors,
+the National Science Foundation under grant MCS80-05144,
+and the Defense Advance Research Projects Agency (DoD) under
+Arpa Order No. 4031 monitored by Naval Electronic System Command under
+Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983)
+.PP
+I would like to thank Larry A. Wehr for advice that lead
+to the first version of
+.I fsck
+and Rick B. Brandt for adapting
+.I fsck
+to
+UNIX/TS. (T. Kowalski, July 1979)
+.sp 2
+.SH
+\s+2References\s0
+.LP
+.IP [Dolotta78] 20
+Dolotta, T. A., and Olsson, S. B. eds.,
+.I "UNIX User's Manual, Edition 1.1\^" ,
+January 1978.
+.IP [Joy83] 20
+Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D.
+4.2BSD System Manual,
+.I "University of California at Berkeley" ,
+.I "Computer Systems Research Group Technical Report"
+#4, 1982.
+.IP [McKusick84] 20
+McKusick, M., Joy, W., Leffler, S., and Fabry, R.
+A Fast File System for UNIX,
+\fIACM Transactions on Computer Systems 2\fP, 3.
+pp. 181-197, August 1984.
+.IP [Ritchie78] 20
+Ritchie, D. M., and Thompson, K.,
+The UNIX Time-Sharing System,
+.I "The Bell System Technical Journal"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1905-29.
+.IP [Thompson78] 20
+Thompson, K.,
+UNIX Implementation,
+.I "The Bell System Technical Journal\^"
+.B 57 ,
+6 (July-August 1978, Part 2), pp. 1931-46.
+.ds RH Appendix A \- Fsck Error Conditions
+.bp
diff --git a/sbin/fsck_ffs/SMM.doc/4.t b/sbin/fsck_ffs/SMM.doc/4.t
new file mode 100644
index 0000000..5ea8179
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/4.t
@@ -0,0 +1,1424 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/5/93
+.\"
+.ds RH Appendix A \- Fsck Error Conditions
+.NH
+Appendix A \- Fsck Error Conditions
+.NH 2
+Conventions
+.PP
+.I Fsck
+is
+a multi-pass file system check program.
+Each file system pass invokes a different Phase of the
+.I fsck
+program.
+After the initial setup,
+.I fsck
+performs successive Phases over each file system,
+checking blocks and sizes,
+path-names,
+connectivity,
+reference counts,
+and the map of free blocks,
+(possibly rebuilding it),
+and performs some cleanup.
+.LP
+Normally
+.I fsck
+is run non-interactively to
+.I preen
+the file systems after an unclean halt.
+While preen'ing a file system,
+it will only fix corruptions that are expected
+to occur from an unclean halt.
+These actions are a proper subset of the actions that
+.I fsck
+will take when it is running interactively.
+Throughout this appendix many errors have several options
+that the operator can take.
+When an inconsistency is detected,
+.I fsck
+reports the error condition to the operator.
+If a response is required,
+.I fsck
+prints a prompt message and
+waits for a response.
+When preen'ing most errors are fatal.
+For those that are expected,
+the response taken is noted.
+This appendix explains the meaning of each error condition,
+the possible responses, and the related error conditions.
+.LP
+The error conditions are organized by the
+.I Phase
+of the
+.I fsck
+program in which they can occur.
+The error conditions that may occur
+in more than one Phase
+will be discussed in initialization.
+.NH 2
+Initialization
+.PP
+Before a file system check can be performed, certain
+tables have to be set up and certain files opened.
+This section concerns itself with the opening of files and
+the initialization of tables.
+This section lists error conditions resulting from
+command line options,
+memory requests,
+opening of files,
+status of files,
+file system size checks,
+and creation of the scratch file.
+All the initialization errors are fatal
+when the file system is being preen'ed.
+.sp
+.LP
+.B "\fIC\fP option?"
+.br
+\fIC\fP is not a legal option to
+.I fsck ;
+legal options are \-b, \-c, \-y, \-n, and \-p.
+.I Fsck
+terminates on this error condition.
+See the
+.I fsck (8)
+manual entry for further detail.
+.sp
+.LP
+.B "cannot alloc NNN bytes for blockmap"
+.br
+.B "cannot alloc NNN bytes for freemap"
+.br
+.B "cannot alloc NNN bytes for statemap"
+.br
+.B "cannot alloc NNN bytes for lncntp"
+.br
+.I Fsck 's
+request for memory for its virtual
+memory tables failed.
+This should never happen.
+.I Fsck
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't open checklist file: \fIF\fP"
+.br
+The file system checklist file
+\fIF\fP (usually
+.I /etc/fstab )
+can not be opened for reading.
+.I Fsck
+terminates on this error condition.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't stat root"
+.br
+.I Fsck 's
+request for statistics about the root directory ``/'' failed.
+This should never happen.
+.I Fsck
+terminates on this error condition.
+See a guru.
+.sp
+.LP
+.B "Can't stat \fIF\fP"
+.br
+.B "Can't make sense out of name \fIF\fP"
+.br
+.I Fsck 's
+request for statistics about the file system \fIF\fP failed.
+When running manually,
+it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "Can't open \fIF\fP"
+.br
+.I Fsck 's
+request attempt to open the file system \fIF\fP failed.
+When running manually, it ignores this file system
+and continues checking the next file system given.
+Check access modes of \fIF\fP.
+.sp
+.LP
+.B "\fIF\fP: (NO WRITE)"
+.br
+Either the \-n flag was specified or
+.I fsck 's
+attempt to open the file system \fIF\fP for writing failed.
+When running manually,
+all the diagnostics are printed out,
+but no modifications are attempted to fix them.
+.sp
+.LP
+.B "file is not a block or character device; OK"
+.br
+You have given
+.I fsck
+a regular file name by mistake.
+Check the type of the file specified.
+.LP
+Possible responses to the OK prompt are:
+.IP YES
+ignore this error condition.
+.IP NO
+ignore this file system and continues checking
+the next file system given.
+.sp
+.LP
+.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock optimization parameter is neither OPT_TIME
+nor OPT_SPACE.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The superblock is set to request optimization to minimize
+running time of the system.
+(If optimization to minimize disk space utilization is
+desired, it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The superblock minimum space percentage is greater than 99%
+or less then 0%.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The minfree parameter is set to 10%.
+(If some other percentage is desired,
+it can be set using \fItunefs\fP(8).)
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The file system interleave is less than or equal to zero.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The interleave parameter is set to 1.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)"
+.br
+The number of physical sectors per track is less than the number
+of usable sectors per track.
+.LP
+Possible responses to the SET TO DEFAULT prompt are:
+.IP YES
+The npsect parameter is set to the number of usable sectors per track.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+One of the following messages will appear:
+.br
+.B "MAGIC NUMBER WRONG"
+.br
+.B "NCG OUT OF RANGE"
+.br
+.B "CPG OUT OF RANGE"
+.br
+.B "NCYL DOES NOT JIVE WITH NCG*CPG"
+.br
+.B "SIZE PREPOSTEROUSLY LARGE"
+.br
+.B "TRASHED VALUES IN SUPER BLOCK"
+.br
+and will be followed by the message:
+.br
+.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP"
+.br
+.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE"
+.br
+.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)."
+.br
+The super block has been corrupted.
+An alternative super block must be selected from among those
+listed by
+.I newfs
+(8) when the file system was created.
+For file systems with a blocksize less than 32K,
+specifying \-b 32 is a good first choice.
+.sp
+.LP
+.B "INTERNAL INCONSISTENCY: \fIM\fP"
+.br
+.I Fsck 's
+has had an internal panic, whose message is specified as \fIM\fP.
+This should never happen.
+See a guru.
+.sp
+.LP
+.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for moving to a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+Often,
+however the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for reading a specified block number \fIB\fP in
+the file system failed.
+This should never happen.
+See a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+It will retry the read and print out the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be read.
+If
+.I fsck
+ever tries to write back one of the blocks on which the read failed
+it will print the message:
+.br
+.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK"
+.br
+where \fIN\fP indicates the sector that was written with zero's.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)"
+.br
+.I Fsck 's
+request for writing a specified block number \fIB\fP
+in the file system failed.
+The disk is write-protected;
+check the write protect lock on the drive.
+If that is not the problem, see a guru.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+attempt to continue to run the file system check.
+The write operation will be retried with the failed blocks
+indicated by the message:
+.br
+.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP"
+.br
+where \fIN\fP indicates the sectors that could not be written.
+If the disk is experiencing hardware problems, the problem will persist.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If the block was part of the virtual memory buffer
+cache,
+.I fsck
+will terminate with the message ``Fatal I/O error''.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "bad inode number DDD to ginode"
+.br
+An internal error has attempted to read non-existent inode \fIDDD\fP.
+This error causes
+.I fsck
+to exit.
+See a guru.
+.NH 2
+Phase 1 \- Check Blocks and Sizes
+.PP
+This phase concerns itself with
+the inode list.
+This section lists error conditions resulting from
+checking inode types,
+setting up the zero-link-count table,
+examining inode block numbers for bad or duplicate blocks,
+checking inode size,
+and checking inode format.
+All errors in this phase except
+.B "INCORRECT BLOCK COUNT"
+and
+.B "PARTIALLY TRUNCATED INODE"
+are fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)"
+.br
+The mode word of the inode \fII\fP indicates that the inode is not a
+special block inode, special character inode, socket inode, regular inode,
+symbolic link, or directory inode.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+This will always invoke the UNALLOCATED error condition in Phase 2
+for each directory entry pointing to this inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)"
+.br
+.I Fsck
+has found inode \fII\fP whose size is shorter than the number of
+blocks allocated to it.
+This condition should only occur if the system crashes while in the
+midst of truncating a file.
+When preen'ing the file system,
+.I fsck
+completes the truncation to the specified size.
+.LP
+Possible responses to SALVAGE are:
+.IP YES
+complete the truncation to the size specified in the inode.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "LINK COUNT TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table for
+.I fsck
+containing allocated inodes with a link count of
+zero cannot allocate more memory.
+Increase the virtual memory for
+.I fsck .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If another allocated inode with a zero link count is found,
+this error condition is repeated.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "\fIB\fP BAD I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP with a number
+lower than the number of the first data block in the file system or
+greater than the number of the last block
+in the file system.
+This error condition may invoke the
+.B "EXCESSIVE BAD BLKS"
+error condition in Phase 1 (see next paragraph) if
+inode \fII\fP has too many block numbers outside the file system range.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks with a number
+lower than the number of the first data block in the file system or greater than
+the number of last block in the file system associated with inode \fII\fP.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "BAD STATE DDD TO BLKERR"
+.br
+An internal error has scrambled
+.I fsck 's
+state map to have the impossible value \fIDDD\fP.
+.I Fsck
+exits immediately.
+See a guru.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that is already claimed by
+another inode.
+This error condition may invoke the
+.B "EXCESSIVE DUP BLKS"
+error condition in Phase 1 if
+inode \fII\fP has too many block numbers claimed by other inodes.
+This error condition will always invoke Phase 1b and the
+.B "BAD/DUP"
+error condition in Phase 2 and Phase 4.
+.sp
+.LP
+.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)"
+.br
+There is more than a tolerable number (usually 10) of blocks claimed by other
+inodes.
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the rest of the blocks in this inode
+and continue checking with the next inode in the file system.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUP TABLE OVERFLOW (CONTINUE)"
+.br
+An internal table in
+.I fsck
+containing duplicate block numbers cannot allocate any more space.
+Increase the amount of virtual memory available to
+.I fsck .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+continue with the program.
+This error condition will not allow a complete check of the file system.
+A second run of
+.I fsck
+should be made to re-check this file system.
+If another duplicate block is found, this error condition will repeat.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)"
+.br
+Inode \fII\fP is neither allocated nor unallocated.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)"
+.br
+The block count for inode \fII\fP is \fIX\fP blocks,
+but should be \fIY\fP blocks.
+When preen'ing the count is corrected.
+.LP
+Possible responses to the CORRECT prompt are:
+.IP YES
+replace the block count of inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 1B: Rescan for More Dups
+.PP
+When a duplicate block is found in the file system, the file system is
+rescanned to find the inode that previously claimed that block.
+This section lists the error condition when the duplicate block is found.
+.sp
+.LP
+.B "\fIB\fP DUP I=\fII\fP"
+.br
+Inode \fII\fP contains block number \fIB\fP that
+is already claimed by another inode.
+This error condition will always invoke the
+.B "BAD/DUP"
+error condition in Phase 2.
+You can determine which inodes have overlapping blocks by examining
+this error condition and the DUP error condition in Phase 1.
+.NH 2
+Phase 2 \- Check Pathnames
+.PP
+This phase concerns itself with removing directory entries
+pointing to
+error conditioned inodes
+from Phase 1 and Phase 1b.
+This section lists error conditions resulting from
+root inode mode and status,
+directory inode pointers in range,
+and directory entries pointing to bad inodes,
+and directory integrity checks.
+All errors in this phase are fatal if the file system is being preen'ed,
+except for directories not being a multiple of the blocks size
+and extraneous hard links.
+.sp
+.LP
+.B "ROOT INODE UNALLOCATED (ALLOCATE)"
+.br
+The root inode (usually inode number 2) has no allocate mode bits.
+This should never happen.
+.LP
+Possible responses to the ALLOCATE prompt are:
+.IP YES
+allocate inode 2 as the root inode.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will exit.
+.sp
+.LP
+.B "ROOT INODE NOT DIRECTORY (REALLOCATE)"
+.br
+The root inode (usually inode number 2)
+is not directory inode type.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will then prompt with
+.B "FIX"
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+replace the root inode's type to be a directory.
+If the root inode's data blocks are not directory blocks,
+many error conditions will be produced.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "DUPS/BAD IN ROOT INODE (REALLOCATE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks in the root inode (usually inode number 2) for the file system.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+clear the existing contents of the root inode
+and reallocate it.
+The files and directories usually found in the root will be recovered
+in Phase 3 and put into
+.I lost+found .
+If the attempt to allocate the root fails,
+.I fsck
+will exit with the message:
+.br
+.B "CANNOT ALLOCATE ROOT INODE" .
+.IP NO
+.I fsck
+will then prompt with
+.B "CONTINUE" .
+.LP
+Possible responses to the CONTINUE prompt are:
+.IP YES
+ignore the
+.B "DUPS/BAD"
+error condition in the root inode and
+attempt to continue to run the file system check.
+If the root inode is not correct,
+then this may result in many other error conditions.
+.IP NO
+terminate the program.
+.sp
+.LP
+.B "NAME TOO LONG \fIF\fP"
+.br
+An excessively long path name has been found.
+This usually indicates loops in the file system name space.
+This can occur if the super user has made circular links to directories.
+The offending links must be removed (by a guru).
+.sp
+.LP
+.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has an inode number \fII\fP that is greater than
+the end of the inode list.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+A directory or file entry \fIF\fP points to an unallocated inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks or bad blocks
+associated with directory or file entry \fIF\fP, inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)"
+.br
+A directory entry \fIF\fP has a size \fIS\fP that is zero.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+the directory entry \fIF\fP is removed;
+this will always invoke the BAD/DUP error condition in Phase 4.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fIF\fP has been found whose size \fIS\fP
+is less than the minimum size directory.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP,
+and directory name \fIF\fP are printed.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+increase the size of the directory to the minimum directory size.
+.IP NO
+ignore this directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP.
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)"
+.br
+A directory with an inconsistent internal state has been found.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+throw away all entries up to the next directory boundary (usually 512-byte)
+boundary.
+This drastic action can throw away up to 42 entries,
+and should be taken only after other recovery efforts have failed.
+.IP NO
+skip up to the next directory boundary and resume reading,
+but do not modify the directory.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `.' does
+does not equal \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `.' to be equal to \fII\fP.
+.IP NO
+leave the inode number for `.' unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose first entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `.' with inode number equal to \fII\fP.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose first entry is \fIF\fP.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'"
+.br
+A directory \fII\fP has been found whose first entry is not `.'.
+.I Fsck
+cannot resolve this problem as it should never happen.
+See a guru.
+.sp
+.LP
+.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `.'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `.'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose inode number for `..' does
+does not equal the parent of \fII\fP.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+change the inode number for `..' to be equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the inode number for `..' unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found whose second entry is unallocated.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+build an entry for `..' with inode number equal to the parent of \fII\fP
+(``\fB..\fP'' in the root inode points to itself).
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP"
+.br
+A directory \fII\fP has been found whose second entry is \fIF\fP.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the offending entry \fIF\fP
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP"
+.br
+.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'"
+.br
+A directory \fII\fP has been found whose second entry is not `..'.
+.I Fsck
+cannot resolve this problem.
+The file system should be mounted and the second entry in the directory
+moved elsewhere.
+The file system should then be unmounted and
+.I fsck
+should be run again.
+.sp
+.LP
+.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)"
+.br
+A directory \fII\fP has been found that has more than one entry for `..'.
+.LP
+Possible responses to the FIX prompt are:
+.IP YES
+remove the extra entry for `..'.
+.IP NO
+leave the directory unchanged.
+.sp
+.LP
+.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE)
+.br
+.I Fsck
+has found a hard link, \fIN\fP, to a directory, \fID\fP.
+When preen'ing the extraneous links are ignored.
+.LP
+Possible responses to the REMOVE prompt are:
+.IP YES
+delete the extraneous entry, \fIN\fP.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD RETURN STATE \fIS\fP FROM DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be returned
+from the routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.sp
+.LP
+.B "BAD STATE \fIS\fP FOR ROOT INODE"
+.br
+An internal error has caused an impossible state \fIS\fP to be assigned
+to the root inode.
+.I Fsck
+exits.
+See a guru.
+.NH 2
+Phase 3 \- Check Connectivity
+.PP
+This phase concerns itself with the directory connectivity seen in
+Phase 2.
+This section lists error conditions resulting from
+unreferenced directories,
+and missing or full
+.I lost+found
+directories.
+.sp
+.LP
+.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+The directory inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of directory inode \fII\fP are printed.
+When preen'ing, the directory is reconnected if its size is non-zero,
+otherwise it is cleared.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect directory inode \fII\fP to the file system in the
+directory for lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 3
+if there are problems connecting directory inode \fII\fP to \fIlost+found\fP.
+This may also invoke the CONNECTED error condition in Phase 3 if the link
+was successful.
+.IP NO
+ignore this error condition.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP"
+.br
+This is an advisory message indicating a directory inode \fII1\fP was
+successfully connected to the
+.I lost+found
+directory.
+The parent inode \fII2\fP of the directory inode \fII1\fP is
+replaced by the inode number of the
+.I lost+found
+directory.
+.sp
+.LP
+.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST)
+.br
+A directory \fIF\fP has been found with size \fIS\fP that is not
+a multiple of the directory blocksize \fIB\fP
+(this can reoccur in Phase 3 if it is not adjusted in Phase 2).
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+the length is rounded up to the appropriate block size.
+This error can occur on 4.2BSD file systems.
+Thus when preen'ing the file system only a warning is printed
+and the directory is adjusted.
+.IP NO
+ignore the error condition.
+.sp
+.LP
+.B "BAD INODE \fIS\fP TO DESCEND"
+.br
+An internal error has caused an impossible state \fIS\fP to be passed to the
+routine that descends the file system directory structure.
+.I Fsck
+exits.
+See a guru.
+.NH 2
+Phase 4 \- Check Reference Counts
+.PP
+This phase concerns itself with the link count information
+seen in Phase 2 and Phase 3.
+This section lists error conditions resulting from
+unreferenced files,
+missing or full
+.I lost+found
+directory,
+incorrect link counts for files, directories, symbolic links, or special files,
+unreferenced files, symbolic links, and directories,
+and bad or duplicate blocks in files, symbolic links, and directories.
+All errors in this phase are correctable if the file system is being preen'ed
+except running out of space in the \fIlost+found\fP directory.
+.sp
+.LP
+.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)"
+.br
+Inode \fII\fP was not connected to a directory entry
+when the file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and
+modify time \fIT\fP of inode \fII\fP are printed.
+When preen'ing the file is cleared if either its size or its
+link count is zero,
+otherwise it is reconnected.
+.LP
+Possible responses to the RECONNECT prompt are:
+.IP YES
+reconnect inode \fII\fP to the file system in the directory for
+lost files (usually \fIlost+found\fP).
+This may invoke the
+.I lost+found
+error condition in Phase 4
+if there are problems connecting inode \fII\fP to
+.I lost+found .
+.IP NO
+ignore this error condition.
+This will always invoke the CLEAR error condition in Phase 4.
+.sp
+.LP
+.B "(CLEAR)"
+.br
+The inode mentioned in the immediately previous error condition can not be
+reconnected.
+This cannot occur if the file system is being preen'ed,
+since lack of space to reconnect files is a fatal error.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "NO lost+found DIRECTORY (CREATE)"
+.br
+There is no
+.I lost+found
+directory in the root directory of the file system;
+When preen'ing
+.I fsck
+tries to create a \fIlost+found\fP directory.
+.LP
+Possible responses to the CREATE prompt are:
+.IP YES
+create a \fIlost+found\fP directory in the root of the file system.
+This may raise the message:
+.br
+.B "NO SPACE LEFT IN / (EXPAND)"
+.br
+See below for the possible responses.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "lost+found IS NOT A DIRECTORY (REALLOCATE)"
+.br
+The entry for
+.I lost+found
+is not a directory.
+.LP
+Possible responses to the REALLOCATE prompt are:
+.IP YES
+allocate a directory inode, and change \fIlost+found\fP to reference it.
+The previous inode reference by the \fIlost+found\fP name is not cleared.
+Thus it will either be reclaimed as an UNREF'ed inode or have its
+link count ADJUST'ed later in this Phase.
+Inability to create a \fIlost+found\fP directory generates the message:
+.br
+.B "SORRY. CANNOT CREATE lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "NO SPACE LEFT IN /lost+found (EXPAND)"
+.br
+There is no space to add another entry to the
+.I lost+found
+directory in the root directory
+of the file system.
+When preen'ing the
+.I lost+found
+directory is expanded.
+.LP
+Possible responses to the EXPAND prompt are:
+.IP YES
+the
+.I lost+found
+directory is expanded to make room for the new entry.
+If the attempted expansion fails
+.I fsck
+prints the message:
+.br
+.B "SORRY. NO SPACE IN lost+found DIRECTORY"
+.br
+and aborts the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+Clean out unnecessary entries in
+.I lost+found .
+This error is fatal if the file system is being preen'ed.
+.IP NO
+abort the attempt to linkup the lost inode.
+This will always invoke the UNREF error condition in Phase 4.
+.sp
+.LP
+.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)"
+.br
+The link count for inode \fII\fP,
+is \fIX\fP but should be \fIY\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP
+are printed.
+When preen'ing the link count is adjusted unless the number of references
+is increasing, a condition that should never occur unless precipitated
+by a hardware failure.
+When the number of references is increasing under preen mode,
+.I fsck
+exits with the message:
+.br
+.B "LINK COUNT INCREASING"
+.LP
+Possible responses to the ADJUST prompt are:
+.IP YES
+replace the link count of file inode \fII\fP with \fIY\fP.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Inode \fII\fP, was not connected to a directory entry when the
+file system was traversed.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+When preen'ing,
+this is a file that was not connected because its size or link count was zero,
+hence it is cleared.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)"
+.br
+Phase 1 or Phase 1b have found duplicate blocks
+or bad blocks associated with
+inode \fII\fP.
+The owner \fIO\fP, mode \fIM\fP, size \fIS\fP,
+and modify time \fIT\fP of inode \fII\fP
+are printed.
+This error cannot arise when the file system is being preen'ed,
+as it would have caused a fatal error earlier.
+.LP
+Possible responses to the CLEAR prompt are:
+.IP YES
+de-allocate inode \fII\fP by zeroing its contents.
+.IP NO
+ignore this error condition.
+.NH 2
+Phase 5 - Check Cyl groups
+.PP
+This phase concerns itself with the free-block and used-inode maps.
+This section lists error conditions resulting from
+allocated blocks in the free-block maps,
+free blocks missing from free-block maps,
+and the total free-block count incorrect.
+It also lists error conditions resulting from
+free inodes in the used-inode maps,
+allocated inodes missing from used-inode maps,
+and the total used-inode count incorrect.
+.sp
+.LP
+.B "CG \fIC\fP: BAD MAGIC NUMBER"
+.br
+The magic number of cylinder group \fIC\fP is wrong.
+This usually indicates that the cylinder group maps have been destroyed.
+When running manually the cylinder group is marked as needing
+to be reconstructed.
+This error is fatal if the file system is being preen'ed.
+.sp
+.LP
+.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)"
+.br
+A cylinder group block map is missing some free blocks.
+During preen'ing the maps are reconstructed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the free block map.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "SUMMARY INFORMATION BAD (SALVAGE)"
+.br
+The summary information was found to be incorrect.
+When preen'ing,
+the summary information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the summary information.
+.IP NO
+ignore this error condition.
+.sp
+.LP
+.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)"
+.br
+The superblock free block information was found to be incorrect.
+When preen'ing,
+the superblock free block information is recomputed.
+.LP
+Possible responses to the SALVAGE prompt are:
+.IP YES
+reconstruct the superblock free block information.
+.IP NO
+ignore this error condition.
+.NH 2
+Cleanup
+.PP
+Once a file system has been checked, a few cleanup functions are performed.
+This section lists advisory messages about
+the file system
+and modify status of the file system.
+.sp
+.LP
+.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)"
+.br
+This is an advisory message indicating that
+the file system checked contained
+\fIV\fP files using
+\fIW\fP fragment sized blocks leaving
+\fIX\fP fragment sized blocks free in the file system.
+The numbers in parenthesis breaks the free count down into
+\fIY\fP free fragments and
+\fIZ\fP free full sized blocks.
+.sp
+.LP
+.B "***** REBOOT UNIX *****"
+.br
+This is an advisory message indicating that
+the root file system has been modified by
+.I fsck.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck
+may be undone by the in-core copies of tables
+UNIX keeps.
+When preen'ing,
+.I fsck
+will exit with a code of 4.
+The standard auto-reboot script distributed with 4.3BSD
+interprets an exit code of 4 by issuing a reboot system call.
+.sp
+.LP
+.B "***** FILE SYSTEM WAS MODIFIED *****"
+.br
+This is an advisory message indicating that
+the current file system was modified by
+.I fsck.
+If this file system is mounted or is the current root file system,
+.I fsck
+should be halted and UNIX rebooted.
+If UNIX is not rebooted immediately,
+the work done by
+.I fsck
+may be undone by the in-core copies of tables
+UNIX keeps.
diff --git a/sbin/fsck_ffs/SMM.doc/Makefile b/sbin/fsck_ffs/SMM.doc/Makefile
new file mode 100644
index 0000000..26823bc
--- /dev/null
+++ b/sbin/fsck_ffs/SMM.doc/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/03.fsck
+SRCS= 0.t 1.t 2.t 3.t 4.t
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
new file mode 100644
index 0000000..9a2b923
--- /dev/null
+++ b/sbin/fsck_ffs/dir.c
@@ -0,0 +1,720 @@
+/*
+ * 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 const 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 <stdio.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, ".."
+};
+
+
+static int chgino __P((struct inodesc *idesc));
+static int dircheck __P((struct inodesc *idesc, struct direct *dp));
+static int expanddir __P((struct dinode *dp, char *name));
+static void freedir __P((ino_t ino, ino_t parent));
+static struct direct * fsck_readdir __P((struct inodesc *idesc));
+static struct bufarea * getdirblk __P((daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *idesc));
+
+/*
+ * Propagate connected state through the tree.
+ */
+void
+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.
+ */
+int
+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.
+ */
+int
+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);
+}
+
+void
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+void
+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);
+}
+
+void
+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();
+ }
+ }
+}
+
+int
+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];
+ dirp->d_namlen = newent.d_namlen;
+ } else {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ dirp->d_type = newent.d_namlen;
+ dirp->d_namlen = 0;
+# else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+# endif
+ }
+ dirp->d_reclen = newent.d_reclen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+int
+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);
+}
+
+int
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+
+ 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);
+ /*
+ * The parent directory, because of the ordering
+ * guarantees, has had the link count incremented
+ * for the child, but no entry was made. This
+ * fixes the parent link count so that fsck does
+ * not need to be rerun.
+ */
+ lncntp[parentdir]++;
+
+ }
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+int
+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
+ */
+int
+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
+ */
+int
+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
+ */
+ino_t
+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
+ */
+static void
+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.
+ */
+int
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
new file mode 100644
index 0000000..57cb17a
--- /dev/null
+++ b/sbin/fsck_ffs/fsck.h
@@ -0,0 +1,293 @@
+/*
+ * 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 fflag; /* force fs check (ignore clean flag) */
+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 */
+int returntosingle; /* return to single user mode */
+int rerun; /* rerun fsck. Only used in non-preen mode */
+
+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 */
+unsigned 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
+
+/* dir.c */
+void adjust __P((struct inodesc *idesc, short lcnt));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *idesc));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void propagate __P((void));
+
+/* ffs_subr.c */
+void ffs_fragacct __P((struct fs *fs, int fragmap, long *fraglist, int cnt));
+
+/* inode.c */
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, daddr_t blk));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+int chkrange __P((daddr_t blk, int cnt));
+int ckinode __P((struct dinode *dp, struct inodesc *idesc));
+void clri __P((struct inodesc *idesc, char *type, int flag));
+int findino __P((struct inodesc *idesc));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+struct dinode * ginode __P((ino_t inumber));
+struct inoinfo * getinoinfo __P((ino_t inumber));
+struct dinode * getnextinode __P((ino_t inumber));
+void inodirty __P((void));
+void inocleanup __P((void));
+void pinode __P((ino_t ino));
+void resetinodebuf __P((void));
+int findname __P((struct inodesc *idesc));
+
+/* pass1.c */
+void pass1 __P((void));
+int pass1check __P((struct inodesc *idesc));
+
+/* pass1b.c */
+void pass1b __P((void));
+
+/* pass2.c */
+void pass2 __P((void));
+
+/* pass3.c */
+void pass3 __P((void));
+
+/* pass4.c */
+void pass4 __P((void));
+int pass4check __P((struct inodesc *idesc));
+
+/* pass5.c */
+void pass5 __P((void));
+
+/* preen.c */
+char *blockcheck __P((char *name));
+int checkfstab __P((int preen, int maxrun,int (*docheck)(), int (*chkit)()));
+
+/* setup.c */
+int setup __P((char *dev));
+
+/* utilities.c */
+int allocblk __P((long frags));
+int bread __P((int fd, char *buf, daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, daddr_t blk, long size));
+void catch __P((int));
+void catchquit __P((int));
+void ckfini __P((void));
+int dofix __P((struct inodesc *idesc, char *msg));
+void errexit __P((const char *s1, ...)) __dead2;
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((daddr_t blkno, long frags));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, daddr_t blk, long size));
+struct bufarea * getdatablk __P((daddr_t blkno, long size));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+void panic __P((const char *, ...)) __dead2;
+void pfatal __P((const char *s1, ...));
+void pwarn __P((const char *s1, ...));
+int reply __P((char *question));
+void voidquit __P((int));
diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8
new file mode 100644
index 0000000..c8ae6fa
--- /dev/null
+++ b/sbin/fsck_ffs/fsck_ffs.8
@@ -0,0 +1,298 @@
+.\" 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 f
+.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 clean flag of each filesystem's superblock is examined and only those filesystems that
+are not marked clean are checked. If the
+.Fl f
+option is specified, the filesystems
+will be checked regardless of the state of their clean flag.
+.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
+Holes in directories.
+.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 newfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
new file mode 100644
index 0000000..5adac75
--- /dev/null
+++ b/sbin/fsck_ffs/inode.c
@@ -0,0 +1,619 @@
+/*
+ * 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 const char sccsid[] = "@(#)inode.c 8.5 (Berkeley) 2/8/95";
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *idesc, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ int ret;
+ long n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+ char pathbuf[MAXPATHLEN + 1];
+
+ 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 || dp->di_blocks == 0)))
+ 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) {
+ if (idesc->id_type == DATA && ndb >= 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size = (ap - &dino.di_db[0]) *
+ sblock.fs_bsize;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+
+ }
+ }
+ 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);
+ } else {
+ if (idesc->id_type == DATA && remsize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= remsize;
+ remsize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ break;
+ }
+ }
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+static int
+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];
+ char pathbuf[MAXPATHLEN + 1];
+ struct dinode *dp;
+
+ 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);
+ }
+ } else {
+ if (idesc->id_type == DATA && isize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= isize;
+ isize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ bp->b_flags &= ~B_INUSE;
+ return(STOP);
+ }
+ }
+ }
+ 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.
+ */
+int
+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++);
+}
+
+void
+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);
+}
+
+void
+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.
+ */
+void
+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;
+ if (inumber == ROOTINO)
+ inp->i_parent = ROOTINO;
+ else
+ 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.
+ */
+void
+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;
+}
+
+void
+inodirty()
+{
+ dirty(pbp);
+}
+
+void
+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();
+ }
+}
+
+int
+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);
+}
+
+int
+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);
+}
+
+void
+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.tv_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+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.tv_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
+ */
+void
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ struct dinode *dp;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
new file mode 100644
index 0000000..363bba7
--- /dev/null
+++ b/sbin/fsck_ffs/main.c
@@ -0,0 +1,342 @@
+/*
+ * 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 const 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 const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include "fsck.h"
+static int argtoi __P((int flag, char *req, char *str, int base));
+static int docheck __P((struct fstab *fsp));
+static int checkfilesys __P((char *filesys, char *mntpt, long auxdata,
+ int child));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern char *optarg;
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb: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 'f':
+ fflag++;
+ 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);
+}
+
+int
+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.
+ */
+int
+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 */
+int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ 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);
+ }
+
+ if (preen && sblock.fs_clean && !fflag) {
+ pwarn("clean, %ld free ", sblock.fs_cstotal.cs_nffree +
+ sblock.fs_frag * sblock.fs_cstotal.cs_nbfree);
+ printf("(%ld frags, %ld blocks, %.1f%% fragmentation)\n",
+ sblock.fs_cstotal.cs_nffree,
+ sblock.fs_cstotal.cs_nbfree,
+ (float)(sblock.fs_cstotal.cs_nffree * 100) /
+ sblock.fs_dsize);
+ 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, %ld.%ld%% 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 (rerun)
+ printf("\n***** PLEASE RERUN FSCK *****\n");
+ if (hotroot) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_UFS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
new file mode 100644
index 0000000..3fec63a
--- /dev/null
+++ b/sbin/fsck_ffs/pass1.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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *idesc));
+
+void
+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();
+}
+
+void
+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;
+
+ 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) {
+ symbuf = alloca(secsize);
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)secsize) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %ld(%s) of size %ld\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) || dp->di_blocks == 0) {
+ 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();
+ }
+}
+
+int
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c
new file mode 100644
index 0000000..1450bd8
--- /dev/null
+++ b/sbin/fsck_ffs/pass1b.c
@@ -0,0 +1,101 @@
+/*
+ * 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 const 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;
+
+void
+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;
+ }
+ }
+}
+
+int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
new file mode 100644
index 0000000..382207e
--- /dev/null
+++ b/sbin/fsck_ffs/pass2.c
@@ -0,0 +1,434 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+void
+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();
+}
+
+int
+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.
+ */
+int
+blksort(inpp1, inpp2)
+ struct inoinfo **inpp1, **inpp2;
+{
+
+ return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
new file mode 100644
index 0000000..963c41a
--- /dev/null
+++ b/sbin/fsck_ffs/pass3.c
@@ -0,0 +1,72 @@
+/*
+ * 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 const 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"
+
+void
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
new file mode 100644
index 0000000..df8f722
--- /dev/null
+++ b/sbin/fsck_ffs/pass4.c
@@ -0,0 +1,135 @@
+/*
+ * 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 const 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();
+
+void
+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);
+ }
+ }
+}
+
+int
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c
new file mode 100644
index 0000000..a6ed6a1
--- /dev/null
+++ b/sbin/fsck_ffs/pass5.c
@@ -0,0 +1,338 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <string.h>
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0;
+ 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;
+ sbdirty();
+ }
+ if (fs->fs_fmod != 0) {
+ pwarn("MODIFIED FLAG SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+ }
+ if (fs->fs_clean == 0) {
+ pwarn("CLEAN FLAG NOT SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_clean = 1;
+ sbdirty();
+ }
+ }
+}
diff --git a/sbin/fsck_ffs/preen.c b/sbin/fsck_ffs/preen.c
new file mode 100644
index 0000000..b560861
--- /dev/null
+++ b/sbin/fsck_ffs/preen.c
@@ -0,0 +1,383 @@
+/*
+ * 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 const 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 <ufs/ufs/dinode.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fstab.h>
+#include "fsck.h"
+
+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;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static int startdisk __P((struct disk *dk, int (*checkit)()));
+static struct disk *finddisk __P((char *name));
+static char *unrawname __P((char *name));
+static char *rawname __P((char *name));
+
+int nrun, ndisks;
+char hotroot;
+
+int
+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 || (passno == 1 && fsp->fs_passno == 1)) {
+ name = blockcheck(fsp->fs_spec);
+ if (name) {
+ sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0);
+ if (sumstatus)
+ 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)) != 0 &&
+ 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)) != 0
+ && 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)) != 0
+ && 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 = 0;
+
+ 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);
+}
+
+void
+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;
+}
+
+int
+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;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ 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;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(name) - 1;
+ if (l > 0 && name[l] == '/')
+ /* remove trailing slash */
+ name[l] = '\0';
+ if(!(fsinfo=getfsfile(name))) {
+ printf("Can't resolve %s to character special device",
+ name);
+ return (0);
+ }
+ name = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ printf("Warning: Can't find blockdevice corresponding to name %s\n",
+ name);
+ return (name);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
new file mode 100644
index 0000000..5fb5697
--- /dev/null
+++ b/sbin/fsck_ffs/setup.c
@@ -0,0 +1,478 @@
+/*
+ * 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 const 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 <stdio.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)
+
+static int readsb __P((int listerr));
+static void badsb __P((int listerr, char *s));
+static int calcsb __P((char *dev, int devfd, struct fs *fs));
+static struct disklabel * getdisklabel __P((char *s, int fd));
+
+
+int
+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");
+ lp = getdisklabel((char *)NULL, fsreadfd);
+ if (lp)
+ 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).");
+ bflag = 0;
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ bflag = 0;
+ }
+ 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.
+ */
+static int
+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);
+}
+
+static void
+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!
+ */
+int
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = index(dev, '\0') - 1;
+ if (cp == (char *)-1 || ((*cp < 'a' || *cp > 'h') && !isdigit(*cp))) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ bzero((char *)fs, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_cpg = pp->p_cpg;
+ fs->fs_size = pp->p_size;
+ fs->fs_ntrak = lp->d_ntracks;
+ fs->fs_nsect = lp->d_nsectors;
+ fs->fs_spc = lp->d_secpercyl;
+ fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ fs->fs_cgmask = 0xffffffff;
+ for (i = fs->fs_ntrak; i > 1; i >>= 1)
+ fs->fs_cgmask <<= 1;
+ if (!POWEROF2(fs->fs_ntrak))
+ fs->fs_cgmask <<= 1;
+ fs->fs_cgoffset = roundup(
+ howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+ fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+ fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+ for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c
new file mode 100644
index 0000000..d0c91fa
--- /dev/null
+++ b/sbin/fsck_ffs/utilities.c
@@ -0,0 +1,595 @@
+/*
+ * 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 const 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 <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, daddr_t blk));
+
+int
+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);
+ }
+}
+
+int
+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.
+ */
+void
+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;
+ }
+}
+
+void
+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);
+ }
+}
+
+void
+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");
+}
+
+void
+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);
+}
+
+int
+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);
+}
+
+void
+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
+ */
+int
+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
+ */
+void
+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
+ */
+void
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+int
+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);
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+void
+errexit(const char *s1, ...)
+{
+ va_list ap;
+ va_start(ap,s1);
+ vfprintf(stdout, s1, ap);
+ va_end(ap);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+void
+pfatal(const char *s, ...)
+{
+
+ va_list ap;
+ va_start(ap,s);
+ if (preen) {
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+void
+pwarn(const char *s, ...)
+{
+ va_list ap;
+ va_start(ap,s);
+ if (preen)
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+void
+#ifdef __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+#endif
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(fmt);
+}
+#endif
diff --git a/sbin/fsck_ifs/Makefile b/sbin/fsck_ifs/Makefile
new file mode 100644
index 0000000..e31e992
--- /dev/null
+++ b/sbin/fsck_ifs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= fsck
+MAN8= fsck.8
+SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_ifs/dir.c b/sbin/fsck_ifs/dir.c
new file mode 100644
index 0000000..9a2b923
--- /dev/null
+++ b/sbin/fsck_ifs/dir.c
@@ -0,0 +1,720 @@
+/*
+ * 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 const 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 <stdio.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, ".."
+};
+
+
+static int chgino __P((struct inodesc *idesc));
+static int dircheck __P((struct inodesc *idesc, struct direct *dp));
+static int expanddir __P((struct dinode *dp, char *name));
+static void freedir __P((ino_t ino, ino_t parent));
+static struct direct * fsck_readdir __P((struct inodesc *idesc));
+static struct bufarea * getdirblk __P((daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *idesc));
+
+/*
+ * Propagate connected state through the tree.
+ */
+void
+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.
+ */
+int
+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.
+ */
+int
+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);
+}
+
+void
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+void
+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);
+}
+
+void
+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();
+ }
+ }
+}
+
+int
+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];
+ dirp->d_namlen = newent.d_namlen;
+ } else {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ dirp->d_type = newent.d_namlen;
+ dirp->d_namlen = 0;
+# else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+# endif
+ }
+ dirp->d_reclen = newent.d_reclen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+int
+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);
+}
+
+int
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+
+ 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);
+ /*
+ * The parent directory, because of the ordering
+ * guarantees, has had the link count incremented
+ * for the child, but no entry was made. This
+ * fixes the parent link count so that fsck does
+ * not need to be rerun.
+ */
+ lncntp[parentdir]++;
+
+ }
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+int
+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
+ */
+int
+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
+ */
+int
+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
+ */
+ino_t
+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
+ */
+static void
+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.
+ */
+int
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/sbin/fsck_ifs/fsck.h b/sbin/fsck_ifs/fsck.h
new file mode 100644
index 0000000..57cb17a
--- /dev/null
+++ b/sbin/fsck_ifs/fsck.h
@@ -0,0 +1,293 @@
+/*
+ * 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 fflag; /* force fs check (ignore clean flag) */
+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 */
+int returntosingle; /* return to single user mode */
+int rerun; /* rerun fsck. Only used in non-preen mode */
+
+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 */
+unsigned 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
+
+/* dir.c */
+void adjust __P((struct inodesc *idesc, short lcnt));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *idesc));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void propagate __P((void));
+
+/* ffs_subr.c */
+void ffs_fragacct __P((struct fs *fs, int fragmap, long *fraglist, int cnt));
+
+/* inode.c */
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, daddr_t blk));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+int chkrange __P((daddr_t blk, int cnt));
+int ckinode __P((struct dinode *dp, struct inodesc *idesc));
+void clri __P((struct inodesc *idesc, char *type, int flag));
+int findino __P((struct inodesc *idesc));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+struct dinode * ginode __P((ino_t inumber));
+struct inoinfo * getinoinfo __P((ino_t inumber));
+struct dinode * getnextinode __P((ino_t inumber));
+void inodirty __P((void));
+void inocleanup __P((void));
+void pinode __P((ino_t ino));
+void resetinodebuf __P((void));
+int findname __P((struct inodesc *idesc));
+
+/* pass1.c */
+void pass1 __P((void));
+int pass1check __P((struct inodesc *idesc));
+
+/* pass1b.c */
+void pass1b __P((void));
+
+/* pass2.c */
+void pass2 __P((void));
+
+/* pass3.c */
+void pass3 __P((void));
+
+/* pass4.c */
+void pass4 __P((void));
+int pass4check __P((struct inodesc *idesc));
+
+/* pass5.c */
+void pass5 __P((void));
+
+/* preen.c */
+char *blockcheck __P((char *name));
+int checkfstab __P((int preen, int maxrun,int (*docheck)(), int (*chkit)()));
+
+/* setup.c */
+int setup __P((char *dev));
+
+/* utilities.c */
+int allocblk __P((long frags));
+int bread __P((int fd, char *buf, daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, daddr_t blk, long size));
+void catch __P((int));
+void catchquit __P((int));
+void ckfini __P((void));
+int dofix __P((struct inodesc *idesc, char *msg));
+void errexit __P((const char *s1, ...)) __dead2;
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((daddr_t blkno, long frags));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, daddr_t blk, long size));
+struct bufarea * getdatablk __P((daddr_t blkno, long size));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+void panic __P((const char *, ...)) __dead2;
+void pfatal __P((const char *s1, ...));
+void pwarn __P((const char *s1, ...));
+int reply __P((char *question));
+void voidquit __P((int));
diff --git a/sbin/fsck_ifs/fsck_ifs.8 b/sbin/fsck_ifs/fsck_ifs.8
new file mode 100644
index 0000000..c8ae6fa
--- /dev/null
+++ b/sbin/fsck_ifs/fsck_ifs.8
@@ -0,0 +1,298 @@
+.\" 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 f
+.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 clean flag of each filesystem's superblock is examined and only those filesystems that
+are not marked clean are checked. If the
+.Fl f
+option is specified, the filesystems
+will be checked regardless of the state of their clean flag.
+.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
+Holes in directories.
+.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 newfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck_ifs/inode.c b/sbin/fsck_ifs/inode.c
new file mode 100644
index 0000000..5adac75
--- /dev/null
+++ b/sbin/fsck_ifs/inode.c
@@ -0,0 +1,619 @@
+/*
+ * 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 const char sccsid[] = "@(#)inode.c 8.5 (Berkeley) 2/8/95";
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *idesc, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ int ret;
+ long n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+ char pathbuf[MAXPATHLEN + 1];
+
+ 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 || dp->di_blocks == 0)))
+ 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) {
+ if (idesc->id_type == DATA && ndb >= 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size = (ap - &dino.di_db[0]) *
+ sblock.fs_bsize;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+
+ }
+ }
+ 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);
+ } else {
+ if (idesc->id_type == DATA && remsize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= remsize;
+ remsize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ break;
+ }
+ }
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ return (KEEPON);
+}
+
+static int
+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];
+ char pathbuf[MAXPATHLEN + 1];
+ struct dinode *dp;
+
+ 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);
+ }
+ } else {
+ if (idesc->id_type == DATA && isize > 0) {
+ /* An empty block in a directory XXX */
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
+ pathbuf);
+ if (reply("ADJUST LENGTH") == 1) {
+ dp = ginode(idesc->id_number);
+ dp->di_size -= isize;
+ isize = 0;
+ printf(
+ "YOU MUST RERUN FSCK AFTERWARDS\n");
+ rerun = 1;
+ inodirty();
+ bp->b_flags &= ~B_INUSE;
+ return(STOP);
+ }
+ }
+ }
+ 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.
+ */
+int
+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++);
+}
+
+void
+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);
+}
+
+void
+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.
+ */
+void
+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;
+ if (inumber == ROOTINO)
+ inp->i_parent = ROOTINO;
+ else
+ 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.
+ */
+void
+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;
+}
+
+void
+inodirty()
+{
+ dirty(pbp);
+}
+
+void
+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();
+ }
+}
+
+int
+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);
+}
+
+int
+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);
+}
+
+void
+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.tv_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+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.tv_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
+ */
+void
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ struct dinode *dp;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/sbin/fsck_ifs/main.c b/sbin/fsck_ifs/main.c
new file mode 100644
index 0000000..363bba7
--- /dev/null
+++ b/sbin/fsck_ifs/main.c
@@ -0,0 +1,342 @@
+/*
+ * 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 const 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 const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <fstab.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include "fsck.h"
+static int argtoi __P((int flag, char *req, char *str, int base));
+static int docheck __P((struct fstab *fsp));
+static int checkfilesys __P((char *filesys, char *mntpt, long auxdata,
+ int child));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern char *optarg;
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb: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 'f':
+ fflag++;
+ 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);
+}
+
+int
+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.
+ */
+int
+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 */
+int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ 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);
+ }
+
+ if (preen && sblock.fs_clean && !fflag) {
+ pwarn("clean, %ld free ", sblock.fs_cstotal.cs_nffree +
+ sblock.fs_frag * sblock.fs_cstotal.cs_nbfree);
+ printf("(%ld frags, %ld blocks, %.1f%% fragmentation)\n",
+ sblock.fs_cstotal.cs_nffree,
+ sblock.fs_cstotal.cs_nbfree,
+ (float)(sblock.fs_cstotal.cs_nffree * 100) /
+ sblock.fs_dsize);
+ 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, %ld.%ld%% 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 (rerun)
+ printf("\n***** PLEASE RERUN FSCK *****\n");
+ if (hotroot) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_UFS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+ return (0);
+}
diff --git a/sbin/fsck_ifs/pass1.c b/sbin/fsck_ifs/pass1.c
new file mode 100644
index 0000000..3fec63a
--- /dev/null
+++ b/sbin/fsck_ifs/pass1.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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *idesc));
+
+void
+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();
+}
+
+void
+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;
+
+ 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) {
+ symbuf = alloca(secsize);
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)secsize) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %ld(%s) of size %ld\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) || dp->di_blocks == 0) {
+ 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();
+ }
+}
+
+int
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass1b.c b/sbin/fsck_ifs/pass1b.c
new file mode 100644
index 0000000..1450bd8
--- /dev/null
+++ b/sbin/fsck_ifs/pass1b.c
@@ -0,0 +1,101 @@
+/*
+ * 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 const 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;
+
+void
+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;
+ }
+ }
+}
+
+int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass2.c b/sbin/fsck_ifs/pass2.c
new file mode 100644
index 0000000..382207e
--- /dev/null
+++ b/sbin/fsck_ifs/pass2.c
@@ -0,0 +1,434 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+void
+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();
+}
+
+int
+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.
+ */
+int
+blksort(inpp1, inpp2)
+ struct inoinfo **inpp1, **inpp2;
+{
+
+ return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ifs/pass3.c b/sbin/fsck_ifs/pass3.c
new file mode 100644
index 0000000..963c41a
--- /dev/null
+++ b/sbin/fsck_ifs/pass3.c
@@ -0,0 +1,72 @@
+/*
+ * 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 const 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"
+
+void
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/sbin/fsck_ifs/pass4.c b/sbin/fsck_ifs/pass4.c
new file mode 100644
index 0000000..df8f722
--- /dev/null
+++ b/sbin/fsck_ifs/pass4.c
@@ -0,0 +1,135 @@
+/*
+ * 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 const 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();
+
+void
+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);
+ }
+ }
+}
+
+int
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/sbin/fsck_ifs/pass5.c b/sbin/fsck_ifs/pass5.c
new file mode 100644
index 0000000..a6ed6a1
--- /dev/null
+++ b/sbin/fsck_ifs/pass5.c
@@ -0,0 +1,338 @@
+/*
+ * 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 const 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 <stdio.h>
+#include <string.h>
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos = 0;
+ 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;
+ sbdirty();
+ }
+ if (fs->fs_fmod != 0) {
+ pwarn("MODIFIED FLAG SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+ }
+ if (fs->fs_clean == 0) {
+ pwarn("CLEAN FLAG NOT SET IN SUPERBLOCK");
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("FIX") == 1) {
+ fs->fs_clean = 1;
+ sbdirty();
+ }
+ }
+}
diff --git a/sbin/fsck_ifs/preen.c b/sbin/fsck_ifs/preen.c
new file mode 100644
index 0000000..b560861
--- /dev/null
+++ b/sbin/fsck_ifs/preen.c
@@ -0,0 +1,383 @@
+/*
+ * 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 const 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 <ufs/ufs/dinode.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fstab.h>
+#include "fsck.h"
+
+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;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static int startdisk __P((struct disk *dk, int (*checkit)()));
+static struct disk *finddisk __P((char *name));
+static char *unrawname __P((char *name));
+static char *rawname __P((char *name));
+
+int nrun, ndisks;
+char hotroot;
+
+int
+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 || (passno == 1 && fsp->fs_passno == 1)) {
+ name = blockcheck(fsp->fs_spec);
+ if (name) {
+ sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0);
+ if (sumstatus)
+ 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)) != 0 &&
+ 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)) != 0
+ && 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)) != 0
+ && 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 = 0;
+
+ 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);
+}
+
+void
+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;
+}
+
+int
+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;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ 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;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(name) - 1;
+ if (l > 0 && name[l] == '/')
+ /* remove trailing slash */
+ name[l] = '\0';
+ if(!(fsinfo=getfsfile(name))) {
+ printf("Can't resolve %s to character special device",
+ name);
+ return (0);
+ }
+ name = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ printf("Warning: Can't find blockdevice corresponding to name %s\n",
+ name);
+ return (name);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/sbin/fsck_ifs/setup.c b/sbin/fsck_ifs/setup.c
new file mode 100644
index 0000000..5fb5697
--- /dev/null
+++ b/sbin/fsck_ifs/setup.c
@@ -0,0 +1,478 @@
+/*
+ * 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 const 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 <stdio.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)
+
+static int readsb __P((int listerr));
+static void badsb __P((int listerr, char *s));
+static int calcsb __P((char *dev, int devfd, struct fs *fs));
+static struct disklabel * getdisklabel __P((char *s, int fd));
+
+
+int
+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");
+ lp = getdisklabel((char *)NULL, fsreadfd);
+ if (lp)
+ 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).");
+ bflag = 0;
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ bflag = 0;
+ }
+ 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.
+ */
+static int
+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);
+}
+
+static void
+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!
+ */
+int
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = index(dev, '\0') - 1;
+ if (cp == (char *)-1 || ((*cp < 'a' || *cp > 'h') && !isdigit(*cp))) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ bzero((char *)fs, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_cpg = pp->p_cpg;
+ fs->fs_size = pp->p_size;
+ fs->fs_ntrak = lp->d_ntracks;
+ fs->fs_nsect = lp->d_nsectors;
+ fs->fs_spc = lp->d_secpercyl;
+ fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ fs->fs_cgmask = 0xffffffff;
+ for (i = fs->fs_ntrak; i > 1; i >>= 1)
+ fs->fs_cgmask <<= 1;
+ if (!POWEROF2(fs->fs_ntrak))
+ fs->fs_cgmask <<= 1;
+ fs->fs_cgoffset = roundup(
+ howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+ fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+ fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+ for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ifs/utilities.c b/sbin/fsck_ifs/utilities.c
new file mode 100644
index 0000000..d0c91fa
--- /dev/null
+++ b/sbin/fsck_ifs/utilities.c
@@ -0,0 +1,595 @@
+/*
+ * 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 const 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 <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, daddr_t blk));
+
+int
+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);
+ }
+}
+
+int
+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.
+ */
+void
+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;
+ }
+}
+
+void
+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);
+ }
+}
+
+void
+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");
+}
+
+void
+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);
+}
+
+int
+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);
+}
+
+void
+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
+ */
+int
+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
+ */
+void
+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
+ */
+void
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+ 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(x)
+ int x;
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+int
+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);
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+void
+errexit(const char *s1, ...)
+{
+ va_list ap;
+ va_start(ap,s1);
+ vfprintf(stdout, s1, ap);
+ va_end(ap);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+void
+pfatal(const char *s, ...)
+{
+
+ va_list ap;
+ va_start(ap,s);
+ if (preen) {
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+void
+pwarn(const char *s, ...)
+{
+ va_list ap;
+ va_start(ap,s);
+ if (preen)
+ printf("%s: ", cdevname);
+ vfprintf(stdout, s, ap);
+ va_end(ap);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+void
+#ifdef __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+#endif
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(fmt);
+}
+#endif
diff --git a/sbin/fsdb/Makefile b/sbin/fsdb/Makefile
new file mode 100644
index 0000000..5fa4d66
--- /dev/null
+++ b/sbin/fsdb/Makefile
@@ -0,0 +1,15 @@
+# $NetBSD: Makefile,v 1.1.1.1 1995/10/08 23:08:36 thorpej Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id$
+
+PROG= fsdb
+MAN8= fsdb.8
+SRCS= fsdb.c fsdbutil.c \
+ dir.c inode.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
+CFLAGS+= -I${.CURDIR}/../fsck
+LDADD+= -ledit -ltermcap
+DPADD+= ${LIBEDIT} ${LIBTERMCAP}
+.PATH: ${.CURDIR}/../fsck ${.CURDIR}/../../sys/ufs/ffs
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsdb/fsdb.8 b/sbin/fsdb/fsdb.8
new file mode 100644
index 0000000..a55a56f
--- /dev/null
+++ b/sbin/fsdb/fsdb.8
@@ -0,0 +1,235 @@
+.\" $NetBSD: fsdb.8,v 1.2 1995/10/08 23:18:08 thorpej Exp $
+.\"
+.\" Copyright (c) 1995 John T. Kohl
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd September 14, 1995
+.Dt FSDB 8
+.Os FreeBSD
+.Sh NAME
+.Nm fsdb
+.Nd FFS debugging/editing tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f
+.Ar fsname
+.Sh DESCRIPTION
+.Nm
+opens
+.Ar fsname
+(usually a raw disk partition) and runs a command loop
+allowing manipulation of the file system's inode data. You are prompted
+to enter a command with
+.Ic "fsdb (inum X)>"
+where
+.Va X
+is the currently selected i-number. The initial selected inode is the
+root of the filesystem (i-number 2).
+The command processor uses the
+.Xr libedit 3
+library, so you can use command line editing to reduce typing if desired.
+When you exit the command loop, the file system superblock is marked
+dirty and any buffered blocks are written to the file system.
+.Pp
+The
+.Fl d
+option enables additional debugging output (which comes primarily from
+.Xr fsck 8 -derived
+code).
+.Pp
+The
+.Fl f
+option is left for historical reasons and has no meaning.
+.Sh COMMANDS
+Besides the built-in
+.Xr libedit 3
+commands,
+.Nm
+supports these commands:
+.Pp
+.Bl -tag -width indent -compact
+.It Cm help
+Print out the list of accepted commands.
+.Pp
+.It Cm inode Ar i-number
+Select inode
+.Ar i-number
+as the new current inode.
+.Pp
+.It Cm back
+Revert to the previously current inode.
+.Pp
+.It Cm clri
+Clear the current inode.
+.Pp
+.It Cm lookup Ar name
+.It Cm cd Ar name
+Find
+.Ar name
+in the current directory and make its inode the current inode.
+.Ar Name
+may be a multi-component name or may begin with slash to indicate that
+the root inode should be used to start the lookup. If some component
+along the pathname is not found, the last valid directory encountered is
+left as the active inode.
+.br
+This command is valid only if the starting inode is a directory.
+.Pp
+.It Cm active
+.It Cm print
+Print out the active inode.
+.Pp
+.It Cm uplink
+Increment the active inode's link count.
+.Pp
+.It Cm downlink
+Decrement the active inode's link count.
+.Pp
+.It Cm linkcount Ar number
+Set the active inode's link count to
+.Ar number .
+.Pp
+.It Cm ls
+List the current inode's directory entries. This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm rm Ar name
+.It Cm del Ar name
+Remove the entry
+.Ar name
+from the current directory inode. This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm ln Ar ino Ar name
+Create a link to inode
+.Ar ino
+under the name
+.Ar name
+in the current directory inode. This command is valid only
+if the current inode is a directory.
+.Pp
+.It Cm chinum Ar dirslot Ar inum
+Change the i-number in directory entry
+.Ar dirslot
+to
+.Ar inum .
+.Pp
+.It Cm chname Ar dirslot Ar name
+Change the name in directory entry
+.Ar dirslot
+to
+.Ar name .
+This command cannot expand a directory entry. You can only rename an
+entry if the name will fit into the existing directory slot.
+.Pp
+.It Cm chtype Ar type
+Change the type of the current inode to
+.Ar type .
+.Ar type
+may be one of:
+.Em file ,
+.Em dir ,
+.Em socket ,
+or
+.Em fifo .
+.Pp
+.It Cm chmod Ar mode
+Change the mode bits of the current inode to
+.Ar mode .
+You cannot change the file type with this subcommand; use
+.Ic chtype
+to do that.
+.Pp
+.It Cm chflags Ar flags
+Change the file flags of the current inode to
+.Ar flags .
+.Pp
+.It Cm chown Ar uid
+Change the owner of the current inode to
+.Ar uid .
+.Pp
+.It Cm chgrp Ar gid
+Change the group of the current inode to
+.Ar gid .
+.Pp
+.It Cm chgen Ar gen
+Change the generation number of the current inode to
+.Ar gen .
+.Pp
+.It Cm mtime Ar time
+.It Cm ctime Ar time
+.It Cm atime Ar time
+Change the modification, change, or access time (respectively) on the
+current inode to
+.Ar time .
+.Ar Time
+should be in the format
+.Em YYYYMMDDHHMMSS[.nsec]
+where
+.Em nsec
+is an optional nanosecond specification. If no nanoseconds are specified, the
+.Va mtimensec ,
+.Va ctimensec ,
+or
+.Va atimensec
+field will be set to zero.
+.Pp
+.It Cm quit, Cm q, Cm exit, Em <EOF>
+Exit the program.
+.El
+.Sh SEE ALSO
+.Xr libedit 3 ,
+.Xr fs 5 ,
+.Xr fsck 8 ,
+.Xr clri 8 .
+.Sh BUGS
+Manipulation of ``short'' symlinks doesn't work (in particular, don't
+try changing a symlink's type).
+.br
+You must specify modes as numbers rather than symbolic names.
+.br
+There are a bunch of other things that you might want to do which
+.Nm
+doesn't implement.
+.br
+The
+.Xr libedit 3
+reference page is not yet written.
+.Sh HISTORY
+.Nm
+uses the source code for
+.Xr fsck 8
+to implement most of the file system manipulation code. The remainder of
+.Nm
+first appeared in NetBSD, written by John T. Kohl.
+.br
+Peter Wemm ported it to FreeBSD.
+.Sh WARNING
+Use this tool with extreme caution--you can damage an FFS file system
+beyond what
+.Xr fsck 8
+can repair.
diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c
new file mode 100644
index 0000000..3993581
--- /dev/null
+++ b/sbin/fsdb/fsdb.c
@@ -0,0 +1,895 @@
+/* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (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 rcsid[] = "$NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <histedit.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include "fsdb.h"
+#include "fsck.h"
+
+extern char *__progname; /* from crt0.o */
+
+void usage __P((void));
+int cmdloop __P((void));
+
+void
+usage()
+{
+ errx(1, "usage: %s [-d] -f <fsname>", __progname);
+}
+
+int returntosingle = 0;
+
+/*
+ * We suck in lots of fsck code, and just pick & choose the stuff we want.
+ *
+ * fsreadfd is set up to read from the file system, fswritefd to write to
+ * the file system.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, rval;
+ char *fsys = NULL;
+ struct stat stb;
+
+ while (-1 != (ch = getopt(argc, argv, "f:d"))) {
+ switch (ch) {
+ case 'f':
+ fsys = optarg;
+ break;
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (fsys == NULL) {
+ if (argc != 1)
+ usage();
+ else
+ fsys = argv[0];
+ }
+
+ if (!setup(fsys))
+ errx(1, "cannot set up file system `%s'", fsys);
+ printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
+ sblock.fs_fsmnt);
+ rval = cmdloop();
+ sblock.fs_clean = 0; /* mark it dirty */
+ sbdirty();
+ ckfini();
+ printf("*** FILE SYSTEM MARKED DIRTY\n");
+ printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
+ printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
+ exit(rval);
+}
+
+#define CMDFUNC(func) int func __P((int argc, char *argv[]))
+#define CMDFUNCSTART(func) int func(argc, argv) \
+ int argc; \
+ char *argv[];
+
+CMDFUNC(helpfn);
+CMDFUNC(focus); /* focus on inode */
+CMDFUNC(active); /* print active inode */
+CMDFUNC(focusname); /* focus by name */
+CMDFUNC(zapi); /* clear inode */
+CMDFUNC(uplink); /* incr link */
+CMDFUNC(downlink); /* decr link */
+CMDFUNC(linkcount); /* set link count */
+CMDFUNC(quit); /* quit */
+CMDFUNC(ls); /* list directory */
+CMDFUNC(rm); /* remove name */
+CMDFUNC(ln); /* add name */
+CMDFUNC(newtype); /* change type */
+CMDFUNC(chmode); /* change mode */
+CMDFUNC(chlen); /* change length */
+CMDFUNC(chaflags); /* change flags */
+CMDFUNC(chgen); /* change generation */
+CMDFUNC(chowner); /* change owner */
+CMDFUNC(chgroup); /* Change group */
+CMDFUNC(back); /* pop back to last ino */
+CMDFUNC(chmtime); /* Change mtime */
+CMDFUNC(chctime); /* Change ctime */
+CMDFUNC(chatime); /* Change atime */
+CMDFUNC(chinum); /* Change inode # of dirent */
+CMDFUNC(chname); /* Change dirname of dirent */
+
+struct cmdtable cmds[] = {
+ { "help", "Print out help", 1, 1, helpfn },
+ { "?", "Print out help", 1, 1, helpfn },
+ { "inode", "Set active inode to INUM", 2, 2, focus },
+ { "clri", "Clear inode INUM", 2, 2, zapi },
+ { "lookup", "Set active inode by looking up NAME", 2, 2, focusname },
+ { "cd", "Set active inode by looking up NAME", 2, 2, focusname },
+ { "back", "Go to previous active inode", 1, 1, back },
+ { "active", "Print active inode", 1, 1, active },
+ { "print", "Print active inode", 1, 1, active },
+ { "uplink", "Increment link count", 1, 1, uplink },
+ { "downlink", "Decrement link count", 1, 1, downlink },
+ { "linkcount", "Set link count to COUNT", 2, 2, linkcount },
+ { "ls", "List current inode as directory", 1, 1, ls },
+ { "rm", "Remove NAME from current inode directory", 2, 2, rm },
+ { "del", "Remove NAME from current inode directory", 2, 2, rm },
+ { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln },
+ { "chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum },
+ { "chname", "Change dir entry number INDEX to NAME", 3, 3, chname },
+ { "chtype", "Change type of current inode to TYPE", 2, 2, newtype },
+ { "chmod", "Change mode of current inode to MODE", 2, 2, chmode },
+ { "chlen", "Change length of current inode to LENGTH", 2, 2, chlen },
+ { "chown", "Change owner of current inode to OWNER", 2, 2, chowner },
+ { "chgrp", "Change group of current inode to GROUP", 2, 2, chgroup },
+ { "chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags },
+ { "chgen", "Change generation number of current inode to GEN", 2, 2, chgen },
+ { "mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime },
+ { "ctime", "Change ctime of current inode to CTIME", 2, 2, chctime },
+ { "atime", "Change atime of current inode to ATIME", 2, 2, chatime },
+ { "quit", "Exit", 1, 1, quit },
+ { "q", "Exit", 1, 1, quit },
+ { "exit", "Exit", 1, 1, quit },
+ { NULL, 0, 0, 0 },
+};
+
+int
+helpfn(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmdtable *cmdtp;
+
+ printf("Commands are:\n%-10s %5s %5s %s\n",
+ "command", "min argc", "max argc", "what");
+
+ for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
+ printf("%-10s %5u %5u %s\n",
+ cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
+ return 0;
+}
+
+char *
+prompt(el)
+ EditLine *el;
+{
+ static char pstring[64];
+ snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
+ return pstring;
+}
+
+
+int
+cmdloop()
+{
+ char *line;
+ const char *elline;
+ int cmd_argc, rval = 0, known;
+#define scratch known
+ char **cmd_argv;
+ struct cmdtable *cmdp;
+ History *hist;
+ EditLine *elptr;
+
+ curinode = ginode(ROOTINO);
+ curinum = ROOTINO;
+ printactive();
+
+ hist = history_init();
+ history(hist, H_EVENT, 100); /* 100 elt history buffer */
+
+ elptr = el_init(__progname, stdin, stdout);
+ el_set(elptr, EL_EDITOR, "emacs");
+ el_set(elptr, EL_PROMPT, prompt);
+ el_set(elptr, EL_HIST, history, hist);
+ el_source(elptr, NULL);
+
+ while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
+ if (debug)
+ printf("command `%s'\n", line);
+
+ history(hist, H_ENTER, elline);
+
+ line = strdup(elline);
+ cmd_argv = crack(line, &cmd_argc);
+ /*
+ * el_parse returns -1 to signal that it's not been handled
+ * internally.
+ */
+ if (el_parse(elptr, cmd_argc, cmd_argv) != -1)
+ continue;
+ if (cmd_argc) {
+ known = 0;
+ for (cmdp = cmds; cmdp->cmd; cmdp++) {
+ if (!strcmp(cmdp->cmd, cmd_argv[0])) {
+ if (cmd_argc >= cmdp->minargc &&
+ cmd_argc <= cmdp->maxargc)
+ rval = (*cmdp->handler)(cmd_argc, cmd_argv);
+ else
+ rval = argcount(cmdp, cmd_argc, cmd_argv);
+ known = 1;
+ break;
+ }
+ }
+ if (!known)
+ warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
+ } else
+ rval = 0;
+ free(line);
+ if (rval < 0)
+ return rval;
+ if (rval)
+ warnx("rval was %d", rval);
+ }
+ el_end(elptr);
+ history_end(hist);
+ return rval;
+}
+
+struct dinode *curinode;
+ino_t curinum, ocurrent;
+
+#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
+ if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
+ printf("inode %d out of range; range is [%d,%d]\n", \
+ inum, ROOTINO, maxino); \
+ return 1; \
+ }
+
+/*
+ * Focus on given inode number
+ */
+CMDFUNCSTART(focus)
+{
+ ino_t inum;
+ char *cp;
+
+ GETINUM(1,inum);
+ curinode = ginode(inum);
+ ocurrent = curinum;
+ curinum = inum;
+ printactive();
+ return 0;
+}
+
+CMDFUNCSTART(back)
+{
+ curinum = ocurrent;
+ curinode = ginode(curinum);
+ printactive();
+ return 0;
+}
+
+CMDFUNCSTART(zapi)
+{
+ ino_t inum;
+ struct dinode *dp;
+ char *cp;
+
+ GETINUM(1,inum);
+ dp = ginode(inum);
+ clearinode(dp);
+ inodirty();
+ if (curinode) /* re-set after potential change */
+ curinode = ginode(curinum);
+ return 0;
+}
+
+CMDFUNCSTART(active)
+{
+ printactive();
+ return 0;
+}
+
+
+CMDFUNCSTART(quit)
+{
+ return -1;
+}
+
+CMDFUNCSTART(uplink)
+{
+ if (!checkactive())
+ return 1;
+ printf("inode %d link count now %d\n", curinum, ++curinode->di_nlink);
+ inodirty();
+ return 0;
+}
+
+CMDFUNCSTART(downlink)
+{
+ if (!checkactive())
+ return 1;
+ printf("inode %d link count now %d\n", curinum, --curinode->di_nlink);
+ inodirty();
+ return 0;
+}
+
+const char *typename[] = {
+ "unknown",
+ "fifo",
+ "char special",
+ "unregistered #3",
+ "directory",
+ "unregistered #5",
+ "blk special",
+ "unregistered #7",
+ "regular",
+ "unregistered #9",
+ "symlink",
+ "unregistered #11",
+ "socket",
+ "unregistered #13",
+ "whiteout",
+};
+
+int slot;
+
+int
+scannames(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
+ slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
+ dirp->d_namlen, dirp->d_name);
+ return (KEEPON);
+}
+
+CMDFUNCSTART(ls)
+{
+ struct inodesc idesc;
+ checkactivedir(); /* let it go on anyway */
+
+ slot = 0;
+ idesc.id_number = curinum;
+ idesc.id_func = scannames;
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ ckinode(curinode, &idesc);
+ curinode = ginode(curinum);
+
+ return 0;
+}
+
+int findino __P((struct inodesc *idesc)); /* from fsck */
+static int dolookup __P((char *name));
+
+static int
+dolookup(name)
+ char *name;
+{
+ struct inodesc idesc;
+
+ if (!checkactivedir())
+ return 0;
+ idesc.id_number = curinum;
+ idesc.id_func = findino;
+ idesc.id_name = name;
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ if (ckinode(curinode, &idesc) & FOUND) {
+ curinum = idesc.id_parent;
+ curinode = ginode(curinum);
+ printactive();
+ return 1;
+ } else {
+ warnx("name `%s' not found in current inode directory", name);
+ return 0;
+ }
+}
+
+CMDFUNCSTART(focusname)
+{
+ char *p, *val;
+
+ if (!checkactive())
+ return 1;
+
+ ocurrent = curinum;
+
+ if (argv[1][0] == '/') {
+ curinum = ROOTINO;
+ curinode = ginode(ROOTINO);
+ } else {
+ if (!checkactivedir())
+ return 1;
+ }
+ for (p = argv[1]; p != NULL;) {
+ while ((val = strsep(&p, "/")) != NULL && *val == '\0');
+ if (val) {
+ printf("component `%s': ", val);
+ fflush(stdout);
+ if (!dolookup(val)) {
+ curinode = ginode(curinum);
+ return(1);
+ }
+ }
+ }
+ return 0;
+}
+
+CMDFUNCSTART(ln)
+{
+ ino_t inum;
+ struct dinode *dp;
+ int rval;
+ char *cp;
+
+ GETINUM(1,inum);
+
+ if (!checkactivedir())
+ return 1;
+ rval = makeentry(curinum, inum, argv[2]);
+ if (rval)
+ printf("Ino %d entered as `%s'\n", inum, argv[2]);
+ else
+ printf("could not enter name? weird.\n");
+ curinode = ginode(curinum);
+ return rval;
+}
+
+CMDFUNCSTART(rm)
+{
+ int rval;
+
+ if (!checkactivedir())
+ return 1;
+ rval = changeino(curinum, argv[1], 0);
+ if (rval & ALTERED) {
+ printf("Name `%s' removed\n", argv[1]);
+ return 0;
+ } else {
+ printf("could not remove name? weird.\n");
+ return 1;
+ }
+}
+
+long slotcount, desired;
+
+int
+chinumfunc(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (slotcount++ == desired) {
+ dirp->d_ino = idesc->id_parent;
+ return STOP|ALTERED|FOUND;
+ }
+ return KEEPON;
+}
+
+CMDFUNCSTART(chinum)
+{
+ int rval;
+ char *cp;
+ ino_t inum;
+ struct inodesc idesc;
+
+ slotcount = 0;
+ if (!checkactivedir())
+ return 1;
+ GETINUM(2,inum);
+
+ desired = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' || desired < 0) {
+ printf("invalid slot number `%s'\n", argv[1]);
+ return 1;
+ }
+
+ idesc.id_number = curinum;
+ idesc.id_func = chinumfunc;
+ idesc.id_fix = IGNORE;
+ idesc.id_type = DATA;
+ idesc.id_parent = inum; /* XXX convenient hiding place */
+
+ if (ckinode(curinode, &idesc) & FOUND)
+ return 0;
+ else {
+ warnx("no %sth slot in current directory", argv[1]);
+ return 1;
+ }
+}
+
+int
+chnamefunc(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ struct direct testdir;
+
+ if (slotcount++ == desired) {
+ /* will name fit? */
+ testdir.d_namlen = strlen(idesc->id_name);
+ if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
+ dirp->d_namlen = testdir.d_namlen;
+ strcpy(dirp->d_name, idesc->id_name);
+ return STOP|ALTERED|FOUND;
+ } else
+ return STOP|FOUND; /* won't fit, so give up */
+ }
+ return KEEPON;
+}
+
+CMDFUNCSTART(chname)
+{
+ int rval;
+ char *cp;
+ ino_t inum;
+ struct inodesc idesc;
+
+ slotcount = 0;
+ if (!checkactivedir())
+ return 1;
+
+ desired = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0') {
+ printf("invalid slot number `%s'\n", argv[1]);
+ return 1;
+ }
+
+ idesc.id_number = curinum;
+ idesc.id_func = chnamefunc;
+ idesc.id_fix = IGNORE;
+ idesc.id_type = DATA;
+ idesc.id_name = argv[2];
+
+ rval = ckinode(curinode, &idesc);
+ if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
+ return 0;
+ else if (rval & FOUND) {
+ warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
+ return 1;
+ } else {
+ warnx("no %sth slot in current directory", argv[1]);
+ return 1;
+ }
+}
+
+struct typemap {
+ const char *typename;
+ int typebits;
+} typenamemap[] = {
+ {"file", IFREG},
+ {"dir", IFDIR},
+ {"socket", IFSOCK},
+ {"fifo", IFIFO},
+};
+
+CMDFUNCSTART(newtype)
+{
+ int rval = 1;
+ int type;
+ struct typemap *tp;
+
+ if (!checkactive())
+ return 1;
+ type = curinode->di_mode & IFMT;
+ for (tp = typenamemap;
+ tp < &typenamemap[sizeof(typemap)/sizeof(*typemap)];
+ tp++) {
+ if (!strcmp(argv[1], tp->typename)) {
+ printf("setting type to %s\n", tp->typename);
+ type = tp->typebits;
+ break;
+ }
+ }
+ if (tp == &typenamemap[sizeof(typemap)/sizeof(*typemap)]) {
+ warnx("type `%s' not known", argv[1]);
+ warnx("try one of `file', `dir', `socket', `fifo'");
+ return 1;
+ }
+ curinode->di_mode &= ~IFMT;
+ curinode->di_mode |= type;
+ inodirty();
+ printactive();
+ return 0;
+}
+
+CMDFUNCSTART(chlen)
+{
+ int rval = 1;
+ long len;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ len = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' || len < 0) {
+ warnx("bad length `%s'", argv[1]);
+ return 1;
+ }
+
+ curinode->di_size = len;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(chmode)
+{
+ int rval = 1;
+ long modebits;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ modebits = strtol(argv[1], &cp, 8);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad modebits `%s'", argv[1]);
+ return 1;
+ }
+
+ curinode->di_mode &= ~07777;
+ curinode->di_mode |= modebits;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(chaflags)
+{
+ int rval = 1;
+ u_long flags;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ flags = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad flags `%s'", argv[1]);
+ return 1;
+ }
+
+ if (flags > UINT_MAX) {
+ warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
+ return(1);
+ }
+ curinode->di_flags = flags;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(chgen)
+{
+ int rval = 1;
+ long gen;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ gen = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad gen `%s'", argv[1]);
+ return 1;
+ }
+
+ if (gen > INT_MAX || gen < INT_MIN) {
+ warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
+ return(1);
+ }
+ curinode->di_gen = gen;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(linkcount)
+{
+ int rval = 1;
+ int lcnt;
+ char *cp;
+
+ if (!checkactive())
+ return 1;
+
+ lcnt = strtol(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ warnx("bad link count `%s'", argv[1]);
+ return 1;
+ }
+ if (lcnt > USHRT_MAX || lcnt < 0) {
+ warnx("max link count is %d\n", USHRT_MAX);
+ return 1;
+ }
+
+ curinode->di_nlink = lcnt;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(chowner)
+{
+ int rval = 1;
+ unsigned long uid;
+ char *cp;
+ struct passwd *pwd;
+
+ if (!checkactive())
+ return 1;
+
+ uid = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ /* try looking up name */
+ if (pwd = getpwnam(argv[1])) {
+ uid = pwd->pw_uid;
+ } else {
+ warnx("bad uid `%s'", argv[1]);
+ return 1;
+ }
+ }
+
+ curinode->di_uid = uid;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+CMDFUNCSTART(chgroup)
+{
+ int rval = 1;
+ unsigned long gid;
+ char *cp;
+ struct group *grp;
+
+ if (!checkactive())
+ return 1;
+
+ gid = strtoul(argv[1], &cp, 0);
+ if (cp == argv[1] || *cp != '\0' ) {
+ if (grp = getgrnam(argv[1])) {
+ gid = grp->gr_gid;
+ } else {
+ warnx("bad gid `%s'", argv[1]);
+ return 1;
+ }
+ }
+
+ curinode->di_gid = gid;
+ inodirty();
+ printactive();
+ return rval;
+}
+
+int
+dotime(name, rts)
+ char *name;
+ struct timespec *rts;
+{
+ char *p, *val;
+ struct tm t;
+ int32_t sec;
+ int32_t nsec;
+ p = strchr(name, '.');
+ if (p) {
+ *p = '\0';
+ nsec = strtoul(++p, &val, 0);
+ if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
+ warnx("invalid nanoseconds");
+ goto badformat;
+ }
+ } else
+ nsec = 0;
+ if (strlen(name) != 14) {
+badformat:
+ warnx("date format: YYYYMMDDHHMMSS[.nsec]");
+ return 1;
+ }
+
+ for (p = name; *p; p++)
+ if (*p < '0' || *p > '9')
+ goto badformat;
+
+ p = name;
+#define VAL() ((*p++) - '0')
+ t.tm_year = VAL();
+ t.tm_year = VAL() + t.tm_year * 10;
+ t.tm_year = VAL() + t.tm_year * 10;
+ t.tm_year = VAL() + t.tm_year * 10 - 1900;
+ t.tm_mon = VAL();
+ t.tm_mon = VAL() + t.tm_mon * 10 - 1;
+ t.tm_mday = VAL();
+ t.tm_mday = VAL() + t.tm_mday * 10;
+ t.tm_hour = VAL();
+ t.tm_hour = VAL() + t.tm_hour * 10;
+ t.tm_min = VAL();
+ t.tm_min = VAL() + t.tm_min * 10;
+ t.tm_sec = VAL();
+ t.tm_sec = VAL() + t.tm_sec * 10;
+ t.tm_isdst = -1;
+
+ sec = mktime(&t);
+ if (sec == -1) {
+ warnx("date/time out of range");
+ return 1;
+ }
+ rts->tv_sec = sec;
+ rts->tv_nsec = nsec;
+ return 0;
+}
+
+CMDFUNCSTART(chmtime)
+{
+ if (dotime(argv[1], &curinode->di_ctime))
+ return 1;
+ inodirty();
+ printactive();
+ return 0;
+}
+
+CMDFUNCSTART(chatime)
+{
+ if (dotime(argv[1], &curinode->di_ctime))
+ return 1;
+ inodirty();
+ printactive();
+ return 0;
+}
+
+CMDFUNCSTART(chctime)
+{
+ if (dotime(argv[1], &curinode->di_ctime))
+ return 1;
+ inodirty();
+ printactive();
+ return 0;
+}
diff --git a/sbin/fsdb/fsdb.h b/sbin/fsdb/fsdb.h
new file mode 100644
index 0000000..af51907
--- /dev/null
+++ b/sbin/fsdb/fsdb.h
@@ -0,0 +1,56 @@
+/* $NetBSD: fsdb.h,v 1.2 1995/10/08 23:18:11 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (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 int bread __P((int fd, char *buf, daddr_t blk, long size));
+extern void bwrite __P((int fd, char *buf, daddr_t blk, long size));
+extern void rwerror __P((char *mesg, daddr_t blk));
+extern int reply __P((char *question));
+
+extern long dev_bsize;
+extern long secsize;
+extern int fsmodified;
+extern int fsfd;
+
+struct cmdtable {
+ const char *cmd;
+ const char *helptxt;
+ unsigned int minargc;
+ unsigned int maxargc;
+ int (*handler) __P((int argc, char *argv[]));
+};
+extern struct dinode *curinode;
+extern ino_t curinum;
+
+int argcount __P((struct cmdtable *cmdp, int argc, char *argv[]));
+char **crack __P((char *line, int *argc));
+void printstat __P((const char *cp, ino_t inum, struct dinode *dp));
+int printactive __P((void));
+int checkactive __P((void));
+int checkactivedir __P((void));
diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c
new file mode 100644
index 0000000..2ea7d6c
--- /dev/null
+++ b/sbin/fsdb/fsdbutil.c
@@ -0,0 +1,204 @@
+/* $NetBSD: fsdbutil.c,v 1.2 1995/10/08 23:18:12 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1995 John T. Kohl
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (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 rcsid[] = "$NetBSD: fsdbutil.c,v 1.2 1995/10/08 23:18:12 thorpej Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "fsdb.h"
+#include "fsck.h"
+
+char **
+crack(line, argc)
+ char *line;
+ int *argc;
+{
+ static char *argv[8];
+ int i;
+ char *p, *val;
+ for (p = line, i = 0; p != NULL && i < 8; i++) {
+ while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0')
+ /**/;
+ if (val)
+ argv[i] = val;
+ else
+ break;
+ }
+ *argc = i;
+ return argv;
+}
+
+int
+argcount(cmdp, argc, argv)
+ struct cmdtable *cmdp;
+ int argc;
+ char *argv[];
+{
+ if (cmdp->minargc == cmdp->maxargc)
+ warnx("command `%s' takes %u arguments", cmdp->cmd, cmdp->minargc-1);
+ else
+ warnx("command `%s' takes from %u to %u arguments",
+ cmdp->cmd, cmdp->minargc-1, cmdp->maxargc-1);
+
+ warnx("usage: %s: %s", cmdp->cmd, cmdp->helptxt);
+ return 1;
+}
+
+void
+printstat(cp, inum, dp)
+ const char *cp;
+ ino_t inum;
+ struct dinode *dp;
+{
+ struct group *grp;
+ struct passwd *pw;
+ char *p;
+
+ printf("%s: ", cp);
+ switch (dp->di_mode & IFMT) {
+ case IFDIR:
+ puts("directory");
+ break;
+ case IFREG:
+ puts("regular file");
+ break;
+ case IFBLK:
+ printf("block special (%d,%d)",
+ major(dp->di_rdev), minor(dp->di_rdev));
+ break;
+ case IFCHR:
+ printf("character special (%d,%d)",
+ major(dp->di_rdev), minor(dp->di_rdev));
+ break;
+ case IFLNK:
+ fputs("symlink",stdout);
+ if (dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
+ dp->di_blocks == 0)
+ printf(" to `%.*s'\n", (int) dp->di_size, (char *)dp->di_shortlink);
+ else
+ putchar('\n');
+ break;
+ case IFSOCK:
+ puts("socket");
+ break;
+ case IFIFO:
+ puts("fifo");
+ break;
+ }
+ printf("I=%lu MODE=%o SIZE=%qu", inum, dp->di_mode, dp->di_size);
+ p = ctime(&dp->di_mtime.tv_sec);
+ printf("\n\tMTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ dp->di_mtime.tv_nsec);
+ p = ctime(&dp->di_ctime.tv_sec);
+ printf("\n\tCTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ dp->di_ctime.tv_nsec);
+ p = ctime(&dp->di_atime.tv_sec);
+ printf("\n\tATIME=%15.15s %4.4s [%d nsec]\n", &p[4], &p[20],
+ dp->di_atime.tv_nsec);
+
+ if (pw = getpwuid(dp->di_uid))
+ printf("OWNER=%s ", pw->pw_name);
+ else
+ printf("OWNUID=%u ", dp->di_uid);
+ if (grp = getgrgid(dp->di_gid))
+ printf("GRP=%s ", grp->gr_name);
+ else
+ printf("GID=%u ", dp->di_gid);
+
+ printf("LINKCNT=%hd FLAGS=%#x BLKCNT=%x GEN=%x\n", dp->di_nlink, dp->di_flags,
+ dp->di_blocks, dp->di_gen);
+}
+
+int
+checkactive()
+{
+ if (!curinode) {
+ warnx("no current inode\n");
+ return 0;
+ }
+ return 1;
+}
+
+int
+checkactivedir()
+{
+ if (!curinode) {
+ warnx("no current inode\n");
+ return 0;
+ }
+ if ((curinode->di_mode & IFMT) != IFDIR) {
+ warnx("inode %d not a directory", curinum);
+ return 0;
+ }
+ return 1;
+}
+
+int
+printactive()
+{
+ if (!checkactive())
+ return 1;
+ switch (curinode->di_mode & IFMT) {
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ printstat("current inode", curinum, curinode);
+ break;
+ case 0:
+ printf("current inode %d: unallocated inode\n", curinum);
+ break;
+ default:
+ printf("current inode %d: screwy itype 0%o (mode 0%o)?\n",
+ curinum, curinode->di_mode & IFMT, curinode->di_mode);
+ break;
+ }
+ return 0;
+}
diff --git a/sbin/i386/Makefile b/sbin/i386/Makefile
new file mode 100644
index 0000000..f2d418a
--- /dev/null
+++ b/sbin/i386/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.2 1994/12/02 23:53:23 wollman Exp $
+
+SUBDIR= comcontrol fdisk ft mount_msdos nextboot
+#NOTYET: cxconfig
+
+.include <bsd.subdir.mk>
diff --git a/sbin/i386/Makefile.inc b/sbin/i386/Makefile.inc
new file mode 100644
index 0000000..c961f85
--- /dev/null
+++ b/sbin/i386/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id$
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/sbin/i386/comcontrol/Makefile b/sbin/i386/comcontrol/Makefile
new file mode 100644
index 0000000..abc8d0a
--- /dev/null
+++ b/sbin/i386/comcontrol/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 5.4 (Berkeley) 6/5/91
+
+PROG= comcontrol
+MAN8= comcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/comcontrol/comcontrol.8 b/sbin/i386/comcontrol/comcontrol.8
new file mode 100644
index 0000000..574386b
--- /dev/null
+++ b/sbin/i386/comcontrol/comcontrol.8
@@ -0,0 +1,63 @@
+.Dd May 15, 1994
+.Dt COMCONTROL 8
+.Os FreeBSD
+.Sh NAME
+.Nm comcontrol
+.Nd control an sio device.
+.Sh SYNOPSIS
+.Nm comcontrol
+.Ar sio_special_device
+.Op options
+.Sh DESCRIPTION
+.Nm Comcontrol
+is used to examine and modify some of the special characteristics
+of the specified sio device.
+If no arguments other than the device are specified,
+it prints the settings of all controllable characteristics.
+This usage requires only read access on the device.
+Only the superuser can change the settings.
+.Pp
+The following options are available:
+.Bl -tag -width Fl
+.It Cm dtrwait Ar number
+Set the time to wait after dropping DTR
+to the given number.
+The units are hundredths of a second.
+The default is 300 hundredths, i.e., 3 seconds.
+This option needed mainly to set proper recover time after
+modem reset.
+.El
+.Bl -tag -width Fl
+.It Cm drainwait Ar number
+Set the time to wait for output drain
+to the given number.
+The units are seconds.
+The default is 0, i.e. wait forever.
+This option needed mainly to specify upper limit of minutes
+to prevent modem hanging.
+.El
+.Pp
+The standard way to use
+.Nm comcontrol
+is to put invocations of it in the
+.Ar /etc/rc.serial
+startup script.
+.Sh SEE ALSO
+.Xr sio 4 ,
+.Xr stty 1 .
+.Sh FILES
+.Bl -tag -width Pa
+.It Pa /dev/ttyd?
+dialin devices, hardwired terminals
+.It Pa /dev/cuaa?
+dialout devices.
+.Sh AUTHOR
+Christopher G. Demetriou
+.Sh BUGS
+.Nm comcontrol
+should be named
+.Nm siocontrol .
+.Sh HISTORY
+Originally part of cgd's com package patches, version 0.2.1, to 386BSD 0.1.
+Once controlled bidirectional capabilities. Little is left to control now
+that these capabilities are standard.
diff --git a/sbin/i386/comcontrol/comcontrol.c b/sbin/i386/comcontrol/comcontrol.c
new file mode 100644
index 0000000..17c2882
--- /dev/null
+++ b/sbin/i386/comcontrol/comcontrol.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1992 Christopher G. Demetriou
+ * 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. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* comcontrol.c */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+
+void usage(char *progname)
+{
+ fprintf(stderr, "usage: %s <filename> [dtrwait <n>] [drainwait <n>]\n", progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int res = 0;
+ int dtrwait = -1, drainwait = -1;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ fd = open(argv[1], O_RDONLY|O_NONBLOCK, 0);
+ if (fd < 0) {
+ perror("open");
+ fprintf(stderr, "%s: couldn't open file %s\n", argv[0], argv[1]);
+ return 1;
+ }
+
+ if (argc == 2) {
+ if (ioctl(fd, TIOCMGDTRWAIT, &dtrwait) < 0) {
+ res = 1;
+ perror("TIOCMGDTRWAIT");
+ }
+ if (ioctl(fd, TIOCGDRAINWAIT, &drainwait) < 0) {
+ res = 1;
+ perror("TIOCGDRAINWAIT");
+ }
+ printf("dtrwait %d drainwait %d\n", dtrwait, drainwait);
+ } else {
+ char *prg = argv[0];
+
+ while (argv[2] != NULL) {
+ if (!strcmp(argv[2],"dtrwait")) {
+ if (dtrwait >= 0)
+ usage(prg);
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage(prg);
+ dtrwait = atoi(argv[3]);
+ argv += 2;
+ } else if (!strcmp(argv[2],"drainwait")) {
+ if (drainwait >= 0)
+ usage(prg);
+ if (argv[3] == NULL || !isdigit(argv[3][0]))
+ usage(prg);
+ drainwait = atoi(argv[3]);
+ argv += 2;
+ } else
+ usage(prg);
+ }
+ if (dtrwait >= 0) {
+ if (ioctl(fd, TIOCMSDTRWAIT, &dtrwait) < 0) {
+ res = 1;
+ perror("TIOCMSDTRWAIT");
+ }
+ }
+ if (drainwait >= 0) {
+ if (ioctl(fd, TIOCSDRAINWAIT, &drainwait) < 0) {
+ res = 1;
+ perror("TIOCSDRAINWAIT");
+ }
+ }
+ }
+
+ close(fd);
+ return res;
+}
diff --git a/sbin/i386/cxconfig/Makefile b/sbin/i386/cxconfig/Makefile
new file mode 100644
index 0000000..59c828d
--- /dev/null
+++ b/sbin/i386/cxconfig/Makefile
@@ -0,0 +1,4 @@
+PROG = cxconfig
+MAN8 = cxconfig.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/cxconfig/cxconfig.8 b/sbin/i386/cxconfig/cxconfig.8
new file mode 100644
index 0000000..9907435
--- /dev/null
+++ b/sbin/i386/cxconfig/cxconfig.8
@@ -0,0 +1,297 @@
+.TH Cronyx-Sigma 1
+.rm ES
+.rm EE
+.de ES
+.PP
+.nf
+.in +0.5i
+..
+.de EE
+.in -0.5i
+.fi
+..
+.na
+.SH NAME
+.B cxconfig
+\- channel options management utility for Cronyx-Sigma adapter
+.SH DESCRIPTION
+.PP
+The \fBcxconfig\fP utility is used for configuring the channel options of
+the Cronyx-Sigma adapter.
+.PP
+To change cha options the channel should be free: the corresponding
+network interface in ``down'' state, the asynchronous terminal device /dev/tty*
+closed.
+Generally, the channel options are set up during the operating
+system startup, for example from the \fI/etc/rc\fP file.
+.PP
+Note, that not all options have a sense for every particular
+case, and an attempt to set some of them can hang up the channel or
+the whole adapter.
+.SH "Usage"
+.IP "cxconfig"
+The brief information about all channels.
+.IP "cxconfig -a"
+The full information about all channels.
+.IP "cxconfig <channel>"
+The brief information about the channel.
+.IP "cxconfig -a <channel>"
+The full information about the channel.
+.IP "cxconfig <channel> <option>..."
+Setting the channel options.
+.SH "Channel options"
+.IP ispeed=#
+Set the receiver baud rate to the number given.
+The maximal value is 256000 bits/sec.
+In the synchronous mode the receiver baud rate is significant
+only when DPLL mode is used.
+.IP ospeed=#
+Set the transmitter baud rate to the number given.
+The maximal value is 256000 bits/sec.
+In the synchronous mode the transmitter baud rate is significant
+only in the case of the internal clock source.
+If receiver and transmitter have equal data rate, then it could
+be set by specifying only the numerical argument.
+.IP async
+Set the asynchronous channel mode.
+.IP "hdlc, bisync, bsc, x.21, x21
+Set the synchronous channel mode: HDLC, Bisync (BSC) or X.21.
+.IP ppp
+Set the link-level protocol: PPP/HDLC. The built-in simplified synchronous PPP
+implementation is used (see RFC-1548, RFC-1549).
+.IP cisco
+Set the link-level protocol: Cisco/HDLC (see RFC-1547).
+This protocol is intended for compatibility with old models of Cisco routers,
+and with early versions of BSD/386 drivers.
+The extensive usage of this protocol is not recommended.
+.IP ext
+Use the external link-level protocol suite (for BSD/386 only).
+.IP "+keepalive, -keepalive"
+Enable the automatic line state control sub-protocol.
+This setting is not significant when the external link-level protocol is used.
+.IP "+autorts, -autorts"
+Enable the automatic RTS signal control.
+When enabled, the RTS signal goes up only when both halves of
+the receiver ring buffer are free and ready for receive,
+and goes down when one or both buffers are busy.
+.IP "port=rs232, port=rs449, port=v35
+Set the zero channel hardware interface type.
+.SH "Common options"
+.IP "nrz, nrzi, manchester
+Set the data line signal encoding.
+In the case of \fINRZ\fP encoding the zero bit is transmitted by the zero signal
+level, the one bit - by the positive signal level.
+In the case of \fINRZI\fP encoding the zero bit is transmitted by the change of
+the signal level, the one bit - by the constant signal level.
+In the case of \fIManchester\fP encoding the zero bit is encoded as 01 value,
+the one bit - as 10 value.
+.IP "+dpll, -dpll"
+Enable the digital phase locked loop mode (DPLL).
+When enabled, the receiver timing clock signal
+is derived from the received data.
+.IP "+lloop, -lloop"
+Set the local loopback mode.
+.IP "+extclock, -extclock"
+Set the timing clock source of synchronous channels. There are
+two possible variants: \fIexternal clock\fP source or \fIinternal clock\fP
+generation.
+.br
+\fIExternal clock\fP mode is the most common method for connecting
+external modem hardware. In this mode the external timing
+signal is received on TXCIN pin of the connector, and it is
+used as a synchronization clock for transmitting data (TXD).
+.br
+In the case of \fIinternal clock\fP mode the transmitted data (TXD)
+are synchronized using the internal on-board timing generator,
+the internally generated timing signal is driven on the TXCOUT
+pin, and the signal on the TXCIN pin is ignored. This mode
+is used for direct terminal-to-terminal communication,
+e.g. for connecting two computers together in a synchronous mode
+via relatively short cable. This method should also be used
+for testing channels with an external loopback connector.
+.IP fifo=#
+FIFO threshold level setup for receiver and transmitter.
+.IP rfifo=#
+Hardware RTS/CTS flow control FIFO threshold setup.
+.IP "+ctsup, -ctsup"
+Enable/disable interrupts on CTS (Clear To Send) signal setup (0 to 1 transition).
+.IP "+ctsdown, -ctsdown"
+Enable/disable interrupts on CTS (Clear To Send) signal clear (1 to 0 transition).
+.IP "+cdup, -cdup"
+Enable/disable interrupts on CD (Carrier Detect) signal setup (0 to 1 transition).
+.IP "+cddown, -cddown"
+Enable/disable interrupts on CD (Carrier Detect) signal clear (1 to 0 transition).
+.IP "+dsrup, -dsrup"
+Enable/disable interrupts on DSR (Data Set Ready) signal setup (0 to 1 transition).
+.IP "+dsrdown, -dsrdown"
+Enable/disable interrupts on DSR (Data Set Ready) signal clear (1 to 0 transition).
+.SH "Asynchronous mode options"
+.IP cs#
+Select character size: 5, 6, 7 or 8 bits.
+.IP "parodd, pareven
+Parity mode: odd or even.
+.IP "+ignpar, -ignpar
+Disable/enable parity detection.
+.IP nopar
+Disable parity bit generation.
+.IP forcepar
+Force parity: even - 0, odd - 1.
+.IP "stopb1, stopb1.5, stopb2
+Use 1 or 1.5 or 2 stop bits per character.
+.IP "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.IP "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.IP "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.IP "+rloop, -rloop"
+Set the remote loopback mode.
+.IP "+etc, -etc"
+Enable the embedded transmit commands mode.
+.IP "+ixon, -ixon"
+Enable the hardware XON/XOFF flow control support.
+.IP "+ixany, -ixany"
+Use the hardware IXANY mode support.
+.IP "+sdt, -sdt"
+Detect the spec. characters SCHR1 and SCHR2 in the receive data.
+.IP "+flowct, -flowct"
+Receive the flow control spec. characters as data.
+.IP "+rdt, -rdt"
+Detect the spec. characters in range SCRL..SCRH in the receive data.
+.IP "+exdt, -exdt"
+Detect the spec. characters SCHR3 and SCHR4 in the receive data.
+.IP "parintr, parnull, parign, pardisc, parffnull
+Action on parity errors:
+.ES
+ Mode Action
+ -----------------------------------------------------
+ parintr Generate the receiver error interrupt
+ parnull Input the NULL character
+ parign Ignore the error, receive as good data
+ pardisc Ignore the character
+ parffnull Input the sequence <0xFF, NULL, character>
+.EE
+.IP "brkintr, brknull, brkdisc
+Line break state action:
+.ES
+ Mode Action
+ ---------------------------------------------------
+ brkintr Generate the receiver error interrupt
+ brknull Input the NULL character
+ brkdisc Ignore the line break state
+.EE
+.IP "+inlcr, -inlcr"
+Translate received NL characters to CR.
+.IP "+icrnl, -icrnl"
+Translate received CR characters to NL.
+.IP "+igncr, -igncr"
+Ignore received CR characters.
+.IP "+ocrnl, -ocrnl"
+Translate transmitted CR characters to NL.
+.IP "+onlcr, -onlcr"
+Translate transmitted NL characters to CR.
+.IP "+fcerr, -fcerr"
+Process (don't process) the characters, received with errors,
+for special character/flow control matching.
+.IP "+lnext, -lnext"
+Enable the LNEXT character option: the character following
+the LNEXT character is not processed for special character/flow
+control matching.
+.IP "+istrip, -istrip"
+Strip input characters to seven bits.
+.IP schr1=#
+The XON flow control character value.
+.IP schr2=#
+The XOFF flow control character value.
+.IP schr3=#
+The SCHR3 spec. character value.
+.IP schr4=#
+The SCHR4 spec. character value.
+.IP "scrl=#, scrh=#
+The spec. character range (inclusive).
+.IP lnext=#
+The LNEXT spec. character value.
+.SH "HDLC mode options"
+.IP if#
+The minimum number of flags transmitted before a frame is started.
+.IP noaddr
+No frame address recognition.
+.IP "addrlen1, addrlen2"
+Address field length: 1 or 2 bytes.
+.IP "addr1, addr2"
+Addressing mode: 4x1 bytes or 2x2 bytes.
+Registers RFAR1..RFAR4 should contain the address to be matched.
+.IP "+clrdet, -clrdet"
+Enable/disable clear detect for X.21 protocol support.
+.IP "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.IP "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.IP "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.IP "+fcs, -fcs"
+Enable/disable the frame checksum generation and checking.
+.IP "crc-16, crc-v.41
+Select the CRC polynomial: CRC-16 (x^16+x^15+x^2+1)
+or CRC V.41 (x^16+x^12+x^5+1).
+.IP "fcs-crc-16, fcs-v.41
+Frame checksum preset: all zeros (CRC-16) or all ones (CRC V.41).
+.IP "+crcinv, -crcinv"
+Invert (ie. CRC V.41) or don't invert (ie. CRC-16) the transmitted frame checksum.
+.IP "+fcsapd, -fcsapd"
+Pass the received CRC to the host at the end of receiver data buffer.
+.IP "idlemark, idleflag
+Idle mode: idle in mark (transmit all ones) or idle in flag (transmit flag).
+.IP "+syn, -syn"
+Enable/disable sending pad characters before sending flag when coming out
+of the idle mode.
+.IP pad#
+The number of synchronous characters sent (0..4).
+.IP "syn=0xaa, syn=0x00
+Send sync pattern.
+.IP "rfar1=#, rfar2=#, rfar3=#, rfar4=#
+Frame address registers for address recognition.
+.SH EXAMPLES
+.PP
+Set up the channel 7 of the adapter Sigma-400 under FreeBSD.
+Physical 4-wire leased line with Zelax+ M115 short-range modems.
+Synchronous mode, 128000 bits/sec, interface RS-232,
+protocol PPP/HDLC without keepalive support, NRZI encoding,
+DPLL mode, no flow control:
+.ES
+cxconfig cx7 128000 hdlc ppp -keepalive nrzi -cts +dpll -extclock
+ifconfig cx7 158.250.244.2 158.250.244.1 up
+.EE
+.PP
+Set up the channel 0 of the adapter Sigma-100 under FreeBSD.
+Attachment to the near computer by short cable, internal clock source.
+Synchronous mode, 256000 bits/sec, interface RS-232,
+protocol Cisco/HDLC with keepalive support:
+.ES
+cxconfig cx0 hdlc 256000 cisco +keepalive -extclock
+ifconfig cx0 200.1.1.1 200.1.1.2 up
+.EE
+.PP
+Set up the channel 1 of the adapter Sigma-840 under BSD/386.
+Synchronous 64 kbit/sec leased line, external clock source.
+Synchronous mode, interface V.35, external protocol suite:
+.ES
+cxconfig cx1 hdlc ext
+ifconfig cx1 193.124.254.50 193.124.254.49 multicast up
+.EE
+.PP
+Set up the channel 0 of the adapter Sigma-840 under FreeBSD.
+Attachment to the Cisco-4000 router by null-modem cable, internal clock source.
+Synchronous mode, 64000 bits/sec, interface RS-232,
+protocol PPP/HDLC with keepalive support and flow control,
+LCP and IPCP protocols (see RFC-1548 and RFC-1332) debug tracing enabled:
+.ES
+cxconfig cx0 hdlc 64000 port=rs232 ppp +keepalive -extclock +cts
+ifconfig cx0 100.0.0.2 100.0.0.1 debug up
+.EE
+.SH FILES
+.IP /dev/cronyx
+The special device file for adapter options management.
+.SH SEE ALSO
+.PP
+cx(4)
diff --git a/sbin/i386/cxconfig/cxconfig.c b/sbin/i386/cxconfig/cxconfig.c
new file mode 100644
index 0000000..4e2400e
--- /dev/null
+++ b/sbin/i386/cxconfig/cxconfig.c
@@ -0,0 +1,766 @@
+/*
+ * Cronyx-Sigma adapter configuration utility for Unix.
+ *
+ * Copyright (C) 1994 Cronyx Ltd.
+ * Author: Serge Vakulenko, <vak@zebub.msk.su>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
+ *
+ * Usage:
+ * cxconfig [-a]
+ * -- print status of all channels
+ * cxconfig [-a] <channel>
+ * -- print status of the channel
+ * cxconfig <channel> <option>...
+ * -- set channel options
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <machine/cronyx.h>
+#include <net/if.h>
+#include <stdio.h>
+
+#define NBRD 3
+#define CXDEV "/dev/cronyx"
+#define atoi(a) strtol((a), (char**)0, 0)
+
+cx_options_t o;
+cx_stat_t st;
+int aflag;
+int sflag;
+
+char *symbol (unsigned char sym)
+{
+ static char buf[40];
+
+ if (sym < ' ')
+ sprintf (buf, "^%c", sym+0100);
+ else if (sym == '\\')
+ strcat (buf, "\\\\");
+ else if (sym < 127)
+ sprintf (buf, "%c", sym);
+ else
+ sprintf (buf, "\\%03o", sym);
+ return (buf);
+}
+
+unsigned char atosym (char *s)
+{
+ unsigned char c;
+
+ if (*s == '^')
+ return (*++s & 037);
+ if (*s == '\\')
+ return (strtol (++s, 0, 8));
+ return (*s);
+}
+
+void usage ()
+{
+ printf ("Cronyx-Sigma Adapter Configuration Utility, Version 1.0\n");
+ printf ("Copyright (C) 1994 Cronyx Ltd.\n");
+ printf ("Usage:\n");
+ printf ("\tcxconfig [-a]\n");
+ printf ("\t\t-- print status of all channels\n");
+ printf ("\tcxconfig [-a] <channel>\n");
+ printf ("\t\t-- print status of the channel\n");
+ printf ("\tcxconfig <channel> [async | hdlc | bisync | x.21] [ispeed #] [ospeed #]\n");
+ printf ("\t\t[+cts | -cts]\n");
+ printf ("\t\t-- set channel options\n");
+ exit (1);
+}
+
+char *chantype (int type)
+{
+ switch (type) {
+ case T_NONE: return ("none");
+ case T_ASYNC: return ("RS-232");
+ case T_UNIV_RS232: return ("RS-232");
+ case T_UNIV_RS449: return ("RS-232/RS-449");
+ case T_UNIV_V35: return ("RS-232/V.35");
+ case T_SYNC_RS232: return ("RS-232");
+ case T_SYNC_V35: return ("V.35");
+ case T_SYNC_RS449: return ("RS-449");
+ }
+}
+
+char *chanmode (int mode)
+{
+ switch (mode) {
+ case M_ASYNC: return ("Async");
+ case M_HDLC: return ("HDLC");
+ case M_BISYNC: return ("Bisync");
+ case M_X21: return ("X.21");
+ default: return ("???");
+ }
+}
+
+void getchan (int channel)
+{
+ int s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ o.board = channel/NCHAN;
+ o.channel = channel%NCHAN;
+ if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCGETMODE");
+ exit (1);
+ }
+ close (s);
+ if (o.type == T_NONE) {
+ fprintf (stderr, "cx%d: channel %d not configured\n", o.board,
+ o.channel);
+ exit (1);
+ }
+}
+
+int printstats (int channel, int hflag)
+{
+ int s, res;
+
+ s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ st.board = channel/NCHAN;
+ st.channel = channel%NCHAN;
+ res = ioctl (s, CXIOCGETSTAT, (caddr_t)&st);
+ close (s);
+ if (res < 0)
+ return (-1);
+
+ if (hflag)
+ printf ("Chan Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n");
+ printf ("cx%-2d %7ld %7ld %7ld %8ld %7ld %7ld %8ld %7ld %7ld\n",
+ channel, st.rintr, st.tintr, st.mintr, st.ibytes, st.ipkts,
+ st.ierrs, st.obytes, st.opkts, st.oerrs);
+ return (0);
+}
+
+void printallstats ()
+{
+ int b, c;
+
+ printf ("Chan Rintr Tintr Mintr Ibytes Ipkts Ierrs Obytes Opkts Oerrs\n");
+ for (b=0; b<NBRD; ++b)
+ for (c=0; c<NCHAN; ++c)
+ printstats (b*NCHAN + c, 0);
+}
+
+void setchan (int channel)
+{
+ int s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+ o.board = channel/NCHAN;
+ o.channel = channel%NCHAN;
+ if (ioctl (s, CXIOCSETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCSETMODE");
+ exit (1);
+ }
+ close (s);
+}
+
+void printopt ()
+{
+ /* Common channel options */
+ /* channel option register 4 */
+ printf ("\t");
+ printf ("fifo=%d ", o.opt.cor4.thr); /* FIFO threshold */
+ printf ("%cctsdown ", o.opt.cor4.cts_zd ? '+' : '-'); /* detect 1 to 0 transition on the CTS */
+ printf ("%ccddown ", o.opt.cor4.cd_zd ? '+' : '-'); /* detect 1 to 0 transition on the CD */
+ printf ("%cdsrdown ", o.opt.cor4.dsr_zd ? '+' : '-'); /* detect 1 to 0 transition on the DSR */
+ printf ("\n");
+
+ /* channel option register 5 */
+ printf ("\t");
+ printf ("rfifo=%d ", o.opt.cor5.rx_thr); /* receive flow control FIFO threshold */
+ printf ("%cctsup ", o.opt.cor5.cts_od ? '+' : '-'); /* detect 0 to 1 transition on the CTS */
+ printf ("%ccdup ", o.opt.cor5.cd_od ? '+' : '-'); /* detect 0 to 1 transition on the CD */
+ printf ("%cdsrup ", o.opt.cor5.dsr_od ? '+' : '-'); /* detect 0 to 1 transition on the DSR */
+ printf ("\n");
+
+ /* receive clock option register */
+ printf ("\t");
+ printf ("%s ", o.opt.rcor.encod == ENCOD_NRZ ? "nrz" : /* signal encoding */
+ o.opt.rcor.encod == ENCOD_NRZI ? "nrzi" :
+ o.opt.rcor.encod == ENCOD_MANCHESTER ? "manchester" : "???");
+ printf ("%cdpll ", o.opt.rcor.dpll ? '+' : '-'); /* DPLL enable */
+
+ /* transmit clock option register */
+ printf ("%clloop ", o.opt.tcor.llm ? '+' : '-'); /* local loopback mode */
+ printf ("%cextclock ", o.opt.tcor.ext1x ? '+' : '-'); /* external 1x clock mode */
+ printf ("\n");
+
+ switch (o.mode) {
+ case M_ASYNC: /* async mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.aopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.aopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.aopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.aopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.aopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.aopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("%cdsr ", o.aopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */
+ printf ("%ccts ", o.aopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */
+ printf ("%crts ", o.aopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */
+ printf ("%crloop ", o.aopt.cor2.rlm ? '+' : '-'); /* remote loopback mode enable */
+ printf ("%cetc ", o.aopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */
+ printf ("%cxon ", o.aopt.cor2.ixon ? '+' : '-'); /* in-band XON/XOFF enable */
+ printf ("%cxany ", o.aopt.cor2.ixany ? '+' : '-'); /* XON on any character */
+ printf ("\n");
+
+ /* option register 3 */
+ printf ("\t");
+ printf ("%s ", o.aopt.cor3.stopb == STOPB_1 ? "stopb1" : /* stop bit length */
+ o.aopt.cor3.stopb == STOPB_15 ? "stopb1.5" :
+ o.aopt.cor3.stopb == STOPB_2 ? "stopb2" : "???");
+ printf ("%csdt ", o.aopt.cor3.scde ? '+' : '-'); /* special char detection enable */
+ printf ("%cflowct ", o.aopt.cor3.flowct ? '+' : '-'); /* flow control transparency mode */
+ printf ("%crdt ", o.aopt.cor3.rngde ? '+' : '-'); /* range detect enable */
+ printf ("%cexdt ", o.aopt.cor3.escde ? '+' : '-'); /* extended spec. char detect enable */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("%s ", o.aopt.cor6.parerr == PERR_INTR ? "parintr" : /* parity/framing error actions */
+ o.aopt.cor6.parerr == PERR_NULL ? "parnull" :
+ o.aopt.cor6.parerr == PERR_IGNORE ? "parign" :
+ o.aopt.cor6.parerr == PERR_DISCARD ? "pardisc" :
+ o.aopt.cor6.parerr == PERR_FFNULL ? "parffnull" : "???");
+ printf ("%s ", o.aopt.cor6.brk == BRK_INTR ? "brkintr" : /* action on break condition */
+ o.aopt.cor6.brk == BRK_NULL ? "brknull" :
+ o.aopt.cor6.brk == BRK_DISCARD ? "brkdisc" : "???");
+ printf ("%cinlcr ", o.aopt.cor6.inlcr ? '+' : '-'); /* translate NL to CR on input */
+ printf ("%cicrnl ", o.aopt.cor6.icrnl ? '+' : '-'); /* translate CR to NL on input */
+ printf ("%cigncr ", o.aopt.cor6.igncr ? '+' : '-'); /* discard CR on input */
+ printf ("\n");
+
+ /* channel option register 7 */
+ printf ("\t");
+ printf ("%cocrnl ", o.aopt.cor7.ocrnl ? '+' : '-'); /* translate CR to NL on output */
+ printf ("%conlcr ", o.aopt.cor7.onlcr ? '+' : '-'); /* translate NL to CR on output */
+ printf ("%cfcerr ", o.aopt.cor7.fcerr ? '+' : '-'); /* process flow ctl err chars enable */
+ printf ("%clnext ", o.aopt.cor7.lnext ? '+' : '-'); /* LNext option enable */
+ printf ("%cistrip ", o.aopt.cor7.istrip ? '+' : '-'); /* strip 8-bit on input */
+ printf ("\n");
+
+ printf ("\t");
+ printf ("schr1=%s ", symbol (o.aopt.schr1)); /* special character register 1 (XON) */
+ printf ("schr2=%s ", symbol (o.aopt.schr2)); /* special character register 2 (XOFF) */
+ printf ("schr3=%s ", symbol (o.aopt.schr3)); /* special character register 3 */
+ printf ("schr4=%s ", symbol (o.aopt.schr4)); /* special character register 4 */
+ printf ("scrl=%s ", symbol (o.aopt.scrl)); /* special character range low */
+ printf ("scrh=%s ", symbol (o.aopt.scrh)); /* special character range high */
+ printf ("lnext=%s ", symbol (o.aopt.lnxt)); /* LNext character */
+ printf ("\n");
+ break;
+
+ case M_HDLC: /* hdlc mode options */
+ /* hdlc channel option register 1 */
+ printf ("\t");
+ printf ("if%d ", o.hopt.cor1.ifflags); /* number of inter-frame flags sent */
+ printf ("%s ", o.hopt.cor1.admode == ADMODE_NOADDR ? "noaddr" : /* addressing mode */
+ o.hopt.cor1.admode == ADMODE_4_1 ? "addr1" :
+ o.hopt.cor1.admode == ADMODE_2_2 ? "addr2" : "???");
+ printf ("%cclrdet ", o.hopt.cor1.clrdet ? '+' : '-'); /* clear detect for X.21 data transfer phase */
+ printf ("addrlen%d ", o.hopt.cor1.aflo + 1); /* address field length option */
+ printf ("\n");
+
+ /* hdlc channel option register 2 */
+ printf ("\t");
+ printf ("%cdsr ", o.hopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */
+ printf ("%ccts ", o.hopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */
+ printf ("%crts ", o.hopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */
+ printf ("%ccrcinv ", o.hopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */
+ printf ("%cfcsapd ", o.hopt.cor2.fcsapd ? '+' : '-'); /* FCS append */
+ printf ("\n");
+
+ /* hdlc channel option register 3 */
+ printf ("\t");
+ printf ("pad%d ", o.hopt.cor3.padcnt); /* pad character count */
+ printf ("idle%s ", o.hopt.cor3.idle ? "mark" : "flag"); /* idle mode */
+ printf ("%cfcs ", o.hopt.cor3.nofcs ? '-' : '+'); /* FCS disable */
+ printf ("fcs-%s ", o.hopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */
+ printf ("syn=%s ", o.hopt.cor3.syncpat ? "0xAA" : "0x00"); /* send sync pattern */
+ printf ("%csyn ", o.hopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */
+ printf ("\n");
+
+ printf ("\t");
+ printf ("rfar1=0x%02x ", o.hopt.rfar1); /* receive frame address register 1 */
+ printf ("rfar2=0x%02x ", o.hopt.rfar2); /* receive frame address register 2 */
+ printf ("rfar3=0x%02x ", o.hopt.rfar3); /* receive frame address register 3 */
+ printf ("rfar4=0x%02x ", o.hopt.rfar4); /* receive frame address register 4 */
+ printf ("crc-%s ", o.hopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */
+ printf ("\n");
+ break;
+
+ case M_BISYNC: /* bisync mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.bopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.bopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.bopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.bopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.bopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.bopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("syn%d ", o.bopt.cor2.syns+2); /* number of extra SYN chars before a frame */
+ printf ("%ccrcinv ", o.bopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */
+ printf ("%s ", o.bopt.cor2.ebcdic ? "ebcdic" : "ascii"); /* use EBCDIC as char set (instead of ASCII) */
+ printf ("%cbccapd ", o.bopt.cor2.bcc ? '+' : '-'); /* BCC append enable */
+ printf ("%s ", o.bopt.cor2.lrc ? "lrc" : "crc-16"); /* longitudinal redundancy check */
+ printf ("\n");
+
+ /* channel option register 3 */
+ printf ("\t");
+ printf ("pad%d ", o.bopt.cor3.padcnt); /* pad character count */
+ printf ("idle%s ", o.bopt.cor3.idle ? "mark" : "syn"); /* idle mode */
+ printf ("%cfcs ", o.bopt.cor3.nofcs ? '-' : '+'); /* FCS disable */
+ printf ("fcs-%s ", o.bopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */
+ printf ("syn=%s ", o.bopt.cor3.padpat ? "0x55" : "0xAA"); /* send sync pattern */
+ printf ("%csyn ", o.bopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("specterm=%s ", symbol (o.bopt.cor6.specterm)); /* special termination character */
+
+ printf ("crc-%s ", o.bopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */
+ printf ("\n");
+ break;
+
+ case M_X21: /* x.21 mode options */
+ /* channel option register 1 */
+ printf ("\t");
+ printf ("cs%d ", o.xopt.cor1.charlen+1); /* character length, 5..8 */
+ printf ("par%s ", o.xopt.cor1.parity ? "odd" : "even"); /* parity */
+ printf ("%cignpar ", o.xopt.cor1.ignpar ? '+' : '-'); /* ignore parity */
+ if (o.xopt.cor1.parmode != PARM_NORMAL) /* parity mode */
+ printf ("%s ", o.xopt.cor1.parmode == PARM_NOPAR ? "nopar" :
+ o.xopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???");
+ printf ("\n");
+
+ /* channel option register 2 */
+ printf ("\t");
+ printf ("%cetc ", o.xopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */
+
+ /* channel option register 3 */
+ printf ("%csdt ", o.xopt.cor3.scde ? '+' : '-'); /* special char detection enable */
+ printf ("%cstripsyn ", o.xopt.cor3.stripsyn ? '+' : '-'); /* treat SYN chars as special condition */
+ printf ("%cssdt ", o.xopt.cor3.ssde ? '+' : '-'); /* steady state detect enable */
+ printf ("syn%c ", o.xopt.cor3.syn ? '1' : '2'); /* the number of SYN chars on receive */
+ printf ("\n");
+
+ /* channel option register 6 */
+ printf ("\t");
+ printf ("syn=%s ", symbol (o.xopt.cor6.synchar)); /* syn character */
+
+ printf ("schr1=%s ", symbol (o.xopt.schr1)); /* special character register 1 */
+ printf ("schr2=%s ", symbol (o.xopt.schr2)); /* special character register 2 */
+ printf ("schr3=%s ", symbol (o.xopt.schr3)); /* special character register 3 */
+ printf ("\n");
+ break;
+ }
+}
+
+void printchan (int channel)
+{
+ printf ("cx%d (%s) %s", channel, chantype (o.type), chanmode (o.mode));
+ if (o.txbaud == o.rxbaud)
+ printf (" %d", o.rxbaud);
+ else
+ printf (" ospeed=%d ispeed=%d", o.txbaud, o.rxbaud);
+ if ((o.channel == 0 || o.channel == 8) &&
+ (o.type == T_UNIV_V35 || o.type == T_UNIV_RS449))
+ printf (" port=%s", o.iftype ? (o.type == T_UNIV_V35 ?
+ "v35" : "rs449") : "rs232");
+ printf (o.sopt.ext ? " ext" : o.sopt.cisco ? " cisco" : " ppp");
+ printf (" %ckeepalive", o.sopt.keepalive ? '+' : '-');
+ printf (" %cautorts", o.sopt.norts ? '-' : '+');
+ if (*o.master)
+ printf (" master=%s", o.master);
+ printf ("\n");
+ if (aflag)
+ printopt ();
+}
+
+void printall ()
+{
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char buf[BUFSIZ], *cp;
+ int s, c;
+
+ s = socket (AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror ("cxconfig: socket");
+ exit (1);
+ }
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (s, SIOCGIFCONF, (caddr_t)&ifc) < 0) {
+ perror ("cxconfig: SIOCGIFCONF");
+ exit (1);
+ }
+ close (s);
+ s = open (CXDEV, 0);
+ if (s < 0) {
+ perror (CXDEV);
+ exit (1);
+ }
+
+ ifr = ifc.ifc_req;
+#define max(a,b) ((a)>(b) ? (a) : (b))
+#define size(p) max((p).sa_len, sizeof(p))
+ for (cp=buf; cp<buf+ifc.ifc_len; cp+=sizeof(ifr->ifr_name)+size(ifr->ifr_addr)) {
+ ifr = (struct ifreq*) cp;
+ if (ifr->ifr_addr.sa_family != AF_LINK)
+ continue;
+ if (strncmp (ifr->ifr_name, "cx", 2) != 0)
+ continue;
+ c = atoi (ifr->ifr_name + 2);
+ o.board = c/NCHAN;
+ o.channel = c%NCHAN;
+ if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) {
+ perror ("cxconfig: CXIOCGETMODE");
+ exit (1);
+ }
+ printchan (c);
+ }
+ close (s);
+}
+
+void set_interface_type (char *type)
+{
+ if (o.channel != 0 && o.channel != 8) {
+ printf ("interface option is applicable only for channels 0 and 8\n");
+ exit (1);
+ }
+ if (o.type != T_UNIV_V35 && o.type != T_UNIV_RS449) {
+ printf ("interface option is applicable only for universal channels\n");
+ exit (1);
+ }
+ if (! strcasecmp (type, "port=rs232"))
+ o.iftype = 0;
+ else
+ o.iftype = 1;
+}
+
+void set_master (char *ifname)
+{
+ if (o.type == T_ASYNC) {
+ printf ("master option is not applicable for async channels\n");
+ exit (1);
+ }
+ strcpy (o.master, ifname);
+}
+
+void set_async_opt (char *opt)
+{
+ /* channel option register 1 */
+ if (! strncasecmp (opt, "cs", 2)) o.aopt.cor1.charlen = atoi (opt + 2) - 1;
+ else if (! strcasecmp (opt, "parodd")) o.aopt.cor1.parity = 1;
+ else if (! strcasecmp (opt, "pareven")) o.aopt.cor1.parity = 0;
+ else if (! strcasecmp (opt, "-ignpar")) o.aopt.cor1.ignpar = 0;
+ else if (! strcasecmp (opt, "+ignpar")) o.aopt.cor1.ignpar = 1;
+ else if (! strcasecmp (opt, "nopar")) o.aopt.cor1.parmode = PARM_NOPAR;
+ else if (! strcasecmp (opt, "forcepar")) o.aopt.cor1.parmode = PARM_FORCE;
+
+ /* channel option register 2 */
+ else if (! strcasecmp (opt, "-dsr")) o.aopt.cor2.dsrae = 0;
+ else if (! strcasecmp (opt, "+dsr")) o.aopt.cor2.dsrae = 1;
+ else if (! strcasecmp (opt, "-cts")) o.aopt.cor2.ctsae = 0;
+ else if (! strcasecmp (opt, "+cts")) o.aopt.cor2.ctsae = 1;
+ else if (! strcasecmp (opt, "-rts")) o.aopt.cor2.rtsao = 0;
+ else if (! strcasecmp (opt, "+rts")) o.aopt.cor2.rtsao = 1;
+ else if (! strcasecmp (opt, "-rloop")) o.aopt.cor2.rlm = 0;
+ else if (! strcasecmp (opt, "+rloop")) o.aopt.cor2.rlm = 1;
+ else if (! strcasecmp (opt, "-etc")) o.aopt.cor2.etc = 0;
+ else if (! strcasecmp (opt, "+etc")) o.aopt.cor2.etc = 1;
+ else if (! strcasecmp (opt, "-ixon")) o.aopt.cor2.ixon = 0;
+ else if (! strcasecmp (opt, "+ixon")) o.aopt.cor2.ixon = 1;
+ else if (! strcasecmp (opt, "-ixany")) o.aopt.cor2.ixany = 0;
+ else if (! strcasecmp (opt, "+ixany")) o.aopt.cor2.ixany = 1;
+
+ /* option register 3 */
+ else if (! strcasecmp (opt, "stopb1")) o.aopt.cor3.stopb = STOPB_1;
+ else if (! strcasecmp (opt, "stopb1.5")) o.aopt.cor3.stopb = STOPB_15;
+ else if (! strcasecmp (opt, "stopb2")) o.aopt.cor3.stopb = STOPB_2;
+ else if (! strcasecmp (opt, "-sdt")) o.aopt.cor3.scde = 0;
+ else if (! strcasecmp (opt, "+sdt")) o.aopt.cor3.scde = 1;
+ else if (! strcasecmp (opt, "-flowct")) o.aopt.cor3.flowct = 0;
+ else if (! strcasecmp (opt, "+flowct")) o.aopt.cor3.flowct = 1;
+ else if (! strcasecmp (opt, "-rdt")) o.aopt.cor3.rngde = 0;
+ else if (! strcasecmp (opt, "+rdt")) o.aopt.cor3.rngde = 1;
+ else if (! strcasecmp (opt, "-exdt")) o.aopt.cor3.escde = 0;
+ else if (! strcasecmp (opt, "+exdt")) o.aopt.cor3.escde = 1;
+
+ /* channel option register 6 */
+ else if (! strcasecmp (opt, "parintr")) o.aopt.cor6.parerr = PERR_INTR;
+ else if (! strcasecmp (opt, "parnull")) o.aopt.cor6.parerr = PERR_NULL;
+ else if (! strcasecmp (opt, "parign")) o.aopt.cor6.parerr = PERR_IGNORE;
+ else if (! strcasecmp (opt, "pardisc")) o.aopt.cor6.parerr = PERR_DISCARD;
+ else if (! strcasecmp (opt, "parffnull")) o.aopt.cor6.parerr = PERR_FFNULL;
+ else if (! strcasecmp (opt, "brkintr")) o.aopt.cor6.brk = BRK_INTR;
+ else if (! strcasecmp (opt, "brknull")) o.aopt.cor6.brk = BRK_NULL;
+ else if (! strcasecmp (opt, "brkdisc")) o.aopt.cor6.brk = BRK_DISCARD;
+ else if (! strcasecmp (opt, "-inlcr")) o.aopt.cor6.inlcr = 0;
+ else if (! strcasecmp (opt, "+inlcr")) o.aopt.cor6.inlcr = 1;
+ else if (! strcasecmp (opt, "-icrnl")) o.aopt.cor6.icrnl = 0;
+ else if (! strcasecmp (opt, "+icrnl")) o.aopt.cor6.icrnl = 1;
+ else if (! strcasecmp (opt, "-igncr")) o.aopt.cor6.igncr = 0;
+ else if (! strcasecmp (opt, "+igncr")) o.aopt.cor6.igncr = 1;
+
+ /* channel option register 7 */
+ else if (! strcasecmp (opt, "-ocrnl")) o.aopt.cor7.ocrnl = 0;
+ else if (! strcasecmp (opt, "+ocrnl")) o.aopt.cor7.ocrnl = 1;
+ else if (! strcasecmp (opt, "-onlcr")) o.aopt.cor7.onlcr = 0;
+ else if (! strcasecmp (opt, "+onlcr")) o.aopt.cor7.onlcr = 1;
+ else if (! strcasecmp (opt, "-fcerr")) o.aopt.cor7.fcerr = 0;
+ else if (! strcasecmp (opt, "+fcerr")) o.aopt.cor7.fcerr = 1;
+ else if (! strcasecmp (opt, "-lnext")) o.aopt.cor7.lnext = 0;
+ else if (! strcasecmp (opt, "+lnext")) o.aopt.cor7.lnext = 1;
+ else if (! strcasecmp (opt, "-istrip")) o.aopt.cor7.istrip = 0;
+ else if (! strcasecmp (opt, "+istrip")) o.aopt.cor7.istrip = 1;
+
+ else if (! strncasecmp (opt, "schr1=", 6)) o.aopt.schr1 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr2=", 6)) o.aopt.schr2 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr3=", 6)) o.aopt.schr3 = atosym (opt+6);
+ else if (! strncasecmp (opt, "schr4=", 6)) o.aopt.schr4 = atosym (opt+6);
+ else if (! strncasecmp (opt, "scrl=", 5)) o.aopt.scrl = atosym (opt+5);
+ else if (! strncasecmp (opt, "scrh=", 5)) o.aopt.scrh = atosym (opt+5);
+ else if (! strncasecmp (opt, "lnext=", 6)) o.aopt.lnxt = atosym (opt+6);
+ else
+ usage ();
+}
+
+void set_hdlc_opt (char *opt)
+{
+ /* hdlc channel option register 1 */
+ if (! strncasecmp (opt, "if", 2)) o.hopt.cor1.ifflags = atoi (opt + 2);
+ else if (! strcasecmp (opt, "noaddr")) o.hopt.cor1.admode = ADMODE_NOADDR;
+ else if (! strcasecmp (opt, "addr1")) o.hopt.cor1.admode = ADMODE_4_1;
+ else if (! strcasecmp (opt, "addr2")) o.hopt.cor1.admode = ADMODE_2_2;
+ else if (! strcasecmp (opt, "-clrdet")) o.hopt.cor1.clrdet = 0;
+ else if (! strcasecmp (opt, "+clrdet")) o.hopt.cor1.clrdet = 1;
+ else if (! strcasecmp (opt, "addrlen1")) o.hopt.cor1.aflo = 0;
+ else if (! strcasecmp (opt, "addrlen2")) o.hopt.cor1.aflo = 1;
+
+ /* hdlc channel option register 2 */
+ else if (! strcasecmp (opt, "-dsr")) o.hopt.cor2.dsrae = 0;
+ else if (! strcasecmp (opt, "+dsr")) o.hopt.cor2.dsrae = 1;
+ else if (! strcasecmp (opt, "-cts")) o.hopt.cor2.ctsae = 0;
+ else if (! strcasecmp (opt, "+cts")) o.hopt.cor2.ctsae = 1;
+ else if (! strcasecmp (opt, "-rts")) o.hopt.cor2.rtsao = 0;
+ else if (! strcasecmp (opt, "+rts")) o.hopt.cor2.rtsao = 1;
+ else if (! strcasecmp (opt, "-fcsapd")) o.hopt.cor2.fcsapd = 0;
+ else if (! strcasecmp (opt, "+fcsapd")) o.hopt.cor2.fcsapd = 1;
+ else if (! strcasecmp (opt, "-crcinv")) o.hopt.cor2.crcninv = 1;
+ else if (! strcasecmp (opt, "+crcinv")) o.hopt.cor2.crcninv = 0;
+
+ /* hdlc channel option register 3 */
+ else if (! strncasecmp (opt, "pad", 3)) o.hopt.cor3.padcnt = atoi (opt + 3);
+ else if (! strcasecmp (opt, "idlemark")) o.hopt.cor3.idle = 1;
+ else if (! strcasecmp (opt, "idleflag")) o.hopt.cor3.idle = 0;
+ else if (! strcasecmp (opt, "-fcs")) o.hopt.cor3.nofcs = 1;
+ else if (! strcasecmp (opt, "+fcs")) o.hopt.cor3.nofcs = 0;
+ else if (! strcasecmp (opt, "fcs-crc-16")) o.hopt.cor3.fcspre = 1;
+ else if (! strcasecmp (opt, "fcs-v.41")) o.hopt.cor3.fcspre = 0;
+ else if (! strcasecmp (opt, "syn=0xaa")) o.hopt.cor3.syncpat = 1;
+ else if (! strcasecmp (opt, "syn=0x00")) o.hopt.cor3.syncpat = 0;
+ else if (! strcasecmp (opt, "-syn")) o.hopt.cor3.sndpad = 0;
+ else if (! strcasecmp (opt, "+syn")) o.hopt.cor3.sndpad = 1;
+
+ else if (! strncasecmp (opt, "rfar1=", 6)) o.hopt.rfar1 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar2=", 6)) o.hopt.rfar2 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar3=", 6)) o.hopt.rfar3 = atoi (opt + 6);
+ else if (! strncasecmp (opt, "rfar4=", 6)) o.hopt.rfar4 = atoi (opt + 6);
+ else if (! strcasecmp (opt, "crc-16")) o.hopt.cpsr = 1;
+ else if (! strcasecmp (opt, "crc-v.41")) o.hopt.cpsr = 0;
+ else usage ();
+}
+
+void set_bisync_opt (char *opt)
+{
+ usage ();
+}
+
+void set_x21_opt (char *opt)
+{
+ usage ();
+}
+
+int main (int argc, char **argv)
+{
+ int channel;
+
+ for (--argc, ++argv; argc>0 && **argv=='-'; --argc, ++argv)
+ if (! strcasecmp (*argv, "-a"))
+ ++aflag;
+ else if (! strcasecmp (*argv, "-s"))
+ ++sflag;
+ else
+ usage ();
+
+ if (argc <= 0) {
+ if (sflag)
+ printallstats ();
+ else
+ printall ();
+ return (0);
+ }
+
+ if (argv[0][0]=='c' && argv[0][1]=='x')
+ *argv += 2;
+ if (**argv<'0' || **argv>'9')
+ usage ();
+ channel = atoi (*argv);
+ --argc, ++argv;
+
+ if (sflag) {
+ if (printstats (channel, 1) < 0)
+ printf ("channel cx%d not available\n", channel);
+ return (0);
+ }
+
+ getchan (channel);
+
+ if (argc <= 0) {
+ printchan (channel);
+ return (0);
+ }
+
+ for (; argc>0; --argc, ++argv)
+ if (**argv == '(')
+ continue;
+ else if (! strncasecmp (*argv, "ispeed=", 7))
+ o.rxbaud = atoi (*argv+7);
+ else if (! strncasecmp (*argv, "ospeed=", 7))
+ o.txbaud = atoi (*argv+7);
+ else if (! strcasecmp (*argv, "async"))
+ o.mode = M_ASYNC;
+ else if (! strcasecmp (*argv, "hdlc"))
+ o.mode = M_HDLC;
+ else if (! strcasecmp (*argv, "bisync") ||
+ ! strcasecmp (*argv, "bsc"))
+ o.mode = M_BISYNC;
+ else if (! strcasecmp (*argv, "x.21") ||
+ ! strcasecmp (*argv, "x21"))
+ o.mode = M_X21;
+ else if (**argv>='0' && **argv<='9')
+ o.txbaud = o.rxbaud = atoi (*argv);
+ else if (! strcasecmp (*argv, "cisco")) {
+ o.sopt.cisco = 1;
+ o.sopt.ext = 0;
+ } else if (! strcasecmp (*argv, "ppp")) {
+ o.sopt.cisco = 0;
+ o.sopt.ext = 0;
+ } else if (! strcasecmp (*argv, "ext"))
+ o.sopt.ext = 1;
+ else if (! strcasecmp (*argv, "+keepalive"))
+ o.sopt.keepalive = 1;
+ else if (! strcasecmp (*argv, "-keepalive"))
+ o.sopt.keepalive = 0;
+ else if (! strcasecmp (*argv, "+autorts"))
+ o.sopt.norts = 0;
+ else if (! strcasecmp (*argv, "-autorts"))
+ o.sopt.norts = 1;
+ else if (! strcasecmp (*argv, "port=rs232") ||
+ ! strcasecmp (*argv, "port=rs449") ||
+ ! strcasecmp (*argv, "port=v35"))
+ set_interface_type (*argv);
+ else if (! strncasecmp (*argv, "master=",7))
+ set_master (*argv+7);
+
+ /*
+ * Common channel options
+ */
+ /* channel option register 4 */
+ else if (! strcasecmp (*argv, "-ctsdown"))
+ o.opt.cor4.cts_zd = 0;
+ else if (! strcasecmp (*argv, "+ctsdown"))
+ o.opt.cor4.cts_zd = 1;
+ else if (! strcasecmp (*argv, "-cddown"))
+ o.opt.cor4.cd_zd = 0;
+ else if (! strcasecmp (*argv, "+cddown"))
+ o.opt.cor4.cd_zd = 1;
+ else if (! strcasecmp (*argv, "-dsrdown"))
+ o.opt.cor4.dsr_zd = 0;
+ else if (! strcasecmp (*argv, "+dsrdown"))
+ o.opt.cor4.dsr_zd = 1;
+ else if (! strncasecmp (*argv, "fifo=", 5))
+ o.opt.cor4.thr = atoi (*argv + 5);
+
+ /* channel option register 5 */
+ else if (! strcasecmp (*argv, "-ctsup"))
+ o.opt.cor5.cts_od = 0;
+ else if (! strcasecmp (*argv, "+ctsup"))
+ o.opt.cor5.cts_od = 1;
+ else if (! strcasecmp (*argv, "-cdup"))
+ o.opt.cor5.cd_od = 0;
+ else if (! strcasecmp (*argv, "+cdup"))
+ o.opt.cor5.cd_od = 1;
+ else if (! strcasecmp (*argv, "-dsrup"))
+ o.opt.cor5.dsr_od = 0;
+ else if (! strcasecmp (*argv, "+dsrup"))
+ o.opt.cor5.dsr_od = 1;
+ else if (! strncasecmp (*argv, "rfifo=", 6))
+ o.opt.cor5.rx_thr = atoi (*argv + 6);
+
+ /* receive clock option register */
+ else if (! strcasecmp (*argv, "nrz"))
+ o.opt.rcor.encod = ENCOD_NRZ;
+ else if (! strcasecmp (*argv, "nrzi"))
+ o.opt.rcor.encod = ENCOD_NRZI;
+ else if (! strcasecmp (*argv, "manchester"))
+ o.opt.rcor.encod = ENCOD_MANCHESTER;
+ else if (! strcasecmp (*argv, "-dpll"))
+ o.opt.rcor.dpll = 0;
+ else if (! strcasecmp (*argv, "+dpll"))
+ o.opt.rcor.dpll = 1;
+
+ /* transmit clock option register */
+ else if (! strcasecmp (*argv, "-lloop"))
+ o.opt.tcor.llm = 0;
+ else if (! strcasecmp (*argv, "+lloop"))
+ o.opt.tcor.llm = 1;
+ else if (! strcasecmp (*argv, "-extclock"))
+ o.opt.tcor.ext1x = 0;
+ else if (! strcasecmp (*argv, "+extclock"))
+ o.opt.tcor.ext1x = 1;
+
+ /*
+ * Mode dependent channel options
+ */
+ else switch (o.mode) {
+ case M_ASYNC: set_async_opt (*argv); break;
+ case M_HDLC: set_hdlc_opt (*argv); break;
+ case M_BISYNC: set_bisync_opt (*argv); break;
+ case M_X21: set_x21_opt (*argv); break;
+ }
+
+ setchan (channel);
+ return (0);
+}
diff --git a/sbin/i386/fdisk/Makefile b/sbin/i386/fdisk/Makefile
new file mode 100644
index 0000000..5efa9c5
--- /dev/null
+++ b/sbin/i386/fdisk/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.1 (Julian Elischer) 3/28/93
+#
+#
+
+PROG= fdisk
+SRCS= fdisk.c
+MAN8= fdisk.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/fdisk/fdisk.8 b/sbin/i386/fdisk/fdisk.8
new file mode 100644
index 0000000..a43a85a
--- /dev/null
+++ b/sbin/i386/fdisk/fdisk.8
@@ -0,0 +1,409 @@
+.Dd October 4, 1996
+.Dt FDISK 8
+.\".Os BSD 4
+.Sh NAME
+.Nm fdisk
+.Nd DOS partition maintenance program
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl u
+.Op Fl a
+.Op Fl 0123
+.Op Ar disk
+.Bl -tag -width time
+.Nm fdisk
+.Op Fl f Ar configfile
+.Op Fl i
+.Op Fl v
+.Op Fl t
+.Op Ar disk
+.Sh PROLOGUE
+In order for the BIOS to boot the kernel,
+certain conventions must be adhered to.
+Sector 0 of the disk must contain boot code,
+a partition table,
+and a magic number.
+BIOS partitions can be used to break the disk up into several pieces.
+The BIOS brings in sector 0
+(does it really use the code?)
+and verifies the magic number.
+It then searches the 4 BIOS partitions described by sector 0
+to determine which of them is
+.Em active.
+This boot then brings in the secondary boot block from the
+.Em active
+partition and runs it.
+Under DOS,
+you could have one or more partitions with one
+.Em active.
+The DOS
+.Nm
+program can be used to divide space on the disk into partitions and set one
+.Em active.
+.Sh DESCRIPTION
+.Pp
+The FreeBSD program
+.Nm
+serves a similar purpose to the DOS program. The first form is used to
+display partition information or to interactively edit the partition
+table. The second is used to write a partition table using a
+.Ar configfile
+and is designed to be used by other scripts/programs.
+.Pp
+Options are:
+.It Fl u
+Is used for updating (editing) sector 0 of the disk. Ignored if
+.Fl f
+is given.
+.It Fl i
+Initializes sector 0 of the disk. This implies
+.Fl u ,
+unless
+.Fl f
+is given.
+.It Fl a
+Change the active partition only. Ignored if
+.Fl f
+is given.
+.It Fl 0123
+Operate on a single fdisk entry only. Ignored if
+.Fl f
+is given.
+.It Fl f Ar configfile
+Set partition values using the file
+.Ar configfile .
+The
+.Ar configfile
+always modifies existing partitions, unless
+.Fl i
+is also given, in which case all existing partitions are deleted (marked
+as "unused") before the
+.Ar configfile
+is read. The
+.Ar configfile
+can be "-", in which case
+.Ar stdin
+is read. See
+.Em CONFIGURATION FILE ,
+below, for file syntax.
+.Pp
+.Em WARNING:
+when
+.Fl f
+is used, you are not asked if you really want to write the partition
+table (as you are in the interactive mode). Use with caution!
+.It Fl t
+Test mode; do not write partition values. Generally used with the
+.Fl f
+option to see what would be written to the partition table. Implies
+.Fl v .
+.It Fl v
+Be verbose. When
+.Fl f
+is used,
+.Nm
+prints out the partition table that is written to the disk.
+.El
+.Pp
+The final disk name can be provided as a
+.Sq bare
+disk name only, e.g.
+.Ql sd0 ,
+or as a fully qualified device node under
+.Pa /dev .
+If omitted, the disks
+.Ql wd0 ,
+.Ql sd0 ,
+and
+.Ql od0
+are being searched in that order, until one is
+being found responding.
+.Pp
+When called with no arguments, it prints the sector 0 partition table.
+An example follows:
+
+.Bd -literal
+ ******* Working on device /dev/rwd0 *******
+ parameters extracted from in-core disklabel are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ parameters to be used for BIOS calculations are:
+ cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+ Warning: BIOS sector numbering starts with sector 1
+ Information from DOS bootblock is:
+ The data for partition 0 is:
+ sysid 165,(FreeBSD/NetBSD/386BSD)
+ start 495, size 380160 (185 Meg), flag 0
+ beg: cyl 1/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 1 is:
+ sysid 164,(unknown)
+ start 378180, size 2475 (1 Meg), flag 0
+ beg: cyl 764/ sector 1/ head 0;
+ end: cyl 768/ sector 33/ head 14
+ The data for partition 2 is:
+ <UNUSED>
+ The data for partition 3 is:
+ sysid 99,(ISC UNIX, other System V/386, GNU HURD or Mach)
+ start 380656, size 224234 (109 Meg), flag 80
+ beg: cyl 769/ sector 2/ head 0;
+ end: cyl 197/ sector 33/ head 14
+.Ed
+.Pp
+The disk is divided into three partitions that happen to fill the disk.
+The second partition overlaps the end of the first.
+(Used for debugging purposes)
+.Bl -tag -width "cyl, sector and head"
+.It Em "sysid"
+is used to label the partition. FreeBSD reserves the
+magic number 165 decimal (A5 in hex).
+.It Em "start and size"
+fields provide the start address
+and size of a partition in sectors.
+.It Em "flag 80"
+specifies that this is the active partition.
+.It Em "cyl, sector and head"
+fields are used to specify the beginning address
+and end address for the partition.
+.It Em "Note:"
+these numbers are calculated using BIOS's understanding of the disk geometry
+and saved in the bootblock.
+.El
+.Pp
+The flags
+.Fl i
+or
+.Fl u
+are used to indicate that the partition data is to be updated, unless the
+.Fl f
+option is used. If the
+.Fl f
+option is not used, the
+.Nm
+program will enter a conversational mode.
+This mode is designed not to change any data unless you explicitly tell it to.
+.Nm
+selects defaults for its questions to guarantee the above behavior.
+.Pp
+It displays each partition
+and ask if you want to edit it.
+If you say yes,
+it will step through each field showing the old value
+and asking for a new one.
+When you are done with a partition,
+.Nm
+will display it and ask if it is correct.
+.Nm
+will then proceed to the next entry.
+.Pp
+Getting the
+.Em cyl, sector,
+and
+.Em head
+fields correct is tricky.
+So by default,
+they will be calculated for you;
+you can specify them if you choose.
+.Pp
+After all the partitions are processed,
+you are given the option to change the
+.Em active
+partition.
+Finally,
+when the all the data for the first sector has been accumulated,
+you are asked if you really want to rewrite sector 0.
+Only if you answer yes,
+will the data be written to disk.
+.Pp
+The difference between the
+.Fl u
+flag and
+.Fl i
+flag is that
+the
+.Fl u
+flag just edits the fields as they appear on the disk.
+While the
+.Fl i
+flag is used to "initialize" sector 0;
+it will setup the last BIOS partition to use the whole disk for FreeBSD;
+and make it active.
+.Sh NOTES
+.Pp
+The automatic calculation of starting cylinder etc. uses
+a set of figures that represent what the BIOS thinks is the
+geometry of the drive.
+These figures are by default taken from the incore disklabel,
+but the program initially gives you an opportunity to change them.
+This allows the user to create a bootblock that can work with drives
+that use geometry translation under the BIOS.
+.Pp
+If you hand craft your disk layout,
+please make sure that the FreeBSD partition starts on a cylinder boundary.
+A number of decisions made later may assume this.
+(This might not be necessary later.)
+.Pp
+Editing an existing partition will most likely cause you to
+lose all the data in that partition.
+.Pp
+You should run this program interactively once or twice to see how it
+works. This is completely safe as long as you answer the last question
+in the negative. There are subtleties that the program detects that are
+not fully explained in this manual page.
+.Sh CONFIGURATION FILE
+.Pp
+When the
+.Fl f
+option is given, a disk's partition table can be written using values
+from a
+.Ar configfile .
+The syntax of this file is very simple. Each line is either a comment or
+a specification, and whitespace (except for newlines) are ignored:
+.Bl -tag -width Ds
+.It Xo
+.Ic #
+.No Ar comment ...
+.Xc
+Lines beginning with a "#" are comments and are ignored.
+.It Xo
+.Ic g
+.No Ar spec1
+.No Ar spec2
+.No Ar spec3
+.Xc
+Set the BIOS geometry used in partition calculations. There must be
+three values specfied, with a letter preceding each number:
+.Bl -tag -width Ds
+.Sm off
+.It Cm c No Ar num
+.Sm on
+Set the number of cylinders to
+.Ar num .
+.Sm off
+.It Cm h No Ar num
+.Sm on
+Set the number of heads to
+.Ar num .
+.Sm off
+.It Cm s No Ar num
+.Sm on
+Set the number of sectors/track to
+.Ar num .
+.El
+.Pp
+These specs can occur in any order, as the leading letter determines
+which value is which; however, all three must be specified.
+.Pp
+This line must occur before any lines that specify partition
+information.
+.Pp
+It is an error if the following is not true:
+.Pp
+.nf
+ 1 <= number of cylinders
+ 1 <= number of heads <= 256
+ 1 <= number of sectors/track < 64
+.fi
+.Pp
+The number of cylinders should be less than or equal to 1024, but this
+is not enforced, although a warning will be output. Note that bootable
+FreeBSD partitions (the "/" filesystem) must lie completely within the
+first 1024 cylinders; if this is not true, booting may fail.
+Non-bootable partitions do not have this restriction.
+.Pp
+Example (all of these are equivalent), for a disk with 1019 cylinders,
+39 heads, and 63 sectors:
+.Pp
+.nf
+ g c1019 h39 s63
+ g h39 c1019 s63
+ g s63 h39 c1019
+.fi
+.It Xo
+.Ic p
+.No Ar partition
+.No Ar type
+.No Ar start
+.No Ar length
+.Xc
+Set the partition given by
+.Ar partition
+(0-3) to type
+.Ar type ,
+starting at sector
+.Ar start
+for
+.Ar length
+sectors.
+.Pp
+Only those partitions explicitly mentioned by these lines are modified;
+any partition not referenced by a "p" line will not be modified.
+However, if an invalid partition table is present, or the
+.Fl i
+option is specified, all existing partition entries will be cleared
+(marked as unused), and these "p" lines will have to be used to
+explicitly set partition information. If multiple partitions need to be
+set, multiple "p" lines must be specified; one for each partition.
+.Pp
+These partition lines must occur after any geometry specification lines,
+if one is present.
+.Pp
+The
+.Ar type
+is 165 for FreeBSD partitions. Specifying a partition type of zero is
+the same as clearing the partition and marking it as unused; however,
+dummy values (such as "0") must still be specified for
+.Ar start
+and
+.Ar length .
+.Pp
+Note: the start offset will be rounded upwards to a head boundary if
+necessary, and the end offset will be rounded downwards to a cylinder
+boundary if necessary.
+.Pp
+Example: to clear partition 3 and mark it as unused:
+.Pp
+.nf
+ p 3 0 0 0
+.fi
+.Pp
+Example: to set partition 0 to a FreeBSD partition, starting at sector 1
+for 2503871 sectors (note: these numbers will be rounded upwards and
+downwards to correspond to head and cylinder boundaries):
+.Pp
+.nf
+ p 0 165 1 2503871
+.fi
+.It Xo
+.Ic a
+.No Ar partition
+.Xc
+Make
+.Ar partition
+the active partition. Can occur anywhere in the config file, but only
+one must be present.
+.Pp
+Example: to make partition 0 the active partition:
+.Pp
+.nf
+ a 0
+.fi
+
+.El
+.Pp
+.Sh SEE ALSO
+.Xr disklabel 8
+.Sh BUGS
+The entire program should be made more user-friendly.
+.Pp
+Throughout this man page, the term
+.Sq partition
+is used where it should actually be
+.Sq slice ,
+in order to conform with the terms used elsewhere.
+.Pp
+You cannot use this command to completely dedicate a disk to FreeBSD. The
+.Xr disklabel 8
+command must be used for this.
diff --git a/sbin/i386/fdisk/fdisk.c b/sbin/i386/fdisk/fdisk.c
new file mode 100644
index 0000000..38506b5
--- /dev/null
+++ b/sbin/i386/fdisk/fdisk.c
@@ -0,0 +1,1316 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int iotest;
+
+#define LBUF 100
+static char lbuf[LBUF];
+
+/*
+ *
+ * Ported to 386bsd by Julian Elischer Thu Oct 15 20:26:46 PDT 1992
+ *
+ * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University
+ * Copyright (c) 1989 Robert. V. Baron
+ * Created.
+ */
+
+#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
+#define Hex(str, ans, tmp) if (hex(str, &tmp, ans)) ans = tmp
+#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
+
+#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
+
+#define MAX_SEC_SIZE 2048 /* maximum section size that is supported */
+#define MIN_SEC_SIZE 512 /* the sector size to start sensing at */
+int secsize = 0; /* the sensed sector size */
+
+const char *disk;
+const char *disks[] =
+{
+ "/dev/rwd0", "/dev/rsd0", "/dev/rod0", 0
+};
+
+char *name;
+
+struct disklabel disklabel; /* disk parameters */
+
+int cyls, sectors, heads, cylsecs, disksecs;
+
+struct mboot
+{
+ unsigned char padding[2]; /* force the longs to be long alligned */
+ unsigned char bootinst[DOSPARTOFF];
+ struct dos_partition parts[4];
+ unsigned short int signature;
+ /* room to read in MBRs that are bigger then DEV_BSIZE */
+ unsigned char large_sector_overflow[MAX_SEC_SIZE-MIN_SEC_SIZE];
+};
+struct mboot mboot;
+
+#define ACTIVE 0x80
+#define BOOT_MAGIC 0xAA55
+
+int dos_cyls;
+int dos_heads;
+int dos_sectors;
+int dos_cylsecs;
+
+#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
+#define DOSCYL(c) (c & 0xff)
+static int partition = -1;
+
+
+#define MAX_ARGS 10
+
+static int current_line_number;
+
+static int geom_processed = 0;
+static int part_processed = 0;
+static int active_processed = 0;
+
+
+typedef struct cmd {
+ char cmd;
+ int n_args;
+ struct arg {
+ char argtype;
+ int arg_val;
+ } args[MAX_ARGS];
+} CMD;
+
+
+static int a_flag = 0; /* set active partition */
+static int i_flag = 0; /* replace partition data */
+static int u_flag = 0; /* update partition data */
+static int t_flag = 0; /* test only, if f_flag is given */
+static char *f_flag = NULL; /* Read config info from file */
+static int v_flag = 0; /* Be verbose */
+
+static unsigned char bootcode[] = {
+0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
+0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
+0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
+0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
+0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
+0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
+0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
+0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
+0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
+'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
+ 'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
+ 'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
+ 'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
+'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
+ 'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+struct part_type
+{
+ unsigned char type;
+ char *name;
+}part_types[] =
+{
+ {0x00, "unused"}
+ ,{0x01, "Primary DOS with 12 bit FAT"}
+ ,{0x02, "XENIX / filesystem"}
+ ,{0x03, "XENIX /usr filesystem"}
+ ,{0x04, "Primary DOS with 16 bit FAT"}
+ ,{0x05, "Extended DOS"}
+ ,{0x06, "Primary 'big' DOS (> 32MB)"}
+ ,{0x07, "OS/2 HPFS, QNX or Advanced UNIX"}
+ ,{0x08, "AIX filesystem"}
+ ,{0x09, "AIX boot partition or Coherent"}
+ ,{0x0A, "OS/2 Boot Manager or OPUS"}
+ ,{0x10, "OPUS"}
+ ,{0x40, "VENIX 286"}
+ ,{0x50, "DM"}
+ ,{0x51, "DM"}
+ ,{0x52, "CP/M or Microport SysV/AT"}
+ ,{0x56, "GB"}
+ ,{0x61, "Speed"}
+ ,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}
+ ,{0x64, "Novell Netware 2.xx"}
+ ,{0x65, "Novell Netware 3.xx"}
+ ,{0x75, "PCIX"}
+ ,{0x80, "Minix 1.1 ... 1.4a"}
+ ,{0x81, "Minix 1.4b ... 1.5.10"}
+ ,{0x82, "Linux swap"}
+ ,{0x83, "Linux filesystem"}
+ ,{0x93, "Amoeba filesystem"}
+ ,{0x94, "Amoeba bad block table"}
+ ,{0xA5, "FreeBSD/NetBSD/386BSD"}
+ ,{0xA7, "NEXTSTEP"}
+ ,{0xB7, "BSDI BSD/386 filesystem"}
+ ,{0xB8, "BSDI BSD/386 swap"}
+ ,{0xDB, "Concurrent CPM or C.DOS or CTOS"}
+ ,{0xE1, "Speed"}
+ ,{0xE3, "Speed"}
+ ,{0xE4, "Speed"}
+ ,{0xF1, "Speed"}
+ ,{0xF2, "DOS 3.3+ Secondary"}
+ ,{0xF4, "Speed"}
+ ,{0xFF, "BBT (Bad Blocks Table)"}
+};
+
+static void print_s0(int which);
+static void print_part(int i);
+static void init_sector0(unsigned long start);
+static void init_boot(void);
+static void change_part(int i);
+static void print_params();
+static void change_active(int which);
+static void get_params_to_use();
+static void dos(int sec, int size, unsigned char *c, unsigned char *s,
+ unsigned char *h);
+static int open_disk(int u_flag);
+static ssize_t read_disk(off_t sector, void *buf);
+static ssize_t write_disk(off_t sector, void *buf);
+static int get_params();
+static int read_s0();
+static int write_s0();
+static int ok(char *str);
+static int decimal(char *str, int *num, int deflt);
+static char *get_type(int type);
+static int read_config(char *config_file);
+static void reset_boot(void);
+#if 0
+static int hex(char *str, int *num, int deflt);
+static int string(char *str, char **ans);
+#endif
+
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ name = *argv;
+ {register char *cp = name;
+ while (*cp) if (*cp++ == '/') name = cp;
+ }
+
+ for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
+ if (*token++ != '-' || !*token)
+ break;
+ else { register int flag;
+ for ( ; (flag = *token++) ; ) {
+ switch (flag) {
+ case '0':
+ partition = 0;
+ break;
+ case '1':
+ partition = 1;
+ break;
+ case '2':
+ partition = 2;
+ break;
+ case '3':
+ partition = 3;
+ break;
+ case 'a':
+ a_flag = 1;
+ break;
+ case 'f':
+ if (*token)
+ {
+ f_flag = token;
+ token = "";
+ }
+ else
+ {
+ if (argc == 1)
+ {
+ goto usage;
+ }
+ --argc;
+ f_flag = *++argv;
+ }
+ /*
+ * u_flag is needed, because we're
+ * writing to the disk.
+ */
+ u_flag = 1;
+ break;
+ case 'i':
+ i_flag = 1;
+ case 'u':
+ u_flag = 1;
+ break;
+ case 't':
+ t_flag = 1;
+ case 'v':
+ v_flag = 1;
+ break;
+ default:
+ goto usage;
+ }
+ }
+ }
+ }
+
+ if (argc > 0)
+ {
+ static char realname[12];
+
+ if(strncmp(argv[0], "/dev", 4) == 0)
+ disk = argv[0];
+ else
+ {
+ snprintf(realname, 12, "/dev/r%s", argv[0]);
+ disk = realname;
+ }
+
+ if (open_disk(u_flag) < 0)
+ {
+ fprintf(stderr, "Cannot open disk %s (%s)\n",
+ disk, sys_errlist[errno]);
+ exit(1);
+ }
+ }
+ else
+ {
+ int i, rv = 0;
+
+ for(i = 0; disks[i]; i++)
+ {
+ disk = disks[i];
+ rv = open_disk(u_flag);
+ if(rv != -2) break;
+ }
+ if(rv < 0)
+ {
+ fprintf(stderr, "Cannot open any disk (%s)\n",
+ sys_errlist[errno]);
+ exit(1);
+ }
+ }
+
+ printf("******* Working on device %s *******\n",disk);
+
+ if (f_flag)
+ {
+ if (read_s0() || i_flag)
+ {
+ reset_boot();
+ }
+
+ if (!read_config(f_flag))
+ {
+ exit(1);
+ }
+ if (v_flag)
+ {
+ print_s0(-1);
+ }
+ if (!t_flag)
+ {
+ write_s0();
+ }
+ }
+ else
+ {
+ if(u_flag)
+ {
+ get_params_to_use();
+ }
+ else
+ {
+ print_params();
+ }
+
+ if (read_s0())
+ init_sector0(1);
+
+ printf("Media sector size is %d\n", secsize);
+ printf("Warning: BIOS sector numbering starts with sector 1\n");
+ printf("Information from DOS bootblock is:\n");
+ if (partition == -1)
+ for (i = 0; i < NDOSPART; i++)
+ change_part(i);
+ else
+ change_part(partition);
+
+ if (u_flag || a_flag)
+ change_active(partition);
+
+ if (u_flag || a_flag) {
+ if (!t_flag)
+ {
+ printf("\nWe haven't changed the partition table yet. ");
+ printf("This is your last chance.\n");
+ }
+ print_s0(-1);
+ if (!t_flag)
+ {
+ if (ok("Should we write new partition table?"))
+ write_s0();
+ }
+ else
+ {
+ printf("\n-t flag specified -- partition table not written.\n");
+ }
+ }
+ }
+
+ exit(0);
+
+usage:
+ printf("fdisk {-a|-i|-u} [-f <config file> [-t] [-v]] [-{0,1,2,3}] [disk]\n");
+ return(1);
+}
+
+static void
+print_s0(int which)
+{
+int i;
+
+ print_params();
+ printf("Information from DOS bootblock is:\n");
+ if (which == -1)
+ for (i = 0; i < NDOSPART; i++)
+ printf("%d: ", i), print_part(i);
+ else
+ print_part(which);
+}
+
+static struct dos_partition mtpart = { 0 };
+
+static void
+print_part(int i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+
+ if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
+ printf(" start %ld, size %ld (%ld Meg), flag %x\n",
+ partp->dp_start,
+ partp->dp_size, partp->dp_size * secsize / (1024 * 1024),
+ partp->dp_flag);
+ printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
+ ,DPCYL(partp->dp_scyl, partp->dp_ssect)
+ ,DPSECT(partp->dp_ssect)
+ ,partp->dp_shd
+ ,DPCYL(partp->dp_ecyl, partp->dp_esect)
+ ,DPSECT(partp->dp_esect)
+ ,partp->dp_ehd);
+}
+
+
+static void
+init_boot(void)
+{
+ memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
+ mboot.signature = BOOT_MAGIC;
+}
+
+
+static void
+init_sector0(unsigned long start)
+{
+struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
+unsigned long size = disksecs - start;
+
+ init_boot();
+
+ partp->dp_typ = DOSPTYP_386BSD;
+ partp->dp_flag = ACTIVE;
+ partp->dp_start = start;
+ partp->dp_size = size;
+
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+}
+
+static void
+change_part(int i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+ printf("The data for partition %d is:\n", i);
+ print_part(i);
+
+ if (u_flag && ok("Do you want to change it?")) {
+ int tmp;
+
+ if (i_flag) {
+ bzero((char *)partp, sizeof (struct dos_partition));
+ if (i == 3) {
+ init_sector0(1);
+ printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
+ print_part(i);
+ }
+ }
+
+ do {
+ Decimal("sysid", partp->dp_typ, tmp);
+ Decimal("start", partp->dp_start, tmp);
+ Decimal("size", partp->dp_size, tmp);
+
+ if (ok("Explicitly specifiy beg/end address ?"))
+ {
+ int tsec,tcyl,thd;
+ tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
+ thd = partp->dp_shd;
+ tsec = DPSECT(partp->dp_ssect);
+ Decimal("beginning cylinder", tcyl, tmp);
+ Decimal("beginning head", thd, tmp);
+ Decimal("beginning sector", tsec, tmp);
+ partp->dp_scyl = DOSCYL(tcyl);
+ partp->dp_ssect = DOSSECT(tsec,tcyl);
+ partp->dp_shd = thd;
+
+ tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
+ thd = partp->dp_ehd;
+ tsec = DPSECT(partp->dp_esect);
+ Decimal("ending cylinder", tcyl, tmp);
+ Decimal("ending head", thd, tmp);
+ Decimal("ending sector", tsec, tmp);
+ partp->dp_ecyl = DOSCYL(tcyl);
+ partp->dp_esect = DOSSECT(tsec,tcyl);
+ partp->dp_ehd = thd;
+ } else {
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start + partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+ }
+
+ print_part(i);
+ } while (!ok("Are we happy with this entry?"));
+ }
+}
+
+static void
+print_params()
+{
+ printf("parameters extracted from in-core disklabel are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,cyls,heads,sectors,cylsecs);
+ if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
+ printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
+ printf("parameters to be used for BIOS calculations are:\n");
+ printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+ ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
+}
+
+static void
+change_active(int which)
+{
+int i;
+int active = 3, tmp;
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
+
+ if (a_flag && which != -1)
+ active = which;
+ if (!ok("Do you want to change the active partition?"))
+ return;
+ do
+ Decimal("active partition", active, tmp);
+ while (!ok("Are you happy with this choice"));
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ if (active >= 0 && active < NDOSPART)
+ partp[active].dp_flag = ACTIVE;
+}
+
+void
+get_params_to_use()
+{
+ int tmp;
+ print_params();
+ if (ok("Do you want to change our idea of what BIOS thinks ?"))
+ {
+ do
+ {
+ Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
+ Decimal("BIOS's idea of #heads", dos_heads, tmp);
+ Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
+ dos_cylsecs = dos_heads * dos_sectors;
+ print_params();
+ }
+ while(!ok("Are you happy with this choice"));
+ }
+}
+
+
+/***********************************************\
+* Change real numbers into strange dos numbers *
+\***********************************************/
+static void
+dos(sec, size, c, s, h)
+int sec, size;
+unsigned char *c, *s, *h;
+{
+int cy;
+int hd;
+
+ if (sec == 0 && size == 0) {
+ *s = *c = *h = 0;
+ return;
+ }
+
+ cy = sec / ( dos_cylsecs );
+ sec = sec - cy * ( dos_cylsecs );
+
+ hd = sec / dos_sectors;
+ sec = (sec - hd * dos_sectors) + 1;
+
+ *h = hd;
+ *c = cy & 0xff;
+ *s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
+}
+
+int fd;
+
+ /* Getting device status */
+
+static int
+open_disk(int u_flag)
+{
+struct stat st;
+
+ if (stat(disk, &st) == -1) {
+ fprintf(stderr, "%s: Can't get file status of %s\n",
+ name, disk);
+ return -1;
+ }
+ if ( !(st.st_mode & S_IFCHR) )
+ fprintf(stderr,"%s: Device %s is not character special\n",
+ name, disk);
+ if ((fd = open(disk, a_flag || u_flag ? O_RDWR : O_RDONLY)) == -1) {
+ if(errno == ENXIO)
+ return -2;
+ fprintf(stderr,"%s: Can't open device %s\n", name, disk);
+ return -1;
+ }
+ if (get_params(0) == -1) {
+ fprintf(stderr, "%s: Can't get disk parameters on %s\n",
+ name, disk);
+ return -1;
+ }
+ return fd;
+}
+
+static ssize_t
+read_disk(off_t sector, void *buf)
+{
+ lseek(fd,(sector * 512), 0);
+ if( secsize == 0 )
+ for( secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE; secsize *= 2 )
+ {
+ /* try the read */
+ int size = read(fd, buf, secsize);
+ if( size == secsize )
+ /* it worked so return */
+ return secsize;
+ }
+ else
+ return read( fd, buf, secsize );
+
+ /* we failed to read at any of the sizes */
+ return -1;
+}
+
+static ssize_t
+write_disk(off_t sector, void *buf)
+{
+ lseek(fd,(sector * 512), 0);
+ /* write out in the size that the read_disk found worked */
+ return write(fd, buf, secsize);
+}
+
+static int
+get_params()
+{
+
+ if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
+ fprintf(stderr,
+ "%s: Can't get disk parameters on %s; supplying dummy ones\n",
+ name, disk);
+ dos_cyls = cyls = 1;
+ dos_heads = heads = 1;
+ dos_sectors = sectors = 1;
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+ return disksecs;
+ }
+
+ dos_cyls = cyls = disklabel.d_ncylinders;
+ dos_heads = heads = disklabel.d_ntracks;
+ dos_sectors = sectors = disklabel.d_nsectors;
+ dos_cylsecs = cylsecs = heads * sectors;
+ disksecs = cyls * heads * sectors;
+
+ return (disksecs);
+}
+
+
+static int
+read_s0()
+{
+ if (read_disk(0, (char *) mboot.bootinst) == -1) {
+ fprintf(stderr, "%s: Can't read fdisk partition table\n", name);
+ return -1;
+ }
+ if (mboot.signature != BOOT_MAGIC) {
+ fprintf(stderr, "%s: Invalid fdisk partition table found\n",
+ name);
+ /* So should we initialize things */
+ return -1;
+ }
+ return 0;
+}
+
+static int
+write_s0()
+{
+ int flag;
+ if (iotest) {
+ print_s0(-1);
+ return 0;
+ }
+ /*
+ * write enable label sector before write (if necessary),
+ * disable after writing.
+ * needed if the disklabel protected area also protects
+ * sector 0. (e.g. empty disk)
+ */
+ flag = 1;
+#ifdef NOT_NOW
+ if (ioctl(fd, DIOCWLABEL, &flag) < 0)
+ perror("ioctl DIOCWLABEL");
+#endif
+ if (write_disk(0, (char *) mboot.bootinst) == -1) {
+ fprintf(stderr, "%s: Can't write fdisk partition table\n",
+ name);
+ return -1;
+ flag = 0;
+#ifdef NOT_NOW
+ (void) ioctl(fd, DIOCWLABEL, &flag);
+#endif
+ }
+ return(0);
+}
+
+
+static int
+ok(str)
+char *str;
+{
+ printf("%s [n] ", str);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (*lbuf &&
+ (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
+ !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
+ return 1;
+ else
+ return 0;
+}
+
+static int
+decimal(char *str, int *num, int deflt)
+{
+int acc = 0, c;
+char *cp;
+
+ while (1) {
+ printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = acc * 10 + c - '0';
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid decimal number. Try again\n",
+ lbuf);
+ }
+
+}
+
+#if 0
+static int
+hex(char *str, int *num, int deflt)
+{
+int acc = 0, c;
+char *cp;
+
+ while (1) {
+ printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ cp = lbuf;
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c)
+ return 0;
+ while ((c = *cp++)) {
+ if (c <= '9' && c >= '0')
+ acc = (acc << 4) + c - '0';
+ else if (c <= 'f' && c >= 'a')
+ acc = (acc << 4) + c - 'a' + 10;
+ else if (c <= 'F' && c >= 'A')
+ acc = (acc << 4) + c - 'A' + 10;
+ else
+ break;
+ }
+ if (c == ' ' || c == '\t')
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (!c) {
+ *num = acc;
+ return 1;
+ } else
+ printf("%s is an invalid hex number. Try again\n",
+ lbuf);
+ }
+
+}
+
+static int
+string(char *str, char **ans)
+{
+int c;
+char *cp = lbuf;
+
+ while (1) {
+ printf("Supply a string value for \"%s\" [%s] ", str, *ans);
+ fgets(lbuf, LBUF, stdin);
+ lbuf[strlen(lbuf)-1] = 0;
+
+ if (!*lbuf)
+ return 0;
+
+ while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+ if (c == '"') {
+ c = *++cp;
+ *ans = cp;
+ while ((c = *cp) && c != '"') cp++;
+ } else {
+ *ans = cp;
+ while ((c = *cp) && c != ' ' && c != '\t') cp++;
+ }
+
+ if (c)
+ *cp = 0;
+ return 1;
+ }
+}
+#endif
+
+static char *
+get_type(int type)
+{
+ int numentries = (sizeof(part_types)/sizeof(struct part_type));
+ int counter = 0;
+ struct part_type *ptr = part_types;
+
+
+ while(counter < numentries)
+ {
+ if(ptr->type == type)
+ {
+ return(ptr->name);
+ }
+ ptr++;
+ counter++;
+ }
+ return("unknown");
+}
+
+
+static void
+parse_config_line(line, command)
+ char *line;
+ CMD *command;
+{
+ char *cp, *end;
+
+ cp = line;
+ while (1) /* dirty trick used to insure one exit point for this
+ function */
+ {
+ memset(command, 0, sizeof(*command));
+
+ while (isspace(*cp)) ++cp;
+ if (*cp == '\0' || *cp == '#')
+ {
+ break;
+ }
+ command->cmd = *cp++;
+
+ /*
+ * Parse args
+ */
+ while (1)
+ {
+ while (isspace(*cp)) ++cp;
+ if (*cp == '#')
+ {
+ break; /* found comment */
+ }
+ if (isalpha(*cp))
+ {
+ command->args[command->n_args].argtype = *cp++;
+ }
+ if (!isdigit(*cp))
+ {
+ break; /* assume end of line */
+ }
+ end = NULL;
+ command->args[command->n_args].arg_val = strtol(cp, &end, 0);
+ if (cp == end)
+ {
+ break; /* couldn't parse number */
+ }
+ cp = end;
+ command->n_args++;
+ }
+ break;
+ }
+}
+
+
+static int
+process_geometry(command)
+ CMD *command;
+{
+ int status = 1, i;
+
+ while (1)
+ {
+ geom_processed = 1;
+ if (part_processed)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: the geometry specification line must occur before\n\
+ all partition specifications.\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ if (command->n_args != 3)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of geometry args\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ dos_cyls = -1;
+ dos_heads = -1;
+ dos_sectors = -1;
+ for (i = 0; i < 3; ++i)
+ {
+ switch (command->args[i].argtype)
+ {
+ case 'c':
+ dos_cyls = command->args[i].arg_val;
+ break;
+ case 'h':
+ dos_heads = command->args[i].arg_val;
+ break;
+ case 's':
+ dos_sectors = command->args[i].arg_val;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: ERROR line %d: unknown geometry arg type: '%c' (0x%02x)\n",
+ name, current_line_number, command->args[i].argtype,
+ command->args[i].argtype);
+ status = 0;
+ break;
+ }
+ }
+ if (status == 0)
+ {
+ break;
+ }
+
+ dos_cylsecs = dos_heads * dos_sectors;
+
+ /*
+ * Do sanity checks on parameter values
+ */
+ if (dos_cyls < 0)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of cylinders not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ if (dos_cyls == 0 || dos_cyls > 1024)
+ {
+ fprintf(stderr,
+ "%s: WARNING line %d: number of cylinders (%d) may be out-of-range\n\
+ (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
+ is dedicated to FreeBSD).\n",
+ name, current_line_number, dos_cyls);
+ }
+
+ if (dos_heads < 0)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of heads not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ else if (dos_heads < 1 || dos_heads > 256)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of heads must be within (1-256)\n",
+ name, current_line_number);
+ status = 0;
+ }
+
+ if (dos_sectors < 0)
+ {
+ fprintf(stderr, "%s: ERROR line %d: number of sectors not specified\n",
+ name, current_line_number);
+ status = 0;
+ }
+ else if (dos_sectors < 1 || dos_sectors > 63)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: number of sectors must be within (1-63)\n",
+ name, current_line_number);
+ status = 0;
+ }
+
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_partition(command)
+ CMD *command;
+{
+ int status = 0, partition;
+ unsigned long chunks, adj_size, max_end;
+ struct dos_partition *partp;
+
+ while (1)
+ {
+ part_processed = 1;
+ if (command->n_args != 4)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of partition args\n",
+ name, current_line_number);
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 0 || partition > 3)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ partp = ((struct dos_partition *) &mboot.parts) + partition;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ partp->dp_typ = command->args[1].arg_val;
+ partp->dp_start = command->args[2].arg_val;
+ partp->dp_size = command->args[3].arg_val;
+ max_end = partp->dp_start + partp->dp_size;
+
+ if (partp->dp_typ == 0)
+ {
+ /*
+ * Get out, the partition is marked as unused.
+ */
+ /*
+ * Insure that it's unused.
+ */
+ bzero((char *)partp, sizeof (struct dos_partition));
+ status = 1;
+ break;
+ }
+
+ /*
+ * Adjust start upwards, if necessary, to fall on an head boundary.
+ */
+ if (partp->dp_start % dos_sectors != 0)
+ {
+ adj_size =
+ (partp->dp_start / dos_sectors + 1) * dos_sectors;
+ if (adj_size > max_end)
+ {
+ /*
+ * Can't go past end of partition
+ */
+ fprintf(stderr,
+ "%s: ERROR line %d: unable to adjust start of partition %d to fall on\n\
+ a cylinder boundary.\n",
+ name, current_line_number, partition);
+ break;
+ }
+ fprintf(stderr,
+ "%s: WARNING: adjusting start offset of partition '%d' from %d\n\
+ to %d, to round to an head boundary.\n",
+ name, partition, partp->dp_start, adj_size);
+ partp->dp_start = adj_size;
+ }
+
+ /*
+ * Adjust size downwards, if necessary, to fall on a cylinder
+ * boundary.
+ */
+ chunks =
+ ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
+ adj_size = chunks - partp->dp_start;
+ if (adj_size != partp->dp_size)
+ {
+ fprintf(stderr,
+ "%s: WARNING: adjusting size of partition '%d' from %d to %d,\n\
+ to round to a cylinder boundary.\n",
+ name, partition, partp->dp_size, adj_size);
+ if (chunks > 0)
+ {
+ partp->dp_size = adj_size;
+ }
+ else
+ {
+ partp->dp_size = 0;
+ }
+ }
+ if (partp->dp_size < 1)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: size for partition '%d' is zero.\n",
+ name, current_line_number, partition);
+ break;
+ }
+
+ dos(partp->dp_start, partp->dp_size,
+ &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+ dos(partp->dp_start+partp->dp_size - 1, partp->dp_size,
+ &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_active(command)
+ CMD *command;
+{
+ int status = 0, partition, i;
+ struct dos_partition *partp;
+
+ while (1)
+ {
+ active_processed = 1;
+ if (command->n_args != 1)
+ {
+ fprintf(stderr,
+ "%s: ERROR line %d: incorrect number of active args\n",
+ name, current_line_number);
+ status = 0;
+ break;
+ }
+ partition = command->args[0].arg_val;
+ if (partition < 0 || partition > 3)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ /*
+ * Reset active partition
+ */
+ partp = ((struct dos_partition *) &mboot.parts);
+ for (i = 0; i < NDOSPART; i++)
+ partp[i].dp_flag = 0;
+ partp[partition].dp_flag = ACTIVE;
+
+ status = 1;
+ break;
+ }
+ return (status);
+}
+
+
+static int
+process_line(line)
+ char *line;
+{
+ CMD command;
+ int status = 1;
+
+ while (1)
+ {
+ parse_config_line(line, &command);
+ switch (command.cmd)
+ {
+ case 0:
+ /*
+ * Comment or blank line
+ */
+ break;
+ case 'g':
+ /*
+ * Set geometry
+ */
+ status = process_geometry(&command);
+ break;
+ case 'p':
+ status = process_partition(&command);
+ break;
+ case 'a':
+ status = process_active(&command);
+ break;
+ default:
+ status = 0;
+ break;
+ }
+ break;
+ }
+ return (status);
+}
+
+
+static int
+read_config(config_file)
+ char *config_file;
+{
+ FILE *fp = NULL;
+ int status = 1;
+ char buf[1010];
+
+ while (1) /* dirty trick used to insure one exit point for this
+ function */
+ {
+ if (strcmp(config_file, "-") != 0)
+ {
+ /*
+ * We're not reading from stdin
+ */
+ if ((fp = fopen(config_file, "r")) == NULL)
+ {
+ status = 0;
+ break;
+ }
+ }
+ else
+ {
+ fp = stdin;
+ }
+ current_line_number = 0;
+ while (!feof(fp))
+ {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ {
+ break;
+ }
+ ++current_line_number;
+ status = process_line(buf);
+ if (status == 0)
+ {
+ break;
+ }
+ }
+ break;
+ }
+ if (fp)
+ {
+ /*
+ * It doesn't matter if we're reading from stdin, as we've reached EOF
+ */
+ fclose(fp);
+ }
+ return (status);
+}
+
+
+static void
+reset_boot(void)
+{
+ int i;
+ struct dos_partition *partp;
+
+ init_boot();
+ for (i = 0; i < 4; ++i)
+ {
+ partp = ((struct dos_partition *) &mboot.parts) + i;
+ bzero((char *)partp, sizeof (struct dos_partition));
+ }
+}
diff --git a/sbin/i386/ft/Makefile b/sbin/i386/ft/Makefile
new file mode 100644
index 0000000..1cc600b
--- /dev/null
+++ b/sbin/i386/ft/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.4 1995/08/29 14:04:39 ache Exp $
+
+PROG= ft
+MAN8= ft.8
+SRCS= ft.c ftecc.c
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/ft/ft.8 b/sbin/i386/ft/ft.8
new file mode 100644
index 0000000..962001e
--- /dev/null
+++ b/sbin/i386/ft/ft.8
@@ -0,0 +1,86 @@
+.\" Copyright (c) 1980, 1989, 1991 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.
+.\"
+.\" @(#)ft.8
+.\"
+.Dd February 7, 1994
+.Dt FT 8
+.Os BSD 4
+.Sh NAME
+.Nm ft
+.Nd QIC 40/80 floppy tape drive controller
+.Sh SYNOPSIS
+.Nm ft
+.Op Fl f Ar tape
+.Op Ar description
+.Sh DESCRIPTION
+The
+.Nm ft
+command allows multi-volume dump, extract, and view of tape labels, for
+any pre-formatted QIC-40/80 tapes. It is totally system dependent,
+and has nothing to do with the QIC standards.
+.Pp
+.Nm ft
+is used primarily as a filter for tape i/o.
+For example, to save and compress the /usr directory to tape:
+.Bd -literal -offset indent
+% tar cvzf - /usr | ft "/usr save"
+.Ed
+.Pp
+To extract /usr from tape:
+.Bd -literal -offset indent
+% ft | tar xvzf -
+.Ed
+.\" .Sh SEE ALSO
+.\" .Xr qtar 1
+.Sh BUGS
+Formatting/Verifying is in the works. You will need to use your
+existing backup program to do this for the time being.
+.Sh NOTES
+The floppy tape driver supports tape drives such as the Colorado
+Jumbo, Mountain Summit Express, some Archive/Conner models, and
+probably many others. These tape drives attach between your floppy
+disk controller card and your existing floppy disks' ribbon cable.
+This driver does not currently support attachments via a proprietary
+tape controller card or by the parallel port.
+.Pp
+QIC-40/80 drives are more CPU intensive than a SCSI drive. This is
+really only a factor if your machine is networked or has multiple concurrent
+users. For personal use (i.e. your typical home Unix user), response time
+is perfectly acceptable. The tape drives cannot detect write errors.
+Instead, they make up for it by using CRC's, error correction, and bad
+spot mapping. Formatting time is extremely long because of this. The
+drive makes a first pass over the entire tape writing out sectors. It
+then makes a second pass at a slower rate than usual (for sensitivity)
+to detect bad spots on the tape. Typically it takes an hour to format
+a single QIC-80 (120Mb uncompressed) tape.
+.Sh AUTHOR
+Steve Gerakines <steve2@genesis.nred.ma.us>
diff --git a/sbin/i386/ft/ft.c b/sbin/i386/ft/ft.c
new file mode 100644
index 0000000..b1dbbb2
--- /dev/null
+++ b/sbin/i386/ft/ft.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 1993, 1994 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ft.c - simple floppy tape filter
+ *
+ * 06/07/94 v1.0 ++sg
+ * Added support for tape retension. Added retries for ecc failures.
+ * Moved to release.
+ *
+ * 01/28/94 v0.3b (Jim Babb)
+ * Fixed bug when all sectors in a segment are marked bad.
+ *
+ * 10/30/93 v0.3
+ * Minor revisions. Seems pretty stable.
+ *
+ * 09/02/93 v0.2 pl01
+ * Initial revision.
+ *
+ * usage: ft [ -f tape ] [ description ]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/ftape.h>
+
+#define DEFQIC "/dev/rft0"
+
+char buff[QCV_SEGSIZE]; /* scratch buffer */
+char hbuff[QCV_SEGSIZE]; /* header buffer */
+QIC_Header *hptr = (QIC_Header *)hbuff; /* header structure */
+int hsn = -1; /* segment number of header */
+int dhsn = -1; /* segment number of duplicate header */
+int tfd; /* tape file descriptor */
+QIC_Geom geo; /* tape geometry */
+int tvno = 1; /* tape volume number */
+int tvlast; /* TRUE if last volume in set */
+long tvsize = 0; /* tape volume size in bytes */
+long tvtime = NULL; /* tape change time */
+char *tvnote = ""; /* tape note */
+int doretension = 0; /* TRUE if we should retension tape */
+
+/* Lookup the badmap for a given track and segment. */
+#define BADMAP(t,s) hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
+
+/* Retrieve values from a character array. */
+#define UL_VAL(s,p) (*((ULONG *)&(s)[p]))
+#define US_VAL(s,p) (*((USHORT *)&(s)[p]))
+
+#define equal(s1,s2) (strcmp(s1, s2) == 0)
+
+
+/*
+ * Print tape usage and then leave.
+ */
+void
+usage(void)
+{
+ fprintf(stderr, "usage: ft [ -r ] [ -f device ] [ \"description\" ]\n");
+ exit(1);
+}
+
+
+/*
+ * Check status of tape drive
+ */
+int
+check_stat(int fd, int wr)
+{
+ int r, s;
+ int sawit = 0;
+
+ /* get tape status */
+ if (ioctl(fd, QIOSTATUS, &s) < 0) {
+ fprintf(stderr, "could not get drive status\n");
+ return(1);
+ }
+
+ /* wait for the tape drive to become ready */
+ while ((s & QS_READY) == 0) {
+ if (!sawit) {
+ fprintf(stderr, "waiting for drive to become ready...\n");
+ sawit = 1;
+ }
+ sleep(2);
+ if (ioctl(fd, QIOSTATUS, &s) < 0) {
+ fprintf(stderr, "could not get drive status\n");
+ return(1);
+ }
+ }
+
+ if ((s & QS_FMTOK) == 0) {
+ fprintf(stderr, "tape is not formatted\n");
+ return(2);
+ }
+
+ if (wr && (s & QS_RDONLY) != 0) {
+ fprintf(stderr, "tape is write protected\n");
+ return(3);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Convert time_t value to QIC time value.
+ */
+ULONG
+qtimeval(time_t t)
+{
+ struct tm *tp;
+ ULONG r;
+
+ tp = localtime(&t);
+ r = 2678400 * tp->tm_mon +
+ 86400 *(tp->tm_mday-1) +
+ 3600 * tp->tm_hour +
+ 60 * tp->tm_min +
+ tp->tm_sec;
+ r |= (tp->tm_year - 70) << 25;
+ return(r);
+}
+
+
+/*
+ * Return tm struct from QIC date format.
+ */
+struct tm *
+qtime(UCHAR *qt)
+{
+ ULONG *vp = (ULONG *)qt;
+ struct tm t;
+ ULONG v;
+ time_t tv;
+
+ v = *vp;
+ t.tm_year = ((v >> 25) & 0x7f)+70; v &= 0x1ffffff;
+ t.tm_mon = v / 2678400; v %= 2678400;
+ t.tm_mday = v / 86400 + 1; v %= 86400;
+ t.tm_hour = v / 3600; v %= 3600;
+ t.tm_min = v / 60; v %= 60;
+ t.tm_sec = v;
+ t.tm_wday = 0; /* XXX - let mktime do the real work */
+ t.tm_yday = 0;
+ t.tm_isdst = 0;
+ t.tm_gmtoff = 0;
+ t.tm_zone = NULL;
+ tv = mktime(&t);
+ return(localtime(&tv));
+}
+
+
+/*
+ * Return a string, zero terminated.
+ */
+char *qstr(char *str, int nchar)
+{
+ static char tstr[256];
+ strncpy(tstr, str, nchar);
+ tstr[nchar] = '\0';
+ return(tstr);
+}
+
+
+/*
+ * Read header from tape
+ */
+int
+get_header(int fd)
+{
+ int r, sn, bytes;
+ QIC_Segment s;
+ int gothdr = 0;
+
+ if (ioctl(fd, QIOGEOM, &geo) < 0) {
+ fprintf(stderr, "couldn't determine tape geometry\n");
+ return(1);
+ }
+
+ /* Get the header and duplicate */
+ for (sn = 0; sn < 16; sn++) {
+ s.sg_trk = 0;
+ s.sg_seg = sn;
+ s.sg_badmap = 0;
+ s.sg_data = (UCHAR *)&buff[0];
+ ioctl(fd, QIOREAD, &s);
+ r = check_parity(s.sg_data, 0, s.sg_crcmap);
+ if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
+ s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
+ if (hsn >= 0) {
+ dhsn = sn;
+ if (!r && !gothdr) {
+ fprintf(stderr, "using secondary header\n");
+ bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
+ gothdr = 1;
+ }
+ break;
+ }
+ hsn = sn;
+ if (!r) {
+ bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
+ gothdr = 1;
+ } else {
+ fprintf(stderr, "too many errors in primary header\n");
+ }
+ }
+ }
+
+ if (!gothdr) {
+ fprintf(stderr, "couldn't read header segment\n");
+ ioctl(fd, QIOREWIND);
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Open /dev/tty and ask for next volume.
+ */
+ask_vol(int vn)
+{
+ FILE *inp;
+ int fd;
+ char c;
+
+ if ((fd = open("/dev/tty", 2)) < 0) {
+ fprintf(stderr, "argh!! can't open /dev/tty\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "Insert ftfilt volume %02d and press enter:", vn);
+ read(fd, &c, 1);
+ close(fd);
+}
+
+
+/*
+ * Return the name of the tape only.
+ */
+void
+do_getname(void)
+{
+ if (check_stat(tfd, 0)) exit(1);
+ if (get_header(tfd)) exit(1);
+ fprintf(stderr, "\"%s\" - %s",
+ qstr(hptr->qh_tname,44), asctime(qtime(hptr->qh_chgdate)));
+}
+
+
+/*
+ * Extract data from tape to stdout.
+ */
+void
+do_read(void)
+{
+ int sno, vno, sbytes, r, eccfails;
+ long curpos;
+ char *hname;
+ QIC_Segment s;
+
+ /* Process multiple volumes if necessary */
+ vno = 1;
+ for (;;) {
+ if (check_stat(tfd, 0)) {
+ ask_vol(vno);
+ continue;
+ }
+
+ if (doretension) {
+ ioctl(tfd, QIOBOT);
+ ioctl(tfd, QIOEOT);
+ ioctl(tfd, QIOBOT);
+ }
+
+ if (get_header(tfd)) {
+ ask_vol(vno);
+ continue;
+ }
+
+ /* extract volume and header info from label */
+ hname = hptr->qh_tname;
+ hname[43] = '\0';
+ tvno = atoi(&hname[11]);
+ tvlast = (hname[10] == '*') ? 1 : 0;
+ tvsize = atoi(&hname[14]);
+ tvnote = &hname[25];
+ if (vno != tvno || strncmp(hname, "ftfilt", 6) != 0) {
+ fprintf(stderr, "Incorrect volume inserted. This tape is:\n");
+ fprintf(stderr,"\"%s\" - %s\n", hname,
+ asctime(qtime(hptr->qh_chgdate)));
+ ask_vol(vno);
+ continue;
+ }
+
+ /* Process this volume */
+ curpos = 0;
+ eccfails = 0;
+ sno = hptr->qh_first;
+ while (tvsize > 0) {
+ s.sg_trk = sno / geo.g_segtrk;
+ s.sg_seg = sno % geo.g_segtrk;
+ s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
+ sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
+ s.sg_data = (UCHAR *)&buff[0];
+ if (sbytes <= 0) {
+ sno++;
+ continue;
+ }
+ if (ioctl(tfd, QIOREAD, &s) < 0) perror("QIOREAD");
+
+ if (check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap)) {
+ if (++eccfails <= 5) {
+ fprintf(stderr,
+ "ft: retry %d at segment %d byte %ld\n",
+ eccfails, sno, curpos);
+ continue;
+ } else
+ fprintf(stderr,
+ "ft: *** ecc failure in segment %d at byte %ld\n",
+ sno, curpos);
+ }
+ if (tvsize < sbytes) sbytes = tvsize;
+ write(1, s.sg_data, sbytes);
+ tvsize -= sbytes;
+ curpos += sbytes;
+ sno++;
+ eccfails = 0;
+ }
+ if (tvlast) break;
+ ioctl(tfd, QIOREWIND);
+ ask_vol(++vno);
+ }
+}
+
+
+/*
+ * Dump data from stdin to tape.
+ */
+void
+do_write(void)
+{
+ int sno, vno, amt, sbytes;
+ int c, maxseg, r;
+ ULONG qnow;
+ QIC_Segment s;
+ char tmpstr[80];
+
+ qnow = qtimeval(time(NULL));
+ vno = 1;
+
+ for (;;) {
+ if (check_stat(tfd, 1)) {
+ ask_vol(vno);
+ continue;
+ }
+
+ if (doretension) {
+ ioctl(tfd, QIOBOT);
+ ioctl(tfd, QIOEOT);
+ ioctl(tfd, QIOBOT);
+ }
+
+ if (get_header(tfd)) {
+ ask_vol(vno);
+ continue;
+ }
+
+ maxseg = geo.g_segtrk * geo.g_trktape - 1;
+ sno = hptr->qh_first;
+ tvno = vno;
+ tvsize = 0;
+ tvlast = 0;
+
+ /* Process until end of volume or end of data */
+ for (sno = hptr->qh_first; sno < maxseg && tvlast == 0; ++sno) {
+ /* Prepare to load the next segment */
+ s.sg_trk = sno / geo.g_segtrk;
+ s.sg_seg = sno % geo.g_segtrk;
+ s.sg_badmap = BADMAP(s.sg_trk,s.sg_seg);
+ sbytes = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
+ s.sg_data = (UCHAR *)&buff[0];
+
+ /* Ugh. Loop to get the full amt. */
+ for (amt = 0; amt < sbytes; amt += r) {
+ r = read(0, &s.sg_data[amt], sbytes - amt);
+ if (r <= 0) {
+ tvlast = 1;
+ break;
+ }
+ }
+
+ /* skip the segment if *all* sectors are flagged as bad */
+ if (amt) {
+ if (amt < sbytes)
+ bzero(&s.sg_data[amt], sbytes - amt);
+ r = set_parity(s.sg_data, s.sg_badmap);
+ if (r) fprintf(stderr, "** warning: ecc problem !!\n");
+ if (ioctl(tfd, QIOWRITE, &s) < 0) {
+ perror("QIOWRITE");
+ exit(1);
+ }
+ tvsize += amt;
+ }
+ }
+
+ /* Build new header info */
+ /* ftfilt vol*xx yyyyyyyyyy note56789012345678 */
+ /* 01234567890123456789012345678901234567890123 */
+
+ sprintf(tmpstr, "ftfilt vol%s%02d %010d %s",
+ (tvlast) ? "*" : " ", tvno, tvsize, tvnote);
+ strncpy(hptr->qh_tname, tmpstr, 44);
+ UL_VAL(hptr->qh_chgdate,0) = qnow;
+
+ /* Update the header for this volume */
+ if (hsn >= 0) {
+ s.sg_trk = hsn / geo.g_segtrk;
+ s.sg_seg = hsn % geo.g_segtrk;
+ s.sg_badmap = 0;
+ s.sg_data = (UCHAR *)hbuff;
+ r = set_parity(s.sg_data, s.sg_badmap);
+ if (r) fprintf(stderr, "** warning: header ecc problem !!\n");
+ if (ioctl(tfd, QIOWRITE, &s) < 0) {
+ perror("QIOWRITE");
+ exit(1);
+ }
+ }
+ if (dhsn >= 0) {
+ s.sg_trk = dhsn / geo.g_segtrk;
+ s.sg_seg = dhsn % geo.g_segtrk;
+ s.sg_badmap = 0;
+ s.sg_data = (UCHAR *)hbuff;
+ r = set_parity(s.sg_data, s.sg_badmap);
+ if (r) fprintf(stderr, "** warning: duphdr ecc problem !!\n");
+ if (ioctl(tfd, QIOWRITE, &s) < 0) {
+ perror("QIOWRITE");
+ exit(1);
+ }
+ }
+ ioctl(tfd, QIOREWIND);
+ if (tvlast) break;
+ ask_vol(++vno);
+ }
+}
+
+
+/*
+ * Entry.
+ */
+void
+main(int argc, char *argv[])
+{
+ int r, s, i;
+ char *tape, *getenv();
+
+
+ /* Get device from environment, command line will override. */
+ if ((tape = getenv("TAPE")) == NULL) tape = DEFQIC;
+
+ /* Process args. */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') break;
+ switch (argv[i][1]) {
+ case 'f':
+ case 't':
+ if (i == (argc - 1)) usage();
+ tape = argv[++i];
+ break;
+ case 'r':
+ doretension = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (i < (argc - 1)) usage();
+ if (i < argc) {
+ tvnote = argv[i];
+ if (strlen(tvnote) > 18) argv[i][18] = '\0';
+ }
+
+ /* Open the tape device */
+ if ((tfd = open(tape, 2)) < 0) {
+ perror(tape);
+ exit(1);
+ }
+
+ if (!isatty(0))
+ do_write();
+ else if (!isatty(1))
+ do_read();
+ else
+ do_getname();
+
+ close(tfd);
+ exit(0);
+}
diff --git a/sbin/i386/ft/ftecc.c b/sbin/i386/ft/ftecc.c
new file mode 100644
index 0000000..95de073
--- /dev/null
+++ b/sbin/i386/ft/ftecc.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 1994 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ftecc.c - QIC-40/80 Reed-Solomon error correction
+ * 05/30/94 v1.0 ++sg
+ * Did some minor optimization. The multiply by 0xc0 was a dog so it
+ * was replaced with a table lookup. Fixed a couple of places where
+ * bad sectors could go unnoticed. Moved to release.
+ *
+ * 03/22/94 v0.4
+ * Major re-write. It can handle everything required by QIC now.
+ *
+ * 09/14/93 v0.2 pl01
+ * Modified slightly to fit with my driver. Based entirely upon David
+ * L. Brown's package.
+ */
+#include <sys/ftape.h>
+
+/* Inverse matrix */
+struct inv_mat {
+ UCHAR log_denom; /* Log of the denominator */
+ UCHAR zs[3][3]; /* The matrix */
+};
+
+
+/*
+ * Powers of x, modulo 255.
+ */
+static const UCHAR alpha_power[] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
+ 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
+ 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
+ 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
+ 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
+ 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
+ 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
+ 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
+ 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
+ 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
+ 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
+ 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
+ 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
+ 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
+ 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
+ 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
+ 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
+ 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
+ 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
+ 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
+ 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
+ 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
+ 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
+ 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
+ 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
+ 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
+ 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
+ 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
+ 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
+ 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
+ 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01
+};
+
+
+/*
+ * Log table, modulo 255 + 1.
+ */
+static const UCHAR alpha_log[] = {
+ 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
+ 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
+ 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
+ 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
+ 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
+ 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
+ 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
+ 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
+ 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
+ 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
+ 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
+ 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
+ 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
+ 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
+ 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
+ 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
+ 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
+ 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
+ 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
+ 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
+ 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
+ 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
+ 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
+ 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
+ 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
+ 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
+ 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
+ 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
+ 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
+ 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
+ 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
+ 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
+};
+
+
+/*
+ * Multiplication table for 0xc0.
+ */
+static const UCHAR mult_c0[] = {
+ 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
+ 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
+ 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1,
+ 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed,
+ 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9,
+ 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5,
+ 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81,
+ 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d,
+ 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29,
+ 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35,
+ 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11,
+ 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d,
+ 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59,
+ 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45,
+ 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61,
+ 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d,
+ 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e,
+ 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92,
+ 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6,
+ 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa,
+ 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe,
+ 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2,
+ 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6,
+ 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda,
+ 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e,
+ 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72,
+ 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56,
+ 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a,
+ 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e,
+ 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02,
+ 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26,
+ 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a
+};
+
+
+/*
+ * Return number of sectors available in a segment.
+ */
+int
+sect_count(ULONG badmap)
+{
+ int i, amt;
+
+ for (amt = QCV_BLKSEG, i = 0; i < QCV_BLKSEG; i++)
+ if (badmap & (1 << i)) amt--;
+ return(amt);
+}
+
+
+/*
+ * Return number of bytes available in a segment.
+ */
+int
+sect_bytes(ULONG badmap)
+{
+ int i, amt;
+
+ for (amt = QCV_SEGSIZE, i = 0; i < QCV_BLKSEG; i++)
+ if (badmap & (1 << i)) amt -= QCV_BLKSIZE;
+ return(amt);
+}
+
+
+/*
+ * Multiply two numbers in the field.
+ */
+static inline UCHAR
+multiply(UCHAR a, UCHAR b)
+{
+ int tmp;
+
+ if (!a || !b) return(0);
+ tmp = alpha_log[a] + alpha_log[b];
+ if (tmp > 254) tmp -= 255;
+ return(alpha_power[tmp]);
+}
+
+
+/*
+ * Multiply by an exponent.
+ */
+static inline UCHAR
+multiply_out(UCHAR a, int b)
+{
+ int tmp;
+
+ if (!a) return(0);
+ tmp = alpha_log[a] + b;
+ if (tmp > 254) tmp -= 255;
+ return(alpha_power[tmp]);
+}
+
+
+/*
+ * Divide two numbers.
+ */
+static inline UCHAR
+divide(UCHAR a, UCHAR b)
+{
+ int tmp;
+
+ if (!a || !b) return(0);
+ tmp = alpha_log[a] - alpha_log[b];
+ if (tmp < 0) tmp += 255;
+ return (alpha_power[tmp]);
+}
+
+
+/*
+ * Divide using exponent.
+ */
+static inline UCHAR
+divide_out(UCHAR a, UCHAR b)
+{
+ int tmp;
+
+ if (!a) return 0;
+ tmp = alpha_log[a] - b;
+ if (tmp < 0) tmp += 255;
+ return (alpha_power[tmp]);
+}
+
+
+/*
+ * This returns the value z^{a-b}.
+ */
+static inline UCHAR
+z_of_ab(UCHAR a, UCHAR b)
+{
+ int tmp = a - b;
+
+ if (tmp < 0) tmp += 255;
+ return(alpha_power[tmp]);
+}
+
+
+/*
+ * Calculate the inverse matrix for two or three errors. Returns 0
+ * if there is no inverse or 1 if successful.
+ */
+static inline int
+calculate_inverse(int nerrs, int *pblk, struct inv_mat *inv)
+{
+ /* First some variables to remember some of the results. */
+ UCHAR z20, z10, z21, z12, z01, z02;
+ UCHAR i0, i1, i2;
+ UCHAR iv0, iv1, iv2;
+
+ if (nerrs < 2) return(1);
+ if (nerrs > 3) return(0);
+
+ i0 = pblk[0]; i1 = pblk[1]; i2 = pblk[2];
+ if (nerrs == 2) {
+ /* 2 errs */
+ z01 = alpha_power[255 - i0];
+ z02 = alpha_power[255 - i1];
+ inv->log_denom = (z01 ^ z02);
+ if (!inv->log_denom) return(0);
+ inv->log_denom = 255 - alpha_log[inv->log_denom];
+
+ inv->zs[0][0] = multiply_out( 1, inv->log_denom);
+ inv->zs[0][1] = multiply_out(z02, inv->log_denom);
+ inv->zs[1][0] = multiply_out( 1, inv->log_denom);
+ inv->zs[1][1] = multiply_out(z01, inv->log_denom);
+ } else {
+ /* 3 errs */
+ z20 = z_of_ab (i2, i0);
+ z10 = z_of_ab (i1, i0);
+ z21 = z_of_ab (i2, i1);
+ z12 = z_of_ab (i1, i2);
+ z01 = z_of_ab (i0, i1);
+ z02 = z_of_ab (i0, i2);
+ inv->log_denom = (z20 ^ z10 ^ z21 ^ z12 ^ z01 ^ z02);
+ if (!inv->log_denom) return(0);
+ inv->log_denom = 255 - alpha_log[inv->log_denom];
+
+ iv0 = alpha_power[255 - i0];
+ iv1 = alpha_power[255 - i1];
+ iv2 = alpha_power[255 - i2];
+ i0 = alpha_power[i0];
+ i1 = alpha_power[i1];
+ i2 = alpha_power[i2];
+ inv->zs[0][0] = multiply_out(i1 ^ i2, inv->log_denom);
+ inv->zs[0][1] = multiply_out(z21 ^ z12, inv->log_denom);
+ inv->zs[0][2] = multiply_out(iv1 ^ iv2, inv->log_denom);
+ inv->zs[1][0] = multiply_out(i0 ^ i2, inv->log_denom);
+ inv->zs[1][1] = multiply_out(z20 ^ z02, inv->log_denom);
+ inv->zs[1][2] = multiply_out(iv0 ^ iv2, inv->log_denom);
+ inv->zs[2][0] = multiply_out(i0 ^ i1, inv->log_denom);
+ inv->zs[2][1] = multiply_out(z10 ^ z01, inv->log_denom);
+ inv->zs[2][2] = multiply_out(iv0 ^ iv1, inv->log_denom);
+ }
+ return(1);
+}
+
+
+/*
+ * Determine the error magnitudes for a given matrix and syndromes.
+ */
+static inline void
+determine(int nerrs, struct inv_mat *inv, UCHAR *ss, UCHAR *es)
+{
+ int i, j;
+
+ for (i = 0; i < nerrs; i++) {
+ es[i] = 0;
+ for (j = 0; j < nerrs; j++)
+ es[i] ^= multiply(ss[j], inv->zs[i][j]);
+ }
+}
+
+
+/*
+ * Compute the 3 syndrome values.
+ */
+static inline int
+compute_syndromes(UCHAR *data, int nblks, int col, UCHAR *ss)
+{
+ UCHAR r0, r1, r2, t1, t2;
+ UCHAR *rptr;
+
+ rptr = data + col;
+ data += nblks << 10;
+ r0 = r1 = r2 = 0;
+ while (rptr < data) {
+ t1 = *rptr ^ r0;
+ t2 = mult_c0[t1];
+ r0 = t2 ^ r1;
+ r1 = t2 ^ r2;
+ r2 = t1;
+ rptr += QCV_BLKSIZE;
+ }
+ if (r0 || r1 || r2) {
+ ss[0] = divide_out(r0 ^ divide_out(r1 ^ divide_out(r2, 1), 1), nblks);
+ ss[1] = r0 ^ r1 ^ r2;
+ ss[2] = multiply_out(r0 ^ multiply_out(r1 ^ multiply_out(r2, 1), 1), nblks);
+ return(0);
+ }
+ return(1);
+}
+
+
+/*
+ * Calculate the parity bytes for a segment, returns 0 on success (always).
+ */
+int
+set_parity (UCHAR *data, ULONG badmap)
+{
+ UCHAR r0, r1, r2, t1, t2;
+ UCHAR *rptr;
+ int max, row, col;
+
+ max = sect_count(badmap) - 3;
+ col = QCV_BLKSIZE;
+ while (col--) {
+ rptr = data;
+ r0 = r1 = r2 = 0;
+ row = max;
+ while (row--) {
+ t1 = *rptr ^ r0;
+ t2 = mult_c0[t1];
+ r0 = t2 ^ r1;
+ r1 = t2 ^ r2;
+ r2 = t1;
+ rptr += QCV_BLKSIZE;
+ }
+ *rptr = r0; rptr += QCV_BLKSIZE;
+ *rptr = r1; rptr += QCV_BLKSIZE;
+ *rptr = r2;
+ data++;
+ }
+ return(0);
+}
+
+
+/*
+ * Check and correct errors in a block. Returns 0 on success,
+ * 1 if failed.
+ */
+int
+check_parity(UCHAR *data, ULONG badmap, ULONG crcmap)
+{
+ int crcerrs, eblk[3];
+ int col;
+ int i, nblks;
+ UCHAR ss[3], es[3];
+ int i1, i2;
+ struct inv_mat inv;
+
+ nblks = sect_count(badmap);
+
+ /* Count the number of CRC errors and note their locations. */
+ crcerrs = 0;
+ if (crcmap) {
+ for (i = 0; i < nblks; i++) {
+ if (crcmap & (1 << i)) {
+ if (crcerrs == 3) return(1);
+ eblk[crcerrs++] = i;
+ }
+ }
+ }
+
+ /* Calculate the inverse matrix */
+ if (!calculate_inverse(crcerrs, eblk, &inv)) return(1);
+
+ /* Scan each column for problems and attempt to correct. */
+ for (col = 0; col < QCV_BLKSIZE; col++) {
+ if (compute_syndromes(data, nblks, col, ss)) continue;
+ es[0] = es[1] = es[2] = 0;
+
+ /* Analyze the error situation. */
+ switch (crcerrs) {
+ case 0: /* 0 errors >0 failures */
+ if (!ss[0]) return(1);
+ eblk[crcerrs] = alpha_log[divide(ss[1], ss[0])];
+ if (eblk[crcerrs] >= nblks) return(1);
+ es[0] = ss[1];
+ if (++crcerrs > 3) return(1);
+ break;
+
+ case 1: /* 1 error (+ possible failures) */
+ i1 = ss[2] ^ multiply_out(ss[1], eblk[0]);
+ i2 = ss[1] ^ multiply_out(ss[0], eblk[0]);
+ if (!i1 && !i2) { /* only 1 error */
+ inv.zs[0][0] = alpha_power[eblk[0]];
+ inv.log_denom = 0;
+ } else if (!i1 || !i2) { /* too many errors */
+ return(1);
+ } else { /* add failure */
+ eblk[crcerrs] = alpha_log[divide(i1, i2)];
+ if (eblk[crcerrs] >= nblks) return(1);
+ if (++crcerrs > 3) return(1);
+ if (!calculate_inverse(crcerrs, eblk, &inv)) return(1);
+ }
+ determine(crcerrs, &inv, ss, es);
+ break;
+
+ case 2: /* 2 errors */
+ case 3: /* 3 errors */
+ determine(crcerrs, &inv, ss, es);
+ break;
+
+ default:
+ return(1);
+ }
+
+ /* Make corrections. */
+ for (i = 0; i < crcerrs; i++) {
+ data[(eblk[i] << 10) | col] ^= es[i];
+ ss[0] ^= divide_out(es[i], eblk[i]);
+ ss[1] ^= es[i];
+ ss[2] ^= multiply_out(es[i], eblk[i]);
+ }
+ if (ss[0] || ss[1] || ss[2]) return(1);
+ }
+ return(0);
+}
diff --git a/sbin/i386/mount_msdos/Makefile b/sbin/i386/mount_msdos/Makefile
new file mode 100644
index 0000000..19ec0ac
--- /dev/null
+++ b/sbin/i386/mount_msdos/Makefile
@@ -0,0 +1,16 @@
+#
+# $Id: Makefile,v 1.1 1994/09/19 15:30:33 dfr Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/mount_msdos/mount_msdos.8 b/sbin/i386/mount_msdos/mount_msdos.8
new file mode 100644
index 0000000..d6bd154
--- /dev/null
+++ b/sbin/i386/mount_msdos/mount_msdos.8
@@ -0,0 +1,119 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" 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 Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: mount_msdos.8,v 1.2 1994/11/19 17:55:35 nate Exp $
+.\"
+.Dd April 7, 1994
+.Dt MOUNT_MSDOS 8
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm mount_msdos
+.Nd mount an MS-DOS file system
+.Sh SYNOPSIS
+.Nm mount_msdos
+.Op Fl u Ar uid
+.Op Fl g Ar gid
+.Op Fl m Ar mask
+.Pa special
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm mount_msdos
+command attaches the MS-DOS 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, but can be used by any user to mount an
+MS-DOS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl u
+Set the owner of the files in the file system to
+.Ar uid .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g
+Set the group of the files in the file system to
+.Ar gid .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m
+Specify the maximum file permissions for files
+in the file system.
+(For example, a mask of
+.Li 755
+specifies that, by default, the owner should have
+read, write, and execute permissions for files, but
+others should only have read and execute permissions.
+See
+.Xr chmod 1
+for more information about octal file modes.)
+Only the nine low-order bits of
+.Ar mask
+are used.
+The default mask is taken from the
+directory on which the file system is being mounted.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5
+.Sh CAVEATS
+The
+.Nm msdos
+filesystem is not known to work reliably with filesystems created by versions
+of MS-DOS later than version 3.3.
+.Pp
+The limitations on file names imposed by MS-DOS are strange, at best.
+For instance, they are
+limited to single-case, 8 character names with 3 character extensions.
+.Pp
+If you see the warning:
+.Pp
+mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length
+.Pp
+then it is possible that writing to the MS-DOS filesystem would
+produce corruption on the disk. This is a shortcoming in the code
+which needs to be addressed.
+.Sh HISTORY
+The
+.Nm mount_msdos
+utility first appeared in FreeBSD 2.0.
+Its predecessor, the
+.Nm mount_pcfs
+utility appeared in FreeBSD 1, and was abandoned in favor
+of the more aptly-named
+.Nm mount_msdos .
diff --git a/sbin/i386/mount_msdos/mount_msdos.c b/sbin/i386/mount_msdos/mount_msdos.c
new file mode 100644
index 0000000..c90d51b
--- /dev/null
+++ b/sbin/i386/mount_msdos/mount_msdos.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * 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 Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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 const char rcsid[] =
+ "$Id: mount_msdos.c,v 1.4 1996/05/13 17:56:34 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static gid_t a_gid __P((char *));
+static uid_t a_uid __P((char *));
+static mode_t a_mask __P((char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct msdosfs_args args;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask;
+ char *dev, *dir, ndir[MAXPATHLEN+1];
+ struct vfsconf *vfc;
+
+ mntflags = set_gid = set_uid = set_mask = 0;
+ (void)memset(&args, '\0', sizeof(args));
+
+ while ((c = getopt(argc, argv, "u:g:m:o:")) != EOF) {
+ switch (c) {
+ case 'u':
+ args.uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ args.gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ args.mask = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+ if (dir[0] != '/') {
+ warnx("\"%s\" is a relative path", dir);
+ if (getcwd(ndir, sizeof(ndir)) == NULL)
+ err(EX_OSERR, "getcwd");
+ strncat(ndir, "/", sizeof(ndir) - strlen(ndir) - 1);
+ strncat(ndir, dir, sizeof(ndir) - strlen(ndir) - 1);
+ dir = ndir;
+ warnx("using \"%s\" instead", dir);
+ }
+
+ args.fspec = dev;
+ args.export.ex_root = -2; /* unchecked anyway on DOS fs */
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(dir, &sb) == -1)
+ err(EX_OSERR, "stat %s", dir);
+
+ if (!set_uid)
+ args.uid = sb.st_uid;
+ if (!set_gid)
+ args.gid = sb.st_gid;
+ if (!set_mask)
+ args.mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ vfc = getvfsbyname("msdos");
+ if(!vfc && vfsisloadable("msdos")) {
+ if(vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ vfc = getvfsbyname("msdos");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc->vfc_index, dir, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv;
+ char *ep;
+
+ done = 0;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: mount_msdos [-F flags] [-u user] [-g group] [-m mask] bdev dir\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/i386/nextboot/Makefile b/sbin/i386/nextboot/Makefile
new file mode 100644
index 0000000..7ac235f
--- /dev/null
+++ b/sbin/i386/nextboot/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.1 (Julian Elischer) 3/28/93
+#
+#
+
+PROG= nextboot
+SRCS= nextboot.c
+MAN8= nextboot.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/i386/nextboot/nextboot.8 b/sbin/i386/nextboot/nextboot.8
new file mode 100644
index 0000000..089debd
--- /dev/null
+++ b/sbin/i386/nextboot/nextboot.8
@@ -0,0 +1,88 @@
+.\" $Id: nextboot.8,v 1.3 1996/09/23 22:23:15 wosch Exp $
+.Dd July 9, 1996
+.Dt NEXTBOOT 8
+.\".Os BSD 4
+.Sh NAME
+.Nm nextboot
+.Nd Install a default bootstring block on the boot disk
+.Sh SYNOPSIS
+.Nm
+.Op Fl b
+.Ar filename bootstring ...
+.Pp
+.Nm
+.Op Fl ed
+.Ar filename
+.Bl -tag -width time
+.It Fl b
+Is used for bootstrapping (initially configuring) the nameblock. Without
+this,
+.Nm
+will refuse to write to a block that does not already contain the magic
+number.
+.It Fl d
+temporarily disables an existing name block by changing a bit
+in the magic number.
+.It Fl e
+restores the good magic number on a block disabled by -d.
+.El
+.Sh PROLOGUE
+The FreeBSD program
+.Nm
+controls the actions of the boot blocks at the time of the next boot.
+If compiled with the correct option,
+the boot blocks will check the nameblock for a magic number and a
+default name to use for booting. If compiled to do so they will also
+delete the name from the block, ensuring that if the boot should fail,
+then it will not be tried again. It is the job of /etc/rc to use
+.Nm
+to re-install the string if that boot is found to have succeeded.
+This allows a one-time only boot string to be used for such applications
+as remote debugging, and installation of new, untrusted kernels.
+The nameblock is defined at compile time to be the second physical block
+on the disk.
+.Pp
+.Sh DESCRIPTION
+.Nm
+first checks that the disk has an fdisk table and checks that none of the
+partitions defined in that table include the nameblock. If the name block is
+shown to be unused, it will install the bootstrings given as arguments,
+one after the other, each preceded by a small magic number, and NULL
+terminated. The end of the list of strings is delimited by a sequence of
+0xff bytes. If the boot blocks are compiled to write back the nameblock
+after each boot, it will zero out the supplied names as it uses them,
+one per boot,
+until it reaches the 0xff, at which time it will revert to the compiled in
+boot string. At this time the nameblock will contain only zeroed out names.
+.Pp
+An example of usage might be:
+.Bd -literal
+ nextboot -b /dev/rwd0 1:sd(0,a)/kernel.experimental wd(0,a)/kernel.old
+.Ed
+.Pp
+Which would instruct the boot blocks at the next boot,
+to try boot the experimental kernel off the scsi disk.
+If for any reason this failed, the next boot attempt would
+boot the kernel
+.Em /kernel.old
+off the IDE drive. (assuming the write-back option were enabled) If this
+in turn failed. the compiled in default would be used.
+.Pp
+If the write-back feature is disabled, the nextboot program is a convenient way
+to change the default boot string. Note, that should the file specified in
+the nameblock be non-existant, then the name compiled into the boot blocks
+will be used for the boot rather than the next name in the nameblock. The
+nameblock is only consulted ONCE per boot.
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8
+.Sh BUGS
+The entire program should be made more user-friendly.
+The option of whether to write back or not should be stored on the
+disk and not a compile time option. I want to rethink this at some
+later stage to make it co-exist with disks that do not have
+a fdisk partitioning table (i.e. purely disklabel'd systems).
+.Pp
+Whether to write back or not should be specified at run-time in the nameblock
+so that the boot blocks need not be altered to get this feature.
diff --git a/sbin/i386/nextboot/nextboot.c b/sbin/i386/nextboot/nextboot.c
new file mode 100644
index 0000000..2398b56
--- /dev/null
+++ b/sbin/i386/nextboot/nextboot.c
@@ -0,0 +1,231 @@
+
+/*
+ * Copyright (c) 1996 Whistle Communications
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * Whistle Communications allows free use of this software in its "as is"
+ * condition. Whistle Communications disclaims any liability of any kind for
+ * any damages whatsoever resulting from the use of this software.
+ */
+
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+struct mboot
+{
+ unsigned char padding[2]; /* force the longs to be long alligned */
+ unsigned char bootinst[DOSPARTOFF];
+ struct dos_partition parts[4];
+ unsigned short int signature;
+};
+struct mboot mboot;
+
+#define NAMEBLOCK 1 /* 2nd block */
+#define BLOCKSIZE 512
+#define ENABLE_MAGIC 0xfadefeed
+#define DISABLE_MAGIC 0xfadefade
+static int bflag;
+static int eflag;
+static int dflag;
+static int nameblock = NAMEBLOCK;
+
+#define BOOT_MAGIC 0xAA55
+
+extern char *__progname;
+
+static void usage(void) {
+ fprintf (stderr, " usage: %s [-b] device bootstring [bootstring] ...\n"
+ , __progname);
+ fprintf (stderr, " or: %s {-e,-d} device \n" , __progname);
+ fprintf (stderr, "The -e and -d flags are mutually exclusive\n");
+ exit(1);
+}
+
+main (int argc, char** argv)
+{
+ int fd = -1;
+ char namebuf[1024], *cp = namebuf;
+ int i,j;
+ int ch;
+ int part;
+
+ bflag = 0;
+ while ((ch = getopt(argc, argv, "bde")) != EOF) {
+ switch(ch) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ( (dflag + eflag + bflag) > 1 ) {
+ usage();
+ }
+ if (dflag + eflag){
+ if(argc != 1 ) {
+ usage();
+ }
+ } else {
+ if (argc <2) {
+ usage();
+ }
+ }
+ if ((fd = open(argv[0], O_RDWR, 0)) < 0) {
+ perror("open");
+ printf ("file: %s\n",argv[0]);
+ usage();
+ }
+
+ argc--;
+ argv++;
+
+ /*******************************************
+ * Check that we have an MBR
+ */
+ if (lseek(fd,0,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if (read (fd,&mboot.bootinst[0],BLOCKSIZE ) != BLOCKSIZE) {
+ perror("read0");
+ exit(1);
+ }
+ if(mboot.signature != (unsigned short)BOOT_MAGIC) {
+ printf(" no fdisk part.. not touching block 1\n");
+ exit(1);
+ }
+
+ /*******************************************
+ * And check that none of the partitions in it cover the name block;
+ */
+ for ( part = 0; part < 4; part++) {
+ if( mboot.parts[part].dp_size
+ && (mboot.parts[part].dp_start <= NAMEBLOCK)
+ && (mboot.parts[part].dp_start
+ + mboot.parts[part].dp_size > NAMEBLOCK)) {
+ printf(" Name sector lies within a Bios partition.\n");
+ printf(" Aborting write.\n");
+ exit(1);
+ }
+ }
+
+
+ /*******************************************
+ * Now check the name sector itself to see if it's been initialised.
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if ( read (fd,namebuf,BLOCKSIZE ) != BLOCKSIZE) {
+ perror("read1");
+ exit(1);
+ }
+ /*******************************************
+ * check if we are just enabling or disabling
+ * Remember the flags are exlusive..
+ */
+ if(!bflag) { /* don't care what's there if bflag is set */
+ switch(*(unsigned long *)cp)
+ {
+ case DISABLE_MAGIC:
+ case ENABLE_MAGIC:
+ break;
+ default:
+ fprintf(stderr,
+ "namesector not initialised.."
+ "use the -b flag..\n");
+ exit(1);
+ }
+ }
+
+
+ /*******************************************
+ * If the z or r flag is set, damage or restore the magic number..
+ * to disable/enable the feature
+ */
+ if(dflag) {
+ *(unsigned long *)cp = DISABLE_MAGIC;
+ } else {
+ *(unsigned long *)cp = ENABLE_MAGIC;
+ }
+ if ((!dflag) && (!eflag)) {
+ /*******************************************
+ * Create a new namesector in ram
+ */
+ cp += 4;
+ for ( i = 0 ; i < argc ; i++ ) {
+ *cp++ = 'D';
+ *cp++ = 'N';
+ j = strlen(argv[i]);
+ strncpy(cp,argv[i],j);
+ cp += j;
+ *cp++ = 0;
+ }
+ *cp++ = 0xff;
+ *cp++ = 0xff;
+ *cp++ = 0xff;
+ namebuf[BLOCKSIZE-1] = 0; /* paranoid */
+ namebuf[BLOCKSIZE] = 0xff;
+ }
+
+ /*******************************************
+ * write it to disk.
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if(write (fd,namebuf,BLOCKSIZE ) != BLOCKSIZE) {
+ perror("write");
+ exit(1);
+ }
+
+#if 0
+ /*******************************************
+ * just to be safe/paranoid.. read it back..
+ * and print it..
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek (second) ");
+ exit(1);
+ }
+ read (fd,namebuf,512);
+ for (i = 0;i< 16;i++) {
+ for ( j = 0; j < 16; j++) {
+ printf("%02x ",(unsigned char )namebuf[(i*16) + j ]);
+ }
+ printf("\n");
+ }
+#endif
+ exit(0);
+}
+
+
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
new file mode 100644
index 0000000..28fdd26
--- /dev/null
+++ b/sbin/ifconfig/Makefile
@@ -0,0 +1,11 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id$
+
+PROG= ifconfig
+MAN8= ifconfig.8
+DPADD= ${LIBIPX}
+LDADD= -lipx
+COPTS= -DNS -Wall -Wmissing-prototypes -Wcast-qual -Wwrite-strings \
+ -Wnested-externs
+
+.include <bsd.prog.mk>
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
new file mode 100644
index 0000000..9bbea44
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.8
@@ -0,0 +1,305 @@
+.\" 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.
+.\"
+.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
+.\" $Id: ifconfig.8,v 1.8 1996/07/09 02:38:11 julian Exp $
+.\"
+.Dd February 13, 1996
+.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
+.Nm ifconfig
+.Ar -a
+.Nm ifconfig
+.Ar -au
+.Nm ifconfig
+.Ar -ad
+.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 recommended.
+The address or protocol families currently
+supported are
+.Dq inet ,
+.Dq atalk ,
+.\" .Dq iso ,
+and
+.Dq ipx .
+.\" 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 mtu Ar n
+Set the maximum transmission unit of the interface to
+.Ar n ,
+default is interface specific.
+The mtu is used to limit the size of packets that are transmitted on an
+interface.
+Not all interfaces support setting the mtu, and some interfaces have
+range restrictions.
+.It Cm netmask Ar mask
+.\" (Inet and ISO)
+(Inet only)
+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 range
+Under appletalk, set the interface to respond to a
+.Em netrange.
+of the form startnet-endnet. Appletalk uses this scheme instead of
+netmasks though FreeBSD impliments it internally as a set of netmasks.
+.It Cm phase
+The argument following this specifies the version (phase) of the
+Appletalk network attached to the interface. Values of 1 or 2 are permitted.
+.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.
+.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
+The
+.Nm ifconfig
+.Fl a
+command
+displays information on all interfaces. When followed by a configuration
+parameter, it will also set the configuration on all interfaces.
+.Pp
+.Nm Ifconfig
+.Fl au
+is similar to
+.Nm ifconfig
+.Fl a ,
+except it only affects interfaces that are currently marked as up.
+Similarly,
+.Nm ifconfig
+.Fl ad
+affects only interfaces that are marked down.
+.Pp
+The
+.Nm
+program
+displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+.Nm 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 0000000..0bd94ea
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.c
@@ -0,0 +1,1169 @@
+/*
+ * 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 const 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";
+*/
+static const char rcsid[] =
+ "$Id: ifconfig.c,v 1.20 1996/11/21 19:36:09 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+/* IP */
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+/* IPX */
+#define IPXIP
+#define IPTUNNEL
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+
+/* Appletalk */
+#include <netatalk/at.h>
+
+/* XNS */
+#ifdef NS
+#define NSIP
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+/* OSI */
+#ifdef ISO
+#define EON
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct ifreq ifr, ridreq;
+struct ifaliasreq addreq;
+#ifdef ISO
+struct iso_ifreq iso_ridreq;
+struct iso_aliasreq iso_addreq;
+#endif
+struct sockaddr_in netmask;
+struct netrange at_nr; /* AppleTalk net range */
+
+char name[32];
+int flags;
+int metric;
+int mtu;
+#ifdef ISO
+int nsellength = 1;
+#endif
+int setaddr;
+int setipdst;
+int doalias;
+int clearaddr;
+int newaddr = 1;
+int s;
+
+struct afswtch;
+
+void Perror __P((const char *cmd));
+void checkatrange __P((struct sockaddr_at *));
+int ifconfig __P((int argc, char *const *argv, int af,
+ const struct afswtch *rafp));
+void notealias __P((const char *, int));
+void printb __P((const char *s, unsigned value, const char *bits));
+void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+void status __P((void));
+
+typedef void c_func __P((const char *cmd, int arg));
+c_func setatphase, setatrange;
+c_func setifaddr, setifbroadaddr, setifdstaddr, setifnetmask;
+c_func setifipdst;
+c_func setifflags, setifmetric, setifmtu;
+
+#ifdef ISO
+c_func setsnpaoffset, setnsellength;
+#endif
+
+#define NEXTARG 0xffffff
+
+const
+struct cmd {
+ const char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ void (*c_func) __P((const char *, int));
+} cmds[] = {
+ { "up", IFF_UP, setifflags } ,
+ { "down", -IFF_UP, setifflags },
+ { "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 },
+ { "range", NEXTARG, setatrange },
+ { "phase", NEXTARG, setatphase },
+ { "metric", NEXTARG, setifmetric },
+ { "broadcast", NEXTARG, setifbroadaddr },
+ { "ipdst", NEXTARG, setifipdst },
+#ifdef ISO
+ { "snpaoffset", NEXTARG, setsnpaoffset },
+ { "nsellength", NEXTARG, setnsellength },
+#endif
+ { "link0", IFF_LINK0, setifflags },
+ { "-link0", -IFF_LINK0, setifflags },
+ { "link1", IFF_LINK1, setifflags },
+ { "-link1", -IFF_LINK1, setifflags },
+ { "link2", IFF_LINK2, setifflags },
+ { "-link2", -IFF_LINK2, setifflags },
+ { "normal", -IFF_LINK0, setifflags },
+ { "compress", IFF_LINK0, setifflags },
+ { "noicmp", IFF_LINK1, setifflags },
+ { "mtu", NEXTARG, setifmtu },
+ { 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.
+ */
+typedef void af_status __P((int));
+typedef void af_getaddr __P((const char *, int));
+
+af_status in_status, ipx_status, at_status, ether_status;
+af_getaddr in_getaddr, ipx_getaddr, at_getaddr;
+
+#ifdef NS
+af_status xns_status;
+af_getaddr xns_getaddr;
+#endif
+#ifdef ISO
+af_status iso_status;
+af_getaddr iso_getaddr;
+#endif
+
+/* Known address families */
+const
+struct afswtch {
+ const char *af_name;
+ short af_af;
+ af_status *af_status;
+ af_getaddr *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) },
+ { "ipx", AF_IPX, ipx_status, ipx_getaddr,
+ SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
+ { "atalk", AF_APPLETALK, at_status, at_getaddr,
+ SIOCDIFADDR, SIOCAIFADDR, C(addreq), C(addreq) },
+#ifdef NS
+ { "ns", AF_NS, xns_status, xns_getaddr,
+ SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
+#endif
+#ifdef ISO
+ { "iso", AF_ISO, iso_status, iso_getaddr,
+ SIOCDIFADDR_ISO, SIOCAIFADDR_ISO, C(iso_ridreq), C(iso_addreq) },
+#endif
+ { "ether", AF_INET, ether_status, NULL }, /* XXX not real!! */
+ { 0, 0, 0, 0 }
+};
+
+const struct afswtch *afp; /*the address family being set or asked about*/
+
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ caddr_t cp, cplim;
+ struct rt_addrinfo *rtinfo;
+{
+ struct sockaddr *sa;
+ int i;
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+
+/*
+ * Grunge for new-style sysctl() decoding.. :-(
+ * Apologies to the world for committing gross things like this in 1996..
+ */
+struct if_msghdr *ifm;
+struct ifa_msghdr *ifam;
+struct sockaddr_dl *sdl;
+struct rt_addrinfo info;
+char *buf, *lim, *next;
+
+int
+main(argc, argv)
+ int argc;
+ char *const *argv;
+{
+ int af = AF_INET;
+ const struct afswtch *rafp;
+
+ size_t needed;
+ int mib[6];
+ int all;
+
+ rafp = 0;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: ifconfig interface\n%s%s%s%s%s%s%s",
+ "\t[ af [ address [ dest_addr ] ] [ up ] [ down ]",
+ "[ netmask mask ] ]\n",
+ "\t[ metric n ]\n",
+ "\t[ mtu n ]\n",
+ "\t[ arp | -arp ]\n",
+ "\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ] \n",
+ "\t[ -a ] [ -ad ] [ -au ]\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;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ /* if particular family specified, only ask about it */
+ if (afp) {
+ mib[3] = afp->af_af;
+ }
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "iflist-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of interface table");
+ lim = buf + needed;
+
+ all = 0;
+ if (strcmp(name, "-a") == 0)
+ all = 1; /* All interfaces */
+ else if (strcmp(name, "-au") == 0)
+ all = 2; /* All IFF_UPinterfaces */
+ else if (strcmp(name, "-ad") == 0)
+ all = 3; /* All !IFF_UP interfaces */
+
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+
+ ifm = (struct if_msghdr *)next;
+
+ /* XXX: Swallow up leftover NEWADDR messages */
+ if (ifm->ifm_type == RTM_NEWADDR)
+ continue;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ } else {
+ errx(1, "out of sync parsing NET_RT_IFLIST");
+ }
+
+ switch(all) {
+ case -1:
+ case 0:
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
+ continue; /* not same name */
+ break;
+ case 1:
+ break; /* always do it */
+ case 2:
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ break;
+ case 3:
+ if (flags & IFF_UP)
+ continue; /* not down */
+ break;
+ }
+
+ if (all > 0) {
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ }
+
+ if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
+ perror("ifconfig: socket");
+ exit(1);
+ }
+
+ ifconfig(argc, argv, af, rafp);
+
+ close(s);
+
+ if (all == 0) {
+ all = -1; /* flag it as 'done' */
+ break;
+ }
+ }
+ free(buf);
+
+ if (all == 0)
+ errx(1, "interface %s does not exist", name);
+
+
+ exit (0);
+}
+
+
+
+int
+ifconfig(argc,argv,af,rafp)
+ int argc;
+ char *const *argv;
+ int af;
+ const struct afswtch *rafp;
+{
+
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMETRIC)");
+ else
+ metric = ifr.ifr_metric;
+
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0)
+ perror("ioctl (SIOCGIFMTU)");
+ else
+ mtu = ifr.ifr_mtu;
+
+ if (argc == 0) {
+ status();
+ return(0);
+ }
+
+ while (argc > 0) {
+ register const 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], 0);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter);
+ }
+ argc--, argv++;
+ }
+#ifdef ISO
+ if (af == AF_ISO)
+ adjust_nsellength();
+#endif
+ if (setipdst && af==AF_IPX) {
+ struct ipxip_req rq;
+ int size = sizeof(rq);
+
+ rq.rq_ipx = addreq.ifra_addr;
+ rq.rq_ip = addreq.ifra_dstaddr;
+
+ if (setsockopt(s, 0, SO_IPXIP_ROUTE, &rq, size) < 0)
+ Perror("Encapsulation Routing");
+ }
+ if (af == AF_APPLETALK)
+ checkatrange((struct sockaddr_at *) &addreq.ifra_addr);
+#ifdef NS
+ 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");
+ }
+#endif
+ if (clearaddr) {
+ if (rafp->af_ridreq == NULL || rafp->af_difaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, rafp->af_name);
+ clearaddr = NULL;
+ }
+ }
+ 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) {
+ if (rafp->af_ridreq == NULL || rafp->af_difaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, rafp->af_name);
+ newaddr = NULL;
+ }
+ }
+ if (newaddr) {
+ strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, rafp->af_aifaddr, rafp->af_addreq) < 0)
+ Perror("ioctl (SIOCAIFADDR)");
+ }
+ return(0);
+}
+#define RIDADDR 0
+#define ADDR 1
+#define MASK 2
+#define DSTADDR 3
+
+/*ARGSUSED*/
+void
+setifaddr(addr, param)
+ const char *addr;
+ int 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));
+}
+
+void
+setifnetmask(addr, dummy)
+ const char *addr;
+ int dummy __unused;
+{
+ (*afp->af_getaddr)(addr, MASK);
+}
+
+void
+setifbroadaddr(addr, dummy)
+ const char *addr;
+ int dummy __unused;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+void
+setifipdst(addr, dummy)
+ const char *addr;
+ int dummy __unused;
+{
+ in_getaddr(addr, DSTADDR);
+ setipdst++;
+ clearaddr = 0;
+ newaddr = 0;
+}
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
+
+void
+notealias(addr, param)
+ const char *addr;
+ int param;
+{
+ 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*/
+void
+setifdstaddr(addr, param)
+ const char *addr;
+ int param __unused;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+void
+setifflags(vname, value)
+ const char *vname;
+ int 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);
+}
+
+void
+setifmetric(val, dummy)
+ const char *val;
+ int dummy __unused;
+{
+ 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)");
+}
+
+void
+setifmtu(val, dummy)
+ const char *val;
+ int dummy __unused;
+{
+ strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
+ ifr.ifr_mtu = atoi(val);
+ if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0)
+ perror("ioctl (set mtu)");
+}
+
+#ifdef ISO
+void
+setsnpaoffset(val, dummy)
+ char *val;
+ int dummy __unused;
+{
+ iso_addreq.ifra_snpaoffset = atoi(val);
+}
+#endif
+
+#define IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6b6\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.
+ */
+void
+status()
+{
+ const struct afswtch *p = NULL;
+ char *mynext;
+ struct if_msghdr *myifm;
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (metric)
+ printf(" metric %d", metric);
+ if (mtu)
+ printf(" mtu %d", mtu);
+ putchar('\n');
+
+ /*
+ * XXX: Sigh. This is bad, I know. At this point, we may have
+ * *zero* RTM_NEWADDR's, so we have to "feel the water" before
+ * incrementing the loop. One day, I might feel inspired enough
+ * to get the top level loop to pass a count down here so we
+ * dont have to mess with this. -Peter
+ */
+ myifm = ifm;
+
+ while (1) {
+
+ mynext = next + ifm->ifm_msglen;
+
+ if (mynext >= lim)
+ break;
+
+ myifm = (struct if_msghdr *)mynext;
+
+ if (myifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ next = mynext;
+
+ ifm = (struct if_msghdr *)next;
+
+ ifam = (struct ifa_msghdr *)myifm;
+ info.rti_addrs = ifam->ifam_addrs;
+
+ /* Expand the compacted addresses */
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ if (afp) {
+ if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ afp->af_status != ether_status) {
+ p = afp;
+ if (p->af_status != ether_status)
+ (*p->af_status)(1);
+ }
+ } else for (p = afs; p->af_name; p++) {
+ if (p->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+ p->af_status != ether_status)
+ (*p->af_status)(0);
+ }
+ }
+ if (afp == NULL || afp->af_status == ether_status)
+ ether_status(0);
+ else if (afp && !p) {
+ warnx("%s has no %s IFA address!", name, afp->af_name);
+ }
+}
+
+void
+in_status(force)
+ int force;
+{
+ struct sockaddr_in *sin, null_sin;
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_IFA];
+ if (!sin || sin->sin_family != AF_INET) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_INET IFA address!", name); */
+ sin = &null_sin;
+ }
+ printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (!sin)
+ sin = &null_sin;
+ printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_NETMASK];
+ if (!sin)
+ sin = &null_sin;
+ printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr));
+
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (sin && sin->sin_addr.s_addr != 0)
+ printf("broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+void
+ipx_status(force)
+ int force;
+{
+ struct sockaddr_ipx *sipx, null_sipx;
+
+ close(s);
+ s = socket(AF_IPX, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ perror("ifconfig: socket");
+ exit(1);
+ }
+
+ memset(&null_sipx, 0, sizeof(null_sipx));
+
+ sipx = (struct sockaddr_ipx *)info.rti_info[RTAX_IFA];
+ if (!sipx || sipx->sipx_family != AF_IPX) {
+ if (!force)
+ return;
+ warnx("%s has no AF_IPX IFA address!", name);
+ sipx = &null_sipx;
+ }
+ printf("\tipx %s ", ipx_ntoa(sipx->sipx_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ sipx = (struct sockaddr_ipx *)info.rti_info[RTAX_BRD];
+ if (!sipx)
+ sipx = &null_sipx;
+ printf("--> %s ", ipx_ntoa(sipx->sipx_addr));
+ }
+ putchar('\n');
+
+}
+
+void
+at_status(force)
+ int force;
+{
+ struct sockaddr_at *sat, null_sat;
+ struct netrange *nr;
+
+ memset(&null_sat, 0, sizeof(null_sat));
+
+ sat = (struct sockaddr_at *)info.rti_info[RTAX_IFA];
+ if (!sat || sat->sat_family != AF_APPLETALK) {
+ if (!force)
+ return;
+ sat = &null_sat;
+ }
+ nr = &sat->sat_range.r_netrange;
+ printf("\tatalk %d.%d range %d-%d phase %d",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+ ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase);
+ if (flags & IFF_POINTOPOINT) {
+ /* note RTAX_BRD overlap with IFF_BROADCAST */
+ sat = (struct sockaddr_at *)info.rti_info[RTAX_BRD];
+ if (!sat)
+ sat = &null_sat;
+ printf("--> %d.%d",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
+ }
+ if (flags & IFF_BROADCAST) {
+ /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+ sat = (struct sockaddr_at *)info.rti_info[RTAX_BRD];
+ if (sat)
+ printf(" broadcast %d.%d",
+ ntohs(sat->sat_addr.s_net),
+ sat->sat_addr.s_node);
+ }
+
+ putchar('\n');
+}
+
+#ifdef NS
+void
+xns_status(force)
+ int force;
+{
+ struct sockaddr_ns *sns, null_sns;
+
+ close(s);
+ s = socket(AF_NS, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ perror("ifconfig: socket");
+ exit(1);
+ }
+ memset(&null_sns, 0, sizeof(null_sns));
+
+ sns = (struct sockaddr_ns *)info.rti_info[RTAX_IFA];
+ if (!sns || sns->sns_family != AF_NS) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_NS IFA address!", name); */
+ sns = &null_sns;
+ }
+ printf("\tns %s ", ns_ntoa(sns->sns_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ sns = (struct sockaddr_ns *)info.rti_info[RTAX_BRD];
+ if (!sns)
+ sns = &null_sns;
+ printf("--> %s ", ns_ntoa(sns->sns_addr));
+ }
+
+ putchar('\n');
+}
+#endif
+
+#ifdef ISO
+void
+iso_status(force)
+ int force;
+{
+ struct sockaddr_iso *siso, null_siso;
+
+ close(s);
+ s = socket(AF_ISO, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ perror("ifconfig: socket");
+ exit(1);
+ }
+
+ memset(&null_siso, 0, sizeof(null_siso));
+
+ siso = (struct sockaddr_iso *)info.rti_info[RTAX_IFA];
+ if (!siso || siso->siso_family != AF_ISO) {
+ if (!force)
+ return;
+ /* warnx("%s has no AF_ISO IFA address!", name); */
+ siso = &null_siso;
+ }
+ printf("\tiso %s ", iso_ntoa(&siso->siso_addr));
+
+ /* XXX: is this right? is the ISO netmask meant to be before P2P? */
+ siso = (struct sockaddr_iso *)info.rti_info[RTAX_NETMASK];
+ if (siso)
+ printf(" netmask %s ", iso_ntoa(&siso->siso_addr));
+
+ if (flags & IFF_POINTOPOINT) {
+ siso = (struct sockaddr_iso *)info.rti_info[RTAX_BRD];
+ if (!siso)
+ siso = &null_siso;
+ printf("--> %s ", iso_ntoa(&siso->siso_addr));
+ }
+
+ putchar('\n');
+}
+#endif
+
+void
+ether_status(force)
+ int force __unused;
+{
+ char *cp;
+ int n;
+
+ cp = (char *)LLADDR(sdl);
+ if ((n = sdl->sdl_alen) > 0) {
+ if (sdl->sdl_type == IFT_ETHER)
+ printf ("\tether ");
+ else
+ printf ("\tlladdr ");
+ while (--n >= 0)
+ printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' ');
+ putchar('\n');
+ }
+}
+
+void
+Perror(cmd)
+ const char *cmd;
+{
+ 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);
+ }
+}
+
+#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)};
+
+void
+in_getaddr(s, which)
+ const char *s;
+ int which;
+{
+ register struct sockaddr_in *sin = sintab[which];
+ struct hostent *hp;
+ struct netent *np;
+
+ sin->sin_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin_family = AF_INET;
+
+ if (inet_aton(s, &sin->sin_addr))
+ return;
+ if ((hp = gethostbyname(s)) != 0)
+ bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
+ else if ((np = getnetbyname(s)) != 0)
+ 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
+ */
+void
+printb(s, v, bits)
+ const char *s;
+ register unsigned v;
+ register const char *bits;
+{
+ 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++) != '\0') {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+#define SIPX(x) ((struct sockaddr_ipx *) &(x))
+struct sockaddr_ipx *sipxtab[] = {
+SIPX(ridreq.ifr_addr), SIPX(addreq.ifra_addr),
+SIPX(addreq.ifra_mask), SIPX(addreq.ifra_broadaddr)};
+
+void
+ipx_getaddr(addr, which)
+ const char *addr;
+ int which;
+{
+ struct sockaddr_ipx *sipx = sipxtab[which];
+
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_len = sizeof(*sipx);
+ sipx->sipx_addr = ipx_addr(addr);
+ if (which == MASK)
+ printf("Attempt to set IPX netmask will be ineffectual\n");
+}
+
+void
+at_getaddr(addr, which)
+ const char *addr;
+ int which;
+{
+ struct sockaddr_at *sat = (struct sockaddr_at *) &addreq.ifra_addr;
+ u_int net, node;
+
+ sat->sat_family = AF_APPLETALK;
+ sat->sat_len = sizeof(*sat);
+ if (which == MASK)
+ errx(1, "AppleTalk does not use netmasks\n");
+ if (sscanf(addr, "%u.%u", &net, &node) != 2
+ || net > 0xffff || node > 0xfe)
+ errx(1, "%s: illegal address", addr);
+ sat->sat_addr.s_net = htons(net);
+ sat->sat_addr.s_node = node;
+}
+
+/* XXX FIXME -- should use strtoul for better parsing. */
+void
+setatrange(range, dummy)
+ const char *range;
+ int dummy __unused;
+{
+ u_short first = 123, last = 123;
+
+ if (sscanf(range, "%hu-%hu", &first, &last) != 2
+ || first == 0 || first > 0xffff
+ || last == 0 || last > 0xffff || first > last)
+ errx(1, "%s: illegal net range: %u-%u", range, first, last);
+ at_nr.nr_firstnet = htons(first);
+ at_nr.nr_lastnet = htons(last);
+}
+
+void
+setatphase(phase, dummy)
+ const char *phase;
+ int dummy __unused;
+{
+ if (!strcmp(phase, "1"))
+ at_nr.nr_phase = 1;
+ else if (!strcmp(phase, "2"))
+ at_nr.nr_phase = 2;
+ else
+ errx(1, "%s: illegal phase", phase);
+}
+
+void
+checkatrange(struct sockaddr_at *sat)
+{
+ if (at_nr.nr_phase == 0)
+ at_nr.nr_phase = 2; /* Default phase 2 */
+ if (at_nr.nr_firstnet == 0)
+ at_nr.nr_firstnet = /* Default range of one */
+ at_nr.nr_lastnet = sat->sat_addr.s_net;
+printf("\tatalk %d.%d range %d-%d phase %d\n",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+ ntohs(at_nr.nr_firstnet), ntohs(at_nr.nr_lastnet), at_nr.nr_phase);
+ if ((u_short) ntohs(at_nr.nr_firstnet) >
+ (u_short) ntohs(sat->sat_addr.s_net)
+ || (u_short) ntohs(at_nr.nr_lastnet) <
+ (u_short) ntohs(sat->sat_addr.s_net))
+ errx(1, "AppleTalk address is not in range");
+ sat->sat_range.r_netrange = at_nr;
+}
+
+#ifdef NS
+#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)};
+
+void
+xns_getaddr(addr, which)
+ const char *addr;
+ int which;
+{
+ struct sockaddr_ns *sns = snstab[which];
+
+ 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");
+}
+#endif
+
+#ifdef ISO
+#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)};
+
+void
+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;
+ }
+}
+
+void
+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");
+}
+
+void
+fixnsel(s)
+register struct sockaddr_iso *s;
+{
+ if (s->siso_family == 0)
+ return;
+ s->siso_tlen = nsellength;
+}
+
+void
+adjust_nsellength()
+{
+ fixnsel(sisotab[RIDADDR]);
+ fixnsel(sisotab[ADDR]);
+ fixnsel(sisotab[DSTADDR]);
+}
+#endif
diff --git a/sbin/init/Makefile b/sbin/init/Makefile
new file mode 100644
index 0000000..285240b
--- /dev/null
+++ b/sbin/init/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $Id: Makefile,v 1.7 1996/05/04 08:16:20 markm Exp $
+
+PROG= init
+MAN8= init.8
+BINMODE=500
+INSTALLFLAGS=-fschg
+CFLAGS+=-DDEBUGSHELL -DSECURE
+
+.if exists(../../secure) && !defined(NOCRYPT) && !defined(NOSECURE)
+DISTRIBUTION=des
+DPADD= ${LIBUTIL} ${DESCRYPTOBJDIR}/libdescrypt.a
+LDADD= -lutil -L${DESCRYPTOBJDIR} -ldescrypt
+.else
+DPADD= ${LIBUTIL} ${SCRYPTOBJDIR}/libscrypt.a
+LDADD= -lutil -L${SCRYPTOBJDIR} -lscrypt
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/init/NOTES b/sbin/init/NOTES
new file mode 100644
index 0000000..bf75101
--- /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 0000000..9a31dc2
--- /dev/null
+++ b/sbin/init/init.8
@@ -0,0 +1,301 @@
+.\" 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.
+The security levels are:
+.Bl -tag -width flag
+.It Ic -1
+Permanently insecure mode \- always run the 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
+may not be opened for writing.
+.It Ic 2
+Highly secure mode \- same as secure mode, plus disks may not be
+opened for writing (except by
+.Xr mount 2 )
+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
+If the security level is initially -1, then
+.Nm init
+leaves it unchanged.
+Otherwise,
+.Nm init
+arranges to run the system in level 0 mode while single user
+and in level 1 mode while multiuser.
+If level 2 mode is desired while running multiuser,
+it can be set while single user, e.g., in the startup script
+.Pa /etc/rc ,
+using
+.Xr sysctl 8 .
+.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 ;
+.Nm getty
+opens and initializes the tty line
+and
+executes the
+.Xr login 1
+program.
+The
+.Nm 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 5
+file.
+The cycle is
+then restarted by
+.Nm init
+executing a new
+.Nm getty
+for the line.
+.Pp
+Line status (on, off, secure, getty, or window information)
+may be changed in the
+.Xr ttys 5
+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 5
+file.
+When a line is turned off in
+.Xr ttys 5 ,
+.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 5
+file and are now on,
+.Nm init
+executes a new
+.Nm 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 5 ,
+.Nm init
+will not do anything at all to that line.
+However, it will complain that the relationship between lines
+in the
+.Xr ttys 5
+file and records in the
+.Xr utmp 5
+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),
+.Nm 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
+.Nm 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
+.Nm Init
+will terminate all possible processes (again, it will not wait
+for deadlocked processes) and reboot the machine if sent the interrupt
+.Pq Dv INT
+signal, i.e.
+.Dq Li "kill \-INT 1".
+This is useful for shutting the machine down cleanly from inside the kernel
+or from X when the machines appears to be hung.
+.Pp
+The role of
+.Nm init
+is so critical that if it dies, the system will reboot itself
+automatically.
+If, at bootstrap time, the
+.Nm 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 5 .
+.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 0000000..7e73558
--- /dev/null
+++ b/sbin/init/init.c
@@ -0,0 +1,1436 @@
+/*-
+ * 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/mount.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>
+#include <sys/reboot.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 GETTY_NSPACE 3 /* max. spacing count to bring reaction */
+#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;
+#define FALSE 0
+#define TRUE 1
+
+int Reboot = FALSE;
+
+int devfs;
+
+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 */
+ int se_nspace; /* spacing count */
+ char *se_device; /* filename of port */
+ char *se_getty; /* what to run on that port */
+ char *se_getty_argv_space; /* pre-parsed argument array space */
+ char **se_getty_argv; /* pre-parsed argument array */
+ char *se_window; /* window system (started only once) */
+ char *se_window_argv_space; /* pre-parsed argument array space */
+ char **se_window_argv; /* pre-parsed argument array */
+ char *se_type; /* default terminal type */
+ 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, "dsf")) != -1)
+ switch (c) {
+ case 'd':
+ devfs = 1;
+ break;
+ 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");
+
+ /* Mount devfs on /dev */
+ if (devfs) {
+ mount(MOUNT_DEVFS, "/dev", MNT_NOEXEC|MNT_RDONLY, 0);
+ }
+
+ /*
+ * 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, SIGINT, SIGTERM, SIGTSTP, 0);
+ handle(alrm_handler, SIGALRM, 0);
+ sigfillset(&mask);
+ delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
+ SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, 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);
+ 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 (Reboot) {
+ /* Instead of going single user, let's halt the machine */
+ sync();
+ alarm(2);
+ pause();
+ reboot(RB_AUTOBOOT);
+ _exit(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 && *pp->pw_passwd) {
+ 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;
+{
+ char *strk (char *);
+ register int argc = 0;
+ register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
+ * sizeof (char *));
+
+ if ((argv[argc++] = strk(command)) == 0)
+ return 0;
+ while (argv[argc++] = strk((char *) 0))
+ 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_space);
+ free(sp->se_getty_argv);
+ }
+ if (sp->se_window) {
+ free(sp->se_window);
+ free(sp->se_window_argv_space);
+ free(sp->se_window_argv);
+ }
+ if (sp->se_type)
+ free(sp->se_type);
+ 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_space);
+ 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_space = strdup(sp->se_getty);
+ sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
+ if (sp->se_getty_argv == 0) {
+ warning("can't parse getty for port %s", sp->se_device);
+ free(sp->se_getty);
+ free(sp->se_getty_argv_space);
+ sp->se_getty = sp->se_getty_argv_space = 0;
+ return (0);
+ }
+ if (sp->se_window) {
+ free(sp->se_window);
+ free(sp->se_window_argv_space);
+ free(sp->se_window_argv);
+ }
+ sp->se_window = sp->se_window_argv_space = 0;
+ sp->se_window_argv = 0;
+ if (typ->ty_window) {
+ sp->se_window = strdup(typ->ty_window);
+ sp->se_window_argv_space = strdup(sp->se_window);
+ sp->se_window_argv = construct_argv(sp->se_window_argv_space);
+ if (sp->se_window_argv == 0) {
+ warning("can't parse window for port %s",
+ sp->se_device);
+ free(sp->se_window_argv_space);
+ free(sp->se_window);
+ sp->se_window = sp->se_window_argv_space = 0;
+ return (0);
+ }
+ }
+ if (sp->se_type)
+ free(sp->se_type);
+ sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 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;
+ char term[64], *env[2];
+
+ 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");
+
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strcat(term, sp->se_type);
+ env[0] = term;
+ env[1] = 0;
+ }
+ else
+ env[0] = 0;
+ execve(sp->se_window_argv[0], sp->se_window_argv, env);
+ 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);
+ int too_quick = 0;
+ char term[64], *env[2];
+
+ if (current_time >= sp->se_started &&
+ current_time - sp->se_started < GETTY_SPACING) {
+ if (++sp->se_nspace > GETTY_NSPACE) {
+ sp->se_nspace = 0;
+ too_quick = 1;
+ }
+ } else
+ sp->se_nspace = 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 (too_quick) {
+ warning("getty repeating too quickly on port %s, sleeping %d secs",
+ sp->se_device, GETTY_SLEEP);
+ sleep((unsigned) GETTY_SLEEP);
+ }
+
+ if (sp->se_window) {
+ start_window_system(sp);
+ sleep(WINDOW_WAIT);
+ }
+
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
+
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strcat(term, sp->se_type);
+ env[0] = term;
+ env[1] = 0;
+ }
+ else
+ env[0] = 0;
+ execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
+ 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 SIGINT:
+ Reboot = TRUE;
+ 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;
+ char *old_getty, *old_window, *old_type;
+
+ 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;
+ old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
+ old_window = sp->se_window ? strdup(sp->se_window) : 0;
+ old_type = sp->se_type ? strdup(sp->se_type) : 0;
+ 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);
+ }
+ else if ( !old_getty
+ || !old_type && sp->se_type
+ || old_type && !sp->se_type
+ || !old_window && sp->se_window
+ || old_window && !sp->se_window
+ || strcmp(old_getty, sp->se_getty) != 0
+ || old_window && strcmp(old_window, sp->se_window) != 0
+ || old_type && strcmp(old_type, sp->se_type) != 0
+ ) {
+ /* Don't set SE_SHUTDOWN here */
+ sp->se_nspace = 0;
+ sp->se_started = 0;
+ kill(sp->se_process, SIGHUP);
+ }
+ if (old_getty)
+ free(old_getty);
+ if (old_getty)
+ free(old_window);
+ if (old_type)
+ free(old_type);
+ 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;
+}
+char *
+strk (char *p)
+{
+ static char *t;
+ char *q;
+ int c;
+
+ if (p)
+ t = p;
+ if (!t)
+ return 0;
+
+ c = *t;
+ while (c == ' ' || c == '\t' )
+ c = *++t;
+ if (!c) {
+ t = 0;
+ return 0;
+ }
+ q = t;
+ if (c == '\'') {
+ c = *++t;
+ q = t;
+ while (c && c != '\'')
+ c = *++t;
+ if (!c) /* unterminated string */
+ q = t = 0;
+ else
+ *t++ = 0;
+ } else {
+ while (c && c != ' ' && c != '\t' )
+ c = *++t;
+ *t++ = 0;
+ if (!c)
+ t = 0;
+ }
+ return q;
+}
diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h
new file mode 100644
index 0000000..abb874a
--- /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/ipfw/Makefile b/sbin/ipfw/Makefile
new file mode 100644
index 0000000..3effc23
--- /dev/null
+++ b/sbin/ipfw/Makefile
@@ -0,0 +1,5 @@
+PROG= ipfw
+
+MAN8= ipfw.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
new file mode 100644
index 0000000..8f65213a
--- /dev/null
+++ b/sbin/ipfw/ipfw.8
@@ -0,0 +1,358 @@
+.Dd July 20, 1996
+.Dt IPFW 8 SMM
+.Os FreeBSD
+.Sh NAME
+.Nm ipfw
+.Nd controlling utility for IP firewall
+.Sh SYNOPSIS
+.Nm
+.Ar file
+.Nm ipfw
+flush
+.Nm ipfw
+zero
+.Oo
+.Ar number
+.Oc
+.Nm ipfw
+delete
+.Ar number
+.Nm ipfw
+.Oo
+.Fl aftN
+.Oc
+list
+.Nm ipfw
+add
+.Oo
+.Ar number
+.Oc
+.Ar action
+.Oo
+log
+.Oc
+.Ar proto
+from
+.Ar src
+to
+.Ar dst
+.Oo
+via
+.Ar name|ipno
+.Oc
+.Oo
+.Ar options
+.Oc
+.Sh DESCRIPTION
+If used as shown in the first synopsis line, the
+.Ar file
+will be read line by line and applied as arguments to the
+.Nm
+command.
+.Pp
+The
+.Nm
+code works by going through the rule-list for each packet,
+until a match is found.
+All rules have two associated counters, a packet count and
+a byte count.
+These counters are updated when a packet matches the rule.
+.Pp
+The rules are ordered by a ``line-number'' from 1 to 65534 that is used
+to order and delete rules. Rules are tried in increasing order, and the
+first rule that matches a packet applies.
+Multiple rules may share the same number and apply in
+the order in which they were added.
+.Pp
+If a rule is added without a number, it numbered 100 higher
+than the previous rule. If the highest defined rule number is
+greater than 65434, new rules are appended to the last rule.
+.Pp
+The delete operation deletes the first rule with number
+.Ar number ,
+if any.
+.Pp
+The list command prints out the current rule set.
+.Pp
+The zero operation zeroes the counters associated with rule number
+.Ar number .
+.Pp
+The flush operation removes all rules.
+.Pp
+One rule is always present:
+.Bd -literal -offset center
+65535 deny all from any to any
+.Ed
+
+This rule is the default policy, i.e., don't allow anything at all.
+Your job in setting up rules is to modify this policy to match your needs.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.It Fl a
+While listing, show counter values. This option is the only way to see
+accounting records.
+.It Fl f
+Don't ask for confirmation for commands that can cause problems if misused
+(ie; flush).
+.Ar Note ,
+if there is no tty associated with the process, this is implied.
+.It Fl t
+While listing, show last match timestamp.
+.It Fl N
+Try to resolve addresses and service names in output.
+.El
+.Pp
+.Ar action :
+.Bl -hang -offset flag -width 1234567890123456
+.It Nm allow
+Allow packets that match rule.
+The search terminates.
+.It Nm pass
+Same as allow.
+.It Nm accept
+Same as allow.
+.It Nm count
+Update counters for all packets that match rule.
+The search continues with the next rule.
+.It Nm deny
+Discard packets that match this rule.
+The search terminates.
+.It Nm reject
+Discard packets that match this rule, and try to send an ICMP notice.
+The search terminates.
+.It Nm divert port
+Divert packets that match this rule to the divert socket bound to port
+.Ar port .
+The search terminates.
+.El
+.Pp
+When a packet matches a rule with the
+.Nm log
+keyword, a message will be printed on the console.
+If the kernel was compiled with the
+.Nm IP_FIREWALL_VERBOSE_LIMIT
+option, then logging will cease after the number of packets
+specified by the option are received for that particular
+chain entry. Logging may then be re-enabled by clearing
+the packet counter for that entry.
+.Pp
+.Ar proto :
+.Bl -hang -offset flag -width 1234567890123456
+.It Nm ip
+All packets match.
+.It Nm all
+All packets match.
+.It Nm tcp
+Only TCP packets match.
+.It Nm udp
+Only UDP packets match.
+.It Nm icmp
+Only ICMP packets match.
+.It Nm <number|name>
+Only packets for the specified protocol matches (see
+.Pa /etc/protocols
+for a complete list).
+.El
+.Pp
+.Ar src
+and
+.Ar dst :
+.Pp
+.Bl -hang -offset flag
+.It <address/mask> [ports]
+.El
+.Pp
+The
+.Em <address/mask>
+may be specified as:
+.Bl -hang -offset flag -width 1234567890123456
+.It Ar ipno
+An ipnumber of the form 1.2.3.4.
+Only this exact ip number match the rule.
+.It Ar ipno/bits
+An ipnumber with a mask width of the form 1.2.3.4/24.
+In this case all ip numbers from 1.2.3.0 to 1.2.3.255 will match.
+.It Ar ipno:mask
+An ipnumber with a mask width of the form 1.2.3.4:255.255.240.0.
+In this case all ip numbers from 1.2.0.0 to 1.2.15.255 will match.
+.El
+.Pp
+With the TCP and UDP
+.Em protocols ,
+an optional
+.Em port
+may be specified as:
+.Pp
+.Bl -hang -offset flag
+.It Ns {port|port-port} Ns Op ,port Ns Op ,...
+.El
+.Pp
+Service names (from
+.Pa /etc/services )
+may not be used instead of a numeric port value.
+Also, note that a range may only be specified as the first value,
+and the port list is limited to
+.Nm IP_FW_MAX_PORTS
+(as defined in /usr/src/sys/netinet/ip_fw.h)
+ports.
+.Pp
+If ``via''
+.Ar name
+is specified, only packets received via or on their way out of an interface
+matching
+.Ar name
+will match this rule.
+.Pp
+If ``via''
+.Ar ipno
+is specified, only packets received via or on their way out of an interface
+having the address
+.Ar ipno
+will match this rule.
+.Pp
+.Ar options :
+.Bl -hang -offset flag -width 1234567890123456
+.It frag
+Matches if the packet is a fragment and this is not the first fragment
+of the datagram.
+.It in
+Matches if this packet was on the way in.
+.It out
+Matches if this packet was on the way out.
+.It ipoptions Ar spec
+Matches if the IP header contains the comma separated list of
+options specified in
+.Ar spec .
+The supported IP options are:
+.Nm ssrr
+(strict source route),
+.Nm lsrr
+(loose source route),
+.Nm rr
+(record packet route), and
+.Nm ts
+(timestamp).
+The absence of a particular option may be denoted
+with a ``!''.
+.It established
+Matches packets that have the RST or ACK bits set.
+TCP packets only.
+.It setup
+Matches packets that have the SYN bit set but no ACK bit.
+TCP packets only.
+.It tcpflags Ar spec
+Matches if the TCP header contains the comma separated list of
+flags specified in
+.Ar spec .
+The supported TCP flags are:
+.Nm fin ,
+.Nm syn ,
+.Nm rst ,
+.Nm psh ,
+.Nm ack ,
+and
+.Nm urg .
+The absence of a particular flag may be denoted
+with a ``!''.
+.It icmptypes Ar types
+Matches if the ICMP type is in the list
+.Ar types .
+The list may be specified as any combination of ranges
+or individual types separated by commas.
+.El
+.Sh CHECKLIST
+Here are some important points to consider when designing your
+rules:
+.Bl -bullet -hang -offset flag -width 1234567890123456
+.It
+Remember that you filter both packets going in and out.
+Most connections need packets going in both directions.
+.It
+Remember to test very carefully.
+It is a good idea to be near the console when doing this.
+.It
+Don't forget the loopback interface.
+.El
+.Sh FINE POINTS
+There is one kind of packet that the firewall will always discard,
+that is an IP fragment with a fragment offset of one.
+This is a valid packet, but it only has one use, to try to circumvent
+firewalls.
+.Pp
+If you are logged in over a network, loading the LKM version of
+.Nm
+is probably not as straightforward as you would think.
+I recommend this command line:
+.Bd -literal -offset center
+modload /lkm/ipfw_mod.o && \e
+ipfw add 32000 allow all from any to any
+.Ed
+
+Along the same lines, doing an
+.Bd -literal -offset center
+ipfw flush
+.Ed
+
+in similar surroundings is also a bad idea.
+.Sh PACKET DIVERSION
+A divert socket bound to the specified port will receive all packets diverted
+to that port; see
+.Xr divert 4 .
+If no socket is bound to the destination port, or if the kernel
+wasn't compiled with divert socket support, diverted packets are dropped.
+.Sh EXAMPLES
+This command adds an entry which denies all tcp packets from
+.Em hacker.evil.org
+to the telnet port of
+.Em wolf.tambov.su
+from being forwarded by the host:
+.Pp
+.Dl ipfw add deny tcp from hacker.evil.org to wolf.tambov.su 23
+.Pp
+This one disallows any connection from the entire hackers network to
+my host:
+.Pp
+.Dl ipfw addf deny all from 123.45.67.0/24 to my.host.org
+.Pp
+Here is good usage of list command to see accounting records:
+.Pp
+.Dl ipfw -at l
+.Pp
+or in short form
+.Pp
+.Dl ipfw -a l
+.Pp
+This rule diverts all incoming packets from 192.168.2.0/24 to divert port 5000:
+.Pp
+.Dl ipfw divert 5000 all from 192.168.2.0/24 to any in
+.Sh SEE ALSO
+.Xr divert 4 ,
+.Xr ip 4 ,
+.Xr ipfirewall 4 ,
+.Xr protocols 5 ,
+.Xr services 5 ,
+.Xr reboot 8 ,
+.Xr syslogd 8
+.Sh BUGS
+.Pp
+.Em WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!
+.Pp
+This program can put your computer in rather unusable state. When
+using it for the first time, work on the console of the computer, and
+do
+.Em NOT
+do anything you don't understand.
+.Pp
+When manipulating/adding chain entries, service and protocol names are
+not accepted.
+.Sh AUTHORS
+Ugen J. S. Antsilevich,
+Poul-Henning Kamp,
+Alex Nash,
+Archie Cobbs.
+API based upon code written by Daniel Boulet for BSDI.
+.Sh HISTORY
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
new file mode 100644
index 0000000..7487b21
--- /dev/null
+++ b/sbin/ipfw/ipfw.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * NEW command line interface for IP firewall facility
+ *
+ * $Id: ipfw.c,v 1.34 1996/10/17 01:05:03 alex Exp $
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip_fw.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+int lineno = -1;
+char progname[BUFSIZ]; /* Program name for errors */
+
+int s; /* main RAW socket */
+int do_resolv=0; /* Would try to resolv all */
+int do_acct=0; /* Show packet/byte count */
+int do_time=0; /* Show time stamps */
+int do_force=0; /* Don't ask for confirmation */
+
+int
+mask_bits(m_ad)
+ struct in_addr m_ad;
+{
+ int h_fnd=0,h_num=0,i;
+ u_long mask;
+
+ mask=ntohl(m_ad.s_addr);
+ for (i=0;i<sizeof(u_long)*CHAR_BIT;i++) {
+ if (mask & 1L) {
+ h_fnd=1;
+ h_num++;
+ } else {
+ if (h_fnd)
+ return -1;
+ }
+ mask=mask>>1;
+ }
+ return h_num;
+}
+
+void
+print_port(prot, port, comma)
+ u_char prot;
+ u_short port;
+ const char *comma;
+{
+ struct servent *se;
+ struct protoent *pe;
+ const char *protocol;
+ int printed = 0;
+
+ if (do_resolv) {
+ pe = getprotobynumber(prot);
+ if (pe)
+ protocol = pe->p_name;
+ else
+ protocol = NULL;
+
+ se = getservbyport(htons(port), protocol);
+ if (se) {
+ printf("%s%s", comma, se->s_name);
+ printed = 1;
+ }
+ }
+ if (!printed)
+ printf("%s%d",comma,port);
+}
+
+void
+show_ipfw(chain)
+ struct ip_fw *chain;
+{
+ char *comma;
+ u_long adrt;
+ struct hostent *he;
+ struct protoent *pe;
+ int i, mb;
+
+ if (do_resolv)
+ setservent(1/*stayopen*/);
+
+ printf("%05u ", chain->fw_number);
+
+ if (do_acct)
+ printf("%10lu %10lu ",chain->fw_pcnt,chain->fw_bcnt);
+
+ if (do_time)
+ {
+ if (chain->timestamp)
+ {
+ char timestr[30];
+
+ strcpy(timestr, ctime((time_t *)&chain->timestamp));
+ *strchr(timestr, '\n') = '\0';
+ printf("%s ", timestr);
+ }
+ else
+ printf(" ");
+ }
+
+ switch (chain->fw_flg & IP_FW_F_COMMAND)
+ {
+ case IP_FW_F_ACCEPT:
+ printf("allow");
+ break;
+ case IP_FW_F_DIVERT:
+ printf("divert %u", chain->fw_divert_port);
+ break;
+ case IP_FW_F_COUNT:
+ printf("count");
+ break;
+ case IP_FW_F_DENY:
+ if (chain->fw_flg & IP_FW_F_ICMPRPL)
+ printf("reject");
+ else
+ printf("deny");
+ break;
+ default:
+ errx(1, "impossible");
+ }
+
+ if (chain->fw_flg & IP_FW_F_PRN)
+ printf(" log");
+
+ pe = getprotobynumber(chain->fw_prot);
+ if (pe)
+ printf(" %s", pe->p_name);
+ else
+ printf("%u", chain->fw_prot);
+
+ printf(" from ");
+
+ adrt=ntohl(chain->fw_smsk.s_addr);
+ if (adrt==ULONG_MAX && do_resolv) {
+ adrt=(chain->fw_src.s_addr);
+ he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
+ if (he==NULL) {
+ printf(inet_ntoa(chain->fw_src));
+ } else
+ printf("%s",he->h_name);
+ } else {
+ if (adrt!=ULONG_MAX) {
+ mb=mask_bits(chain->fw_smsk);
+ if (mb == 0) {
+ printf("any");
+ } else {
+ if (mb > 0) {
+ printf(inet_ntoa(chain->fw_src));
+ printf("/%d",mb);
+ } else {
+ printf(inet_ntoa(chain->fw_src));
+ printf(":");
+ printf(inet_ntoa(chain->fw_smsk));
+ }
+ }
+ } else
+ printf(inet_ntoa(chain->fw_src));
+ }
+
+ if (chain->fw_prot != IPPROTO_IP) {
+ comma = " ";
+ for (i=0;i<chain->fw_nsp; i++ ) {
+ print_port(chain->fw_prot, chain->fw_pts[i], comma);
+ if (i==0 && (chain->fw_flg & IP_FW_F_SRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+ }
+
+ printf(" to ");
+
+ adrt=ntohl(chain->fw_dmsk.s_addr);
+ if (adrt==ULONG_MAX && do_resolv) {
+ adrt=(chain->fw_dst.s_addr);
+ he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
+ if (he==NULL) {
+ printf(inet_ntoa(chain->fw_dst));
+ } else
+ printf("%s",he->h_name);
+ } else {
+ if (adrt!=ULONG_MAX) {
+ mb=mask_bits(chain->fw_dmsk);
+ if (mb == 0) {
+ printf("any");
+ } else {
+ if (mb > 0) {
+ printf(inet_ntoa(chain->fw_dst));
+ printf("/%d",mb);
+ } else {
+ printf(inet_ntoa(chain->fw_dst));
+ printf(":");
+ printf(inet_ntoa(chain->fw_dmsk));
+ }
+ }
+ } else
+ printf(inet_ntoa(chain->fw_dst));
+ }
+
+ comma = " ";
+ for (i=0;i<chain->fw_ndp;i++) {
+ print_port(chain->fw_prot, chain->fw_pts[chain->fw_nsp+i],
+ comma);
+ if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+
+ if ((chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
+ ;
+ else if (chain->fw_flg & IP_FW_F_IN)
+ printf(" in");
+ else if (chain->fw_flg & IP_FW_F_OUT)
+ printf(" out");
+
+ if (chain->fw_flg&IP_FW_F_IFNAME && chain->fw_via_name[0]) {
+ char ifnb[FW_IFNLEN+1];
+ printf(" via ");
+ strncpy(ifnb,chain->fw_via_name,FW_IFNLEN);
+ ifnb[FW_IFNLEN]='\0';
+ if (chain->fw_flg & IP_FW_F_IFUWILD)
+ printf("%s*",ifnb);
+ else
+ printf("%s%d",ifnb,chain->fw_via_unit);
+ } else if (chain->fw_via_ip.s_addr) {
+ printf(" via ");
+ printf(inet_ntoa(chain->fw_via_ip));
+ }
+
+ if (chain->fw_flg & IP_FW_F_FRAG)
+ printf(" frag");
+
+ if (chain->fw_ipopt || chain->fw_ipnopt) {
+ int _opt_printed = 0;
+#define PRINTOPT(x) {if (_opt_printed) printf(",");\
+ printf(x); _opt_printed = 1;}
+
+ printf(" ipopt ");
+ if (chain->fw_ipopt & IP_FW_IPOPT_SSRR) PRINTOPT("ssrr");
+ if (chain->fw_ipnopt & IP_FW_IPOPT_SSRR) PRINTOPT("!ssrr");
+ if (chain->fw_ipopt & IP_FW_IPOPT_LSRR) PRINTOPT("lsrr");
+ if (chain->fw_ipnopt & IP_FW_IPOPT_LSRR) PRINTOPT("!lsrr");
+ if (chain->fw_ipopt & IP_FW_IPOPT_RR) PRINTOPT("rr");
+ if (chain->fw_ipnopt & IP_FW_IPOPT_RR) PRINTOPT("!rr");
+ if (chain->fw_ipopt & IP_FW_IPOPT_TS) PRINTOPT("ts");
+ if (chain->fw_ipnopt & IP_FW_IPOPT_TS) PRINTOPT("!ts");
+ }
+
+ if (chain->fw_tcpf & IP_FW_TCPF_ESTAB)
+ printf(" established");
+ else if (chain->fw_tcpf == IP_FW_TCPF_SYN &&
+ chain->fw_tcpnf == IP_FW_TCPF_ACK)
+ printf(" setup");
+ else if (chain->fw_tcpf || chain->fw_tcpnf) {
+ int _flg_printed = 0;
+#define PRINTFLG(x) {if (_flg_printed) printf(",");\
+ printf(x); _flg_printed = 1;}
+
+ printf(" tcpflg ");
+ if (chain->fw_tcpf & IP_FW_TCPF_FIN) PRINTFLG("fin");
+ if (chain->fw_tcpnf & IP_FW_TCPF_FIN) PRINTFLG("!fin");
+ if (chain->fw_tcpf & IP_FW_TCPF_SYN) PRINTFLG("syn");
+ if (chain->fw_tcpnf & IP_FW_TCPF_SYN) PRINTFLG("!syn");
+ if (chain->fw_tcpf & IP_FW_TCPF_RST) PRINTFLG("rst");
+ if (chain->fw_tcpnf & IP_FW_TCPF_RST) PRINTFLG("!rst");
+ if (chain->fw_tcpf & IP_FW_TCPF_PSH) PRINTFLG("psh");
+ if (chain->fw_tcpnf & IP_FW_TCPF_PSH) PRINTFLG("!psh");
+ if (chain->fw_tcpf & IP_FW_TCPF_ACK) PRINTFLG("ack");
+ if (chain->fw_tcpnf & IP_FW_TCPF_ACK) PRINTFLG("!ack");
+ if (chain->fw_tcpf & IP_FW_TCPF_URG) PRINTFLG("urg");
+ if (chain->fw_tcpnf & IP_FW_TCPF_URG) PRINTFLG("!urg");
+ }
+ if (chain->fw_flg & IP_FW_F_ICMPBIT) {
+ int type_index;
+ int first = 1;
+
+ printf(" icmptype");
+
+ for (type_index = 0; type_index < 256; ++type_index)
+ if (chain->fw_icmptypes[type_index / (sizeof(unsigned) * 8)] &
+ (1U << (type_index % (sizeof(unsigned) * 8)))) {
+ printf("%c%d", first == 1 ? ' ' : ',', type_index);
+ first = 0;
+ }
+ }
+ printf("\n");
+ if (do_resolv)
+ endservent();
+}
+
+void
+list(ac, av)
+ int ac;
+ char **av;
+{
+ struct ip_fw *r;
+ struct ip_fw rules[1024];
+ int l,i;
+
+ memset(rules,0,sizeof rules);
+ l = sizeof rules;
+ i = getsockopt(s, IPPROTO_IP, IP_FW_GET, rules, &l);
+ if (i < 0)
+ err(2,"getsockopt(IP_FW_GET)");
+ for (r=rules; l >= sizeof rules[0]; r++, l-=sizeof rules[0])
+ show_ipfw(r);
+}
+
+void
+show_usage(str)
+ char *str;
+{
+ if (str)
+ fprintf(stderr,"%s: ERROR - %s\n",progname,str);
+ fprintf(stderr,
+"Usage:\n"
+"\t%s [options]\n"
+"\t\tflush\n"
+"\t\tadd [number] rule\n"
+"\t\tdelete number\n"
+"\t\tlist [number]\n"
+"\t\tzero [number]\n"
+"\trule:\taction proto src dst extras...\n"
+"\t\taction: {allow|deny|reject|count|divert port} [log]\n"
+"\t\tproto: {ip|tcp|udp|icmp|<number>}}\n"
+"\t\tsrc: from {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+"\t\tdst: to {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+"\textras:\n"
+"\t\tfragment\n"
+"\t\t{in|out|inout}\n"
+"\t\tvia {ifname|ip}\n"
+"\t\t{established|setup}\n"
+"\t\ttcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
+"\t\tipoptions [!]{ssrr|lsrr|rr|ts},...\n"
+"\t\ticmptypes {type},...\n"
+"\t\tproto {ipproto},...\n"
+, progname
+);
+
+
+ fprintf(stderr,"See man %s(8) for proper usage.\n",progname);
+ exit (1);
+}
+
+int
+lookup_host (host, ipaddr)
+ char *host;
+ struct in_addr *ipaddr;
+{
+ struct hostent *he = gethostbyname(host);
+
+ if (!he)
+ return(-1);
+
+ *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+
+ return(0);
+}
+
+void
+fill_ip(ipno, mask, acp, avp)
+ struct in_addr *ipno, *mask;
+ int *acp;
+ char ***avp;
+{
+ int ac = *acp;
+ char **av = *avp;
+ char *p = 0, md = 0;
+
+ if (ac && !strncmp(*av,"any",strlen(*av))) {
+ ipno->s_addr = mask->s_addr = 0; av++; ac--;
+ } else {
+ p = strchr(*av, '/');
+ if (!p)
+ p = strchr(*av, ':');
+ if (p) {
+ md = *p;
+ *p++ = '\0';
+ }
+
+ if (lookup_host(*av,ipno) != 0)
+ show_usage("ip number\n");
+ switch (md) {
+ case ':':
+ if (!inet_aton(p,mask))
+ show_usage("ip number\n");
+ break;
+ case '/':
+ if (atoi(p) == 0) {
+ mask->s_addr = 0;
+ } else {
+ mask->s_addr = htonl(0xffffffff << (32 - atoi(p)));
+ }
+ break;
+ default:
+ mask->s_addr = htonl(0xffffffff);
+ break;
+ }
+ ipno->s_addr &= mask->s_addr;
+ av++;
+ ac--;
+ }
+ *acp = ac;
+ *avp = av;
+}
+
+void
+add_port(cnt, ptr, off, port)
+ u_short *cnt, *ptr, off, port;
+{
+ if (off + *cnt >= IP_FW_MAX_PORTS)
+ errx(1, "too many ports (max is %d)", IP_FW_MAX_PORTS);
+ ptr[off+*cnt] = port;
+ (*cnt)++;
+}
+
+int
+fill_port(cnt, ptr, off, arg)
+ u_short *cnt, *ptr, off;
+ char *arg;
+{
+ char *s;
+ int initial_range = 0;
+
+ s = strchr(arg,'-');
+ if (s) {
+ *s++ = '\0';
+ if (strchr(arg, ','))
+ errx(1, "port range must be first in list");
+ add_port(cnt, ptr, off, *arg ? atoi(arg) : 0x0000);
+ arg = s;
+ s = strchr(arg,',');
+ if (s)
+ *s++ = '\0';
+ add_port(cnt, ptr, off, *arg ? atoi(arg) : 0xffff);
+ arg = s;
+ initial_range = 1;
+ }
+ while (arg != NULL) {
+ s = strchr(arg,',');
+ if (s)
+ *s++ = '\0';
+ add_port(cnt, ptr, off, atoi(arg));
+ arg = s;
+ }
+ return initial_range;
+}
+
+void
+fill_tcpflag(set, reset, vp)
+ u_char *set, *reset;
+ char **vp;
+{
+ char *p = *vp,*q;
+ u_char *d;
+
+ while (p && *p) {
+ struct tpcflags {
+ char * name;
+ u_char value;
+ } flags[] = {
+ { "syn", IP_FW_TCPF_SYN },
+ { "fin", IP_FW_TCPF_FIN },
+ { "ack", IP_FW_TCPF_ACK },
+ { "psh", IP_FW_TCPF_PSH },
+ { "rst", IP_FW_TCPF_RST },
+ { "urg", IP_FW_TCPF_URG }
+ };
+ int i;
+
+ if (*p == '!') {
+ p++;
+ d = reset;
+ } else {
+ d = set;
+ }
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
+ if (!strncmp(p, flags[i].name, strlen(p))) {
+ *d |= flags[i].value;
+ break;
+ }
+ if (i == sizeof(flags) / sizeof(flags[0]))
+ show_usage("invalid tcp flag\n");
+ p = q;
+ }
+}
+
+void
+fill_ipopt(set, reset, vp)
+ u_char *set, *reset;
+ char **vp;
+{
+ char *p = *vp,*q;
+ u_char *d;
+
+ while (p && *p) {
+ if (*p == '!') {
+ p++;
+ d = reset;
+ } else {
+ d = set;
+ }
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ if (!strncmp(p,"ssrr",strlen(p))) *d |= IP_FW_IPOPT_SSRR;
+ if (!strncmp(p,"lsrr",strlen(p))) *d |= IP_FW_IPOPT_LSRR;
+ if (!strncmp(p,"rr",strlen(p))) *d |= IP_FW_IPOPT_RR;
+ if (!strncmp(p,"ts",strlen(p))) *d |= IP_FW_IPOPT_TS;
+ p = q;
+ }
+}
+
+void
+fill_icmptypes(types, vp, fw_flg)
+ u_long *types;
+ char **vp;
+ u_short *fw_flg;
+{
+ char *c = *vp;
+
+ while (*c)
+ {
+ unsigned long icmptype;
+
+ if ( *c == ',' )
+ ++c;
+
+ icmptype = strtoul(c, &c, 0);
+
+ if ( *c != ',' && *c != '\0' )
+ show_usage("invalid ICMP type");
+
+ if (icmptype > 255)
+ show_usage("ICMP types are between 0 and 255 inclusive");
+
+ types[icmptype / (sizeof(unsigned) * 8)] |=
+ 1 << (icmptype % (sizeof(unsigned) * 8));
+ *fw_flg |= IP_FW_F_ICMPBIT;
+ }
+}
+
+void
+delete(ac,av)
+ int ac;
+ char **av;
+{
+ struct ip_fw rule;
+ int i;
+
+ memset(&rule, 0, sizeof rule);
+
+ av++; ac--;
+
+ /* Rule number */
+ if (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ }
+
+ i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
+ if (i)
+ err(1,"setsockopt(IP_FW_DEL)");
+}
+
+int
+verify_interface(rule)
+ struct ip_fw *rule;
+{
+ struct ifreq ifr;
+
+ /*
+ * If a unit was specified, check for that exact interface.
+ * If a wildcard was specified, check for unit 0.
+ */
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
+ rule->fw_via_name,
+ rule->fw_flg & IP_FW_F_IFUWILD ? 0 : rule->fw_via_unit);
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ return(-1); /* interface isn't recognized by the kernel */
+
+ return(0); /* interface exists */
+}
+
+void
+add(ac,av)
+ int ac;
+ char **av;
+{
+ struct ip_fw rule;
+ int i;
+ u_char proto;
+ struct protoent *pe;
+
+ memset(&rule, 0, sizeof rule);
+
+ av++; ac--;
+
+ /* Rule number */
+ if (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ }
+
+ /* Action */
+ if (ac && (!strncmp(*av,"accept",strlen(*av))
+ || !strncmp(*av,"pass",strlen(*av))
+ || !strncmp(*av,"allow",strlen(*av))
+ || !strncmp(*av,"permit",strlen(*av)))) {
+ rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
+ } else if (ac && !strncmp(*av,"count",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
+ } else if (ac && !strncmp(*av,"divert",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
+ if (!ac)
+ show_usage("missing divert port");
+ rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
+ if (rule.fw_divert_port == 0)
+ show_usage("illegal divert port");
+ } else if (ac && (!strncmp(*av,"deny",strlen(*av)))) {
+ rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
+ } else if (ac && !strncmp(*av,"reject",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_DENY|IP_FW_F_ICMPRPL; av++; ac--;
+ } else {
+ show_usage("missing/unrecognized action\n");
+ }
+
+ /* [log] */
+ if (ac && !strncmp(*av,"log",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
+ }
+
+ /* protocol */
+ if (ac) {
+ if ((proto = atoi(*av)) > 0) {
+ rule.fw_prot = proto; av++; ac--;
+ } else if (!strncmp(*av,"all",strlen(*av))) {
+ rule.fw_prot = IPPROTO_IP; av++; ac--;
+ } else if ((pe = getprotobyname(*av)) != NULL) {
+ rule.fw_prot = pe->p_proto; av++; ac--;
+ } else {
+ show_usage("invalid protocol\n");
+ }
+ } else
+ show_usage("missing protocol\n");
+
+ /* from */
+ if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
+ else show_usage("missing ``from''\n");
+
+ fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
+
+ if (ac && isdigit(**av)) {
+ if (fill_port(&rule.fw_nsp, &rule.fw_pts, 0, *av, 0))
+ rule.fw_flg |= IP_FW_F_SRNG;
+ av++; ac--;
+ }
+
+ /* to */
+ if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
+ else show_usage("missing ``to''\n");
+
+ if (!ac) show_usage("Missing arguments\n");
+
+ fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
+
+ if (ac && isdigit(**av)) {
+ if (fill_port(&rule.fw_ndp, &rule.fw_pts, rule.fw_nsp, *av, 0))
+ rule.fw_flg |= IP_FW_F_DRNG;
+ av++; ac--;
+ }
+
+ if ((rule.fw_prot != IPPROTO_TCP) &&
+ (rule.fw_prot != IPPROTO_UDP) &&
+ (rule.fw_nsp || rule.fw_ndp)) {
+ show_usage("only TCP and UDP protocols are valid with port specifications");
+ }
+
+ while (ac) {
+ if (ac && !strncmp(*av,"via",strlen(*av))) {
+ if (rule.fw_via_ip.s_addr || (rule.fw_flg & IP_FW_F_IFNAME)) {
+ show_usage("multiple 'via' options specified");
+ }
+
+ av++; ac--;
+ if (!isdigit(**av)) {
+ char *q;
+
+ strcpy(rule.fw_via_name, *av);
+ for (q = rule.fw_via_name; *q && !isdigit(*q) && *q != '*'; q++)
+ continue;
+ if (*q == '*')
+ rule.fw_flg |= IP_FW_F_IFUWILD;
+ else
+ rule.fw_via_unit = atoi(q);
+ *q = '\0';
+ rule.fw_flg |= IP_FW_F_IFNAME;
+ if (verify_interface(&rule) != 0)
+ fprintf(stderr, "Warning: interface does not exist\n");
+ } else if (inet_aton(*av,&rule.fw_via_ip) == INADDR_NONE) {
+ show_usage("bad IP# after via\n");
+ }
+ av++; ac--;
+ continue;
+ }
+ if (!strncmp(*av,"fragment",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_FRAG; av++; ac--; continue;
+ }
+ if (!strncmp(*av,"in",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_IN; av++; ac--; continue;
+ }
+ if (!strncmp(*av,"out",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_OUT; av++; ac--; continue;
+ }
+ if (ac > 1 && !strncmp(*av,"ipoptions",strlen(*av))) {
+ av++; ac--;
+ fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
+ av++; ac--; continue;
+ }
+ if (rule.fw_prot == IPPROTO_TCP) {
+ if (!strncmp(*av,"established",strlen(*av))) {
+ rule.fw_tcpf |= IP_FW_TCPF_ESTAB;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"setup",strlen(*av))) {
+ rule.fw_tcpf |= IP_FW_TCPF_SYN;
+ rule.fw_tcpnf |= IP_FW_TCPF_ACK;
+ av++; ac--; continue;
+ }
+ if (ac > 1 && !strncmp(*av,"tcpflags",strlen(*av))) {
+ av++; ac--;
+ fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
+ av++; ac--; continue;
+ }
+ }
+ if (rule.fw_prot == IPPROTO_ICMP) {
+ if (ac > 1 && !strncmp(*av,"icmptypes",strlen(*av))) {
+ av++; ac--;
+ fill_icmptypes(rule.fw_icmptypes, av, &rule.fw_flg);
+ av++; ac--; continue;
+ }
+ }
+ printf("%d %s\n",ac,*av);
+ show_usage("Unknown argument\n");
+ }
+
+ show_ipfw(&rule);
+ i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
+ if (i)
+ err(1,"setsockopt(IP_FW_ADD)");
+}
+
+void
+zero (ac, av)
+ int ac;
+ char **av;
+{
+ av++; ac--;
+
+ if (!ac) {
+ /* clear all entries */
+ if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0) {
+ fprintf(stderr,"%s: setsockopt failed.\n",progname);
+ exit(1);
+ }
+ printf("Accounting cleared.\n");
+ } else {
+ /* clear a specific entry */
+ struct ip_fw rule;
+
+ memset(&rule, 0, sizeof rule);
+
+ /* Rule number */
+ if (isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+
+ if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, &rule, sizeof rule))
+ err(1, "setsockopt(Zero)");
+ printf("Entry %d cleared\n", rule.fw_number);
+ }
+ else {
+ show_usage("expected number");
+ }
+ }
+}
+
+int
+ipfw_main(ac,av)
+ int ac;
+ char **av;
+{
+
+ char ch;
+ extern int optind;
+
+
+ if ( ac == 1 ) {
+ show_usage(NULL);
+ }
+
+ /* Set the force flag for non-interactive processes */
+ do_force = !isatty(STDIN_FILENO);
+
+ while ((ch = getopt(ac, av ,"aftN")) != EOF)
+ switch(ch) {
+ case 'a':
+ do_acct=1;
+ break;
+ case 'f':
+ do_force=1;
+ break;
+ case 't':
+ do_time=1;
+ break;
+ case 'N':
+ do_resolv=1;
+ break;
+ default:
+ show_usage("Unrecognised switch");
+ }
+
+ ac -= optind;
+ if (*(av+=optind)==NULL) {
+ show_usage("Bad arguments");
+ }
+
+ if (!strncmp(*av, "add", strlen(*av))) {
+ add(ac,av);
+ } else if (!strncmp(*av, "delete", strlen(*av))) {
+ delete(ac,av);
+ } else if (!strncmp(*av, "flush", strlen(*av))) {
+ int do_flush = 0;
+
+ if ( do_force )
+ do_flush = 1;
+ else {
+ int c;
+
+ /* Ask the user */
+ printf("Are you sure? [yn] ");
+ do {
+ fflush(stdout);
+ c = toupper(getc(stdin));
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'Y' && c != 'N');
+ printf("\n");
+ if (c == 'Y')
+ do_flush = 1;
+ }
+ if ( do_flush ) {
+ if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0)<0) {
+ fprintf(stderr,"%s: setsockopt failed.\n",progname);
+ exit(1);
+ }
+ printf("Flushed all rules.\n");
+ }
+ } else if (!strncmp(*av, "zero", strlen(*av))) {
+ zero(ac,av);
+ } else if (!strncmp(*av, "print", strlen(*av))) {
+ list(--ac,++av);
+ } else if (!strncmp(*av, "list", strlen(*av))) {
+ list(--ac,++av);
+ } else {
+ show_usage("Bad arguments");
+ }
+ return 0;
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+#define MAX_ARGS 32
+ char buf[BUFSIZ];
+ char *args[MAX_ARGS];
+ char linename[10];
+ int i;
+ FILE *f;
+
+ strcpy(progname,*av);
+
+ s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
+ if ( s < 0 ) {
+ fprintf(stderr,"%s: Can't open raw socket.\n"
+ "Must be root to use this program.\n",progname);
+ exit(1);
+ }
+
+ setbuf(stdout,0);
+
+ if (av[1] && !access(av[1], R_OK)) {
+ lineno = 0;
+ f = fopen(av[1], "r");
+ while (fgets(buf, BUFSIZ, f)) {
+ if (buf[strlen(buf)-1]=='\n')
+ buf[strlen(buf)-1] = 0;
+
+ lineno++;
+ sprintf(linename, "Line %d", lineno);
+ args[0] = linename;
+
+ args[1] = buf;
+ while(*args[1] == ' ')
+ args[1]++;
+ i = 2;
+ while((args[i] = strchr(args[i-1],' '))) {
+ *(args[i]++) = 0;
+ while(*args[i] == ' ')
+ args[i]++;
+ i++;
+ }
+ if (*args[i-1] == 0)
+ i--;
+ args[i] = NULL;
+
+ ipfw_main(i, args);
+ }
+ fclose(f);
+ } else
+ ipfw_main(ac,av);
+ return 0;
+}
diff --git a/sbin/ldconfig/Makefile b/sbin/ldconfig/Makefile
new file mode 100644
index 0000000..1dfb159
--- /dev/null
+++ b/sbin/ldconfig/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.7 1994/04/13 20:49:42 ats Exp $
+
+PROG= ldconfig
+SRCS= ldconfig.c shlib.c support.c
+LDDIR?= $(.CURDIR)/..
+CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE)
+LDFLAGS+=-static
+BINDIR= /sbin
+MAN8= ldconfig.8
+
+.PATH: $(LDDIR) $(LDDIR)/$(MACHINE)
+
+.include <bsd.prog.mk>
diff --git a/sbin/ldconfig/ldconfig.8 b/sbin/ldconfig/ldconfig.8
new file mode 100644
index 0000000..652ebf9
--- /dev/null
+++ b/sbin/ldconfig/ldconfig.8
@@ -0,0 +1,131 @@
+.\"
+.\" Copyright (c) 1993 Paul Kranenburg
+.\" 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 Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ldconfig.8,v 1.9 1996/10/10 23:14:22 jdp Exp $
+.\"
+.Dd October 3, 1993
+.Dt LDCONFIG 8
+.Os FreeBSD
+.Sh NAME
+.Nm ldconfig
+.Nd configure the shared library cache
+.Sh SYNOPSIS
+.Nm ldconfig
+.Op Fl mrsv
+.Op Fl f Ar hints_file
+.Op Ar directory Ar ...
+.Sh DESCRIPTION
+.Nm
+is used to prepare a set of
+.Dq hints
+for use by the run-time linker
+.Xr ld.so 1
+to facilitate quick lookup of shared libraries available in multiple
+directories. It scans a set of built-in system directories and any
+.Ar directories
+specified on the command line (in the given order) looking for shared
+libraries and stores the results in the file
+.Pa /var/run/ld.so.hints
+to forestall the overhead that would otherwise result from the
+directory search operations
+.Xr ld.so 1
+would have to perform to load the required shared libraries.
+.Pp
+The shared libraries so found will be automatically available for loading
+if needed by the program being prepared for execution. This obviates the need
+for storing search paths within the executable.
+.Pp
+The
+.Ev LD_LIBRARY_PATH
+environment variable can be used to override the use of
+directories (or the order thereof) from the cache or to specify additional
+directories where shared libraries might be found.
+.Ev LD_LIBRARY_PATH
+is a
+.Sq \:
+separated list of directory paths which are searched by
+.Xr ld.so 1
+when it needs to load a shared library. It can be viewed as the run-time
+equivalent of the
+.Fl L
+switch of
+.Xr ld 1 .
+.Pp
+.Nm Ldconfig
+is typically run as part of the boot sequence.
+.Pp
+The following options recognized by
+.Nm ldconfig:
+.Bl -tag -width indent
+.It Fl f Ar hints_file
+Read and/or update the specified hints file, instead of
+.Pa /var/run/ld.so.hints .
+This option is provided primarily for testing.
+.It Fl m
+Instead of replacing the contents of the hints file
+with those found in the directories specified,
+.Dq merge
+in new entries.
+Directories recorded in the hints file by previous runs of
+.Nm
+are also rescanned for new shared libraries.
+.It Fl r
+List the current contents of the hints file
+on the standard output. The hints file is not modified.
+.It Fl s
+Do not scan the built-in system directory
+.Pq Dq /usr/lib
+for shared libraries.
+.It Fl v
+Switch on verbose mode.
+.Sh Security
+Special care must be taken when loading shared libraries into the address
+space of
+.Ev set-user-Id
+programs. Whenever such a program is run,
+.Nm ld.so
+will only load shared libraries from the hints
+file. In particular, the
+.Ev LD_LIBRARY_PATH
+is not used to search for libraries. Thus, the role of ldconfig is dual. In
+addition to building a set of hints for quick lookup, it also serves to
+specify the trusted collection of directories from which shared objects can
+be safely loaded. It is presumed that the set of directories specified to
+.Nm ldconfig
+are under control of the system's administrator.
+.Sh FILES
+.Pa /var/run/ld.so.hints
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr link 5
+.Sh HISTORY
+A
+.Nm
+utility first appeared in SunOS 4.0, it appeared in its current form
+in FreeBSD 1.1.
diff --git a/sbin/ldconfig/ldconfig.c b/sbin/ldconfig/ldconfig.c
new file mode 100644
index 0000000..1189d6d
--- /dev/null
+++ b/sbin/ldconfig/ldconfig.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1993,1995 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ldconfig.c,v 1.15 1996/10/10 23:14:23 jdp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <errno.h>
+#include <err.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <machine/param.h>
+
+#include <link.h>
+#include "shlib.h"
+#include "support.h"
+
+#if DEBUG
+/* test */
+#undef _PATH_LD_HINTS
+#define _PATH_LD_HINTS "./ld.so.hints"
+#endif
+
+#undef major
+#undef minor
+
+extern char *__progname;
+
+static int verbose;
+static int nostd;
+static int justread;
+static int merge;
+static char *hints_file = _PATH_LD_HINTS;
+
+struct shlib_list {
+ /* Internal list of shared libraries found */
+ char *name;
+ char *path;
+ int dewey[MAXDEWEY];
+ int ndewey;
+#define major dewey[0]
+#define minor dewey[1]
+ struct shlib_list *next;
+};
+
+static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
+static char *dir_list;
+
+static void enter __P((char *, char *, char *, int *, int));
+static int dodir __P((char *, int));
+static int buildhints __P((void));
+static int readhints __P((void));
+static void listhints __P((void));
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int i, c;
+ int rval = 0;
+
+ while ((c = getopt(argc, argv, "f:mrsv")) != EOF) {
+ switch (c) {
+ case 'f':
+ hints_file = optarg;
+ break;
+ case 'm':
+ merge = 1;
+ break;
+ case 'r':
+ justread = 1;
+ break;
+ case 's':
+ nostd = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ errx(1, "Usage: %s [-mrsv] [-f hints_file] [dir ...]",
+ __progname);
+ break;
+ }
+ }
+
+ dir_list = strdup("");
+
+ if (justread || merge) {
+ if ((rval = readhints()) != 0)
+ return rval;
+ }
+
+ if (!nostd && !merge)
+ std_search_path();
+
+ if (!justread) { /* Add any directories from the command line */
+ for (i = optind; i < argc; i++) {
+ if (access(argv[i], F_OK) == -1) { /* Doesn't exist */
+ warn("%s", argv[i]);
+ rval = -1;
+ } else
+ add_search_path(argv[i]);
+ }
+ }
+
+ for (i = 0; i < n_search_dirs; i++) {
+ char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
+ free(dir_list);
+ dir_list = cp;
+ }
+
+ if (justread) {
+ listhints();
+ return 0;
+ }
+
+ for (i = 0; i < n_search_dirs; i++)
+ rval |= dodir(search_dirs[i], 1);
+
+ rval |= buildhints();
+
+ return rval;
+}
+
+int
+dodir(dir, silent)
+char *dir;
+int silent;
+{
+ DIR *dd;
+ struct dirent *dp;
+ char name[MAXPATHLEN];
+ int dewey[MAXDEWEY], ndewey;
+
+ if ((dd = opendir(dir)) == NULL) {
+ if (silent && errno == ENOENT) /* Ignore the error */
+ return 0;
+ warn("%s", dir);
+ return -1;
+ }
+
+ while ((dp = readdir(dd)) != NULL) {
+ register int n;
+ register char *cp;
+
+ /* Check for `lib' prefix */
+ if (dp->d_name[0] != 'l' ||
+ dp->d_name[1] != 'i' ||
+ dp->d_name[2] != 'b')
+ continue;
+
+ /* Copy the entry minus prefix */
+ (void)strcpy(name, dp->d_name + 3);
+ n = strlen(name);
+ if (n < 4)
+ continue;
+
+ /* Find ".so." in name */
+ for (cp = name + n - 4; cp > name; --cp) {
+ if (cp[0] == '.' &&
+ cp[1] == 's' &&
+ cp[2] == 'o' &&
+ cp[3] == '.')
+ break;
+ }
+ if (cp <= name)
+ continue;
+
+ *cp = '\0';
+ if (!isdigit(*(cp+4)))
+ continue;
+
+ bzero((caddr_t)dewey, sizeof(dewey));
+ ndewey = getdewey(dewey, cp + 4);
+ enter(dir, dp->d_name, name, dewey, ndewey);
+ }
+
+ return 0;
+}
+
+static void
+enter(dir, file, name, dewey, ndewey)
+char *dir, *file, *name;
+int dewey[], ndewey;
+{
+ struct shlib_list *shp;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ if (strcmp(name, shp->name) != 0 || major != shp->major)
+ continue;
+
+ /* Name matches existing entry */
+ if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
+
+ /* Update this entry with higher versioned lib */
+ if (verbose)
+ printf("Updating lib%s.%d.%d to %s/%s\n",
+ shp->name, shp->major, shp->minor,
+ dir, file);
+
+ free(shp->name);
+ shp->name = strdup(name);
+ free(shp->path);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ }
+ break;
+ }
+
+ if (shp)
+ /* Name exists: older version or just updated */
+ return;
+
+ /* Allocate new list element */
+ if (verbose)
+ printf("Adding %s/%s\n", dir, file);
+
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = strdup(name);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, MAXDEWEY);
+ shp->ndewey = ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+}
+
+
+int
+hinthash(cp, vmajor)
+char *cp;
+int vmajor;
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+
+ return k;
+}
+
+int
+buildhints()
+{
+ struct hints_header hdr;
+ struct hints_bucket *blist;
+ struct shlib_list *shp;
+ char *strtab;
+ int i, n, str_index = 0;
+ int strtab_sz = 0; /* Total length of strings */
+ int nhints = 0; /* Total number of hints */
+ int fd;
+ char *tmpfile;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ strtab_sz += 1 + strlen(shp->name);
+ strtab_sz += 1 + strlen(shp->path);
+ nhints++;
+ }
+
+ /* Fill hints file header */
+ hdr.hh_magic = HH_MAGIC;
+ hdr.hh_version = LD_HINTS_VERSION_2;
+ hdr.hh_nbucket = 1 * nhints;
+ n = hdr.hh_nbucket * sizeof(struct hints_bucket);
+ hdr.hh_hashtab = sizeof(struct hints_header);
+ hdr.hh_strtab = hdr.hh_hashtab + n;
+ hdr.hh_dirlist = strtab_sz;
+ strtab_sz += 1 + strlen(dir_list);
+ hdr.hh_strtab_sz = strtab_sz;
+ hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
+
+ if (verbose)
+ printf("Totals: entries %d, buckets %ld, string size %d\n",
+ nhints, hdr.hh_nbucket, strtab_sz);
+
+ /* Allocate buckets and string table */
+ blist = (struct hints_bucket *)xmalloc(n);
+ bzero((char *)blist, n);
+ for (i = 0; i < hdr.hh_nbucket; i++)
+ /* Empty all buckets */
+ blist[i].hi_next = -1;
+
+ strtab = (char *)xmalloc(strtab_sz);
+
+ /* Enter all */
+ for (shp = shlib_head; shp; shp = shp->next) {
+ struct hints_bucket *bp;
+
+ bp = blist +
+ (hinthash(shp->name, shp->major) % hdr.hh_nbucket);
+
+ if (bp->hi_pathx) {
+ int i;
+
+ for (i = 0; i < hdr.hh_nbucket; i++) {
+ if (blist[i].hi_pathx == 0)
+ break;
+ }
+ if (i == hdr.hh_nbucket) {
+ warnx("Bummer!");
+ return -1;
+ }
+ while (bp->hi_next != -1)
+ bp = &blist[bp->hi_next];
+ bp->hi_next = i;
+ bp = blist + i;
+ }
+
+ /* Insert strings in string table */
+ bp->hi_namex = str_index;
+ strcpy(strtab + str_index, shp->name);
+ str_index += 1 + strlen(shp->name);
+
+ bp->hi_pathx = str_index;
+ strcpy(strtab + str_index, shp->path);
+ str_index += 1 + strlen(shp->path);
+
+ /* Copy versions */
+ bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
+ bp->hi_ndewey = shp->ndewey;
+ }
+
+ /* Copy search directories */
+ strcpy(strtab + str_index, dir_list);
+ str_index += 1 + strlen(dir_list);
+
+ /* Sanity check */
+ if (str_index != strtab_sz) {
+ errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
+ }
+
+ tmpfile = concat(hints_file, ".XXXXXX", "");
+ if ((tmpfile = mktemp(tmpfile)) == NULL) {
+ warn("%s", tmpfile);
+ return -1;
+ }
+
+ umask(0); /* Create with exact permissions */
+ if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, 0444)) == -1) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ if (write(fd, &hdr, sizeof(struct hints_header)) !=
+ sizeof(struct hints_header)) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
+ hdr.hh_nbucket * sizeof(struct hints_bucket)) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (write(fd, strtab, strtab_sz) != strtab_sz) {
+ warn("%s", hints_file);
+ return -1;
+ }
+ if (close(fd) != 0) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ /* Install it */
+ if (unlink(hints_file) != 0 && errno != ENOENT) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ if (rename(tmpfile, hints_file) != 0) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+readhints()
+{
+ int fd;
+ caddr_t addr;
+ long msize;
+ struct hints_header *hdr;
+ struct hints_bucket *blist;
+ char *strtab;
+ struct shlib_list *shp;
+ int i;
+
+ if ((fd = open(hints_file, O_RDONLY, 0)) == -1) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ msize = PAGE_SIZE;
+ addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0);
+
+ if (addr == (caddr_t)-1) {
+ warn("%s", hints_file);
+ return -1;
+ }
+
+ hdr = (struct hints_header *)addr;
+ if (HH_BADMAG(*hdr)) {
+ warnx("%s: Bad magic: %o",
+ hints_file, hdr->hh_magic);
+ return -1;
+ }
+
+ if (hdr->hh_version != LD_HINTS_VERSION_1 &&
+ hdr->hh_version != LD_HINTS_VERSION_2) {
+ warnx("Unsupported version: %d", hdr->hh_version);
+ return -1;
+ }
+
+ if (hdr->hh_ehints > msize) {
+ if (mmap(addr+msize, hdr->hh_ehints - msize,
+ PROT_READ, MAP_COPY|MAP_FIXED,
+ fd, msize) != (caddr_t)(addr+msize)) {
+
+ warn("%s", hints_file);
+ return -1;
+ }
+ }
+ close(fd);
+
+ blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
+ strtab = (char *)(addr + hdr->hh_strtab);
+
+ for (i = 0; i < hdr->hh_nbucket; i++) {
+ struct hints_bucket *bp = &blist[i];
+
+ /* Sanity check */
+ if (bp->hi_namex >= hdr->hh_strtab_sz) {
+ warnx("Bad name index: %#x", bp->hi_namex);
+ return -1;
+ }
+ if (bp->hi_pathx >= hdr->hh_strtab_sz) {
+ warnx("Bad path index: %#x", bp->hi_pathx);
+ return -1;
+ }
+
+ /* Allocate new list element */
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = strdup(strtab + bp->hi_namex);
+ shp->path = strdup(strtab + bp->hi_pathx);
+ bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = bp->hi_ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+ }
+ if (hdr->hh_version >= LD_HINTS_VERSION_2)
+ add_search_path(strtab + hdr->hh_dirlist);
+
+ return 0;
+}
+
+static void
+listhints()
+{
+ struct shlib_list *shp;
+ int i;
+
+ printf("%s:\n", hints_file);
+ printf("\tsearch directories: %s\n", dir_list);
+
+ for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
+ printf("\t%d:-l%s.%d.%d => %s\n",
+ i, shp->name, shp->major, shp->minor, shp->path);
+
+ return;
+}
diff --git a/sbin/md5/Makefile b/sbin/md5/Makefile
new file mode 100644
index 0000000..8c53af2
--- /dev/null
+++ b/sbin/md5/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= md5
+SRCS= md5.c
+
+LDADD+= -lmd
+DPADD+= /usr/lib/libmd.a
+
+.include <bsd.prog.mk>
diff --git a/sbin/md5/global.h b/sbin/md5/global.h
new file mode 100644
index 0000000..ee25514
--- /dev/null
+++ b/sbin/md5/global.h
@@ -0,0 +1,30 @@
+/* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+ function argument prototyping.
+The following makes PROTOTYPES default to 0 if it has not already
+ been defined with C compiler flags.
+ */
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+ returns an empty list.
+ */
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
diff --git a/sbin/md5/md5.1 b/sbin/md5/md5.1
new file mode 100644
index 0000000..153054a
--- /dev/null
+++ b/sbin/md5/md5.1
@@ -0,0 +1,46 @@
+.TH MD5 1 "Feb 14, 1994"
+.SH NAME
+md5 \- calculate a message-digest fingerprint (checksum) for a file
+.SH SYNOPSIS
+.B md5
+[ -p | -t | -x | -sstring | filename(s) ]
+.SH DESCRIPTION
+.B md5
+takes as input a message of arbitrary length and produces
+as output a 128-bit "fingerprint" or "message digest" of the input.
+It is conjectured that it is computationally infeasible to produce
+two messages having the same message digest, or to produce any
+message having a given prespecified target message digest.
+The MD5 algorithm is intended for digital signature applications, where a
+large file must be "compressed" in a secure manner before being
+encrypted with a private (secret) key under a public-key cryptosystem
+such as
+.I RSA.
+.SH OPTIONS
+The following four options may be used in any combination, except
+that
+.B "filename(s)"
+must be the last objects on the command line.
+.in +5
+.PP
+.B -sstring
+prints a checksum of the given "string".
+.PP
+.B -p
+echos stdin to stdout and appends the MD5 sum to stdout.
+.PP
+.B -t
+runs a built-in time trial.
+.PP
+.B -x
+runs a built-in test script.
+.PP
+.B filename(s)
+prints a checksum(s) for each of the files.
+.SH "SEE ALSO"
+.BR cksum (1)
+.PP
+RFC 1321 describes in detail the MD2, MD4, and MD5 message-digest algorithms.
+.SH ACKNOWLEDGEMENTS
+This program is placed in the public domain for free general use by
+RSA Data Security.
diff --git a/sbin/md5/md5.c b/sbin/md5/md5.c
new file mode 100644
index 0000000..e3e5986
--- /dev/null
+++ b/sbin/md5/md5.c
@@ -0,0 +1,177 @@
+/*
+ * $Id: md5.c,v 1.6 1995/07/12 09:14:46 phk Exp $
+ *
+ * Derived from:
+ */
+
+/*
+ * MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/*
+ * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+ * rights reserved.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <sys/types.h>
+#include <md5.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+
+/*
+ * Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 1000
+#define TEST_BLOCK_COUNT 1000
+
+static void MDString PROTO_LIST((char *));
+static void MDTimeTrial PROTO_LIST((void));
+static void MDTestSuite PROTO_LIST((void));
+static void MDFilter PROTO_LIST((int));
+
+/* Main driver.
+
+Arguments (may be any combination):
+ -sstring - digests string
+ -t - runs time trial
+ -x - runs test script
+ filename - digests file
+ (none) - digests standard input
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ char *p;
+ char buf[33];
+
+ if (argc > 1)
+ for (i = 1; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == 's')
+ MDString(argv[i] + 2);
+ else if (strcmp(argv[i], "-t") == 0)
+ MDTimeTrial();
+ else if (strcmp(argv[i], "-p") == 0)
+ MDFilter(1);
+ else if (strcmp(argv[i], "-x") == 0)
+ MDTestSuite();
+ else {
+ p = MD5File(argv[i],buf);
+ if (!p)
+ perror(argv[i]);
+ else
+ printf("MD5 (%s) = %s\n", argv[i], p);
+ }
+ else
+ MDFilter(0);
+
+ return (0);
+}
+/*
+ * Digests a string and prints the result.
+ */
+static void
+MDString(string)
+ char *string;
+{
+ unsigned int len = strlen(string);
+ char buf[33];
+
+ printf("MD5 (\"%s\") = %s\n", string, MD5Data(string, len, buf));
+}
+/*
+ * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
+ */
+static void
+MDTimeTrial()
+{
+ MD5_CTX context;
+ time_t endTime, startTime;
+ unsigned char block[TEST_BLOCK_LEN];
+ unsigned int i;
+ char *p, buf[33];
+
+ printf
+ ("MD5 time trial. Digesting %d %d-byte blocks ...",
+ TEST_BLOCK_LEN, TEST_BLOCK_COUNT);
+
+ /* Initialize block */
+ for (i = 0; i < TEST_BLOCK_LEN; i++)
+ block[i] = (unsigned char) (i & 0xff);
+
+ /* Start timer */
+ time(&startTime);
+
+ /* Digest blocks */
+ MD5Init(&context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ MD5Update(&context, block, TEST_BLOCK_LEN);
+ p = MD5End(&context,buf);
+
+ /* Stop timer */
+ time(&endTime);
+
+ printf(" done\n");
+ printf("Digest = %s", p);
+ printf("\nTime = %ld seconds\n", (long) (endTime - startTime));
+ /* Be careful that endTime-startTime is not zero. (Bug fix from Ric
+ * Anderson, ric@Artisoft.COM.) */
+ printf
+ ("Speed = %ld bytes/second\n",
+ (long) TEST_BLOCK_LEN * (long) TEST_BLOCK_COUNT / ((endTime - startTime) != 0 ? (endTime - startTime) : 1));
+}
+/*
+ * Digests a reference suite of strings and prints the results.
+ */
+static void
+MDTestSuite()
+{
+ printf("MD5 test suite:\n");
+
+ MDString("");
+ MDString("a");
+ MDString("abc");
+ MDString("message digest");
+ MDString("abcdefghijklmnopqrstuvwxyz");
+ MDString
+ ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ MDString
+ ("1234567890123456789012345678901234567890\
+1234567890123456789012345678901234567890");
+}
+
+/*
+ * Digests the standard input and prints the result.
+ */
+static void
+MDFilter(int pipe)
+{
+ MD5_CTX context;
+ int len;
+ unsigned char buffer[BUFSIZ], digest[16];
+ char buf[33];
+
+ MD5Init(&context);
+ while (len = fread(buffer, 1, BUFSIZ, stdin)) {
+ if(pipe && (len != fwrite(buffer, 1, len, stdout))) {
+ perror("stdout");
+ exit(1);
+ }
+ MD5Update(&context, buffer, len);
+ }
+ printf("%s\n", MD5End(&context,buf));
+}
diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile
new file mode 100644
index 0000000..f19c4f7
--- /dev/null
+++ b/sbin/mknod/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= mknod
+MAN8= mknod.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8
new file mode 100644
index 0000000..b825e7c
--- /dev/null
+++ b/sbin/mknod/mknod.8
@@ -0,0 +1,108 @@
+.\" 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
+.Pp
+Major and minor device numbers can be given in any format acceptable to
+.Xr strtoul 3 ,
+so that a leading
+.Ql 0x
+indicates a hexadecimal number, and a leading
+.Ql 0
+will cause the number to be interpreted as octal.
+.Sh SEE ALSO
+.Xr mknod 2 ,
+.Xr MAKEDEV 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c
new file mode 100644
index 0000000..a3050eb
--- /dev/null
+++ b/sbin/mknod/mknod.c
@@ -0,0 +1,118 @@
+/*
+ * 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 const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ dev_t dev;
+ char *endp;
+ long major, minor;
+ mode_t mode;
+ int range_error;
+
+ 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);
+ }
+
+ errno = 0;
+ major = (long)strtoul(argv[3], &endp, 0);
+ if (endp == argv[3] || *endp != '\0') {
+ (void)fprintf(stderr,
+ "mknod: %s: non-numeric major number\n", argv[3]);
+ exit(1);
+ }
+ range_error = errno;
+ errno = 0;
+ minor = (long)strtoul(argv[4], &endp, 0);
+ if (endp == argv[3] || *endp != '\0') {
+ (void)fprintf(stderr,
+ "mknod: %s: non-numeric minor number\n", argv[4]);
+ exit(1);
+ }
+ range_error |= errno;
+ dev = makedev(major, minor);
+ if (range_error || major(dev) != major || minor(dev) != minor) {
+ (void)fprintf(stderr,
+ "mknod: major or minor number too large\n");
+ exit(1);
+ }
+
+ if (mknod(argv[1], mode, dev) != 0) {
+ (void)fprintf(stderr,
+ "mknod: %s: %s\n", argv[1], strerror(errno));
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/sbin/modload/Makefile b/sbin/modload/Makefile
new file mode 100644
index 0000000..265869f
--- /dev/null
+++ b/sbin/modload/Makefile
@@ -0,0 +1,42 @@
+#
+# Makefile for modload
+#
+# 25 May 93 Terry Lambert Original
+#
+# Copyright (c) 1993 Terrence R. Lambert.
+# 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 Terrence R. Lambert.
+# 4. The name Terrence R. Lambert may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: Makefile,v 1.4 1994/02/09 15:00:32 deraadt Exp $
+#
+
+PROG= modload
+MAN8= modload.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/modload/modload.8 b/sbin/modload/modload.8
new file mode 100644
index 0000000..1043e92
--- /dev/null
+++ b/sbin/modload/modload.8
@@ -0,0 +1,123 @@
+.\" Copyright (c) 1993 Christopher G. Demetriou
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: modload.8,v 1.6 1995/10/28 13:06:09 peter Exp $
+.\"
+.Dd September 22, 1994
+.Dt MODLOAD 8
+.Os
+.Sh NAME
+.Nm modload
+.Nd load a kernel module
+.Sh SYNOPSIS
+.Nm modload
+.Op Fl dquv
+.Op Fl A Ar kernel
+.Op Fl e Ar entry
+.Op Fl p Ar postinstall
+.Op Fl o Ar output_file
+.Ar input_file
+.Sh DESCRIPTION
+The
+.Nm
+utility loads a loadable kernel module into a running system.
+The input file is an object file (.o file).
+.Pp
+The options to
+.Nm
+are as follows:
+.Bl -tag -width indent
+.It Fl d
+Debug. Used to debug
+.Nm
+itself.
+.It Fl q
+Be very quiet.
+.It Fl u
+Delete the loaded module
+.Pq Ar output_file
+after loading. If the output file was not specified, this option causes the
+temporary file to be kept rather than deleted.
+.It Fl v
+Print comments about the loading process.
+.It Fl A Ar kernel
+Specify the file that is passed to the linker
+to resolve module references to external symbols.
+The symbol file must be for the currently running
+kernel or the module is likely to crash the system.
+.It Fl e Ar entry
+Specify the module entry point.
+This is passed by
+.Nm
+to
+.Xr ld 1
+when the module is linked.
+The default module entry point name is the module name with `_mod' appended.
+.It Fl p Ar postinstall
+Specify the name of a shell script or program that will
+be executed if the module is successfully loaded. It
+is always passed the module id (in decimal) and module
+type (in hexadecimal) as the first two arguments.
+For loadable drivers, the third argument is
+the block or character major device number.
+For a loadable system call, the third argument is the system
+call number.
+.It Fl o Ar output_file
+Specify the name of the output file that is produced by
+the linker. If this option is not specified, a file in the /tmp directory
+is used with the name generated from the module name with a `.out' extension.
+.El
+.Sh FILES
+.Bl -tag -width /usr/include/sys/lkm.h -compact
+.It Pa /kernel
+default file passed to the linker to resolve external
+references in the module
+.It Pa /usr/include/sys/lkm.h
+file containing definitions of module types
+.\" .It Pa output file.
+.\" default output file name
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with a status of 0 on success
+and with a nonzero status if an error occurs.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr lkm 4 ,
+.Xr modunload 8 ,
+.Xr modstat 8
+.Sh HISTORY
+The
+.Nm
+command was designed to be similar in functionality
+to the corresponding command in
+.Tn "SunOS 4.1.3" .
+.Sh AUTHOR
+.Bl -tag
+Terrence R. Lambert, terry@cs.weber.edu
+.El
+.Sh BUGS
+The loadable device driver framework can
+only reserve either a character or block device entry, not both.
diff --git a/sbin/modload/modload.c b/sbin/modload/modload.c
new file mode 100644
index 0000000..71dffc3
--- /dev/null
+++ b/sbin/modload/modload.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1993 Terrence R. Lambert.
+ * 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 Terrence R. Lambert.
+ * 4. The name Terrence R. Lambert may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: modload.c,v 1.13 1996/08/13 00:50:59 pst Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <err.h>
+#include <a.out.h>
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/lkm.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include "pathnames.h"
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+int debug = 0;
+int verbose = 0;
+int quiet = 0;
+int dounlink = 0;
+
+extern char *sys_siglist[];
+
+/*
+ * Expected linker options:
+ *
+ * -A executable to link against
+ * -e entry point
+ * -o output file
+ * -T address to link to in hex (assumes it's a page boundry)
+ * <target> object file
+ */
+
+void
+linkcmd(kernel, entry, outfile, address, object)
+ char *kernel, *entry, *outfile;
+ u_int address; /* XXX */
+ char *object;
+{
+ char addrbuf[32], entrybuf[_POSIX2_LINE_MAX];
+ pid_t pid;
+ int status;
+
+ snprintf(entrybuf, sizeof entrybuf, "_%s", entry);
+ snprintf(addrbuf, sizeof addrbuf, "%x", address);
+
+ if (debug)
+ printf("%s -A %s -e %s -o %s -T %s %s\n",
+ _PATH_LD, kernel, entrybuf, outfile,
+ addrbuf, object);
+
+ pid = fork();
+ if(pid < 0) {
+ err(18, "fork");
+ }
+
+ if(pid == 0) {
+ execl(_PATH_LD, "ld", "-A", kernel, "-e", entrybuf, "-o",
+ outfile, "-T", addrbuf, object, (char *)0);
+ exit(128 + errno);
+ }
+
+ waitpid(pid, &status, 0);
+
+ if(WIFSIGNALED(status)) {
+ errx(1, "%s got signal: %s", _PATH_LD,
+ sys_siglist[WTERMSIG(status)]);
+ }
+
+ if(WEXITSTATUS(status) > 128) {
+ errno = WEXITSTATUS(status) - 128;
+ err(1, "exec(%s)", _PATH_LD);
+ }
+
+ if(WEXITSTATUS(status) != 0) {
+ errx(1, "%s: return code %d", _PATH_LD, WEXITSTATUS(status));
+ }
+
+}
+
+void
+usage()
+{
+
+ fprintf(stderr,
+ "usage: modload [-d] [-v] [-q] [-u] [-A <kernel>] [-e <entry]\n");
+ fprintf(stderr,
+ " [-p <postinstall>] [-o <output file>] <input file>\n");
+ exit(1);
+}
+
+int fileopen = 0;
+#define DEV_OPEN 0x01
+#define MOD_OPEN 0x02
+#define PART_RESRV 0x04
+int devfd, modfd;
+struct lmc_resrv resrv;
+
+void
+cleanup()
+{
+ if (fileopen & PART_RESRV) {
+ /*
+ * Free up kernel memory
+ */
+ if (ioctl(devfd, LMUNRESRV, 0) == -1)
+ warn("can't release slot 0x%08x memory", resrv.slot);
+ }
+
+ if (fileopen & DEV_OPEN)
+ close(devfd);
+
+ if (fileopen & MOD_OPEN)
+ close(modfd);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char *kname = (char *)getbootfile();
+ char *entry = NULL;
+ char *post = NULL;
+ char *out = NULL;
+ char *modobj;
+ char modout[80], *p;
+ struct exec info_buf;
+ u_int modsize; /* XXX */
+ u_int modentry; /* XXX */
+
+ struct lmc_loadbuf ldbuf;
+ int sz, bytesleft;
+ char buf[MODIOBUF];
+
+ while ((c = getopt(argc, argv, "dvquA:e:p:o:")) != EOF) {
+ switch (c) {
+ case 'd':
+ debug = 1;
+ break; /* debug */
+ case 'v':
+ verbose = 1;
+ break; /* verbose */
+ case 'u':
+ dounlink = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'A':
+ kname = optarg;
+ break; /* kernel */
+ case 'e':
+ entry = optarg;
+ break; /* entry point */
+ case 'p':
+ post = optarg;
+ break; /* postinstall */
+ case 'o':
+ out = optarg;
+ break; /* output file */
+ case '?':
+ usage();
+ default:
+ printf("default!\n");
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ modobj = argv[0];
+
+ atexit(cleanup);
+
+ /*
+ * Open the virtual device device driver for exclusive use (needed
+ * to write the new module to it as our means of getting it in the
+ * kernel).
+ */
+ if ((devfd = open(_PATH_LKM, O_RDWR, 0)) == -1)
+ err(3, _PATH_LKM);
+ fileopen |= DEV_OPEN;
+
+ p = strrchr(modobj, '.');
+ if (!p || strcmp(p, ".o"))
+ errx(2, "module object must end in .o");
+
+ if (!out) {
+ p = strrchr(modobj, '/');
+ if (p)
+ p++; /* skip over '/' */
+ else
+ p = modobj;
+ snprintf(modout, sizeof modout, "%s%sut", _PATH_TMP, p);
+ out = modout;
+ /*
+ * reverse meaning of -u - if we've generated a /tmp
+ * file, remove it automatically...
+ */
+ dounlink = !dounlink;
+ }
+
+ if (!entry) { /* calculate default entry point */
+ entry = strrchr(modobj, '/');
+ if (entry)
+ entry++; /* skip over '/' */
+ else
+ entry = modobj;
+ entry = strdup(entry); /* so we can modify it */
+ if (!entry)
+ errx(1, "Could not allocate memory");
+ entry[strlen(entry) - 2] = '\0'; /* chop off .o */
+ }
+
+ (void) unlink(out);
+ modfd = open(out, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if(modfd < 0) {
+ err(1, "creating %s", out);
+ }
+ close(modfd);
+
+ /*
+ * Prelink to get file size
+ */
+ linkcmd(kname, entry, out, 0, modobj);
+
+ /*
+ * Pre-open the 0-linked module to get the size information
+ */
+ if ((modfd = open(out, O_RDONLY, 0)) == -1)
+ err(4, out);
+ fileopen |= MOD_OPEN;
+
+ /*
+ * Get the load module post load size... do this by reading the
+ * header and doing page counts.
+ */
+ if (read(modfd, &info_buf, sizeof(struct exec)) == -1)
+ err(3, "read `%s'", out);
+
+ /*
+ * Close the dummy module -- we have our sizing information.
+ */
+ close(modfd);
+ fileopen &= ~MOD_OPEN;
+
+ /*
+ * Magic number...
+ */
+ if (N_BADMAG(info_buf))
+ errx(4, "not an a.out format file");
+
+ /*
+ * Calculate the size of the module
+ */
+ modsize = info_buf.a_text + info_buf.a_data + info_buf.a_bss;
+
+ /*
+ * Reserve the required amount of kernel memory -- this may fail
+ * to be successful.
+ */
+ resrv.size = modsize; /* size in bytes */
+ resrv.name = modout; /* objname w/o ".o" */
+ resrv.slot = -1; /* returned */
+ resrv.addr = 0; /* returned */
+ if (ioctl(devfd, LMRESERV, &resrv) == -1)
+ err(9, "can't reserve memory");
+ fileopen |= PART_RESRV;
+
+ /*
+ * Relink at kernel load address
+ */
+ linkcmd(kname, entry, out, resrv.addr, modobj);
+
+ /*
+ * Open the relinked module to load it...
+ */
+ if ((modfd = open(out, O_RDONLY, 0)) == -1)
+ err(4, out);
+ fileopen |= MOD_OPEN;
+
+ /*
+ * Reread the header to get the actual entry point *after* the
+ * relink.
+ */
+ if (read(modfd, &info_buf, sizeof(struct exec)) == -1)
+ err(3, "read `%s'", out);
+
+ /*
+ * Get the entry point (for initialization)
+ */
+ modentry = info_buf.a_entry; /* place to call */
+
+ /*
+ * Seek to the text offset to start loading...
+ */
+ if (lseek(modfd, N_TXTOFF(info_buf), 0) == -1)
+ err(12, "lseek");
+
+ /*
+ * Transfer the relinked module to kernel memory in chunks of
+ * MODIOBUF size at a time.
+ */
+ for (bytesleft = info_buf.a_text + info_buf.a_data;
+ bytesleft > 0;
+ bytesleft -= sz) {
+ sz = min(bytesleft, MODIOBUF);
+ read(modfd, buf, sz);
+ ldbuf.cnt = sz;
+ ldbuf.data = buf;
+ if (ioctl(devfd, LMLOADBUF, &ldbuf) == -1)
+ err(11, "error transferring buffer");
+ }
+
+ /*
+ * Save ourselves before disaster (potentitally) strikes...
+ */
+ sync();
+
+ /*
+ * Trigger the module as loaded by calling the entry procedure;
+ * this will do all necessary table fixup to ensure that state
+ * is maintained on success, or blow everything back to ground
+ * zero on failure.
+ */
+ if (ioctl(devfd, LMREADY, &modentry) == -1)
+ err(14, "error initializing module");
+
+ /*
+ * Success!
+ */
+ fileopen &= ~PART_RESRV; /* loaded */
+ if(!quiet) printf("Module loaded as ID %d\n", resrv.slot);
+
+ if (post) {
+ struct lmc_stat sbuf;
+ char id[16], type[16], offset[16];
+
+ sbuf.id = resrv.slot;
+ if (ioctl(devfd, LMSTAT, &sbuf) == -1)
+ err(15, "error fetching module stats for post-install");
+ sprintf(id, "%d", sbuf.id);
+ sprintf(type, "0x%x", sbuf.type);
+ sprintf(offset, "%d", sbuf.offset);
+ /* XXX the modload docs say that drivers can install bdevsw &
+ cdevsw, but the interface only supports one at a time. sigh. */
+ execl(post, post, id, type, offset, 0);
+ err(16, "can't exec '%s'", post);
+ }
+
+ if(dounlink) {
+ if(unlink(out)) {
+ err(17, "unlink(%s)", out);
+ }
+ }
+
+ return 0;
+}
diff --git a/sbin/modload/pathnames.h b/sbin/modload/pathnames.h
new file mode 100644
index 0000000..4359013
--- /dev/null
+++ b/sbin/modload/pathnames.h
@@ -0,0 +1,5 @@
+/* $Id$ */
+#include <paths.h>
+
+#define _PATH_LKM "/dev/lkm"
+#define _PATH_LD "/usr/bin/ld"
diff --git a/sbin/modunload/Makefile b/sbin/modunload/Makefile
new file mode 100644
index 0000000..3520c48
--- /dev/null
+++ b/sbin/modunload/Makefile
@@ -0,0 +1,42 @@
+#
+# Makefile for modunload
+#
+# 25 May 93 Terry Lambert Original
+#
+# Copyright (c) 1993 Terrence R. Lambert.
+# 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 Terrence R. Lambert.
+# 4. The name Terrence R. Lambert may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: Makefile,v 1.4 1994/02/09 15:00:35 deraadt Exp $
+#
+
+PROG= modunload
+MAN8= modunload.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/modunload/modunload.8 b/sbin/modunload/modunload.8
new file mode 100644
index 0000000..cfeac8e
--- /dev/null
+++ b/sbin/modunload/modunload.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1993 Christopher G. Demetriou
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: modunload.8,v 1.4 1995/10/26 21:44:11 torstenb Exp $
+.\"
+.Dd June 7, 1993
+.Dt MODUNLOAD 8
+.Os
+.Sh NAME
+.Nm modunload
+.Nd unload a kernel module
+.Sh SYNOPSIS
+.Nm modunload
+.Op Fl i Ar module_id
+.Op Fl n Ar module_name
+.Sh DESCRIPTION
+The
+.Nm
+utility unloads a loadable kernel module from a running system.
+The
+.Ar module_id
+or
+.Ar module_name
+is the ID or name of the module as shown by
+.Xr modstat 8 .
+.Pp
+One of the following options must be specified:
+.Bl -tag -width indent
+.It Fl i Ar module_id
+Unload the module with the ID
+.Ar module_id .
+.It Fl n Ar module_name
+Unload the module with the name
+.Ar module_name .
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with a status of 0 on success
+and with a nonzero status if an error occurs.
+.Sh SEE ALSO
+.Xr lkm 4 ,
+.Xr modload 8 ,
+.Xr modstat 8
+.Sh HISTORY
+The
+.Nm
+command was designed to be similar in functionality
+to the corresponding command in
+.Tn "SunOS 4.1.3" .
+.Sh AUTHOR
+.Bl -tag
+Terrence R. Lambert, terry@cs.weber.edu
+.El
diff --git a/sbin/modunload/modunload.c b/sbin/modunload/modunload.c
new file mode 100644
index 0000000..b4593b3
--- /dev/null
+++ b/sbin/modunload/modunload.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1993 Terrence R. Lambert.
+ * 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 Terrence R. Lambert.
+ * 4. The name Terrence R. Lambert may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: modunload.c,v 1.1 1994/08/19 12:07:21 davidg Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <a.out.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/lkm.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include "pathnames.h"
+
+void
+usage()
+{
+
+ fprintf(stderr, "usage: modunload [-i <module id>] [-n <module name>]\n");
+ exit(1);
+}
+
+int devfd;
+
+void
+cleanup()
+{
+
+ close(devfd);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int modnum = -1;
+ char *modname = NULL;
+ struct lmc_unload ulbuf;
+
+ while ((c = getopt(argc, argv, "i:n:")) != EOF) {
+ switch (c) {
+ case 'i':
+ modnum = atoi(optarg);
+ break; /* number */
+ case 'n':
+ modname = optarg;
+ break; /* name */
+ case '?':
+ usage();
+ default:
+ printf("default!\n");
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0 || (modnum == -1 && modname == NULL))
+ usage();
+
+
+ /*
+ * Open the virtual device device driver for exclusive use (needed
+ * to ioctl() to retrive the loaded module(s) status).
+ */
+ if ((devfd = open(_PATH_LKM, O_RDWR, 0)) == -1)
+ err(2, _PATH_LKM);
+
+ atexit(cleanup);
+
+ /*
+ * Unload the requested module.
+ */
+ ulbuf.name = modname;
+ ulbuf.id = modnum;
+
+ if (ioctl(devfd, LMUNLOAD, &ulbuf) == -1) {
+ switch (errno) {
+ case EINVAL: /* out of range */
+ errx(3, "id out of range");
+ case ENOENT: /* no such entry */
+ errx(3, "no such module");
+ default: /* other error (EFAULT, etc) */
+ err(5, "LMUNLOAD");
+ }
+ }
+
+ return 0;
+}
diff --git a/sbin/modunload/pathnames.h b/sbin/modunload/pathnames.h
new file mode 100644
index 0000000..81f70f2
--- /dev/null
+++ b/sbin/modunload/pathnames.h
@@ -0,0 +1,3 @@
+#include <paths.h>
+
+#define _PATH_LKM "/dev/lkm"
diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile
new file mode 100644
index 0000000..6d87935
--- /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.8
+# 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 0000000..c1bd25a
--- /dev/null
+++ b/sbin/mount/getmntopts.3
@@ -0,0 +1,174 @@
+.\" 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" "int *altflagp"
+.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 either
+.Dv flagp
+or
+.Dv altflagp
+(depending on the
+.Dv m_altloc
+field of the option's table entry)
+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 */
+ int m_altloc; /* non-zero to use altflagp rather than flagp */
+};
+.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.
+.It Fa m_altloc
+the bit should be set or cleared in
+.Dv altflagp
+rather than
+.Dv flagp .
+.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 0000000..75c75b2
--- /dev/null
+++ b/sbin/mount/getmntopts.c
@@ -0,0 +1,98 @@
+/*-
+ * 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"
+
+int getmnt_silent = 0;
+
+void
+getmntopts(options, m0, flagp, altflagp)
+ const char *options;
+ const struct mntopt *m0;
+ int *flagp;
+ int *altflagp;
+{
+ const struct mntopt *m;
+ int negative, len;
+ char *opt, *optbuf;
+ int *thisflagp;
+
+ /* 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) {
+ len = strlen(m->m_option);
+ if (strncasecmp(opt, m->m_option, len) == 0)
+ if ( m->m_option[len] == '\0'
+ || m->m_option[len] == '='
+ )
+ break;
+ }
+
+ /* Save flag, or fail if option is not recognised. */
+ if (m->m_option) {
+ thisflagp = m->m_altloc ? altflagp : flagp;
+ if (negative == m->m_inverse)
+ *thisflagp |= m->m_flag;
+ else
+ *thisflagp &= ~m->m_flag;
+ } else if(!getmnt_silent) {
+ 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 0000000..032d70e
--- /dev/null
+++ b/sbin/mount/mntopts.h
@@ -0,0 +1,82 @@
+/*-
+ * 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 */
+ int m_altloc; /* zero if this is a real mount flag */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
+#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 0 }
+#define MOPT_NOAUTO { "auto", 1, 0, 0 }
+#define MOPT_NODEV { "dev", 1, MNT_NODEV, 0 }
+#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 }
+#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 }
+#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 }
+#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 }
+#define MOPT_UNION { "union", 0, MNT_UNION, 0 }
+
+/* Skip this options without any action (needed for checkquota/quotaon) */
+#define MOPT_UQUOTA { "userquota", 0, 0, 0 }
+#define MOPT_GQUOTA { "groupquota", 0, 0, 0 }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
+
+/* Support for old-style "ro", "rw" flags. */
+#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
+#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NOATIME, \
+ MOPT_NOAUTO, \
+ MOPT_NODEV, \
+ MOPT_NOEXEC, \
+ MOPT_NOSUID, \
+ MOPT_RDONLY, \
+ MOPT_UNION
+
+void getmntopts __P((const char *, const struct mntopt *, int *, int *));
+extern int getmnt_silent;
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
new file mode 100644
index 0000000..38a044d
--- /dev/null
+++ b/sbin/mount/mount.8
@@ -0,0 +1,304 @@
+.\" 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
+.\" $Id: mount.8,v 1.11 1996/09/08 13:28:12 davidg Exp $
+.\"
+.Dd March 27, 1994
+.Dt MOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm mount
+.Nd mount file systems
+.Sh SYNOPSIS
+.Nm mount
+.Op Fl adfpruvw
+.Op Fl t Ar ufs | lfs | external_type
+.Nm mount
+.Op Fl dfpruvw
+.Ar special | node
+.Nm mount
+.Op Fl dfpruvw
+.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 a
+Causes all filesystems listed in
+.Pa /etc/fstab
+(except those with the ``noauto'' option) to be mounted. This is normally
+done during system startup.
+.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. Also
+forces the R/W mount of an unclean filesystem (dangerous; use with
+caution).
+.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. Also
+forces the R/W mount of an unclean filesystem (dangerous; use with caution).
+.It noatime
+Do not update the file access time when reading from a file. This option
+is useful on filesystems where there are large numbers of files and
+performance is more critical than updating the file access time (which is
+rarely ever important). This option is currently only supported on local
+filesystems.
+.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.
+Note: this option is worthless if a public available suid or sgid
+wrapper like
+.Xr suidperl
+is installed on your system.
+.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 p
+Print mount information in fstab format. Implies also the
+.Fl v
+option.
+.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
+.Fl t
+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 .
+.Pp
+Most filesystems will be dynamically loaded by their mount programs
+if not already present in the kernel, using the
+.Xr vfsload 3
+subroutine. Because this mechanism requires writable temporary space,
+the filesystem type containing
+.Pa /tmp
+must be compiled into the kernel, and the filesystems containing
+.Pa /tmp
+and
+.Pa /usr/bin/ld
+must be listed in
+.Pa /etc/fstab
+before any filesystems which might be dynamically loaded.
+.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 vfsload 3 ,
+.Xr fstab 5 ,
+.Xr mount_cd9660 8 ,
+.Xr mount_fdesc 8 ,
+.Xr mount_kernfs 8 ,
+.Xr mount_lfs 8 ,
+.Xr mount_mfs 8 ,
+.Xr mount_msdos 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 v1 .
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
new file mode 100644
index 0000000..5592a63
--- /dev/null
+++ b/sbin/mount/mount.c
@@ -0,0 +1,600 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.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 fstab_style = 0;
+
+static char *mnttype[] = INITMOUNTNAMES;
+
+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));
+void putfsent __P((const struct statfs *));
+
+/* 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_NOATIME, "noatime" },
+ { 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, "padfo:rwt:uv")) != EOF)
+ switch (ch) {
+ case 'p':
+ fstab_style = 1;
+ verbose = 1;
+ break;
+ 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 (strstr(fs->fs_mntops, "noauto"))
+ continue;
+ if (mountfs(fs->fs_vfstype, fs->fs_spec,
+ fs->fs_file, init_flags, options,
+ fs->fs_mntops))
+ rval = 1;
+ }
+ else if (fstab_style) {
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (badvfstype(mntbuf[i].f_type, vfslist))
+ continue;
+ putfsent (&mntbuf[i]);
+ }
+ }
+ 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;
+{
+ struct stat sb;
+
+ /* 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 && stat(mntpath, &sb) == 0) {
+ if (!S_ISDIR(sb.st_mode)) {
+ warnx("%s: Not a directory", mntpath);
+ return (1);
+ }
+ } else {
+ warn("%s", mntpath);
+ return (1);
+ }
+
+ if (mntopts == NULL)
+ mntopts = "";
+
+ name = mntpath;
+
+ if (options == NULL) {
+ if (*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. */
+ for (edir = edirs; *edir; edir++) {
+ (void)snprintf(execname,
+ sizeof(execname), "%s/mount_%s", *edir, vfstype);
+ execv(execname, (char * const *)argv);
+ }
+ if (errno == ENOENT) {
+ int len = 0;
+ char *cp;
+ for (edir = edirs; *edir; edir++)
+ len += strlen(*edir) + 2; /* ", " */
+ if ((cp = malloc(len)) == NULL) {
+ warn(NULL);
+ exit(1);
+ }
+ cp[0] = '\0';
+ for (edir = edirs; *edir; edir++) {
+ strcat(cp, *edir);
+ if (edir[1] != NULL)
+ strcat(cp, ", ");
+ }
+ warn("exec mount_%s not found in %s", vfstype, cp);
+ }
+ 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);
+ }
+
+ if (fstab_style)
+ putfsent (&sf);
+ else
+ 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;
+{
+ struct vfsconf *vfc;
+ vfc = getvfsbytype(vfstype);
+
+ if ( ! vfc )
+ return (0);
+
+ return (badvfsname(vfc->vfc_name, 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",
+ "[-dfpruvw] [-o options] [-t ufs | external_type]",
+ "special node",
+ "[-adfpruvw] [-t ufs | external_type]",
+ "[-dfpruvw] special | node");
+ exit(1);
+}
+
+void
+putfsent (ent)
+ const struct statfs *ent;
+{
+ struct fstab *fst;
+
+ printf ("%s\t%s\t%s %s",
+ ent->f_mntfromname, ent->f_mntonname,
+ mnttype[ent->f_type],
+ (ent->f_flags & MNT_RDONLY) ? "ro" : "rw");
+
+ if (ent->f_flags & MNT_SYNCHRONOUS)
+ printf (",sync");
+
+ if (ent->f_flags & MNT_NOEXEC)
+ printf (",noexec");
+
+ if (ent->f_flags & MNT_NOSUID)
+ printf (",nosuid");
+
+ if (ent->f_flags & MNT_NODEV)
+ printf (",nodev");
+
+ if (ent->f_flags & MNT_UNION)
+ printf (",union");
+
+ if (ent->f_flags & MNT_ASYNC)
+ printf (",async");
+
+ if (fst = getfsspec (ent->f_mntfromname))
+ printf ("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if (fst = getfsfile (ent->f_mntonname))
+ printf ("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if (ent->f_type == MOUNT_UFS)
+ printf ("\t1 1\n");
+ else
+ printf ("\t0 0\n");
+}
diff --git a/sbin/mount/mount_ufs.c b/sbin/mount/mount_ufs.c
new file mode 100644
index 0000000..fac1417
--- /dev/null
+++ b/sbin/mount/mount_ufs.c
@@ -0,0 +1,149 @@
+/*-
+ * 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_FORCE,
+ MOPT_UPDATE,
+ MOPT_UQUOTA,
+ MOPT_GQUOTA,
+ { NULL }
+};
+
+int
+mount_ufs(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ extern int optreset;
+ struct ufs_args args;
+ int ch, mntflags;
+ char *fs_name;
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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;
+
+ setvfsent(0);
+ if(!(vfc = getvfsbyname("ufs"))) {
+ if(vfsisloadable("ufs")) {
+ if(vfsload("ufs")) {
+ warn("vfsload(\"ufs\")");
+ return 1;
+ }
+ endvfsent(); /* flush old table */
+ vfc = getvfsbyname("ufs");
+ } else {
+ /*warnx("ufs: filesystem not found");*/
+ }
+ }
+
+ if (mount(vfc ? vfc->vfc_index : 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 0000000..45a4a01
--- /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 0000000..fc192a5
--- /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.8
+
+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 0000000..262bbc1
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.8
@@ -0,0 +1,97 @@
+.\" 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
+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
+.Bx 4.4 .
diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c
new file mode 100644
index 0000000..463b660
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.c
@@ -0,0 +1,151 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id$";
+#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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { "extatt", 0, ISOFSMNT_EXTATT, 1 },
+ { "gens", 0, ISOFSMNT_GENS, 1 },
+ { "rrip", 1, ISOFSMNT_NORRIP, 1 },
+ { NULL }
+};
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct iso_args args;
+ int ch, mntflags, opts;
+ char *dev, *dir;
+ struct vfsconf *vfc;
+
+ 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(optarg, mopts, &mntflags, &opts);
+ 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;
+
+ /*
+ * ISO 9660 filesystems are not writeable.
+ */
+ mntflags |= MNT_RDONLY;
+ args.export.ex_flags = MNT_EXRDONLY;
+
+ args.flags = opts;
+
+ vfc = getvfsbyname("cd9660");
+ if(!vfc && vfsisloadable("cd9660")) {
+ if(vfsload("cd9660")) {
+ err(EX_OSERR, "vfsload(cd9660)");
+ }
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("cd9660");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "cd9660 filesystem not available");
+
+ if (mount(vfc->vfc_index, dir, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_cd9660 [-egrt] [-o options] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_ext2fs/Makefile b/sbin/mount_ext2fs/Makefile
new file mode 100644
index 0000000..cacee97
--- /dev/null
+++ b/sbin/mount_ext2fs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_ext2fs
+SRCS= mount_ext2fs.c getmntopts.c
+MAN8= mount_ext2fs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ext2fs/mount_ext2fs.8 b/sbin/mount_ext2fs/mount_ext2fs.8
new file mode 100644
index 0000000..01bce0a
--- /dev/null
+++ b/sbin/mount_ext2fs/mount_ext2fs.8
@@ -0,0 +1,75 @@
+.\" 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.
+.\"
+.\"
+.Dd "January 31, 1996"
+.Dt MOUNT_EXT2FS 8
+.Os FreeBSD 2.2
+.Sh NAME
+.Nm mount_ext2fs
+.Nd mount a ext2fs file system
+.Sh SYNOPSIS
+.Nm mount_ext2fs
+.Op Fl o Ar options
+.Ar special
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm mount_ext2fs
+command attaches a ext2fs file system
+.Ar special
+device on to the file system tree at the point
+.Ar node .
+.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 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 mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm mount_ext2fs
+function first appeared in
+.Fx 2.2 .
diff --git a/sbin/mount_ext2fs/mount_ext2fs.c b/sbin/mount_ext2fs/mount_ext2fs.c
new file mode 100644
index 0000000..91d96fd
--- /dev/null
+++ b/sbin/mount_ext2fs/mount_ext2fs.c
@@ -0,0 +1,127 @@
+/*-
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_ext2fs.c,v 1.3 1996/07/23 19:29:27 peter Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct ufs_args args;
+ int ch, mntflags;
+ char *fs_name, *options;
+ struct vfsconf *vfc;
+
+ options = NULL;
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ 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;
+
+ vfc = getvfsbyname("ext2fs");
+ if(!vfc && vfsisloadable("ext2fs")) {
+ if(vfsload("ext2fs")) {
+ err(EX_OSERR, "vfsload(ext2fs)");
+ }
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("ext2fs");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "ext2fs filesystem not available");
+
+ if (mount(vfc->vfc_index, fs_name, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", args.fspec);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_ext2fs [-o options] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_ifs/Makefile b/sbin/mount_ifs/Makefile
new file mode 100644
index 0000000..6d87935
--- /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.8
+# We do NOT install the getmntopts.3 man page.
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ifs/getmntopts.3 b/sbin/mount_ifs/getmntopts.3
new file mode 100644
index 0000000..c1bd25a
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.3
@@ -0,0 +1,174 @@
+.\" 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" "int *altflagp"
+.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 either
+.Dv flagp
+or
+.Dv altflagp
+(depending on the
+.Dv m_altloc
+field of the option's table entry)
+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 */
+ int m_altloc; /* non-zero to use altflagp rather than flagp */
+};
+.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.
+.It Fa m_altloc
+the bit should be set or cleared in
+.Dv altflagp
+rather than
+.Dv flagp .
+.El
+.Pp
+Each of the user visible
+.Dv MNT_
+flags has a corresponding
+.Dv MOPT_
+macro which defines an appropriate
+.Li "struct mntopt"
+entry.
+To simplify the program interface and ensure consistency across all
+programs, a general purpose macro,
+.Dv MOPT_STDOPTS ,
+is defined which
+contains an entry for all the generic VFS options.
+In addition, the macros
+.Dv MOPT_FORCE
+and
+.Dv MOPT_UPDATE
+exist to enable the
+.Dv MNT_FORCE
+and
+.Dv MNT_UPDATE
+flags to be set.
+Finally, the table must be terminated by an entry with a NULL
+first element.
+.Sh EXAMPLES
+Most commands will use the standard option set.
+Local filesystems which support the
+.Dv MNT_UPDATE
+flag, would also have an
+.Dv MOPT_UPDATE
+entry.
+This can be declared and used as follows:
+.Bd -literal
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+ ...
+ mntflags = 0;
+ ...
+ getmntopts(options, mopts, &mntflags)
+ ...
+.Ed
+.Sh DIAGNOSTICS
+The
+.Nm getmntopts
+function displays an error message and exits if an
+unrecognized option is encountered.
+.Sh SEE ALSO
+.Xr err 3 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Fn getmntopts
+function appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_ifs/getmntopts.c b/sbin/mount_ifs/getmntopts.c
new file mode 100644
index 0000000..75c75b2
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.c
@@ -0,0 +1,98 @@
+/*-
+ * 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"
+
+int getmnt_silent = 0;
+
+void
+getmntopts(options, m0, flagp, altflagp)
+ const char *options;
+ const struct mntopt *m0;
+ int *flagp;
+ int *altflagp;
+{
+ const struct mntopt *m;
+ int negative, len;
+ char *opt, *optbuf;
+ int *thisflagp;
+
+ /* 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) {
+ len = strlen(m->m_option);
+ if (strncasecmp(opt, m->m_option, len) == 0)
+ if ( m->m_option[len] == '\0'
+ || m->m_option[len] == '='
+ )
+ break;
+ }
+
+ /* Save flag, or fail if option is not recognised. */
+ if (m->m_option) {
+ thisflagp = m->m_altloc ? altflagp : flagp;
+ if (negative == m->m_inverse)
+ *thisflagp |= m->m_flag;
+ else
+ *thisflagp &= ~m->m_flag;
+ } else if(!getmnt_silent) {
+ errx(1, "-o %s: option not supported", opt);
+ }
+ }
+
+ free(optbuf);
+}
diff --git a/sbin/mount_ifs/mntopts.h b/sbin/mount_ifs/mntopts.h
new file mode 100644
index 0000000..032d70e
--- /dev/null
+++ b/sbin/mount_ifs/mntopts.h
@@ -0,0 +1,82 @@
+/*-
+ * 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 */
+ int m_altloc; /* zero if this is a real mount flag */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
+#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 0 }
+#define MOPT_NOAUTO { "auto", 1, 0, 0 }
+#define MOPT_NODEV { "dev", 1, MNT_NODEV, 0 }
+#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 }
+#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 }
+#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 }
+#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 }
+#define MOPT_UNION { "union", 0, MNT_UNION, 0 }
+
+/* Skip this options without any action (needed for checkquota/quotaon) */
+#define MOPT_UQUOTA { "userquota", 0, 0, 0 }
+#define MOPT_GQUOTA { "groupquota", 0, 0, 0 }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
+
+/* Support for old-style "ro", "rw" flags. */
+#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
+#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NOATIME, \
+ MOPT_NOAUTO, \
+ MOPT_NODEV, \
+ MOPT_NOEXEC, \
+ MOPT_NOSUID, \
+ MOPT_RDONLY, \
+ MOPT_UNION
+
+void getmntopts __P((const char *, const struct mntopt *, int *, int *));
+extern int getmnt_silent;
diff --git a/sbin/mount_ifs/mount.8 b/sbin/mount_ifs/mount.8
new file mode 100644
index 0000000..38a044d
--- /dev/null
+++ b/sbin/mount_ifs/mount.8
@@ -0,0 +1,304 @@
+.\" 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
+.\" $Id: mount.8,v 1.11 1996/09/08 13:28:12 davidg Exp $
+.\"
+.Dd March 27, 1994
+.Dt MOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm mount
+.Nd mount file systems
+.Sh SYNOPSIS
+.Nm mount
+.Op Fl adfpruvw
+.Op Fl t Ar ufs | lfs | external_type
+.Nm mount
+.Op Fl dfpruvw
+.Ar special | node
+.Nm mount
+.Op Fl dfpruvw
+.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 a
+Causes all filesystems listed in
+.Pa /etc/fstab
+(except those with the ``noauto'' option) to be mounted. This is normally
+done during system startup.
+.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. Also
+forces the R/W mount of an unclean filesystem (dangerous; use with
+caution).
+.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. Also
+forces the R/W mount of an unclean filesystem (dangerous; use with caution).
+.It noatime
+Do not update the file access time when reading from a file. This option
+is useful on filesystems where there are large numbers of files and
+performance is more critical than updating the file access time (which is
+rarely ever important). This option is currently only supported on local
+filesystems.
+.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.
+Note: this option is worthless if a public available suid or sgid
+wrapper like
+.Xr suidperl
+is installed on your system.
+.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 p
+Print mount information in fstab format. Implies also the
+.Fl v
+option.
+.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
+.Fl t
+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 .
+.Pp
+Most filesystems will be dynamically loaded by their mount programs
+if not already present in the kernel, using the
+.Xr vfsload 3
+subroutine. Because this mechanism requires writable temporary space,
+the filesystem type containing
+.Pa /tmp
+must be compiled into the kernel, and the filesystems containing
+.Pa /tmp
+and
+.Pa /usr/bin/ld
+must be listed in
+.Pa /etc/fstab
+before any filesystems which might be dynamically loaded.
+.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 vfsload 3 ,
+.Xr fstab 5 ,
+.Xr mount_cd9660 8 ,
+.Xr mount_fdesc 8 ,
+.Xr mount_kernfs 8 ,
+.Xr mount_lfs 8 ,
+.Xr mount_mfs 8 ,
+.Xr mount_msdos 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 v1 .
diff --git a/sbin/mount_ifs/mount.c b/sbin/mount_ifs/mount.c
new file mode 100644
index 0000000..5592a63
--- /dev/null
+++ b/sbin/mount_ifs/mount.c
@@ -0,0 +1,600 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.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 fstab_style = 0;
+
+static char *mnttype[] = INITMOUNTNAMES;
+
+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));
+void putfsent __P((const struct statfs *));
+
+/* 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_NOATIME, "noatime" },
+ { 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, "padfo:rwt:uv")) != EOF)
+ switch (ch) {
+ case 'p':
+ fstab_style = 1;
+ verbose = 1;
+ break;
+ 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 (strstr(fs->fs_mntops, "noauto"))
+ continue;
+ if (mountfs(fs->fs_vfstype, fs->fs_spec,
+ fs->fs_file, init_flags, options,
+ fs->fs_mntops))
+ rval = 1;
+ }
+ else if (fstab_style) {
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (badvfstype(mntbuf[i].f_type, vfslist))
+ continue;
+ putfsent (&mntbuf[i]);
+ }
+ }
+ 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;
+{
+ struct stat sb;
+
+ /* 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 && stat(mntpath, &sb) == 0) {
+ if (!S_ISDIR(sb.st_mode)) {
+ warnx("%s: Not a directory", mntpath);
+ return (1);
+ }
+ } else {
+ warn("%s", mntpath);
+ return (1);
+ }
+
+ if (mntopts == NULL)
+ mntopts = "";
+
+ name = mntpath;
+
+ if (options == NULL) {
+ if (*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. */
+ for (edir = edirs; *edir; edir++) {
+ (void)snprintf(execname,
+ sizeof(execname), "%s/mount_%s", *edir, vfstype);
+ execv(execname, (char * const *)argv);
+ }
+ if (errno == ENOENT) {
+ int len = 0;
+ char *cp;
+ for (edir = edirs; *edir; edir++)
+ len += strlen(*edir) + 2; /* ", " */
+ if ((cp = malloc(len)) == NULL) {
+ warn(NULL);
+ exit(1);
+ }
+ cp[0] = '\0';
+ for (edir = edirs; *edir; edir++) {
+ strcat(cp, *edir);
+ if (edir[1] != NULL)
+ strcat(cp, ", ");
+ }
+ warn("exec mount_%s not found in %s", vfstype, cp);
+ }
+ 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);
+ }
+
+ if (fstab_style)
+ putfsent (&sf);
+ else
+ 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;
+{
+ struct vfsconf *vfc;
+ vfc = getvfsbytype(vfstype);
+
+ if ( ! vfc )
+ return (0);
+
+ return (badvfsname(vfc->vfc_name, 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",
+ "[-dfpruvw] [-o options] [-t ufs | external_type]",
+ "special node",
+ "[-adfpruvw] [-t ufs | external_type]",
+ "[-dfpruvw] special | node");
+ exit(1);
+}
+
+void
+putfsent (ent)
+ const struct statfs *ent;
+{
+ struct fstab *fst;
+
+ printf ("%s\t%s\t%s %s",
+ ent->f_mntfromname, ent->f_mntonname,
+ mnttype[ent->f_type],
+ (ent->f_flags & MNT_RDONLY) ? "ro" : "rw");
+
+ if (ent->f_flags & MNT_SYNCHRONOUS)
+ printf (",sync");
+
+ if (ent->f_flags & MNT_NOEXEC)
+ printf (",noexec");
+
+ if (ent->f_flags & MNT_NOSUID)
+ printf (",nosuid");
+
+ if (ent->f_flags & MNT_NODEV)
+ printf (",nodev");
+
+ if (ent->f_flags & MNT_UNION)
+ printf (",union");
+
+ if (ent->f_flags & MNT_ASYNC)
+ printf (",async");
+
+ if (fst = getfsspec (ent->f_mntfromname))
+ printf ("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if (fst = getfsfile (ent->f_mntonname))
+ printf ("\t%u %u\n", fst->fs_freq, fst->fs_passno);
+ else if (ent->f_type == MOUNT_UFS)
+ printf ("\t1 1\n");
+ else
+ printf ("\t0 0\n");
+}
diff --git a/sbin/mount_ifs/mount_ufs.c b/sbin/mount_ifs/mount_ufs.c
new file mode 100644
index 0000000..fac1417
--- /dev/null
+++ b/sbin/mount_ifs/mount_ufs.c
@@ -0,0 +1,149 @@
+/*-
+ * 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_FORCE,
+ MOPT_UPDATE,
+ MOPT_UQUOTA,
+ MOPT_GQUOTA,
+ { NULL }
+};
+
+int
+mount_ufs(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ extern int optreset;
+ struct ufs_args args;
+ int ch, mntflags;
+ char *fs_name;
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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;
+
+ setvfsent(0);
+ if(!(vfc = getvfsbyname("ufs"))) {
+ if(vfsisloadable("ufs")) {
+ if(vfsload("ufs")) {
+ warn("vfsload(\"ufs\")");
+ return 1;
+ }
+ endvfsent(); /* flush old table */
+ vfc = getvfsbyname("ufs");
+ } else {
+ /*warnx("ufs: filesystem not found");*/
+ }
+ }
+
+ if (mount(vfc ? vfc->vfc_index : MOUNT_UFS, fs_name, mntflags, &args) < 0) {
+ (void)fprintf(stderr, "%s on %s: ", args.fspec, fs_name);
+ switch (errno) {
+ case EMFILE:
+ (void)fprintf(stderr, "mount table full.\n");
+ break;
+ case EINVAL:
+ if (mntflags & MNT_UPDATE)
+ (void)fprintf(stderr,
+ "Specified device does not match mounted device.\n");
+ else
+ (void)fprintf(stderr,
+ "Incorrect super block.\n");
+ break;
+ default:
+ (void)fprintf(stderr, "%s\n", strerror(errno));
+ break;
+ }
+ return (1);
+ }
+ return (0);
+}
+
+void
+ufs_usage()
+{
+ (void)fprintf(stderr, "usage: mount_ufs [-o options] special node\n");
+ exit(1);
+}
diff --git a/sbin/mount_ifs/pathnames.h b/sbin/mount_ifs/pathnames.h
new file mode 100644
index 0000000..45a4a01
--- /dev/null
+++ b/sbin/mount_ifs/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.2 (Berkeley) 3/27/94
+ */
+
+#define _PATH_SBIN "/sbin"
+#define _PATH_USRSBIN "/usr/sbin"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/sbin/mount_lfs/Makefile b/sbin/mount_lfs/Makefile
new file mode 100644
index 0000000..0020b8b
--- /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.8
+
+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 0000000..11ccfe6
--- /dev/null
+++ b/sbin/mount_lfs/mount_lfs.8
@@ -0,0 +1,129 @@
+.\" 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 BUGS
+LFS does not currently work in any variant of FreeBSD and should therefore
+not be used.
+.Sh HISTORY
+The
+.Nm mount_lfs
+function first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_lfs/mount_lfs.c b/sbin/mount_lfs/mount_lfs.c
new file mode 100644
index 0000000..67c1b31
--- /dev/null
+++ b/sbin/mount_lfs/mount_lfs.c
@@ -0,0 +1,165 @@
+/*-
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_lfs.c,v 1.4 1996/05/13 17:43:05 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_UPDATE,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+static void invoke_cleaner __P((char *, int, int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct ufs_args args;
+ int ch, mntflags, noclean;
+ char *fs_name, *options;
+ struct vfsconf *vfc;
+ int short_rds, cleaner_debug;
+
+
+ options = NULL;
+ mntflags = noclean = short_rds = cleaner_debug = 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, 0);
+ 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;
+
+ vfc = getvfsbyname("lfs");
+ if(!vfc && vfsisloadable("lfs")) {
+ if(vfsload("lfs"))
+ err(EX_OSERR, "vfsload(lfs)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("lfs");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "lfs filesystem is not available");
+
+ if (mount(vfc ? vfc->vfc_index : MOUNT_LFS, fs_name, mntflags, &args))
+ err(EX_OSERR, args.fspec);
+
+ if (!noclean)
+ invoke_cleaner(fs_name, short_rds, cleaner_debug);
+ /* NOTREACHED */
+
+ exit(0);
+}
+
+static void
+invoke_cleaner(name, short_rds, cleaner_debug)
+ char *name;
+ int short_rds;
+ int cleaner_debug;
+{
+ 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(EX_OSERR, "exec %s", _PATH_LFS_CLEANERD);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_lfs [-dns] [-o options] special node\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_lfs/pathnames.h b/sbin/mount_lfs/pathnames.h
new file mode 100644
index 0000000..fafbf71
--- /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_msdos/Makefile b/sbin/mount_msdos/Makefile
new file mode 100644
index 0000000..19ec0ac
--- /dev/null
+++ b/sbin/mount_msdos/Makefile
@@ -0,0 +1,16 @@
+#
+# $Id: Makefile,v 1.1 1994/09/19 15:30:33 dfr Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_msdos/mount_msdos.8 b/sbin/mount_msdos/mount_msdos.8
new file mode 100644
index 0000000..d6bd154
--- /dev/null
+++ b/sbin/mount_msdos/mount_msdos.8
@@ -0,0 +1,119 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" 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 Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: mount_msdos.8,v 1.2 1994/11/19 17:55:35 nate Exp $
+.\"
+.Dd April 7, 1994
+.Dt MOUNT_MSDOS 8
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm mount_msdos
+.Nd mount an MS-DOS file system
+.Sh SYNOPSIS
+.Nm mount_msdos
+.Op Fl u Ar uid
+.Op Fl g Ar gid
+.Op Fl m Ar mask
+.Pa special
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm mount_msdos
+command attaches the MS-DOS 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, but can be used by any user to mount an
+MS-DOS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl u
+Set the owner of the files in the file system to
+.Ar uid .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g
+Set the group of the files in the file system to
+.Ar gid .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m
+Specify the maximum file permissions for files
+in the file system.
+(For example, a mask of
+.Li 755
+specifies that, by default, the owner should have
+read, write, and execute permissions for files, but
+others should only have read and execute permissions.
+See
+.Xr chmod 1
+for more information about octal file modes.)
+Only the nine low-order bits of
+.Ar mask
+are used.
+The default mask is taken from the
+directory on which the file system is being mounted.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5
+.Sh CAVEATS
+The
+.Nm msdos
+filesystem is not known to work reliably with filesystems created by versions
+of MS-DOS later than version 3.3.
+.Pp
+The limitations on file names imposed by MS-DOS are strange, at best.
+For instance, they are
+limited to single-case, 8 character names with 3 character extensions.
+.Pp
+If you see the warning:
+.Pp
+mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length
+.Pp
+then it is possible that writing to the MS-DOS filesystem would
+produce corruption on the disk. This is a shortcoming in the code
+which needs to be addressed.
+.Sh HISTORY
+The
+.Nm mount_msdos
+utility first appeared in FreeBSD 2.0.
+Its predecessor, the
+.Nm mount_pcfs
+utility appeared in FreeBSD 1, and was abandoned in favor
+of the more aptly-named
+.Nm mount_msdos .
diff --git a/sbin/mount_msdos/mount_msdos.c b/sbin/mount_msdos/mount_msdos.c
new file mode 100644
index 0000000..c90d51b
--- /dev/null
+++ b/sbin/mount_msdos/mount_msdos.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * 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 Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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 const char rcsid[] =
+ "$Id: mount_msdos.c,v 1.4 1996/05/13 17:56:34 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static gid_t a_gid __P((char *));
+static uid_t a_uid __P((char *));
+static mode_t a_mask __P((char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct msdosfs_args args;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask;
+ char *dev, *dir, ndir[MAXPATHLEN+1];
+ struct vfsconf *vfc;
+
+ mntflags = set_gid = set_uid = set_mask = 0;
+ (void)memset(&args, '\0', sizeof(args));
+
+ while ((c = getopt(argc, argv, "u:g:m:o:")) != EOF) {
+ switch (c) {
+ case 'u':
+ args.uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ args.gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ args.mask = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+ if (dir[0] != '/') {
+ warnx("\"%s\" is a relative path", dir);
+ if (getcwd(ndir, sizeof(ndir)) == NULL)
+ err(EX_OSERR, "getcwd");
+ strncat(ndir, "/", sizeof(ndir) - strlen(ndir) - 1);
+ strncat(ndir, dir, sizeof(ndir) - strlen(ndir) - 1);
+ dir = ndir;
+ warnx("using \"%s\" instead", dir);
+ }
+
+ args.fspec = dev;
+ args.export.ex_root = -2; /* unchecked anyway on DOS fs */
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(dir, &sb) == -1)
+ err(EX_OSERR, "stat %s", dir);
+
+ if (!set_uid)
+ args.uid = sb.st_uid;
+ if (!set_gid)
+ args.gid = sb.st_gid;
+ if (!set_mask)
+ args.mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ vfc = getvfsbyname("msdos");
+ if(!vfc && vfsisloadable("msdos")) {
+ if(vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ vfc = getvfsbyname("msdos");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc->vfc_index, dir, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv;
+ char *ep;
+
+ done = 0;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: mount_msdos [-F flags] [-u user] [-g group] [-m mask] bdev dir\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_msdosfs/Makefile b/sbin/mount_msdosfs/Makefile
new file mode 100644
index 0000000..19ec0ac
--- /dev/null
+++ b/sbin/mount_msdosfs/Makefile
@@ -0,0 +1,16 @@
+#
+# $Id: Makefile,v 1.1 1994/09/19 15:30:33 dfr Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_msdosfs/mount_msdosfs.8 b/sbin/mount_msdosfs/mount_msdosfs.8
new file mode 100644
index 0000000..d6bd154
--- /dev/null
+++ b/sbin/mount_msdosfs/mount_msdosfs.8
@@ -0,0 +1,119 @@
+.\"
+.\" Copyright (c) 1993,1994 Christopher G. Demetriou
+.\" 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 Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: mount_msdos.8,v 1.2 1994/11/19 17:55:35 nate Exp $
+.\"
+.Dd April 7, 1994
+.Dt MOUNT_MSDOS 8
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm mount_msdos
+.Nd mount an MS-DOS file system
+.Sh SYNOPSIS
+.Nm mount_msdos
+.Op Fl u Ar uid
+.Op Fl g Ar gid
+.Op Fl m Ar mask
+.Pa special
+.Pa node
+.Sh DESCRIPTION
+The
+.Nm mount_msdos
+command attaches the MS-DOS 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, but can be used by any user to mount an
+MS-DOS file system on any directory that they own (provided,
+of course, that they have appropriate access to the device that
+contains the file system).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl u
+Set the owner of the files in the file system to
+.Ar uid .
+The default owner is the owner of the directory
+on which the file system is being mounted.
+.It Fl g
+Set the group of the files in the file system to
+.Ar gid .
+The default group is the group of the directory
+on which the file system is being mounted.
+.It Fl m
+Specify the maximum file permissions for files
+in the file system.
+(For example, a mask of
+.Li 755
+specifies that, by default, the owner should have
+read, write, and execute permissions for files, but
+others should only have read and execute permissions.
+See
+.Xr chmod 1
+for more information about octal file modes.)
+Only the nine low-order bits of
+.Ar mask
+are used.
+The default mask is taken from the
+directory on which the file system is being mounted.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5
+.Sh CAVEATS
+The
+.Nm msdos
+filesystem is not known to work reliably with filesystems created by versions
+of MS-DOS later than version 3.3.
+.Pp
+The limitations on file names imposed by MS-DOS are strange, at best.
+For instance, they are
+limited to single-case, 8 character names with 3 character extensions.
+.Pp
+If you see the warning:
+.Pp
+mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length
+.Pp
+then it is possible that writing to the MS-DOS filesystem would
+produce corruption on the disk. This is a shortcoming in the code
+which needs to be addressed.
+.Sh HISTORY
+The
+.Nm mount_msdos
+utility first appeared in FreeBSD 2.0.
+Its predecessor, the
+.Nm mount_pcfs
+utility appeared in FreeBSD 1, and was abandoned in favor
+of the more aptly-named
+.Nm mount_msdos .
diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c
new file mode 100644
index 0000000..c90d51b
--- /dev/null
+++ b/sbin/mount_msdosfs/mount_msdosfs.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * 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 Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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 const char rcsid[] =
+ "$Id: mount_msdos.c,v 1.4 1996/05/13 17:56:34 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static gid_t a_gid __P((char *));
+static uid_t a_uid __P((char *));
+static mode_t a_mask __P((char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct msdosfs_args args;
+ struct stat sb;
+ int c, mntflags, set_gid, set_uid, set_mask;
+ char *dev, *dir, ndir[MAXPATHLEN+1];
+ struct vfsconf *vfc;
+
+ mntflags = set_gid = set_uid = set_mask = 0;
+ (void)memset(&args, '\0', sizeof(args));
+
+ while ((c = getopt(argc, argv, "u:g:m:o:")) != EOF) {
+ switch (c) {
+ case 'u':
+ args.uid = a_uid(optarg);
+ set_uid = 1;
+ break;
+ case 'g':
+ args.gid = a_gid(optarg);
+ set_gid = 1;
+ break;
+ case 'm':
+ args.mask = a_mask(optarg);
+ set_mask = 1;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 2 != argc)
+ usage();
+
+ dev = argv[optind];
+ dir = argv[optind + 1];
+ if (dir[0] != '/') {
+ warnx("\"%s\" is a relative path", dir);
+ if (getcwd(ndir, sizeof(ndir)) == NULL)
+ err(EX_OSERR, "getcwd");
+ strncat(ndir, "/", sizeof(ndir) - strlen(ndir) - 1);
+ strncat(ndir, dir, sizeof(ndir) - strlen(ndir) - 1);
+ dir = ndir;
+ warnx("using \"%s\" instead", dir);
+ }
+
+ args.fspec = dev;
+ args.export.ex_root = -2; /* unchecked anyway on DOS fs */
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ if (!set_gid || !set_uid || !set_mask) {
+ if (stat(dir, &sb) == -1)
+ err(EX_OSERR, "stat %s", dir);
+
+ if (!set_uid)
+ args.uid = sb.st_uid;
+ if (!set_gid)
+ args.gid = sb.st_gid;
+ if (!set_mask)
+ args.mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+
+ vfc = getvfsbyname("msdos");
+ if(!vfc && vfsisloadable("msdos")) {
+ if(vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ vfc = getvfsbyname("msdos");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc->vfc_index, dir, mntflags, &args) < 0)
+ err(EX_OSERR, "%s", dev);
+
+ exit (0);
+}
+
+gid_t
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+ char *gname;
+ gid_t gid;
+
+ if ((gr = getgrnam(s)) != NULL)
+ gid = gr->gr_gid;
+ else {
+ for (gname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ gid = atoi(gname);
+ else
+ errx(EX_NOUSER, "unknown group id: %s", gname);
+ }
+ return (gid);
+}
+
+uid_t
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+ char *uname;
+ uid_t uid;
+
+ if ((pw = getpwnam(s)) != NULL)
+ uid = pw->pw_uid;
+ else {
+ for (uname = s; *s && isdigit(*s); ++s);
+ if (!*s)
+ uid = atoi(uname);
+ else
+ errx(EX_NOUSER, "unknown user id: %s", uname);
+ }
+ return (uid);
+}
+
+mode_t
+a_mask(s)
+ char *s;
+{
+ int done, rv;
+ char *ep;
+
+ done = 0;
+ if (*s >= '0' && *s <= '7') {
+ done = 1;
+ rv = strtol(optarg, &ep, 8);
+ }
+ if (!done || rv < 0 || *ep)
+ errx(EX_USAGE, "invalid file mode: %s", s);
+ return (rv);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: mount_msdos [-F flags] [-u user] [-g group] [-m mask] bdev dir\n");
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile
new file mode 100644
index 0000000..ebf516b
--- /dev/null
+++ b/sbin/mount_nfs/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_nfs
+SRCS= mount_nfs.c getmntopts.c
+MAN8= mount_nfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -DNFS -I${MOUNT}
+.PATH: ${MOUNT}
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS
+DPADD= ${LIBKRB} ${LIBDES}
+LDADD= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.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 0000000..5048d4a
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.8
@@ -0,0 +1,294 @@
+.\" 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
+.\"
+.\" $Id$
+.\""
+.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 3KPTUbcdilqs
+.Op Fl D Ar deadthresh
+.Op Fl I Ar readdirsize
+.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 and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl 3
+Use the NFS Version 3 protocol (Version 2 is the default).
+.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 I
+Set the readdir read size to the specified value. The value should normally
+be a multiple of DIRBLKSIZ that is <= the read size for the mount.
+.It Fl K
+Pass Kerberos authenticators to the server for client-to-server
+user-credential mapping.
+This requires that the kernel be built with the NFSKERB option.
+(Refer to the INTERNET-DRAFT titled
+.%T "Authentication Mechanisms for ONC RPC" ,
+for more information.)
+.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 P
+Use a reserved socket port number.
+This is useful for mounting servers that require clients to use a
+reserved port number on the mistaken belief that this makes NFS
+more secure. (For the rare case where the client has a trusted root account
+but untrustworthy users and the network cables are in secure areas this does
+help, but for normal desktop clients this does not apply.)
+.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 U
+Force the mount protocol to use UDP transport, even for TCP NFS mounts.
+(Necessary for some old 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.
+Trying a value greater than 1 for this is suggested 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 NFS port number 2049.
+.It Fl d
+Turn off the dynamic retransmit timeout estimator.
+This may be useful for UDP mounts that exhibit high retry rates,
+since it is possible that the dynamically estimated timeout interval is too
+short.
+.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 l
+Used with NQNFS and NFSV3 to specify that the \fBReaddirPlus\fR RPC should
+be used.
+This option reduces RPC traffic for cases such as
+.Dq "ls -l" ,
+but tends to flood the attribute and name caches with prefetched entries.
+Try this option and see whether performance improves or degrades. Probably
+most useful for client to server network interconnects with a large bandwidth
+times delay product.
+.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.
+The following NFS specific option is also available:
+.Bl -tag -width indent
+.It port=<port_number>
+Use specified port number for NFS requests.
+The default is to query the portmapper for the NFS port.
+.El
+.Pp
+.Bl -tag -width "dumbtimerXX"
+\fBHistoric \&-o options\fR
+.Pp
+Use of these options is deprecated, they are only mentioned here for
+compatibility with historic versions of
+.Nm mount_nfs .
+.It bg
+Same as
+.Fl b .
+.It conn
+Same as
+.Fl c .
+.It dumbtimer
+Same as
+.Fl d .
+.It intr
+Same as
+.Fl i .
+.It kerb
+Same as
+.Fl K .
+.It nfsv3
+Same as
+.Fl 3 .
+.It rdirplus
+Same as
+.Fl l .
+.It mntudp
+Same as
+.Fl U .
+.It resvport
+Same as
+.Fl P .
+.It seqpacket
+Same as
+.Fl p .
+.It nqnfs
+Same as
+.Fl q .
+.It soft
+Same as
+.Fl s .
+.It tcp
+Same as
+.Fl T.
+.El
+.It Fl q
+Use the leasing extensions to the NFS Version 3 protocol to maintain cache consistency.
+This protocol Version 2, referred to as Not Quite Nfs (NQNFS),
+is only supported by this updated release of NFS code.
+(It is not backwards compatible with the release of NQNFS that went out on
+4.4BSD-Lite. To interoperate with a 4.4BSD-Lite NFS system you will have to
+avoid this option until you have had an opportunity to upgrade the NFS code
+on all your 4.4BSD-Lite based systems.)
+.It Fl r
+Set the read data size to the specified value.
+It should normally 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.
+(Normally, the -d option should be specified when using this option to manually
+tune the timeout
+interval.)
+.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 0000000..659e26b
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -0,0 +1,788 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_nfs.c,v 1.13 1996/05/13 17:43:06 wollman Exp $";
+#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 NFSKERB
+#include <des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+#ifdef __FreeBSD__
+#define ALTF_BG 0x1
+#define ALTF_NOCONN 0x2
+#define ALTF_DUMBTIMR 0x4
+#define ALTF_INTR 0x8
+#define ALTF_KERB 0x10
+#define ALTF_NFSV3 0x20
+#define ALTF_RDIRPLUS 0x40
+#define ALTF_MNTUDP 0x80
+#define ALTF_RESVPORT 0x100
+#define ALTF_SEQPACKET 0x200
+#define ALTF_NQNFS 0x400
+#define ALTF_SOFT 0x800
+#define ALTF_TCP 0x1000
+#define ALTF_PORT 0x2000
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ { "bg", 0, ALTF_BG, 1 },
+ { "conn", 1, ALTF_NOCONN, 1 },
+ { "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
+ { "intr", 0, ALTF_INTR, 1 },
+#ifdef NFSKERB
+ { "kerb", 0, ALTF_KERB, 1 },
+#endif
+ { "nfsv3", 0, ALTF_NFSV3, 1 },
+ { "rdirplus", 0, ALTF_RDIRPLUS, 1 },
+ { "mntudp", 0, ALTF_MNTUDP, 1 },
+ { "resvport", 0, ALTF_RESVPORT, 1 },
+#ifdef ISO
+ { "seqpacket", 0, ALTF_SEQPACKET, 1 },
+#endif
+ { "nqnfs", 0, ALTF_NQNFS, 1 },
+ { "soft", 0, ALTF_SOFT, 1 },
+ { "tcp", 0, ALTF_TCP, 1 },
+ { "port=", 0, ALTF_PORT, 1 },
+ { NULL }
+};
+#else
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ { NULL }
+};
+#endif
+
+struct nfs_args nfsdefargs = {
+ (struct sockaddr *)0,
+ sizeof (struct sockaddr_in),
+ SOCK_DGRAM,
+ 0,
+ (u_char *)0,
+ 0,
+ 0,
+ NFS_WSIZE,
+ NFS_RSIZE,
+ NFS_READDIRSIZE,
+ 10,
+ NFS_RETRANS,
+ NFS_MAXGRPS,
+ NFS_DEFRAHEAD,
+ NQ_DEFLEASE,
+ NQ_DEADTHRESH,
+ (char *)0,
+};
+
+struct nfhret {
+ u_long stat;
+ long vers;
+ long auth;
+ long fhsize;
+ u_char nfh[NFSX_V3FHMAX];
+};
+#define DEF_RETRY 10000
+#define BGRND 1
+#define ISBGRND 2
+int retrycnt = DEF_RETRY;
+int opflags = 0;
+int nfsproto = IPPROTO_UDP;
+int mnttcp_ok = 1;
+u_short port_no = 0;
+
+#ifdef NFSKERB
+char inst[INST_SZ];
+char realm[REALM_SZ];
+struct {
+ u_long kind;
+ KTEXT_ST kt;
+} ktick;
+struct nfsrpc_nickverf kverf;
+struct nfsrpc_fullblock kin, kout;
+NFSKERBKEY_T kivec;
+CREDENTIALS kcr;
+struct timeval ktv;
+NFSKERBKEYSCHED_T kerb_keysched;
+#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));
+void usage __P((void)) __dead2;
+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, altflags, i, nfssvc_flag, num;
+ char *name, *p, *spec;
+ struct vfsconf *vfc;
+#ifdef NFSKERB
+ uid_t last_ruid;
+
+ last_ruid = -1;
+ (void)strcpy(realm, KRB_REALM);
+ if (sizeof (struct nfsrpc_nickverf) != RPCX_NICKVERF ||
+ sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK ||
+ ((char *)&ktick.kt) - ((char *)&ktick) != NFSX_UNSIGNED ||
+ ((char *)ktick.kt.dat) - ((char *)&ktick) != 2 * NFSX_UNSIGNED)
+ fprintf(stderr, "Yikes! NFSKERB structs not packed!!\n");
+#endif /* NFSKERB */
+ retrycnt = DEF_RETRY;
+
+ mntflags = 0;
+ altflags = 0;
+ nfsargs = nfsdefargs;
+ nfsargsp = &nfsargs;
+ while ((c = getopt(argc, argv,
+ "3a:bcdD:g:I:iKL:lm:o:PpqR:r:sTt:w:x:U")) != EOF)
+ switch (c) {
+ case '3':
+ nfsargsp->flags |= NFSMNT_NFSV3;
+ break;
+ 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);
+#ifdef __FreeBSD__
+ set_rpc_maxgrouplist(num);
+#endif
+ nfsargsp->maxgrouplist = num;
+ nfsargsp->flags |= NFSMNT_MAXGRPS;
+ break;
+ case 'I':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -I value -- %s", optarg);
+ nfsargsp->readdirsize = num;
+ nfsargsp->flags |= NFSMNT_READDIRSIZE;
+ break;
+ case 'i':
+ nfsargsp->flags |= NFSMNT_INT;
+ break;
+#ifdef NFSKERB
+ case 'K':
+ nfsargsp->flags |= NFSMNT_KERB;
+ break;
+#endif
+ 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_RDIRPLUS;
+ break;
+#ifdef NFSKERB
+ case 'm':
+ (void)strncpy(realm, optarg, REALM_SZ - 1);
+ realm[REALM_SZ - 1] = '\0';
+ break;
+#endif
+ case 'o':
+#ifdef __FreeBSD__
+ getmntopts(optarg, mopts, &mntflags, &altflags);
+ if(altflags & ALTF_BG)
+ opflags |= BGRND;
+ if(altflags & ALTF_NOCONN)
+ nfsargsp->flags |= NFSMNT_NOCONN;
+ if(altflags & ALTF_DUMBTIMR)
+ nfsargsp->flags |= NFSMNT_DUMBTIMR;
+ if(altflags & ALTF_INTR)
+ nfsargsp->flags |= NFSMNT_INT;
+#ifdef NFSKERB
+ if(altflags & ALTF_KERB)
+ nfsargsp->flags |= NFSMNT_KERB;
+#endif
+ if(altflags & ALTF_NFSV3)
+ nfsargsp->flags |= NFSMNT_NFSV3;
+ if(altflags & ALTF_RDIRPLUS)
+ nfsargsp->flags |= NFSMNT_RDIRPLUS;
+ if(altflags & ALTF_MNTUDP)
+ mnttcp_ok = 0;
+ if(altflags & ALTF_RESVPORT)
+ nfsargsp->flags |= NFSMNT_RESVPORT;
+#ifdef ISO
+ if(altflags & ALTF_SEQPACKET)
+ nfsargsp->sotype = SOCK_SEQPACKET;
+#endif
+ if(altflags & ALTF_NQNFS)
+ nfsargsp->flags |= (NFSMNT_NQNFS|NFSMNT_NFSV3);
+ if(altflags & ALTF_SOFT)
+ nfsargsp->flags |= NFSMNT_SOFT;
+ if(altflags & ALTF_TCP) {
+ nfsargsp->sotype = SOCK_STREAM;
+ nfsproto = IPPROTO_TCP;
+ }
+ if(altflags & ALTF_PORT)
+ port_no = atoi(strstr(optarg, "port=") + 5);
+ altflags = 0;
+#else
+ getmntopts(optarg, mopts, &mntflags);
+#endif
+ break;
+ case 'P':
+ nfsargsp->flags |= NFSMNT_RESVPORT;
+ break;
+#ifdef ISO
+ case 'p':
+ nfsargsp->sotype = SOCK_SEQPACKET;
+ break;
+#endif
+ case 'q':
+ nfsargsp->flags |= (NFSMNT_NQNFS | NFSMNT_NFSV3);
+ 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;
+ nfsproto = IPPROTO_TCP;
+ 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;
+ case 'U':
+ mnttcp_ok = 0;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ spec = *argv++;
+ name = *argv;
+
+ if (!getnfsargs(spec, nfsargsp))
+ exit(1);
+
+#ifdef __FreeBSD__
+ vfc = getvfsbyname("nfs");
+ if(!vfc && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(EX_OSERR, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("nfs");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "nfs filesystem is not loadable");
+
+ if (mount(vfc->vfc_index, name, mntflags, nfsargsp))
+#else
+ if (mount(MOUNT_NFS, name, mntflags, nfsargsp))
+#endif
+ 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 NFSKERB
+ /*
+ * 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.
+ * Get the Kerberos credential structure so that
+ * we have the seesion key and get a ticket for
+ * this uid.
+ * For more info see the IETF Draft "Authentication
+ * in ONC RPC".
+ */
+ if (ncd.ncd_authuid != last_ruid) {
+ char buf[512];
+ (void)sprintf(buf, "%s%d",
+ TKT_ROOT, ncd.ncd_authuid);
+ krb_set_tkt_string(buf);
+ last_ruid = ncd.ncd_authuid;
+ }
+ setreuid(ncd.ncd_authuid, 0);
+ kret = krb_get_cred(NFS_KERBSRV, inst, realm, &kcr);
+ if (kret == RET_NOTKT) {
+ kret = get_ad_tkt(NFS_KERBSRV, inst, realm,
+ DEFAULT_TKT_LIFE);
+ if (kret == KSUCCESS)
+ kret = krb_get_cred(NFS_KERBSRV, inst, realm,
+ &kcr);
+ }
+ if (kret == KSUCCESS)
+ kret = krb_mk_req(&ktick.kt, NFS_KERBSRV, inst,
+ realm, 0);
+
+ /*
+ * Fill in the AKN_FULLNAME authenticator and verfier.
+ * Along with the Kerberos ticket, we need to build
+ * the timestamp verifier and encrypt it in CBC mode.
+ */
+ if (kret == KSUCCESS &&
+ ktick.kt.length <= (RPCAUTH_MAXSIZ-3*NFSX_UNSIGNED)
+ && gettimeofday(&ktv, (struct timezone *)0) == 0) {
+ ncd.ncd_authtype = RPCAUTH_KERB4;
+ ncd.ncd_authstr = (u_char *)&ktick;
+ ncd.ncd_authlen = nfsm_rndup(ktick.kt.length) +
+ 3 * NFSX_UNSIGNED;
+ ncd.ncd_verfstr = (u_char *)&kverf;
+ ncd.ncd_verflen = sizeof (kverf);
+ bcopy((caddr_t)kcr.session, (caddr_t)ncd.ncd_key,
+ sizeof (kcr.session));
+ kin.t1 = htonl(ktv.tv_sec);
+ kin.t2 = htonl(ktv.tv_usec);
+ kin.w1 = htonl(NFS_KERBTTL);
+ kin.w2 = htonl(NFS_KERBTTL - 1);
+ bzero((caddr_t)kivec, sizeof (kivec));
+
+ /*
+ * Encrypt kin in CBC mode using the session
+ * key in kcr.
+ */
+ XXX
+
+ /*
+ * Finally, fill the timestamp verifier into the
+ * authenticator and verifier.
+ */
+ ktick.kind = htonl(RPCAKN_FULLNAME);
+ kverf.kind = htonl(RPCAKN_FULLNAME);
+ NFS_KERBW1(ktick.kt) = kout.w1;
+ ktick.kt.length = htonl(ktick.kt.length);
+ kverf.verf.t1 = kout.t1;
+ kverf.verf.t2 = kout.t2;
+ kverf.verf.w2 = kout.w2;
+ nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH;
+ }
+ setreuid(0, 0);
+#endif /* NFSKERB */
+ }
+ }
+ 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, nfsvers, mntvers;
+ char *hostp, *delimp;
+#ifdef NFSKERB
+ 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);
+ }
+ } else if ((hp = gethostbyname(hostp)) != NULL) {
+ bcopy(hp->h_addr, (caddr_t)&saddr.sin_addr, hp->h_length);
+ } else {
+ warnx("can't get net id for host");
+ return (0);
+ }
+#ifdef NFSKERB
+ if ((nfsargsp->flags & NFSMNT_KERB)) {
+ if ((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);
+ }
+ bcopy(hp->h_addr, (caddr_t)&saddr.sin_addr, hp->h_length);
+ strncpy(inst, hp->h_name, INST_SZ);
+ inst[INST_SZ - 1] = '\0';
+ if (cp = strchr(inst, '.'))
+ *cp = '\0';
+ }
+#endif /* NFSKERB */
+
+ if (nfsargsp->flags & NFSMNT_NFSV3) {
+ nfsvers = 3;
+ mntvers = 3;
+ } else {
+ nfsvers = 2;
+ mntvers = 1;
+ }
+ nfhret.stat = EACCES; /* Mark not yet successful */
+ while (retrycnt > 0) {
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(PMAPPORT);
+ if ((tport = port_no ? port_no :
+ pmap_getport(&saddr, RPCPROG_NFS,
+ nfsvers, nfsproto)) == 0) {
+ if ((opflags & ISBGRND) == 0)
+ clnt_pcreateerror("NFS Portmap");
+ } else {
+ saddr.sin_port = 0;
+ pertry.tv_sec = 10;
+ pertry.tv_usec = 0;
+ if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM)
+ clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers,
+ &so, 0, 0);
+ else
+ clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers,
+ pertry, &so);
+ if (clp == NULL) {
+ if ((opflags & ISBGRND) == 0)
+ clnt_pcreateerror("Cannot MNT RPC");
+ } else {
+ clp->cl_auth = authunix_create_default();
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ if (nfsargsp->flags & NFSMNT_KERB)
+ nfhret.auth = RPCAUTH_KERB4;
+ else
+ nfhret.auth = RPCAUTH_UNIX;
+ nfhret.vers = mntvers;
+ 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);
+ errno = nfhret.stat;
+ 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->fhsize = nfhret.fhsize;
+ 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;
+ register struct nfhret *np;
+{
+ register int i;
+ long auth, authcnt, authfnd = 0;
+
+ if (!xdr_u_long(xdrsp, &np->stat))
+ return (0);
+ if (np->stat)
+ return (1);
+ switch (np->vers) {
+ case 1:
+ np->fhsize = NFSX_V2FH;
+ return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
+ case 3:
+ if (!xdr_long(xdrsp, &np->fhsize))
+ return (0);
+ if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
+ return (0);
+ if (!xdr_long(xdrsp, &authcnt))
+ return (0);
+ for (i = 0; i < authcnt; i++) {
+ if (!xdr_long(xdrsp, &auth))
+ return (0);
+ if (auth == np->auth)
+ authfnd++;
+ }
+
+ /*
+ * Some servers, such as DEC's OSF/1 return a nil authenticator
+ * list to indicate RPCAUTH_UNIX.
+ */
+ if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
+ np->stat = EAUTH;
+ return (1);
+ };
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "\
+usage: mount_nfs [-3KPTUbcdilqs] [-D deadthresh] [-I readdirsize]\n\
+ [-L leaseterm] [-R retrycnt] [-a maxreadahead] [-g maxgroups]\n\
+ [-m realm] [-o options] [-r readsize] [-t timeout] [-w writesize]\n\
+ [-x retrans] rhost:path node\n");
+ exit(1);
+}
diff --git a/sbin/mount_null/Makefile b/sbin/mount_null/Makefile
new file mode 100644
index 0000000..3f8b3ef
--- /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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../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 0000000..1a47b27
--- /dev/null
+++ b/sbin/mount_null/mount_null.8
@@ -0,0 +1,221 @@
+.\"
+.\" 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
+.\" $Id$
+.\"
+.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 providing 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 examining 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 modifying 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 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
+.Bx 4.4 .
diff --git a/sbin/mount_null/mount_null.c b/sbin/mount_null/mount_null.c
new file mode 100644
index 0000000..e78c3fc
--- /dev/null
+++ b/sbin/mount_null/mount_null.c
@@ -0,0 +1,145 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_null.c,v 1.4 1996/05/13 17:43:08 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <miscfs/nullfs/null.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+int subdir __P((const char *, const char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct null_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+ struct vfsconf *vfc;
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ if (realpath(argv[0], target) == 0)
+ err(EX_OSERR, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ vfc = getvfsbyname("null");
+ if(!vfc && vfsisloadable("null")) {
+ if(vfsload("null"))
+ err(EX_OSERR, "vfsload(null)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("null");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "null filesystem is not available");
+
+ if (mount(vfc->vfc_index, argv[1], mntflags, &args))
+ err(EX_OSERR, target);
+ 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);
+}
+
+static 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 0000000..3f8b3ef
--- /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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8
new file mode 100644
index 0000000..1a47b27
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -0,0 +1,221 @@
+.\"
+.\" 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
+.\" $Id$
+.\"
+.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 providing 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 examining 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 modifying 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 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
+.Bx 4.4 .
diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c
new file mode 100644
index 0000000..e78c3fc
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.c
@@ -0,0 +1,145 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_null.c,v 1.4 1996/05/13 17:43:08 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <miscfs/nullfs/null.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+int subdir __P((const char *, const char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct null_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+ struct vfsconf *vfc;
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch(ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ if (realpath(argv[0], target) == 0)
+ err(EX_OSERR, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ vfc = getvfsbyname("null");
+ if(!vfc && vfsisloadable("null")) {
+ if(vfsload("null"))
+ err(EX_OSERR, "vfsload(null)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("null");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "null filesystem is not available");
+
+ if (mount(vfc->vfc_index, argv[1], mntflags, &args))
+ err(EX_OSERR, target);
+ 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);
+}
+
+static 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 0000000..85eca12
--- /dev/null
+++ b/sbin/mount_portal/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id$
+
+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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_portal/activate.c b/sbin/mount_portal/activate.c
new file mode 100644
index 0000000..3361798
--- /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 0000000..3eb4af3
--- /dev/null
+++ b/sbin/mount_portal/conf.c
@@ -0,0 +1,337 @@
+/*
+ * 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 1995/02/21 04:05:20 wollman Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.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]) */
+ regex_t p_rx; /* RE to match against pathname () */
+ int p_rxvalid; /* non-zero if valid regular expression */
+ 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);
+
+}
+
+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)) {
+ int val;
+
+ curp = p; /* XXX */
+ val = regcomp(&p->p_rx, p->p_key, REG_EXTENDED | REG_NOSUB);
+ if (val) {
+ char errbuf[_POSIX2_LINE_MAX];
+ regerror(val, &p->p_rx, errbuf, sizeof errbuf);
+ syslog(LOG_ERR, "%s:%s: regcomp %s: %s",
+ conf_file, curp->p_lno, curp->p_key, errbuf);
+ regfree(&p->p_rx);
+ p->p_rxvalid = 0;
+ } else {
+ p->p_rxvalid = 1;
+ }
+ curp = 0; /* XXX */
+ } else {
+ p->p_rxvalid = 0;
+ }
+ p->p_lno = lno;
+
+ return (p);
+}
+
+/*
+ * Free a path structure
+ */
+static void pfree(p)
+path *p;
+{
+ free(p->p_args);
+ if (p->p_rxvalid) {
+ regfree(&p->p_rx);
+ }
+ 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_rxvalid) {
+ if (!regexec(&p->p_rx, key, 0, 0, 0)) {
+ 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 0000000..5c1f177
--- /dev/null
+++ b/sbin/mount_portal/mount_portal.8
@@ -0,0 +1,137 @@
+.\"
+.\" 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
+.Bx 4.4 .
diff --git a/sbin/mount_portal/mount_portal.c b/sbin/mount_portal/mount_portal.c
new file mode 100644
index 0000000..1608e22
--- /dev/null
+++ b/sbin/mount_portal/mount_portal.c
@@ -0,0 +1,291 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.6 1996/05/13 17:43:09 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/time.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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+#include "portald.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(sig)
+int sig;
+{
+ readcf ++;
+}
+
+static void sigchld(sig)
+int sig;
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ /* wrtp - waitpid _doesn't_ return 0 when no children! */
+#ifdef notdef
+ if (pid < 0)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+#endif
+}
+
+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];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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)) {
+ errx(EX_SOFTWARE, "portal socket name too long");
+ }
+ 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) {
+ err(EX_OSERR, "socket");
+ }
+ (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;
+
+ vfc = getvfsbyname("portal");
+ if(!vfc && vfsisloadable("portal")) {
+ if(vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("portal");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc ? vfc->vfc_index : 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);
+ signal(SIGHUP, sighup);
+
+ /*
+ * 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) {
+#ifdef DEBUG
+ printf ("re-reading configuration file\n");
+#endif
+ 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_ZERO(&fdset);
+ FD_SET(so, &fdset);
+ rc = select(so+1, &fdset, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ 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(EX_OSERR);
+ }
+ 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);
+ exit(0); /* stupid errors.... tidied up... wrtp*/
+ 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(EX_USAGE);
+}
diff --git a/sbin/mount_portal/pathnames.h b/sbin/mount_portal/pathnames.h
new file mode 100644
index 0000000..2532114
--- /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 0000000..5b5a773
--- /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 0000000..fbe111b
--- /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 0000000..d1eba94
--- /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 0000000..06e3382
--- /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 0000000..a9cd2ca3
--- /dev/null
+++ b/sbin/mount_portal/pt_file.c
@@ -0,0 +1,108 @@
+/*
+ * 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.2 1994/09/19 13:52:38 ache Exp $
+ */
+
+#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;
+ gid_t 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]);
+ printf ("fflag = %x, oflag = %x\n", pcr->pcr_flag, (pcr->pcr_flag)-1);
+#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);
+
+ /* dmb convert kernel flags to oflags, see <fcntl.h> */
+ fd = open(pbuf, (pcr->pcr_flag)-1, 0777);
+ 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 0000000..11f1453
--- /dev/null
+++ b/sbin/mount_portal/pt_tcp.c
@@ -0,0 +1,164 @@
+/*
+ * 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.1.1 1994/05/26 06:34:34 rgrimes Exp $
+ */
+
+#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;
+ u_short 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;
+ }
+#ifdef DEBUG
+ printf ("inet address for %s is %s\n", host, inet_ntoa(*ipp[0]));
+#endif
+
+ sp = getservbyname(port, "tcp");
+ if (sp != NULL)
+ s_port = (u_short)sp->s_port;
+ else {
+ s_port = htons ((u_short)strtol (port, (char**)NULL, 10));
+ if (s_port == 0)
+ return (EINVAL);
+ }
+#ifdef DEBUG
+ printf ("port number for %s is %d\n", port, s_port);
+#endif
+
+ 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 0000000..85eca12
--- /dev/null
+++ b/sbin/mount_portalfs/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id$
+
+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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_portalfs/activate.c b/sbin/mount_portalfs/activate.c
new file mode 100644
index 0000000..3361798
--- /dev/null
+++ b/sbin/mount_portalfs/activate.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)activate.c 8.2 (Berkeley) 3/27/94
+ *
+ * $Id: activate.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include "portald.h"
+
+/*
+ * Scan the providers list and call the
+ * appropriate function.
+ */
+static int activate_argv(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ provider *pr;
+
+ for (pr = providers; pr->pr_match; pr++)
+ if (strcmp(v[0], pr->pr_match) == 0)
+ return ((*pr->pr_func)(pcr, key, v, so, fdp));
+
+ return (ENOENT);
+}
+
+static int get_request(so, pcr, key, klen)
+int so;
+struct portal_cred *pcr;
+char *key;
+int klen;
+{
+ struct iovec iov[2];
+ struct msghdr msg;
+ int n;
+
+ iov[0].iov_base = (caddr_t) pcr;
+ iov[0].iov_len = sizeof(*pcr);
+ iov[1].iov_base = key;
+ iov[1].iov_len = klen;
+
+ bzero((char *) &msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ n = recvmsg(so, &msg, 0);
+ if (n < 0)
+ return (errno);
+
+ if (n <= sizeof(*pcr))
+ return (EINVAL);
+
+ n -= sizeof(*pcr);
+ key[n] = '\0';
+
+ return (0);
+}
+
+static void send_reply(so, fd, error)
+int so;
+int fd;
+int error;
+{
+ int n;
+ struct iovec iov;
+ struct msghdr msg;
+ struct {
+ struct cmsghdr cmsg;
+ int fd;
+ } ctl;
+
+ /*
+ * Line up error code. Don't worry about byte ordering
+ * because we must be sending to the local machine.
+ */
+ iov.iov_base = (caddr_t) &error;
+ iov.iov_len = sizeof(error);
+
+ /*
+ * Build a msghdr
+ */
+ bzero((char *) &msg, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /*
+ * If there is a file descriptor to send then
+ * construct a suitable rights control message.
+ */
+ if (fd >= 0) {
+ ctl.fd = fd;
+ ctl.cmsg.cmsg_len = sizeof(ctl);
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ msg.msg_control = (caddr_t) &ctl;
+ msg.msg_controllen = ctl.cmsg.cmsg_len;
+ }
+
+ /*
+ * Send to kernel...
+ */
+ if ((n = sendmsg(so, &msg, MSG_EOR)) < 0)
+ syslog(LOG_ERR, "send: %s", strerror(errno));
+#ifdef DEBUG
+ fprintf(stderr, "sent %d bytes\n", n);
+#endif
+ sleep(1); /*XXX*/
+#ifdef notdef
+ if (shutdown(so, 2) < 0)
+ syslog(LOG_ERR, "shutdown: %s", strerror(errno));
+#endif
+ /*
+ * Throw away the open file descriptor
+ */
+ (void) close(fd);
+}
+
+void activate(q, so)
+qelem *q;
+int so;
+{
+ struct portal_cred pcred;
+ char key[MAXPATHLEN+1];
+ int error;
+ char **v;
+ int fd = -1;
+
+ /*
+ * Read the key from the socket
+ */
+ error = get_request(so, &pcred, key, sizeof(key));
+ if (error) {
+ syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error));
+ goto drop;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "lookup key %s\n", key);
+#endif
+
+ /*
+ * Find a match in the configuration file
+ */
+ v = conf_match(q, key);
+
+ /*
+ * If a match existed, then find an appropriate portal
+ * otherwise simply return ENOENT.
+ */
+ if (v) {
+ error = activate_argv(&pcred, key, v, so, &fd);
+ if (error)
+ fd = -1;
+ else if (fd < 0)
+ error = -1;
+ } else {
+ error = ENOENT;
+ }
+
+ if (error >= 0)
+ send_reply(so, fd, error);
+
+drop:;
+ close(so);
+}
diff --git a/sbin/mount_portalfs/conf.c b/sbin/mount_portalfs/conf.c
new file mode 100644
index 0000000..3eb4af3
--- /dev/null
+++ b/sbin/mount_portalfs/conf.c
@@ -0,0 +1,337 @@
+/*
+ * 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 1995/02/21 04:05:20 wollman Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.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]) */
+ regex_t p_rx; /* RE to match against pathname () */
+ int p_rxvalid; /* non-zero if valid regular expression */
+ 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);
+
+}
+
+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)) {
+ int val;
+
+ curp = p; /* XXX */
+ val = regcomp(&p->p_rx, p->p_key, REG_EXTENDED | REG_NOSUB);
+ if (val) {
+ char errbuf[_POSIX2_LINE_MAX];
+ regerror(val, &p->p_rx, errbuf, sizeof errbuf);
+ syslog(LOG_ERR, "%s:%s: regcomp %s: %s",
+ conf_file, curp->p_lno, curp->p_key, errbuf);
+ regfree(&p->p_rx);
+ p->p_rxvalid = 0;
+ } else {
+ p->p_rxvalid = 1;
+ }
+ curp = 0; /* XXX */
+ } else {
+ p->p_rxvalid = 0;
+ }
+ p->p_lno = lno;
+
+ return (p);
+}
+
+/*
+ * Free a path structure
+ */
+static void pfree(p)
+path *p;
+{
+ free(p->p_args);
+ if (p->p_rxvalid) {
+ regfree(&p->p_rx);
+ }
+ 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_rxvalid) {
+ if (!regexec(&p->p_rx, key, 0, 0, 0)) {
+ return p->p_argv + 1;
+ }
+ } else {
+ if (strncmp(p->p_key, key, strlen(p->p_key)) == 0)
+ return (p->p_argv+1);
+ }
+ }
+
+ return (0);
+}
diff --git a/sbin/mount_portalfs/mount_portalfs.8 b/sbin/mount_portalfs/mount_portalfs.8
new file mode 100644
index 0000000..5c1f177
--- /dev/null
+++ b/sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,137 @@
+.\"
+.\" 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
+.Bx 4.4 .
diff --git a/sbin/mount_portalfs/mount_portalfs.c b/sbin/mount_portalfs/mount_portalfs.c
new file mode 100644
index 0000000..1608e22
--- /dev/null
+++ b/sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,291 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.6 1996/05/13 17:43:09 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/time.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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+#include "portald.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(sig)
+int sig;
+{
+ readcf ++;
+}
+
+static void sigchld(sig)
+int sig;
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ /* wrtp - waitpid _doesn't_ return 0 when no children! */
+#ifdef notdef
+ if (pid < 0)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+#endif
+}
+
+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];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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)) {
+ errx(EX_SOFTWARE, "portal socket name too long");
+ }
+ 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) {
+ err(EX_OSERR, "socket");
+ }
+ (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;
+
+ vfc = getvfsbyname("portal");
+ if(!vfc && vfsisloadable("portal")) {
+ if(vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("portal");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc ? vfc->vfc_index : 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);
+ signal(SIGHUP, sighup);
+
+ /*
+ * 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) {
+#ifdef DEBUG
+ printf ("re-reading configuration file\n");
+#endif
+ 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_ZERO(&fdset);
+ FD_SET(so, &fdset);
+ rc = select(so+1, &fdset, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ 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(EX_OSERR);
+ }
+ 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);
+ exit(0); /* stupid errors.... tidied up... wrtp*/
+ 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(EX_USAGE);
+}
diff --git a/sbin/mount_portalfs/pathnames.h b/sbin/mount_portalfs/pathnames.h
new file mode 100644
index 0000000..2532114
--- /dev/null
+++ b/sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: pathnames.h,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */
diff --git a/sbin/mount_portalfs/portal.conf b/sbin/mount_portalfs/portal.conf
new file mode 100644
index 0000000..5b5a773
--- /dev/null
+++ b/sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id: portal.conf,v 1.1 1992/05/27 06:50:13 jsp Exp jsp $
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe
+foo/ exec ./bar bar baz
diff --git a/sbin/mount_portalfs/portald.h b/sbin/mount_portalfs/portald.h
new file mode 100644
index 0000000..fbe111b
--- /dev/null
+++ b/sbin/mount_portalfs/portald.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portald.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: portald.h,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <sys/cdefs.h>
+#include <miscfs/portal/portal.h>
+
+/*
+ * Meta-chars in an RE. Paths in the config file containing
+ * any of these characters will be matched using regexec, other
+ * paths will be prefix-matched.
+ */
+#define RE_CHARS ".|()[]*+?\\^$"
+
+typedef struct qelem qelem;
+
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+typedef struct provider provider;
+struct provider {
+ char *pr_match;
+ int (*pr_func) __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_file __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_tcp __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+
+/*
+ * Global functions
+ */
+extern void activate __P((qelem *q, int so));
+extern char **conf_match __P((qelem *q, char *key));
+extern void conf_read __P((qelem *q, char *conf));
diff --git a/sbin/mount_portalfs/pt_conf.c b/sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..d1eba94
--- /dev/null
+++ b/sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: pt_conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "portald.h"
+
+provider providers[] = {
+ { "exec", portal_exec },
+ { "file", portal_file },
+ { "tcp", portal_tcp },
+ { 0, 0 }
+};
diff --git a/sbin/mount_portalfs/pt_exec.c b/sbin/mount_portalfs/pt_exec.c
new file mode 100644
index 0000000..06e3382
--- /dev/null
+++ b/sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: pt_exec.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+int portal_exec(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ return (ENOEXEC);
+}
+
diff --git a/sbin/mount_portalfs/pt_file.c b/sbin/mount_portalfs/pt_file.c
new file mode 100644
index 0000000..a9cd2ca3
--- /dev/null
+++ b/sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,108 @@
+/*
+ * 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.2 1994/09/19 13:52:38 ache Exp $
+ */
+
+#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;
+ gid_t 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]);
+ printf ("fflag = %x, oflag = %x\n", pcr->pcr_flag, (pcr->pcr_flag)-1);
+#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);
+
+ /* dmb convert kernel flags to oflags, see <fcntl.h> */
+ fd = open(pbuf, (pcr->pcr_flag)-1, 0777);
+ if (fd < 0)
+ error = errno;
+ else
+ error = 0;
+
+ if (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */
+ error = errno;
+ syslog(LOG_ERR, "setcred: %s", strerror(error));
+ if (fd >= 0) {
+ (void) close(fd);
+ fd = -1;
+ }
+ }
+
+ if (error == 0)
+ *fdp = fd;
+
+#ifdef DEBUG
+ fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error);
+#endif
+
+ return (error);
+}
diff --git a/sbin/mount_portalfs/pt_tcp.c b/sbin/mount_portalfs/pt_tcp.c
new file mode 100644
index 0000000..11f1453
--- /dev/null
+++ b/sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,164 @@
+/*
+ * 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.1.1 1994/05/26 06:34:34 rgrimes Exp $
+ */
+
+#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;
+ u_short 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;
+ }
+#ifdef DEBUG
+ printf ("inet address for %s is %s\n", host, inet_ntoa(*ipp[0]));
+#endif
+
+ sp = getservbyname(port, "tcp");
+ if (sp != NULL)
+ s_port = (u_short)sp->s_port;
+ else {
+ s_port = htons ((u_short)strtol (port, (char**)NULL, 10));
+ if (s_port == 0)
+ return (EINVAL);
+ }
+#ifdef DEBUG
+ printf ("port number for %s is %d\n", port, s_port);
+#endif
+
+ 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_std/Makefile b/sbin/mount_std/Makefile
new file mode 100644
index 0000000..ff01131
--- /dev/null
+++ b/sbin/mount_std/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_std
+SRCS= mount_std.c getmntopts.c
+MAN8= mount_std.8 mount_devfs.8 mount_fdesc.8 mount_kernfs.8 mount_procfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+LINKS= ${BINDIR}/mount_std ${BINDIR}/mount_devfs \
+ ${BINDIR}/mount_std ${BINDIR}/mount_fdesc \
+ ${BINDIR}/mount_std ${BINDIR}/mount_kernfs \
+ ${BINDIR}/mount_std ${BINDIR}/mount_procfs
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_std/mount_devfs.8 b/sbin/mount_std/mount_devfs.8
new file mode 100644
index 0000000..14d8b0c
--- /dev/null
+++ b/sbin/mount_std/mount_devfs.8
@@ -0,0 +1,91 @@
+.\"
+.\" 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_devfs.8 8.2 (Berkeley) 3/27/94
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_DEFVS 8
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm mount_devfs
+.Nd mount the /devs file system
+.Sh SYNOPSIS
+.Nm mount_devfs
+.Op Fl o Ar options
+.Ar "devfs"
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_devfs
+command attaches an instance of the kernel's device
+namespace to the global filesystem namespace.
+The conventional mount point is
+.Pa /devs .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The filesystem includes several directories, links, symbolic links and devices,
+some of which can also be written.
+In a chroot'ed environment,
+.Nm
+can be used to create a new
+.Pa /devs
+mount point.
+.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 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_devfs
+utility first appeared in
+.Fx 2.0 .
diff --git a/sbin/mount_std/mount_fdesc.8 b/sbin/mount_std/mount_fdesc.8
new file mode 100644
index 0000000..abb3d64
--- /dev/null
+++ b/sbin/mount_std/mount_fdesc.8
@@ -0,0 +1,172 @@
+.\"
+.\" 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
+.Bx 4.4 .
diff --git a/sbin/mount_std/mount_kernfs.8 b/sbin/mount_std/mount_kernfs.8
new file mode 100644
index 0000000..df0f954
--- /dev/null
+++ b/sbin/mount_std/mount_kernfs.8
@@ -0,0 +1,132 @@
+.\"
+.\" 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
+.Bx 4.4 .
diff --git a/sbin/mount_std/mount_procfs.8 b/sbin/mount_std/mount_procfs.8
new file mode 100644
index 0000000..87a21ad
--- /dev/null
+++ b/sbin/mount_std/mount_procfs.8
@@ -0,0 +1,254 @@
+.\"
+.\" 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
+.Bx 4.4 .
diff --git a/sbin/mount_std/mount_std.8 b/sbin/mount_std/mount_std.8
new file mode 100644
index 0000000..680c8fa
--- /dev/null
+++ b/sbin/mount_std/mount_std.8
@@ -0,0 +1,137 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: mount_std.8,v 1.2 1996/05/14 15:16:46 wollman Exp $
+.\"
+.Dd May 13, 1996
+.Dt MOUNT_STD 8
+.Os FreeBSD 2.2
+.Sh NAME
+.Nm mount_std
+.Nd mount ``standard'' filesystems
+.Sh SYNOPSIS
+.Nm mount_ Ns Ar fsname
+.Op Fl o Ar options
+.Ar "devfs"
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+command is a generic mechanism for attaching ``standard'' filesystems to
+the filesystem. A ``standard'' filesystem is one which:
+.Bl -enum -offset indent
+.It
+accepts only the standard
+.Fl o
+options
+.Dq ro
+.Pq ``rdonly'' ,
+.Dq rw ,
+.Dq nodev ,
+.Dq noexec ,
+.Dq nosuid ,
+and
+.Dq union .
+.It
+has a kernel filesystem module name the same as its user-visible name.
+.It
+requires no other special processing on the part of the
+.Nm mount_std
+command.
+.El
+.Pp
+The
+.Nm
+command examines its zeroth command-line argument (the name by which
+it was called) to determine the type of filesystem to be mounted. If
+it is called by a name which does not end in
+.Dq Li _ Ns Ar fsname ,
+.Nm
+will assume (for compatibility
+with
+.Xr mount 8 )
+that the zeroth argument contains only the name of the filesystem type.
+The
+.Nm
+command is normally installed with appropriate links to commands for
+the distributed filesystems which can be mounted in this way;
+for information on the function of each filesystem, see the manual page
+for that specific
+.Nm mount_ Ns Ar fsname
+command.
+.Sh DIAGNOSTICS
+.Bl -diag
+.It argv[0] must end in _fsname
+The
+.Nm mount_std
+command was called with a zero'th argument of
+.Dq Li mount_std .
+.It vfsload(%s)
+.Nm
+was unable to load a kernel module implementing the %s filesystem
+type.
+.It %s filesystem not available
+The specified filesystem type was not present in the kernel and no
+loadable module for it was found.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr getvfsbyname 3 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr mount_devfs 8 ,
+.Xr mount_fdesc 8 ,
+.Xr mount_kernfs 8 ,
+.Xr mount_procfs 8
+.Sh HISTORY
+The
+.Nm mount_std
+utility first appeared in
+.Fx 2.2 .
+Loadable filesystem modules first appeared in
+.Fx 2.0 .
+The
+.Dq fdesc ,
+.Dq kernfs ,
+and
+.Dq procfs
+filesystem types first appeared in
+.Fx 2.0 ;
+the
+.Dq devfs
+filesystem type first appeared in
+.Fx 2.2 .
diff --git a/sbin/mount_std/mount_std.c b/sbin/mount_std/mount_std.c
new file mode 100644
index 0000000..afe0354
--- /dev/null
+++ b/sbin/mount_std/mount_std.c
@@ -0,0 +1,132 @@
+/*
+ * 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 const char rcsid[] =
+ "$Id: mount_std.c,v 1.2 1996/05/14 15:16:49 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+static const char *fsname;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, mntflags;
+ struct vfsconf *vfc;
+
+ /*
+ * XXX
+ * mount(8) calls the mount programs with an argv[0] which is
+ * /just/ the filesystem name. So, if there is no underscore
+ * in argv[0], we assume that we are being called from mount(8)
+ * and that argv[0] is thus the name of the filesystem type.
+ */
+ fsname = strrchr(argv[0], '_');
+ if (fsname) {
+ if (strcmp(fsname, "_std") == 0)
+ errx(EX_USAGE, "argv[0] must end in _fsname");
+ fsname++;
+ } else {
+ fsname = argv[0];
+ }
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ vfc = getvfsbyname(fsname);
+ if(!vfc && vfsisloadable(fsname)) {
+ if(vfsload(fsname)) {
+ err(EX_OSERR, "vfsload(%s)", fsname);
+ }
+ endvfsent();
+ vfc = getvfsbyname(fsname);
+ }
+ if (!vfc)
+ errx(EX_OSERR, "%s filesystem not available", fsname);
+
+ if (mount(vfc->vfc_index, argv[1], mntflags, NULL))
+ err(EX_OSERR, NULL);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_%s [-o options] what_to_mount mount_point\n",
+ fsname);
+ exit(EX_USAGE);
+}
diff --git a/sbin/mount_umap/Makefile b/sbin/mount_umap/Makefile
new file mode 100644
index 0000000..780b52c
--- /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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../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 0000000..b6cced9
--- /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
+.Sh HISTORY
+The
+.Nm mount_umap
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_umap/mount_umap.c b/sbin/mount_umap/mount_umap.c
new file mode 100644
index 0000000..0542ab3
--- /dev/null
+++ b/sbin/mount_umap/mount_umap.c
@@ -0,0 +1,249 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_umap.c,v 1.6 1996/05/13 17:43:18 wollman Exp $";
+#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 <sysexits.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.
+ */
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+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];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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(EX_NOINPUT, "%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(EX_OSERR, "%s%s", mapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ mapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", mapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(fp, "%d\n", &nentries)) != 1)
+ errx(EX_DATAERR, "%s: nentries not found%s", mapfile, not);
+ if (nentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "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(EX_OSERR, "%s%s", mapfile, not);
+ if (feof(fp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ mapfile, not);
+ errx(EX_DATAERR, "%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(EX_NOINPUT, "%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(EX_OSERR, "%s%s", gmapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ gmapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", gmapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(gfp, "%d\n", &gnentries)) != 1)
+ errx(EX_DATAERR, "nentries not found%s", gmapfile, not);
+ if (gnentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "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(gfp, "%lu %lu\n",
+ &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) {
+ if (ferror(gfp))
+ err(EX_OSERR, "%s%s", gmapfile, not);
+ if (feof(gfp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ gmapfile, not);
+ errx(EX_DATAERR, "%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;
+
+ vfc = getvfsbyname("umap");
+ if(!vfc && vfsisloadable("umap")) {
+ if(vfsload("umap"))
+ err(1, "vfsload(umap)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("umap");
+ }
+ if (!vfc) {
+ errx(1, "umap filesystem not available");
+ }
+
+ if (mount(vfc->vfc_index, 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(EX_USAGE);
+}
diff --git a/sbin/mount_umap/sample.group.mapfile b/sbin/mount_umap/sample.group.mapfile
new file mode 100644
index 0000000..69d3d77
--- /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 0000000..03c59e9
--- /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 0000000..059939f
--- /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 0000000..780b52c
--- /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.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_umapfs/mount_umapfs.8 b/sbin/mount_umapfs/mount_umapfs.8
new file mode 100644
index 0000000..b6cced9
--- /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
+.Sh HISTORY
+The
+.Nm mount_umap
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_umapfs/mount_umapfs.c b/sbin/mount_umapfs/mount_umapfs.c
new file mode 100644
index 0000000..0542ab3
--- /dev/null
+++ b/sbin/mount_umapfs/mount_umapfs.c
@@ -0,0 +1,249 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: mount_umap.c,v 1.6 1996/05/13 17:43:18 wollman Exp $";
+#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 <sysexits.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.
+ */
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+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];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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(EX_NOINPUT, "%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(EX_OSERR, "%s%s", mapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ mapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", mapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(fp, "%d\n", &nentries)) != 1)
+ errx(EX_DATAERR, "%s: nentries not found%s", mapfile, not);
+ if (nentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "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(EX_OSERR, "%s%s", mapfile, not);
+ if (feof(fp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ mapfile, not);
+ errx(EX_DATAERR, "%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(EX_NOINPUT, "%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(EX_OSERR, "%s%s", gmapfile, not);
+ if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) {
+ strmode(statbuf.st_mode, buf);
+ err(EX_NOPERM, "%s: improper write permissions (%s)%s",
+ gmapfile, buf, not);
+ }
+ if (statbuf.st_uid != ROOTUSER)
+ errx(EX_NOPERM, "%s does not belong to root%s", gmapfile, not);
+#endif /* MAPSECURITY */
+
+ if ((fscanf(gfp, "%d\n", &gnentries)) != 1)
+ errx(EX_DATAERR, "nentries not found%s", gmapfile, not);
+ if (gnentries > MAPFILEENTRIES)
+ errx(EX_DATAERR,
+ "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(gfp, "%lu %lu\n",
+ &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) {
+ if (ferror(gfp))
+ err(EX_OSERR, "%s%s", gmapfile, not);
+ if (feof(gfp))
+ errx(EX_DATAERR, "%s: unexpected end-of-file%s",
+ gmapfile, not);
+ errx(EX_DATAERR, "%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;
+
+ vfc = getvfsbyname("umap");
+ if(!vfc && vfsisloadable("umap")) {
+ if(vfsload("umap"))
+ err(1, "vfsload(umap)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("umap");
+ }
+ if (!vfc) {
+ errx(1, "umap filesystem not available");
+ }
+
+ if (mount(vfc->vfc_index, 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(EX_USAGE);
+}
diff --git a/sbin/mount_umapfs/sample.group.mapfile b/sbin/mount_umapfs/sample.group.mapfile
new file mode 100644
index 0000000..69d3d77
--- /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 0000000..03c59e9
--- /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 0000000..059939f
--- /dev/null
+++ b/sbin/mount_umapfs/umap_manual
@@ -0,0 +1,175 @@
+
+\appendix
+\section{The umap Layer} \label{sect:umap}
+
+\subsection{Introduction}
+
+Normally, the file system is expected to span a single administrative domain.
+An administrative domain, for these purposes, is a machine or set of
+machines that share common password file information, usually through
+the yellow pages mechanism. File hierarchies that span more
+than one domain leads to certain problems, since the same numerical
+UID in one domain may correspond to a different user in another domain.
+If the system administrator is very careful to ensure that both domains
+contain identical user ID information, The umap layer can be used to
+run between those domains without changes
+
+The umap layer is a file system layer that sits on top of the normal
+file layer. The umap layer maps Unix-style UIDs from
+one domain into the UIDs in the other domain. By setting up the mappings
+properly, the same user with different UIDs in two domains can be seen
+as the same user, from the system point of view, or, conversely, two
+different users with the same UID in the two domains can be distinguished.
+
+First, we define some terms. ``User'' refers to the human (or daemon) that
+has privileges to login, run programs, and access files. ``UID''refers to
+the numerical identifier that uniquely identifies the user within a
+single domain. ``Login name'' refers to the character string the user
+types to log into the system. ``GID'' refers to the numerical group
+identifier used by Unix systems to identify groups of users. ``Group
+name'' is the character string name attached to a particular GID in the
+local {\sf /etc/groups} file or the yellow pages groups file.
+
+In order for the umap layer to work properly, all users
+in either domain must have password file entries in both domains.
+They do not, however, have to have the same numerical UID, nor even the
+same character string login name (the latter is highly recommended,
+if possible, however). Any user not having a UID in one domain will be
+treated as the special user NOBODY by the other domain, probably with
+undesirable consequences. Any user not owning any files in the shared
+sub-trees need not be given a UID in the other domain.
+
+Groups work similarly. The umap layer can translate group ID's between
+domains in the same manner as UID's. Again, any group that wishes to
+participate must have a group ID in both domains,
+though it need not be the same GID in both. If a group in one domain is not
+known in the other domain, that group will be treated as being NULLGROUP.
+The umap layer has no provisions for enrolling UID's from other domains
+as group members, but, since each user from each domain must have some
+UID in every domain, the UID in the local domain can be used to enroll
+the user in the local groups.
+
+NOBODY and NULLGROUP are special reserved UID's and GID's, respectively.
+NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator
+wants to have an appropriate text string appear when these UID's are
+encountered by programs like {\sf ls -l}, he should add these values to
+the password and {\sf /etc/groups} file, or to the appropriate yellow pages.
+If these IDs are already in use in that domain, different values can be
+used for NOBODY and NULLGROUP, but that will require a recompilation of
+the umap layer code and, as a result, the entire kernel. These
+values are defined in the {\sf umap\_info.h} file, kept with the rest of the
+umap source code.
+
+When the umap layer is in use, one of the participating domains is declared
+to be the master. All UID and GID information stored for participating files
+will be stored in vnodes using its mappings, no matter what site the copies of
+the files are stored at. The master domain therefore need not run a copy
+of the umap layer, as it already has all of the correct mappings. All
+other domains must run a umap layer on top of any other layers they use.
+
+\subsection{Setting Up a umap Layer}
+
+The system administrator of a system needing to use the umap layer
+must take several actions.
+First, he must create files containing the necessary UID
+and GID mappings. There is a separate file for user and group IDs. The
+format of the files is the same. The first line contains the total number
+of entries in the file. Each subsequent line contains one mapping. A
+mapping line consists of two numerical UIDs, separated by white space.
+The first is the UID of a user on the local machine. The second is the
+UID for the same user on the master machine. The maximum number of users
+that can be mapped for a single shared sub-tree is 64. The maximum number of
+groups that can be mapped for a single sub-tree is 16. These constants
+are set in the {\sf umap\_info.h} file, and can be changed, but changing them
+requires recompilation. Separate mapping files can be used for each shared
+subtree, or the same mapping files can be shared by several sub-trees.
+
+Below is a sample UID mapping file. There are four entries. UID 5 is mapped
+to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this
+machine, the UID's for users 5, 521, and 7000 are the same as on the master,
+but UID 2002 is for a user whose UID on the master machine is 604. All
+files in the sub-tree belonging to that user have UID 604 in their inodes,
+even on this machine, but the umap layer will ensure that anyone running
+under UID 2002 will have all files in this sub-tree owned by 604 treated as if
+they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree
+will show the login name associated with UID 2002 as the owner.
+
+\noindent4\newline
+5 5\newline
+521 521\newline
+2002 604\newline
+7000 7000\newline
+
+The user and group mapping files should be owned by the root user, and
+should be writable only by that user. If they are not owned by root, or
+are writable by some other user, the umap mounting command will abort.
+
+Normally, the sub-treeis grafted directly into the place in
+the file hierarchy where the it should appear to users.Using the umap
+layer requires that the sub-tree be grafted somewhere else, and
+the umap layer be mounted in the desired position in the file hierarchy.
+Depending on the situation, the underlying sub-tree can be wherever is
+convenient.
+
+\subsection{Troubleshooting umap Layer Problems}
+
+The umap layer code was not built with special convenience or
+robustness in mind, as it is expected to be superseded with a better
+user ID mapping strategy in the near future. As a result, it is not
+very forgiving of errors in being set up. Here are some possible
+problems, and what to do about them.
+
+\begin{itemize}
+
+
+\item{Problem: A file belongs to NOBODY, or group NULLGROUP.
+
+Fixes: The mapping files don't know about this file's real user or group.
+Either they are not in the mapping files, or the counts on the number of
+entries in the mapping files are too low, so entries at the end (including
+these) are being ignored. Add the entries or fix the counts, and either
+unmount and remount the sub-tree, or reboot.}
+
+\item{Problem: A normal operation does not work.
+
+Fixes: Possibly, some mapping has not been set properly. Check to
+see which files are used by the operation and who they appear to be
+owned by. If they are owned by NOBODY or some other suspicious user,
+there may be a problem in the mapping files. Be sure to check groups,
+too. As above, if the counts of mappings in the mapping files are lower
+than the actual numbers of pairs, pairs at the end of the file will be
+ignored. If any changes are made in the mapping files, you will need to
+either unmount and remount or reboot before they will take effect.
+
+Another possible problem can arise because not all Unix utilities
+rely exclusively on numeric UID for identification. For instance,
+SCCS saves the login name in files. If a user's login name on two machines
+isn't the same, SCCS may veto an operation even though Unix file permissions,
+as checked by the umap layer, may say it's OK. There's not much to be
+done in such cases, unless the login name can be changed or one fiddles
+improperly with SCCS information. There may be other, undiscovered cases
+where similar problems arise, some of which may be even harder to handle.}
+
+\item{Problem: Someone has access permissions he should not have.
+
+Fixes: This is probably caused by a mistake in the mapping files. Check
+both user and group mapping files. If any changes are made in the mapping
+files, you will need to unmount and remount the sub-tree or reboot before they
+will take effect.}
+
+\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file.
+
+Fixes: Probably a mistake in the mapping files. In particular, if
+two local UIDs are mapped to a single master UID, stat calls will assign
+ownership to the first local UID occurring in the file, which may or may
+not be what was intended. (Generally speaking, mapping two local UIDs to
+a single master UID is a bad idea, but the software will not prevent it.
+Similarly, mapping a single local UID to two master UIDs is a bad idea,
+but will not be prevented. In this case, only the first mapping of the
+local UID will be done. The second, and all subsequent ones, will be
+ignored.) If any changes are made in the mapping files, you will need to
+unmount and remount the sub-tree or reboot before they will take effect.}
+
+\end{itemize}
+
+\end{document}
diff --git a/sbin/mount_union/Makefile b/sbin/mount_union/Makefile
new file mode 100644
index 0000000..e213137
--- /dev/null
+++ b/sbin/mount_union/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_union
+SRCS= mount_union.c getmntopts.c
+MAN8= mount_union.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.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 0000000..7174e17
--- /dev/null
+++ b/sbin/mount_union/mount_union.8
@@ -0,0 +1,201 @@
+.\" 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_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_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 0000000..54c4647
--- /dev/null
+++ b/sbin/mount_union/mount_union.c
@@ -0,0 +1,152 @@
+/*
+ * 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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static int subdir __P((const char *, const char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct union_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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(EX_OSERR, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ vfc = getvfsbyname("union");
+ if(!vfc && vfsisloadable("union")) {
+ if(vfsload("union"))
+ err(1, "vfsload(union)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("union");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "union filesystem is not available");
+
+ if (mount(vfc->vfc_index, argv[1], mntflags, &args))
+ err(EX_OSERR, target);
+ 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(EX_USAGE);
+}
diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile
new file mode 100644
index 0000000..e213137
--- /dev/null
+++ b/sbin/mount_unionfs/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_union
+SRCS= mount_union.c getmntopts.c
+MAN8= mount_union.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_unionfs/mount_unionfs.8 b/sbin/mount_unionfs/mount_unionfs.8
new file mode 100644
index 0000000..7174e17
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.8
@@ -0,0 +1,201 @@
+.\" 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_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_null 8
+.Sh BUGS
+Without whiteout support from the filesystem backing the upper layer,
+there is no way that delete and rename operations on lower layer
+objects can be done.
+.Dv EROFS
+is returned for this kind of operations along with any others
+which would make modifications to the lower layer, such as
+.Xr chmod 1 .
+.Pp
+Running
+.Xr find 1
+over a union tree has the side-effect of creating
+a tree of shadow directories in the upper layer.
+.Sh HISTORY
+The
+.Nm mount_union
+command first appeared in
+.Bx 4.4 .
diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c
new file mode 100644
index 0000000..54c4647
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.c
@@ -0,0 +1,152 @@
+/*
+ * 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 <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static int subdir __P((const char *, const char *));
+static void usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct union_args args;
+ int ch, mntflags;
+ char target[MAXPATHLEN];
+ struct vfsconf *vfc;
+
+ 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, 0);
+ 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(EX_OSERR, "%s", target);
+
+ if (subdir(target, argv[1]) || subdir(argv[1], target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ vfc = getvfsbyname("union");
+ if(!vfc && vfsisloadable("union")) {
+ if(vfsload("union"))
+ err(1, "vfsload(union)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("union");
+ }
+ if (!vfc)
+ errx(EX_OSERR, "union filesystem is not available");
+
+ if (mount(vfc->vfc_index, argv[1], mntflags, &args))
+ err(EX_OSERR, target);
+ 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(EX_USAGE);
+}
diff --git a/sbin/mountd/Makefile b/sbin/mountd/Makefile
new file mode 100644
index 0000000..f32e11b
--- /dev/null
+++ b/sbin/mountd/Makefile
@@ -0,0 +1,9 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $Id$
+
+PROG= mountd
+CFLAGS+=-DNFS -DMFS -DCD9660 -DMSDOSFS
+MAN5= exports.5 netgroup.5
+MAN8= mountd.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/mountd/exports.5 b/sbin/mountd/exports.5
new file mode 100644
index 0000000..73f0207
--- /dev/null
+++ b/sbin/mountd/exports.5
@@ -0,0 +1,252 @@
+.\" 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"
+and
+.%T "NFS: Network File System Version 3 Specification, Appendix I" .
+.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 at any point within the filesystem,
+including regular files if the ``-r'' option is used on mountd.
+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.
+This requires that the kernel be built with the NFSKERB option.
+.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 4 ).
+.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 0000000..536cf36
--- /dev/null
+++ b/sbin/mountd/mountd.8
@@ -0,0 +1,132 @@
+.\" 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.
+.\"
+.\" From: @(#)mountd.8 8.1 (Berkeley) 6/9/93
+.\" $Id: mountd.8,v 1.4 1995/06/27 11:06:18 dfr Exp $
+.\"
+.Dd September 22, 1994
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm /sbin/mountd
+.Op Fl nr
+.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, Appendix A and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I.
+.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 Fl r
+The
+.Fl r
+option allows mount RPCs requests for regular files to be served.
+Although this seems to violate the mount protocol specification, some diskless
+workstations do mount requests for their swapfiles and expect them to be
+regular files. Since a regular file cannot be specified in /etc/exports, the
+entire file system that the swapfiles reside in will have to be exported with
+the ``-alldirs'' flag.
+.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.
+.Pp
+If
+.Nm mountd
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM was available,
+.Nm mountd
+exits with an error.
+.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
+.It Pa /var/db/mountdtab
+the current list of remote mounted filesystems
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr exports 5 ,
+.Xr modload 8 ,
+.Xr nfsd 8 ,
+.Xr portmap 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm mountd
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/mountd/mountd.c b/sbin/mountd/mountd.c
new file mode 100644
index 0000000..cb33186
--- /dev/null
+++ b/sbin/mountd/mountd.c
@@ -0,0 +1,2139 @@
+/*
+ * 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[] = "From: @(#)mountd.c 8.8 (Berkeley) 2/20/94";*/
+static const char rcsid[] =
+ "$Id: mountd.c,v 1.10 1995/11/17 23:22:34 joerg Exp $";
+#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/nfsproto.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
+#define DP_HOSTSET 0x2
+#define DP_KERB 0x4
+
+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
+#define GT_IGNORE 0x5
+
+struct hostlist {
+ int ht_flag; /* Uses DP_xx bits */
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+struct fhreturn {
+ int fhr_flag;
+ int fhr_vers;
+ nfsfh_t fhr_fh;
+};
+
+/* Global defs */
+char *add_expdir __P((struct dirlist **, char *, int));
+void add_dlist __P((struct dirlist **, struct dirlist *,
+ struct grouplist *, int));
+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 *, 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 grouplist *));
+int get_num __P((char *));
+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 *, caddr_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 resvport_only = 1;
+int dir_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 *udptransp, *tcptransp;
+ int c;
+#ifdef __FreeBSD__
+ struct vfsconf *vfc;
+
+ vfc = getvfsbyname("nfs");
+ if(!vfc && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("nfs");
+ }
+ if(!vfc) {
+ errx(1, "NFS support is not available in the running kernel");
+ }
+#endif /* __FreeBSD__ */
+
+ while ((c = getopt(argc, argv, "dnr")) != EOF)
+ switch (c) {
+ case 'n':
+ resvport_only = 0;
+ break;
+ case 'r':
+ dir_only = 0;
+ break;
+ case 'd':
+ debug = debug ? 0 : 1;
+ break;
+ default:
+ fprintf(stderr, "Usage: mountd [-r] [-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 ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
+ (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "Can't create socket");
+ exit(1);
+ }
+ pmap_unset(RPCPROG_MNT, 1);
+ pmap_unset(RPCPROG_MNT, 3);
+ if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
+ !svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
+ 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;
+ struct fhreturn fhr;
+ struct authunix_parms *ucr;
+ struct stat stb;
+ struct statfs fsb;
+ struct hostent *hp;
+ u_long saddr;
+ u_short sport;
+ char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];
+ int bad = ENOENT, defset, hostset;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ saddr = transp->xp_raddr.sin_addr.s_addr;
+ sport = ntohs(transp->xp_raddr.sin_port);
+ 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 (sport >= IPPORT_RESERVED && resvport_only) {
+ 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
+ * or a regular file if the -r option was specified
+ * and it exists.
+ */
+ if (realpath(rpcpath, dirpath) == 0 ||
+ stat(dirpath, &stb) < 0 ||
+ (!S_ISDIR(stb.st_mode) &&
+ (dir_only || !S_ISREG(stb.st_mode))) ||
+ 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 */
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = ex_search(&fsb.f_fsid);
+ hostset = defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset, &hostset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ if (hostset & DP_HOSTSET)
+ fhr.fhr_flag = hostset;
+ else
+ fhr.fhr_flag = defset;
+ fhr.fhr_vers = rqstp->rq_vers;
+ /* Get the file handle */
+ bzero((caddr_t)&fhr.fhr_fh, sizeof(nfsfh_t));
+ if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 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");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
+ 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");
+ }
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ 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 (sport >= IPPORT_RESERVED && resvport_only) {
+ 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 (sport >= IPPORT_RESERVED && resvport_only) {
+ 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 file handle reply
+ */
+int
+xdr_fhs(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ register struct fhreturn *fhrp = (struct fhreturn *)cp;
+ u_long ok = 0, len, auth;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ switch (fhrp->fhr_vers) {
+ case 1:
+ return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
+ case 3:
+ len = NFSX_V3FH;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
+ return (0);
+ if (fhrp->fhr_flag & DP_KERB)
+ auth = RPCAUTH_KERB4;
+ else
+ auth = RPCAUTH_UNIX;
+ len = 1;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ return (xdr_long(xdrsp, &auth));
+ };
+ return (0);
+}
+
+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 putdef;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ 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;
+ }
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+errout:
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ 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;
+#ifdef __NetBSD__
+ struct msdosfs_args da;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, MOUNT_MFS) ||
+ !strcmp(fsp->f_fstypename, MOUNT_UFS) ||
+ !strcmp(fsp->f_fstypename, MOUNT_MSDOS) ||
+ !strcmp(fsp->f_fstypename, MOUNT_CD9660)) {
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_fstypename, fsp->f_mntonname,
+#else
+ } targs;
+
+ switch (fsp->f_type) {
+ case MOUNT_MFS:
+ case MOUNT_UFS:
+ case MOUNT_CD9660:
+ case MOUNT_MSDOS:
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_type, fsp->f_mntonname,
+#endif
+ 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, tgrp)) {
+ syslog(LOG_ERR, "Bad netgroup %s", cp);
+ getexp_err(ep, tgrp);
+ endnetgrent();
+ goto nextline;
+ }
+ } else if (get_host(cp, grp, tgrp)) {
+ 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 = strdup("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);
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ opt_flags);
+ 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, flags)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int flags;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (flags & OP_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;
+ if (flags & OP_KERB)
+ ep->ex_defdir->dp_flag |= DP_KERB;
+ } else while (grp) {
+ hp = get_ht();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ 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, flags);
+ 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, flags)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+ int flags;
+{
+ 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, flags);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp, flags);
+ 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();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ 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;
+ if (flags & OP_KERB)
+ dp->dp_flag |= DP_KERB;
+ }
+}
+
+/*
+ * 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, hostsetp)
+ struct dirlist *dp;
+ u_long saddr;
+ int *defsetp;
+ int *hostsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ u_long **addrp;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = dp->dp_flag;
+ 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) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ addrp++;
+ }
+ break;
+ case GT_NET:
+ if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+ grp->gr_ptr.gt_net.nt_net) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ 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, hostset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset, &hostset))
+ 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, tgrp)
+ char *cp;
+ struct grouplist *grp;
+ struct grouplist *tgrp;
+{
+ struct grouplist *checkgrp;
+ 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 for %s", cp);
+ 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 for %s", cp);
+ return (1);
+ }
+ }
+ /*
+ * Sanity check: make sure we don't already have an entry
+ * for this host in the grouplist.
+ */
+ checkgrp = tgrp;
+ while (checkgrp) {
+ if (checkgrp->gr_type == GT_HOST &&
+ checkgrp->gr_ptr.gt_hostent != NULL &&
+ !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) {
+ grp->gr_type = GT_IGNORE;
+ return(0);
+ }
+ checkgrp = checkgrp->gr_next;
+ }
+
+ 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;
+ hp->ht_flag = 0;
+ 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;
+#ifdef __NetBSD__
+ struct msdosfs_args da;
+#endif
+ } 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 */
+ case GT_IGNORE:
+ return(0);
+ break;
+ 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.
+ */
+#ifdef __NetBSD__
+ while (mount(fsb->f_fstypename, dirp,
+#else
+ while (mount(fsb->f_type, dirp,
+#endif
+ 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, "Could not remount %s: %m",
+ dirp);
+ 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 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ return (ret);
+}
+
+/*
+ * Just translate an ascii string to an integer.
+ */
+int
+get_num(cp)
+ register char *cp;
+{
+ register int res = 0;
+
+ while (*cp) {
+ if (*cp < '0' || *cp > '9')
+ return (-1);
+ res = res * 10 + (*cp++ - '0');
+ }
+ return (res);
+}
diff --git a/sbin/mountd/netgroup.5 b/sbin/mountd/netgroup.5
new file mode 100644
index 0000000..af7d448
--- /dev/null
+++ b/sbin/mountd/netgroup.5
@@ -0,0 +1,160 @@
+.\" 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 NIS/YP INTERACTION
+On most other platforms,
+.Nm netgroups
+are only used in conjunction with
+NIS and local
+.Pa /etc/netgroup
+files are ignored. With FreeBSD,
+.Nm netgroups
+can be used with either NIS or local files, but there are certain
+caveats to consider. The existing
+.Nm netgroup
+system is extremely inefficient where
+.Fn innetgr 3
+lookups are concerned since
+.Nm netgroup
+memberships are computed on the fly. By contrast, the NIS
+.Nm netgroup
+database consists of three separate maps (netgroup, netgroup.byuser
+and netgroup.byhost) that are keyed to allow
+.Fn innetgr 3
+lookups to be done quickly. The FreeBSD
+.Nm netgroup
+system can interact with the NIS
+.Nm netgroup
+maps in the following ways:
+.Bl -bullet -offset indent
+.It
+If the
+.Pa /etc/netgroup
+file does not exist, or it exists and is empty, or
+it exists and contains only a '+', and NIS is running,
+.Nm netgroup
+lookups will be done exclusively through NIS, with
+.Fn innetgr 3
+taking advantage of the netgroup.byuser and
+netgroup.byhost maps to speed up searches. (This
+is more or less compatible with the behavior of SunOS and
+similar platforms.)
+.It
+If the
+.Pa /etc/netgroup
+exists and contains only local
+.Nm netgroup
+information (with no NIS '+' token), then only the local
+.Nm netgroup
+information will be processed (and NIS will be ignored).
+.It
+If
+.Pa /etc/netgroup
+exists and contains both local netgroup data
+.Pa and
+the NIS '+' token, the local data and the NIS netgroup
+map will be processed as a single combined
+.Nm netgroup
+database. While this configuration is the most flexible, it
+is also the least efficient: in particular,
+.Fn innetgr 3
+lookups will be especially slow if the
+database is large.
+.El
+.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.
+.Pp
+The
+.Nm netgroup
+database should be stored in the form of a
+hashed
+.Xr db 3
+database just like the
+.Xr passwd 5
+database to speed up reverse lookups.
diff --git a/sbin/mountd/pathnames.h b/sbin/mountd/pathnames.h
new file mode 100644
index 0000000..aa1c555
--- /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 0000000..07ceccc
--- /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.8
+
+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 newfs.8 tmpfs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
new file mode 100644
index 0000000..20d6832
--- /dev/null
+++ b/sbin/newfs/mkfs.c
@@ -0,0 +1,1301 @@
+/*
+ * 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>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/ioctl.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 realsectorsize; /* bytes/sector in hardware*/
+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();
+extern char * filename;
+
+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();
+static int charsperline();
+
+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, fd;
+ time_t utime;
+ quad_t sizepb;
+ void started();
+ int width;
+ char tmpbuf[100]; /* XXX this will break in about 2,500 years */
+
+#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(filename) {
+ unsigned char buf[BUFSIZ];
+ unsigned long l,l1;
+ fd = open(filename,O_RDWR|O_TRUNC|O_CREAT,0644);
+ if(fd < 0) {
+ perror(filename);
+ exit(12);
+ }
+ for(l=0;l< fssize * sectorsize;l += l1) {
+ l1 = fssize * sectorsize;
+ if (BUFSIZ < l1)
+ l1 = BUFSIZ;
+ if (l1 != write(fd,buf,l1)) {
+ perror(filename);
+ exit(12);
+ }
+ }
+ membase = mmap(
+ 0,
+ fssize * sectorsize,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ fd,
+ 0);
+ if((int)membase == -1) {
+ perror("mmap");
+ exit(12);
+ }
+ close(fd);
+ } else {
+ 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 - (realsectorsize / DEV_BSIZE), realsectorsize,
+ (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;
+ }
+ /* XXX - hack to prevent overflow of a 32bit block number */
+ sblock.fs_maxfilesize = MIN(sblock.fs_maxfilesize, (u_quad_t) 1 << 39);
+ /*
+ * 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;
+ sblock.fs_clean = 1;
+ /*
+ * 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:\n");
+ i = 0;
+ width = charsperline();
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime);
+ if (mfs)
+ continue;
+ j = sprintf(tmpbuf, " %d,",
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)));
+ if (i+j >= width) {
+ printf("\n");
+ i = 0;
+ }
+ i += j;
+ printf("%s", tmpbuf);
+ fflush(stdout);
+ }
+ 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.tv_sec = utime;
+ node.di_mtime.tv_sec = utime;
+ node.di_ctime.tv_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;
+ }
+}
+
+/*
+ * Determine the number of characters in a
+ * single line.
+ */
+
+static int
+charsperline()
+{
+ int columns;
+ char *cp;
+ struct winsize ws;
+ extern char *getenv();
+
+ columns = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) != -1)
+ columns = ws.ws_col;
+ if (columns == 0 && (cp = getenv("COLUMNS")))
+ columns = atoi(cp);
+ if (columns == 0)
+ columns = 80; /* last resort */
+ return columns;
+}
diff --git a/sbin/newfs/newfs.8 b/sbin/newfs/newfs.8
new file mode 100644
index 0000000..8554a83
--- /dev/null
+++ b/sbin/newfs/newfs.8
@@ -0,0 +1,299 @@
+.\" 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 Ar 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 F Ar file
+.Op Fl T Ar disktype
+.Op Fl a Ar 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.
+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 T Ar disktype
+For backward compatibility and for
+.Nm mount_mfs .
+.It Fl F Ar file
+.Nm mount_mfs
+will use this file for the image of the filesystem. When
+.Nm mount_mfs
+exits, this file will be left behind.
+.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 0 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 n Ar number of distinguished rotational positions
+Determines how many rotational time slots there are in one revolution of
+the disk. Defaults to 1, which essentially disables the rotational position table.
+.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.
+The default is 1.
+If zero is specified, the value from the disklabel will be used.
+.It Fl u Ar sectors/track
+The number of sectors per track available for data allocation by the file
+system.
+The default is 4096.
+If zero is specified, the value from the disklabel will be used.
+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 fdformat 1 ,
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr dumpfs 8 ,
+.Xr disklabel 8 ,
+.Xr diskpart 8 ,
+.Xr fsck 8 ,
+.Xr mount 8 ,
+.Xr scsiformat 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 0000000..44058b1
--- /dev/null
+++ b/sbin/newfs/newfs.c
@@ -0,0 +1,691 @@
+/*
+ * 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 */
+
+/*
+ * Once upon a time...
+ * 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.
+ *
+ * ...but now we make this 0 to disable the rotdelay delay because
+ * modern drives with read/write-behind achieve higher performance
+ * without the delay.
+ */
+#define ROTDELAY 0
+
+/*
+ * 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
+
+/*
+ * Once upon a time...
+ * 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.
+ *
+ * ...but now we make this 1 (which escentially disables the rotational
+ * position table because modern drives with read-ahead and write-behind do
+ * better without the rotational position table.
+ */
+#define NRPOS 1 /* number distinct rotational positions */
+
+/*
+ * About the same time as the above, we knew what went where on the disks.
+ * no longer so, so kill the code which finds the different platters too...
+ * We do this by saying one head, with a lot of sectors on it.
+ * The number of sectors are used to determine the size of a cyl-group.
+ * Kirk suggested one or two meg per "cylinder" so we say two.
+ */
+
+#define NTRACKS 1 /* number of heads */
+
+#define NSECTORS 4096 /* number of sectors */
+
+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 = NTRACKS; /* # tracks/cylinder */
+int nsectors = 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 */
+int realsectorsize; /* bytes/sector in hardware */
+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 */
+int t_or_u_flag = 0; /* user has specified -t or -u */
+u_long memleft; /* virtual memory available */
+caddr_t membase; /* start address of memory based filesystem */
+char *filename;
+#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];
+#ifdef MFS
+ struct vfsconf *vfc;
+#endif
+
+ if (progname = rindex(*argv, '/'))
+ ++progname;
+ else
+ progname = *argv;
+
+ if (strstr(progname, "mfs")) {
+ mfs = 1;
+ Nflag++;
+ }
+
+ opstring = mfs ?
+ "NF:T: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 'F':
+ filename = optarg;
+ break;
+ 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);
+ if (nrpos == 0)
+ nrpos = 1;
+ break;
+ case 'o':
+ if (mfs)
+ getmntopts(optarg, mopts, &mntflags, 0);
+ 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':
+ t_or_u_flag++;
+ if ((ntracks = atoi(optarg)) < 0)
+ fatal("%s: bad total tracks", optarg);
+ break;
+ case 'u':
+ t_or_u_flag++;
+ 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, MAXPHYS / 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;
+ /*
+ * Only complain if -t or -u have been specified; the default
+ * case (4096 sectors per cylinder) is intented to disagree
+ * with the disklabel.
+ */
+ if (t_or_u_flag && 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;
+ }
+#else
+ realsectorsize = sectorsize;
+ if (sectorsize != DEV_BSIZE) { /* XXX */
+ int secperblk = sectorsize / DEV_BSIZE;
+
+ 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;
+#else
+ if (realsectorsize != DEV_BSIZE)
+ pp->p_size /= realsectorsize /DEV_BSIZE;
+#endif
+ 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;
+
+ vfc = getvfsbyname("mfs");
+ if(!vfc && vfsisloadable("mfs")) {
+ if(vfsload("mfs")) {
+ err(1, "vfsload(mfs)");
+ }
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("mfs");
+ }
+
+ if (mount(vfc ? vfc->vfc_index : MOUNT_MFS, argv[1], mntflags,
+ &args) < 0)
+ fatal("%s: %s", argv[1], strerror(errno));
+ if(filename) {
+ munmap(membase,fssize * sectorsize);
+ }
+ }
+#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);
+}
+
+/*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 0000000..2f6c03f
--- /dev/null
+++ b/sbin/newlfs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/18/93
+
+PROG= newlfs
+CFLAGS+=-I${.CURDIR}/../../sys/ufs/lfs
+SRCS= dkcksum.c lfs.c lfs_cksum.c misc.c newfs.c
+MAN8= newlfs.8
+.PATH: ${.CURDIR}/../../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 0000000..55fed69
--- /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 0
+
+/*
+ * 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 1 /* 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 8192
+#define DFL_LFSBLOCK_SHIFT 13
+#define DFL_LFSBLOCK_MASK 0x1FFF
diff --git a/sbin/newlfs/extern.h b/sbin/newlfs/extern.h
new file mode 100644
index 0000000..c65cdba
--- /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 0000000..c22f19f
--- /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_maxsymlinklen */ MAXSYMLINKLEN,
+ /* 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_pad2 */ { 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, bsize, seg_size)
+ int fd;
+ struct disklabel *lp;
+ struct partition *partp;
+ int minfree;
+ int bsize;
+ 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 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 (!(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.tv_sec = dip->di_mtime.tv_sec =
+ dip->di_ctime.tv_sec = lfsp->lfs_tstamp;
+ dip->di_atime.tv_nsec = dip->di_mtime.tv_nsec =
+ dip->di_ctime.tv_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 0000000..074fb71
--- /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 0000000..4f8e72d
--- /dev/null
+++ b/sbin/newlfs/newfs.c
@@ -0,0 +1,483 @@
+/*-
+ * 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 (fsize == 0) {
+ fsize = pp->p_fsize;
+ if (fsize <= 0)
+ fsize = MAX(DFL_FRAGSIZE, lp->d_secsize);
+ }
+
+ if (bsize == 0) {
+ bsize = pp->p_frag * fsize;
+ if (bsize <= 0)
+ bsize = MIN(DFL_LFSBLOCK, 8 * fsize);
+ }
+
+ if (segsize == 0) {
+ segsize = pp->p_cpg * bsize;
+ if (segsize <= 0)
+ segsize = DFL_LFSSEG;
+ }
+
+ /* 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 0000000..cadb04a
--- /dev/null
+++ b/sbin/newlfs/newlfs.8
@@ -0,0 +1,98 @@
+.\" 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 BUGS
+LFS does not currently work in any variant of FreeBSD and should therefore
+not be used.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/sbin/nextboot/Makefile b/sbin/nextboot/Makefile
new file mode 100644
index 0000000..7ac235f
--- /dev/null
+++ b/sbin/nextboot/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.1 (Julian Elischer) 3/28/93
+#
+#
+
+PROG= nextboot
+SRCS= nextboot.c
+MAN8= nextboot.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/nextboot/nextboot.8 b/sbin/nextboot/nextboot.8
new file mode 100644
index 0000000..089debd
--- /dev/null
+++ b/sbin/nextboot/nextboot.8
@@ -0,0 +1,88 @@
+.\" $Id: nextboot.8,v 1.3 1996/09/23 22:23:15 wosch Exp $
+.Dd July 9, 1996
+.Dt NEXTBOOT 8
+.\".Os BSD 4
+.Sh NAME
+.Nm nextboot
+.Nd Install a default bootstring block on the boot disk
+.Sh SYNOPSIS
+.Nm
+.Op Fl b
+.Ar filename bootstring ...
+.Pp
+.Nm
+.Op Fl ed
+.Ar filename
+.Bl -tag -width time
+.It Fl b
+Is used for bootstrapping (initially configuring) the nameblock. Without
+this,
+.Nm
+will refuse to write to a block that does not already contain the magic
+number.
+.It Fl d
+temporarily disables an existing name block by changing a bit
+in the magic number.
+.It Fl e
+restores the good magic number on a block disabled by -d.
+.El
+.Sh PROLOGUE
+The FreeBSD program
+.Nm
+controls the actions of the boot blocks at the time of the next boot.
+If compiled with the correct option,
+the boot blocks will check the nameblock for a magic number and a
+default name to use for booting. If compiled to do so they will also
+delete the name from the block, ensuring that if the boot should fail,
+then it will not be tried again. It is the job of /etc/rc to use
+.Nm
+to re-install the string if that boot is found to have succeeded.
+This allows a one-time only boot string to be used for such applications
+as remote debugging, and installation of new, untrusted kernels.
+The nameblock is defined at compile time to be the second physical block
+on the disk.
+.Pp
+.Sh DESCRIPTION
+.Nm
+first checks that the disk has an fdisk table and checks that none of the
+partitions defined in that table include the nameblock. If the name block is
+shown to be unused, it will install the bootstrings given as arguments,
+one after the other, each preceded by a small magic number, and NULL
+terminated. The end of the list of strings is delimited by a sequence of
+0xff bytes. If the boot blocks are compiled to write back the nameblock
+after each boot, it will zero out the supplied names as it uses them,
+one per boot,
+until it reaches the 0xff, at which time it will revert to the compiled in
+boot string. At this time the nameblock will contain only zeroed out names.
+.Pp
+An example of usage might be:
+.Bd -literal
+ nextboot -b /dev/rwd0 1:sd(0,a)/kernel.experimental wd(0,a)/kernel.old
+.Ed
+.Pp
+Which would instruct the boot blocks at the next boot,
+to try boot the experimental kernel off the scsi disk.
+If for any reason this failed, the next boot attempt would
+boot the kernel
+.Em /kernel.old
+off the IDE drive. (assuming the write-back option were enabled) If this
+in turn failed. the compiled in default would be used.
+.Pp
+If the write-back feature is disabled, the nextboot program is a convenient way
+to change the default boot string. Note, that should the file specified in
+the nameblock be non-existant, then the name compiled into the boot blocks
+will be used for the boot rather than the next name in the nameblock. The
+nameblock is only consulted ONCE per boot.
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8
+.Sh BUGS
+The entire program should be made more user-friendly.
+The option of whether to write back or not should be stored on the
+disk and not a compile time option. I want to rethink this at some
+later stage to make it co-exist with disks that do not have
+a fdisk partitioning table (i.e. purely disklabel'd systems).
+.Pp
+Whether to write back or not should be specified at run-time in the nameblock
+so that the boot blocks need not be altered to get this feature.
diff --git a/sbin/nextboot/nextboot.c b/sbin/nextboot/nextboot.c
new file mode 100644
index 0000000..2398b56
--- /dev/null
+++ b/sbin/nextboot/nextboot.c
@@ -0,0 +1,231 @@
+
+/*
+ * Copyright (c) 1996 Whistle Communications
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * Whistle Communications allows free use of this software in its "as is"
+ * condition. Whistle Communications disclaims any liability of any kind for
+ * any damages whatsoever resulting from the use of this software.
+ */
+
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+struct mboot
+{
+ unsigned char padding[2]; /* force the longs to be long alligned */
+ unsigned char bootinst[DOSPARTOFF];
+ struct dos_partition parts[4];
+ unsigned short int signature;
+};
+struct mboot mboot;
+
+#define NAMEBLOCK 1 /* 2nd block */
+#define BLOCKSIZE 512
+#define ENABLE_MAGIC 0xfadefeed
+#define DISABLE_MAGIC 0xfadefade
+static int bflag;
+static int eflag;
+static int dflag;
+static int nameblock = NAMEBLOCK;
+
+#define BOOT_MAGIC 0xAA55
+
+extern char *__progname;
+
+static void usage(void) {
+ fprintf (stderr, " usage: %s [-b] device bootstring [bootstring] ...\n"
+ , __progname);
+ fprintf (stderr, " or: %s {-e,-d} device \n" , __progname);
+ fprintf (stderr, "The -e and -d flags are mutually exclusive\n");
+ exit(1);
+}
+
+main (int argc, char** argv)
+{
+ int fd = -1;
+ char namebuf[1024], *cp = namebuf;
+ int i,j;
+ int ch;
+ int part;
+
+ bflag = 0;
+ while ((ch = getopt(argc, argv, "bde")) != EOF) {
+ switch(ch) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ( (dflag + eflag + bflag) > 1 ) {
+ usage();
+ }
+ if (dflag + eflag){
+ if(argc != 1 ) {
+ usage();
+ }
+ } else {
+ if (argc <2) {
+ usage();
+ }
+ }
+ if ((fd = open(argv[0], O_RDWR, 0)) < 0) {
+ perror("open");
+ printf ("file: %s\n",argv[0]);
+ usage();
+ }
+
+ argc--;
+ argv++;
+
+ /*******************************************
+ * Check that we have an MBR
+ */
+ if (lseek(fd,0,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if (read (fd,&mboot.bootinst[0],BLOCKSIZE ) != BLOCKSIZE) {
+ perror("read0");
+ exit(1);
+ }
+ if(mboot.signature != (unsigned short)BOOT_MAGIC) {
+ printf(" no fdisk part.. not touching block 1\n");
+ exit(1);
+ }
+
+ /*******************************************
+ * And check that none of the partitions in it cover the name block;
+ */
+ for ( part = 0; part < 4; part++) {
+ if( mboot.parts[part].dp_size
+ && (mboot.parts[part].dp_start <= NAMEBLOCK)
+ && (mboot.parts[part].dp_start
+ + mboot.parts[part].dp_size > NAMEBLOCK)) {
+ printf(" Name sector lies within a Bios partition.\n");
+ printf(" Aborting write.\n");
+ exit(1);
+ }
+ }
+
+
+ /*******************************************
+ * Now check the name sector itself to see if it's been initialised.
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if ( read (fd,namebuf,BLOCKSIZE ) != BLOCKSIZE) {
+ perror("read1");
+ exit(1);
+ }
+ /*******************************************
+ * check if we are just enabling or disabling
+ * Remember the flags are exlusive..
+ */
+ if(!bflag) { /* don't care what's there if bflag is set */
+ switch(*(unsigned long *)cp)
+ {
+ case DISABLE_MAGIC:
+ case ENABLE_MAGIC:
+ break;
+ default:
+ fprintf(stderr,
+ "namesector not initialised.."
+ "use the -b flag..\n");
+ exit(1);
+ }
+ }
+
+
+ /*******************************************
+ * If the z or r flag is set, damage or restore the magic number..
+ * to disable/enable the feature
+ */
+ if(dflag) {
+ *(unsigned long *)cp = DISABLE_MAGIC;
+ } else {
+ *(unsigned long *)cp = ENABLE_MAGIC;
+ }
+ if ((!dflag) && (!eflag)) {
+ /*******************************************
+ * Create a new namesector in ram
+ */
+ cp += 4;
+ for ( i = 0 ; i < argc ; i++ ) {
+ *cp++ = 'D';
+ *cp++ = 'N';
+ j = strlen(argv[i]);
+ strncpy(cp,argv[i],j);
+ cp += j;
+ *cp++ = 0;
+ }
+ *cp++ = 0xff;
+ *cp++ = 0xff;
+ *cp++ = 0xff;
+ namebuf[BLOCKSIZE-1] = 0; /* paranoid */
+ namebuf[BLOCKSIZE] = 0xff;
+ }
+
+ /*******************************************
+ * write it to disk.
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek");
+ exit(1);
+ }
+ if(write (fd,namebuf,BLOCKSIZE ) != BLOCKSIZE) {
+ perror("write");
+ exit(1);
+ }
+
+#if 0
+ /*******************************************
+ * just to be safe/paranoid.. read it back..
+ * and print it..
+ */
+ if (lseek(fd,NAMEBLOCK * BLOCKSIZE,0) == -1) {
+ perror("lseek (second) ");
+ exit(1);
+ }
+ read (fd,namebuf,512);
+ for (i = 0;i< 16;i++) {
+ for ( j = 0; j < 16; j++) {
+ printf("%02x ",(unsigned char )namebuf[(i*16) + j ]);
+ }
+ printf("\n");
+ }
+#endif
+ exit(0);
+}
+
+
diff --git a/sbin/nfsd/Makefile b/sbin/nfsd/Makefile
new file mode 100644
index 0000000..12ce92e
--- /dev/null
+++ b/sbin/nfsd/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+=-DNFS
+MAN8= nfsd.8
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/nfsd/nfsd.8 b/sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..4965876
--- /dev/null
+++ b/sbin/nfsd/nfsd.8
@@ -0,0 +1,134 @@
+.\" 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 and
+.%T "NFS: Network File System Version 3 Protocol Specification" .
+.Pp
+If
+.Nm nfsd
+detects that
+.Tn NFS
+is not loaded in the running kernel, it will attempt
+to load a loadable kernel module containing
+.Tn NFS
+support using
+.Xr modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM is available,
+.Nm nfsd
+will exit with an error.
+.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 modload 8 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsd
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/nfsd/nfsd.c b/sbin/nfsd/nfsd.c
new file mode 100644
index 0000000..6985445
--- /dev/null
+++ b/sbin/nfsd/nfsd.c
@@ -0,0 +1,673 @@
+/*
+ * 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/nfsproto.h>
+#include <nfs/nfs.h>
+
+#ifdef NFSKERB
+#include <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>
+#include <libutil.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s) fprintf(stderr,(s))
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+#ifdef OLD_SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArg = NULL; /* end of argv */
+#endif
+
+#ifdef NFSKERB
+char lnam[ANAME_SZ];
+KTEXT_ST kt;
+AUTH_DAT kauth;
+char inst[INST_SZ];
+struct nfsrpc_fullblock kin, kout;
+struct nfsrpc_fullverf kverf;
+NFSKERBKEY_T kivec;
+struct timeval ktv;
+NFSKERBKEYSCHED_T kerb_keysched;
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void setproctitle __P((char *));
+#endif
+#endif
+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
+ struct timeval ktv;
+ 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;
+#ifdef __FreeBSD__
+ struct vfsconf *vfc;
+
+ vfc = getvfsbyname("nfs");
+ if(!vfc && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("nfs"); /* probably unnecessary */
+ }
+ if(!vfc) {
+ errx(1, "NFS is not available in the running kernel");
+ }
+#endif
+
+#ifdef OLD_SETPROCTITLE
+ /* 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]);
+#endif
+
+#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();
+ };
+ if (!tcpflag && !udpflag)
+ udpflag = 1;
+ 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, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)))
+ err(1, "can't register with portmap for UDP.");
+ if (tcpflag &&
+ (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, 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("server");
+ nfssvc_flag = NFSSVC_NFSD;
+ nsd.nsd_nfsd = NULL;
+#ifdef NFSKERB
+ if (sizeof (struct nfsrpc_fullverf) != RPCX_FULLVERF ||
+ sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK)
+ syslog(LOG_ERR, "Yikes NFSKERB structs not packed!");
+ nsd.nsd_authstr = (u_char *)&kt;
+ nsd.nsd_authlen = sizeof (kt);
+ nsd.nsd_verfstr = (u_char *)&kverf;
+ nsd.nsd_verflen = sizeof (kverf);
+#endif
+ while (nfssvc(nfssvc_flag, &nsd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit(1);
+ }
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
+#ifdef NFSKERB
+ /*
+ * Get the Kerberos ticket out of the authenticator
+ * verify it and convert the principal name to a user
+ * name. The user name is then converted to a set of
+ * user credentials via the password and group file.
+ * Finally, decrypt the timestamp and validate it.
+ * For more info see the IETF Draft "Authentication
+ * in ONC RPC".
+ */
+ kt.length = ntohl(kt.length);
+ if (gettimeofday(&ktv, (struct timezone *)0) == 0 &&
+ kt.length > 0 && kt.length <=
+ (RPCAUTH_MAXSIZ - 3 * NFSX_UNSIGNED)) {
+ kin.w1 = NFS_KERBW1(kt);
+ kt.mbz = 0;
+ (void)strcpy(inst, "*");
+ if (krb_rd_req(&kt, NFS_KERBSRV,
+ inst, nsd.nsd_haddr, &kauth, "") == RD_AP_OK &&
+ krb_kntoln(&kauth, 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();
+
+ /*
+ * Get the timestamp verifier out of the
+ * authenticator and verifier strings.
+ */
+ kin.t1 = kverf.t1;
+ kin.t2 = kverf.t2;
+ kin.w2 = kverf.w2;
+ bzero((caddr_t)kivec, sizeof (kivec));
+ bcopy((caddr_t)kauth.session,
+ (caddr_t)nsd.nsd_key,sizeof(kauth.session));
+
+ /*
+ * Decrypt the timestamp verifier in CBC mode.
+ */
+ XXX
+
+ /*
+ * Validate the timestamp verifier, to
+ * check that the session key is ok.
+ */
+ nsd.nsd_timestamp.tv_sec = ntohl(kout.t1);
+ nsd.nsd_timestamp.tv_usec = ntohl(kout.t2);
+ nsd.nsd_ttl = ntohl(kout.w1);
+ if ((nsd.nsd_ttl - 1) == ntohl(kout.w2))
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
+ }
+#endif /* NFSKERB */
+ }
+ 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, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, 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, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, 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("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, "usage: 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) > 0);
+}
+
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void
+setproctitle(a)
+ char *a;
+{
+ register char *cp;
+ char buf[80];
+
+ cp = Argv[0];
+ (void)snprintf(buf, sizeof(buf), "nfsd-%s", a);
+ (void)strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = '\0';
+ Argv[1] = NULL;
+}
+#endif /* __FreeBSD__ */
+#endif
diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile
new file mode 100644
index 0000000..08d6157
--- /dev/null
+++ b/sbin/nfsiod/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsiod
+CFLAGS+=-DNFS
+MAN8= nfsiod.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/nfsiod/nfsiod.8 b/sbin/nfsiod/nfsiod.8
new file mode 100644
index 0000000..51249e1
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.8
@@ -0,0 +1,93 @@
+.\" 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.
+.\"
+.\" From: @(#)nfsiod.8 8.2 (Berkeley) 2/22/94
+.\" $Id: nfsiod.8,v 1.2 1994/09/22 22:16:59 wollman Exp $
+.\"
+.Dd September 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
+If
+.Nm nfsiod
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM was available,
+.Nm nfsiod
+exits with an error.
+.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 modload 8 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsiod
+utility first appeared in
+.Bx 4.4 .
diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c
new file mode 100644
index 0000000..3100671
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.c
@@ -0,0 +1,185 @@
+/*
+ * 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 <sys/mount.h>
+#include <sys/time.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.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;
+ struct vfsconf *vfc;
+
+ vfc = getvfsbyname("nfs");
+ if(!vfc && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ vfc = getvfsbyname("nfs");
+ }
+
+ if(!vfc) {
+ errx(1, "NFS support is not available in the running kernel");
+ }
+
+#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) > 0) {
+ /* nothing */
+ };
+}
+
+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 0000000..57ac989
--- /dev/null
+++ b/sbin/nologin/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+MAN8= nologin.8
+MAN5= nologin.5
+
+nologin depend lint tags:
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/nologin.sh ${DESTDIR}/sbin/nologin
+
+.include <bsd.prog.mk>
diff --git a/sbin/nologin/nologin.5 b/sbin/nologin/nologin.5
new file mode 100644
index 0000000..75def25
--- /dev/null
+++ b/sbin/nologin/nologin.5
@@ -0,0 +1,62 @@
+.\" 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 5
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+
+.Sh DESCRIPTION
+.Nm Nologin
+disallows logins if the file /etc/nologin exists.
+Programs display the contents of /etc/nologin to the user and exit.
+
+.Sh SECURITY
+Ignored by login(1) for user root.
+
+.Sh FILES
+.Bl -tag -width /etc/nologinxxx -compact
+.It Pa /etc/nologin
+The
+.Nm nologin
+file resides in
+.Pa /etc .
+.El
+
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr rlogin 1 ,
+.Xr telnet 1 ,
+.Xr shutdown 8
diff --git a/sbin/nologin/nologin.8 b/sbin/nologin/nologin.8
new file mode 100644
index 0000000..32a7e73
--- /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 0000000..8ad87fb
--- /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 0000000..a7ed766
--- /dev/null
+++ b/sbin/ping/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= ping
+MAN8= ping.8
+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 0000000..8ec5bc1
--- /dev/null
+++ b/sbin/ping/ping.8
@@ -0,0 +1,346 @@
+.\" 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
+.\" $Id$
+.\"
+.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 dfnqrvRQ
+.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
+Somewhat quiet output.
+Don't display ICMP error messages that are in response to our query messages.
+Originally, the
+.Fl v
+flag was required to display such errors, but
+.Fl v
+displays all ICMP error messages. On a busy machine, this output can
+be overbearing. Without the
+.Fl Q
+flag,
+.Nm
+prints out any ICMP error messages caused by its own ECHO_REQUEST
+messages.
+.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 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.
+.Pp
+The
+.Fl v
+option is not worth much on busy hosts.
+.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 0000000..81ef449
--- /dev/null
+++ b/sbin/ping/ping.c
@@ -0,0 +1,1090 @@
+/*
+ * 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 <signal.h>
+#include <termios.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
+#define F_QUIET2 0x200
+
+/*
+ * 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 */
+
+int reset_kerninfo;
+sig_atomic_t siginfo_p;
+
+char *pr_addr();
+void catcher(), finish(), status(), check_status();
+
+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;
+ struct termios ts;
+ register int i;
+ int ch, fdmask, hold, packlen, preload, sockerrno;
+ u_char *datap, *packet;
+ char *target, hnamebuf[MAXHOSTNAMELEN], *malloc();
+#ifdef IP_OPTIONS
+ char rspace[3 + 4 * NROUTES + 1]; /* record route space */
+#endif
+ struct sigaction si_sa;
+
+ /*
+ * Do the stuff that we need root priv's for *first*, and
+ * then drop our setuid bit. Save error reporting for
+ * after arg parsing.
+ */
+ proto = getprotobyname("icmp");
+ if (proto) {
+ s = socket(AF_INET, SOCK_RAW, proto->p_proto);
+ sockerrno = errno;
+ }
+
+ setuid(getuid());
+
+ preload = 0;
+
+ datap = &outpack[8 + sizeof(struct timeval)];
+ while ((ch = getopt(argc, argv, "QRc: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_QUIET2;
+ 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) {
+ (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
+ exit(1);
+ }
+ if (s < 0) {
+ errno = sockerrno;
+ 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);
+
+ /*
+ * Use sigaction instead of signal() to get unambiguous semantics
+ * for SIGINFO, in particular with SA_RESTART not set.
+ */
+ si_sa.sa_handler = status;
+ sigemptyset(&si_sa.sa_mask);
+ si_sa.sa_flags = 0;
+ if (sigaction(SIGINFO, &si_sa, 0) == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ if (tcgetattr(STDOUT_FILENO, &ts) != -1) {
+ reset_kerninfo = !(ts.c_lflag & NOKERNINFO);
+ ts.c_lflag |= NOKERNINFO;
+ tcsetattr(STDOUT_FILENO, TCSANOW, &ts);
+ }
+
+ 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;
+
+ check_status();
+ 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 host
+ * 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=%.3f 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.
+ * See if it's a reply to something that we sent.
+ * We can compare IP destination, protocol,
+ * and ICMP type and ID.
+ */
+#ifndef icmp_data
+ struct ip *oip = &icp->icmp_ip;
+#else
+ struct ip *oip = (struct ip *)icp->icmp_data;
+#endif
+ struct icmp *oicmp = (struct icmp *)(oip + 1);
+
+ if ((options & F_VERBOSE) ||
+ (!(options & F_QUIET2) &&
+ (oip->ip_dst.s_addr ==
+ ((struct sockaddr_in *)&whereto)->sin_addr.s_addr) &&
+ (oip->ip_p == IPPROTO_ICMP) &&
+ (oicmp->icmp_type == ICMP_ECHO) &&
+ (oicmp->icmp_id == ident))) {
+ (void)printf("%d bytes from %s: ", cc,
+ pr_addr(from->sin_addr.s_addr));
+ pr_icmph(icp);
+ } else
+ return;
+ }
+
+ /* 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;
+}
+
+/*
+ * status --
+ * Print out statistics when SIGINFO is received.
+ */
+
+void
+status(sig)
+ int sig;
+{
+ siginfo_p = 1;
+}
+
+void
+check_status()
+{
+ if (siginfo_p) {
+ siginfo_p = 0;
+ (void)fprintf(stderr,
+ "\r%ld/%ld packets received (%.0f%%) %.3f min / %.3f avg / %.3f max\n",
+ nreceived, ntransmitted,
+ ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0,
+ nreceived ? tmin : 0.0,
+ nreceived + nrepeats ? tsum / (nreceived + nrepeats) : tsum,
+ tmax);
+ }
+}
+
+/*
+ * finish --
+ * Print out statistics, and give up.
+ */
+void
+finish()
+{
+ struct termios ts;
+
+ (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)
+ (void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
+ tmin, tsum / (nreceived + nrepeats), tmax);
+ if (reset_kerninfo && tcgetattr(STDOUT_FILENO, &ts) != -1) {
+ ts.c_lflag &= ~NOKERNINFO;
+ tcsetattr(STDOUT_FILENO, TCSANOW, &ts);
+ }
+
+ if (nreceived)
+ exit(0);
+ else
+ exit(2);
+}
+
+#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 (MTU %d)\n",
+ icp->icmp_nextmtu);
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ (void)printf("Source Route Failed\n");
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ (void)printf("Communication prohibited by filter\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;
+ case ICMP_MASKREQ:
+ (void)printf("Address Mask Request\n");
+ break;
+ case ICMP_MASKREPLY:
+ (void)printf("Address Mask Reply\n");
+ break;
+ case ICMP_ROUTERADVERT:
+ (void)printf("Router Advertisement\n");
+ break;
+ case ICMP_ROUTERSOLICIT:
+ (void)printf("Router Solicitation\n");
+ break;
+ 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\n");
+ (void)printf(" %1x %1x %02x %04x %04x",
+ ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len),
+ ntohs(ip->ip_id));
+ (void)printf(" %1x %04x", (ntohl(ip->ip_off) & 0xe000) >> 13,
+ ntohl(ip->ip_off) & 0x1fff);
+ (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p,
+ ntohs(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 any 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)snprintf(buf, sizeof(buf), "%s",
+ inet_ntoa(*(struct in_addr *)&l));
+ else
+ (void)snprintf(buf, sizeof(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 0000000..7f929c8
--- /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.8
+.PATH: ${.CURDIR}/../fsck
+
+.include <bsd.prog.mk>
diff --git a/sbin/quotacheck/preen.c b/sbin/quotacheck/preen.c
new file mode 100644
index 0000000..b560861
--- /dev/null
+++ b/sbin/quotacheck/preen.c
@@ -0,0 +1,383 @@
+/*
+ * 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 const 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 <ufs/ufs/dinode.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fstab.h>
+#include "fsck.h"
+
+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;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static int startdisk __P((struct disk *dk, int (*checkit)()));
+static struct disk *finddisk __P((char *name));
+static char *unrawname __P((char *name));
+static char *rawname __P((char *name));
+
+int nrun, ndisks;
+char hotroot;
+
+int
+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 || (passno == 1 && fsp->fs_passno == 1)) {
+ name = blockcheck(fsp->fs_spec);
+ if (name) {
+ sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0);
+ if (sumstatus)
+ 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)) != 0 &&
+ 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)) != 0
+ && 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)) != 0
+ && 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 = 0;
+
+ 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);
+}
+
+void
+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;
+}
+
+int
+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;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ 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;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(name) - 1;
+ if (l > 0 && name[l] == '/')
+ /* remove trailing slash */
+ name[l] = '\0';
+ if(!(fsinfo=getfsfile(name))) {
+ printf("Can't resolve %s to character special device",
+ name);
+ return (0);
+ }
+ name = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ printf("Warning: Can't find blockdevice corresponding to name %s\n",
+ name);
+ return (name);
+}
+
+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 0000000..b9bbdad
--- /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 and other additional diagnostic messages.
+.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 0000000..4ca71c5
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.c
@@ -0,0 +1,650 @@
+/*
+ * 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/queue.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()) != NULL)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag) {
+ setpwent();
+ while ((pw = getpwent()) != NULL)
+ (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();
+ dev_bsize = 1;
+ 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;
+ register off_t offset;
+ 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, offset = 0; id <= lastid;
+ id++, offset += sizeof(struct dqblk)) {
+ 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;
+ continue;
+ }
+ if (vflag) {
+ if (aflag)
+ printf("%s: ", fsname);
+ printf("%-8s fixed:", fup->fu_name);
+ if (dqbuf.dqb_curinodes != fup->fu_curinodes)
+ (void)printf("\tinodes %ld -> %ld",
+ dqbuf.dqb_curinodes, fup->fu_curinodes);
+ if (dqbuf.dqb_curblocks != fup->fu_curblocks)
+ (void)printf("\tblocks %ld -> %ld",
+ 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;
+ if (fseek(qfo, (long)offset, SEEK_SET) < 0) {
+ (void) fprintf(stderr,
+ "quotacheck: %s: seek failed: %s\n",
+ quotafile, strerror(errno));
+ return(1);
+ }
+ 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)) != NULL)
+ 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, '=')) != NULL)
+ *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)) != NULL)
+ 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, "%lu", id);
+ if (vflag)
+ printf("unknown %cid: %lu\n",
+ type == USRQUOTA ? 'u' : 'g', 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 0000000..a3ebbcc
--- /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.8 boot_i386.8
+MLINKS= reboot.8 halt.8 reboot.8 fastboot.8 reboot.8 fasthalt.8
+
+ARCH!= uname -m
+
+.if exists (${.CURDIR}/boot_${ARCH}.8)
+MLINKS+= boot_${ARCH}.8 boot.8
+.endif
+
+LINKS= ${BINDIR}/reboot ${BINDIR}/halt ${BINDIR}/reboot ${BINDIR}/fastboot \
+ ${BINDIR}/reboot ${BINDIR}/fasthalt
+
+.include <bsd.prog.mk>
diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8
new file mode 100644
index 0000000..0e89b94
--- /dev/null
+++ b/sbin/reboot/boot_i386.8
@@ -0,0 +1,169 @@
+.\" 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.
+.\"
+.\" Almost completely rewritten for FreeBSD 2.1 by Joerg Wunsch.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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
+.\"
+.\" $Id: boot_i386.8,v 1.3 1995/10/07 12:05:00 joerg Exp $
+.\"
+.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.
+Most 386
+.Tn "PC AT"
+clones attempt to boot the floppy disk drive 0 (otherwise known as
+drive A:) first, and failing that, attempt to boot the hard disk
+controller 1, drive 0 (otherwise known as drive C:, or drive 0x80 in
+the BIOS). The automatic boot will attempt to load
+.Pa /kernel
+from partition
+.Ql a
+of either the floppy or the hard disk.
+This boot may be aborted by typing any character on the keyboard
+at the
+.Ql Boot:
+prompt. At this time, the following input will be accepted:
+.Bl -tag -offset indent -width 10x
+.It \&?
+Give a short listing of the files in the root directory of the default
+boot device, as a hint about available boot files.
+.It Op ctrlr(unit,part) Op /filename Op Fl abcCdhrsv
+Specify boot file and flags.
+.Bl -tag -offset indent -width 10x -compact
+.It ctrlr
+The controller to boot from. Note that the controller is required
+to have BIOS support since the BIOS services are used to load the
+boot file image.
+.Pp
+Common controller names are:
+.Bl -tag -offset indent -width "wdXX" -compact
+.It wd
+ST506, IDE, ESDI, RLL disks on a WD100[2367] or lookalike
+controller
+.It fd
+5 1/4" or 3 1/2" High density floppies
+.It sd
+SCSI disk on any supported SCSI controller
+.It cd
+boot from CDROM
+.It hd
+Pseudo-controller, must be used to specify that unit number
+1 (known to the BIOS as drive 0x81) is on a different controller
+than unit number 0. This can happen for the wd vs. sd case.
+.El
+.It unit
+The unit number of the drive on the controller being used. Either 0
+or 1 for the wd and fd and most sd controllers, between 0 and 6 for
+some newer sd controllers.
+.It part
+The partition letter inside the BSD portion of the disk. See
+.Xr disklabel 8 .
+By convention, only partition
+.Ql a
+contains a bootable image. If sliced disks are used
+.Pq Dq fdisk partitions ,
+only the first BSD slice can be used to boot from. The partition
+letter does always refer to this slice then.
+.It /filename
+The pathname of the file to boot; must be inside the root directory
+of the specified partition. Defaults to
+.Pa /kernel .
+Symbolic links are not supported (hard links are).
+.It Fl abcCdhrsv
+Boot flags:
+.Bl -tag -offset indent -width "-CXX" -compact
+.It Fl a
+ask for the device to install as root file system during kernel
+initialization
+.It Fl b
+do not automatically reboot after shutdown or crash
+.It Fl c
+run UserConfig to modify hardware parameters for the loaded
+kernel
+.It Fl C
+boot from CDROM
+.It Fl d
+enter the DDB kernel debugger
+.Pq see Xr ddb 4
+before configuring any device (except the system's console)
+.It Fl h
+toggle serial/graphics console
+.It Fl r
+do not establish the root directory of the file system
+hierarchy on the device where the boot file is being loaded
+from
+.It Fl s
+boot into single-user mode; if the console is marked as
+.Dq insecure
+.Pq see Xr ttys 5 ,
+the root password must be entered
+.It Fl v
+be verbose during device probing
+.El
+.El
+.El
+.Sh FILES
+.Bl -tag -width /kernelxx -compact
+.It Pa /kernel
+system code
+.\" .It Pa /boot
+.\" system bootstrap
+.El
+.Sh SEE ALSO
+.Xr ddb 4 ,
+.Xr ttys 5 ,
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8 ,
+.Xr disklabel 8 .
+.Sh BUGS
+The disklabel format used by this version of
+.Bx
+is quite
+different from that of other architectures.
+.Pp
+The boot flags are not very self-explanatory, and the alphabet has
+too few characters to implement every potentially useful boot option.
diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8
new file mode 100644
index 0000000..6252093
--- /dev/null
+++ b/sbin/reboot/reboot.8
@@ -0,0 +1,110 @@
+.\" 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 nqp
+.Nm reboot
+.Op Fl nqp
+.Nm fasthalt
+.Op Fl nqp
+.Nm fastboot
+.Op Fl nqp
+.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.
+.It Fl p
+If the
+.Fl p
+option is specified, then the system will turn off the power
+if it can. This is of course likely to make
+.Nm reboot
+rather similar to
+.Nm halt.
+.El
+.Pp
+The
+.Nm fasthalt
+and
+.Nm fastboot
+utilities are nothing more than aliases for the
+.Nm halt
+and
+.Nm reboot
+utilities.
+.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 utmp 5 ,
+.Xr boot 8 ,
+.Xr shutdown 8 ,
+.Xr sync 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 0000000..66e5398
--- /dev/null
+++ b/sbin/reboot/reboot.c
@@ -0,0 +1,208 @@
+/*
+ * 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, pflag, sverrno;
+ char *p, *user;
+
+ if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) {
+ dohalt = 1;
+ howto = RB_HALT;
+ } else
+ howto = 0;
+ lflag = nflag = qflag = 0;
+ while ((ch = getopt(argc, argv, "lnpq")) != EOF)
+ switch(ch) {
+ case 'l': /* Undocumented; used by shutdown. */
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ howto |= RB_NOSYNC;
+ break;
+ case 'p':
+ pflag = 1;
+ howto |= (RB_POWEROFF | RB_HALT);
+ 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 0000000..5d49d00
--- /dev/null
+++ b/sbin/restore/Makefile
@@ -0,0 +1,15 @@
+# @(#)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.8
+MLINKS+=restore.8 rrestore.8
+.PATH: ${.CURDIR}/../dump
+
+.include <bsd.prog.mk>
diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c
new file mode 100644
index 0000000..b092eab
--- /dev/null
+++ b/sbin/restore/dirs.c
@@ -0,0 +1,751 @@
+/*
+ * 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 <machine/endian.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 && (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 (Bcvt)
+ swabst((u_char *)"ls", (u_char *) dp);
+ if (oldinofmt && dp->d_ino != 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ if (Bcvt)
+#else
+ if (!Bcvt)
+#endif
+ dp->d_namlen = dp->d_type;
+ dp->d_type = DT_UNKNOWN;
+ }
+ 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.tv_sec;
+ node.timep[0].tv_usec = dip->di_atime.tv_nsec / 1000;
+ node.timep[1].tv_sec = dip->di_mtime.tv_sec;
+ node.timep[1].tv_usec = dip->di_mtime.tv_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
+ */
+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 0000000..0f64402
--- /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 *));
+void done __P((int)) __dead2;
+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 0000000..1b9616c
--- /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 0000000..936116e
--- /dev/null
+++ b/sbin/restore/main.c
@@ -0,0 +1,347 @@
+/*
+ * 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, argc -= 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch(*ap) {
+ case 'b':
+ case 'f':
+ case 's':
+ if (argc < 1)
+ usage();
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ 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 0000000..f86f349
--- /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/rst0"
diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8
new file mode 100644
index 0000000..844bd8d
--- /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/rst0 .
+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/rst0
+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 ft 8 ,
+.Xr newfs 8 ,
+.Xr mount 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 dump must be done to get a new set of directories
+reflecting the new inode numbering,
+even though the contents of the files is unchanged.
+.Sh HISTORY
+The
+.Nm restore
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c
new file mode 100644
index 0000000..97bd6cc
--- /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 0000000..2202ccd
--- /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 0000000..9488a7f
--- /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 0000000..fcf41e8
--- /dev/null
+++ b/sbin/restore/tape.c
@@ -0,0 +1,1361 @@
+/*
+ * 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.tv_sec;
+ timep[0].tv_usec = curfile.dip->di_atime.tv_nsec / 1000;
+ timep[1].tv_sec = curfile.dip->di_mtime.tv_sec;
+ timep[1].tv_usec = curfile.dip->di_mtime.tv_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 IFIFO:
+ if (mkfifo(name, mode) < 0) {
+ fprintf(stderr, "%s: cannot create FIFO: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) chmod(name, mode);
+ skipfile();
+ utimes(name, timep);
+ return (GOOD);
+
+ 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.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
+ buf->c_dinode.di_mtime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+ buf->c_dinode.di_ctime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+ 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 0000000..b2e442e
--- /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 0000000..c75d0b2
--- /dev/null
+++ b/sbin/route/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= route
+MAN8= route.8
+SRCS= route.c keywords.h # ccitt_addr.h
+CFLAGS+=-I. -Wall -DNS
+CLEANFILES+=keywords.h
+BINOWN= root
+BINMODE=4555
+
+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
+
+./keywords.h: keywords.h
+
+.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 0000000..868f6fc
--- /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 0000000..07a0ddc
--- /dev/null
+++ b/sbin/route/keywords
@@ -0,0 +1,46 @@
+# @(#)keywords 8.2 (Berkeley) 3/19/94
+
+add
+atalk
+blackhole
+change
+cloning
+delete
+dst
+expire
+flush
+gateway
+genmask
+get
+host
+hopcount
+iface
+interface
+ifa
+ifp
+inet
+iso
+link
+llinfo
+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 0000000..f302102
--- /dev/null
+++ b/sbin/route/route.8
@@ -0,0 +1,332 @@
+.\" 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 ,
+.Fl atalk ,
+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.
+Alternately, if the interface is point to point the name of the interface
+itself may be given, in which case the route remains valid even
+if the local or remote addresses change.
+.Pp
+The optional modifiers
+.Fl xns ,
+.Fl osi ,
+.Fl atalk ,
+and
+.Fl link
+specify that all subsequent addresses are in the
+.Tn XNS ,
+.Tn OSI ,
+or
+.Tn AppleTalk
+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 IPXrouted
+.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 0000000..339d841
--- /dev/null
+++ b/sbin/route/route.c
@@ -0,0 +1,1580 @@
+/*
+ *
+ * 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 const 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";
+*/
+static const char rcsid[] =
+ "$Id: route.c,v 1.17 1996/11/01 20:30:37 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#ifdef NS
+#include <netns/ns.h>
+#endif
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#ifdef CCITT
+#include <netccitt/x25.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.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_at sat;
+#ifdef NS
+ struct sockaddr_ns sns;
+#endif
+#ifdef ISO
+ struct sockaddr_iso siso;
+#endif
+ struct sockaddr_dl sdl;
+#ifdef CCITT
+ struct sockaddr_x25 sx25;
+#endif
+} 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();
+int atalk_aton __P((const char *, struct at_addr *));
+char *atalk_ntoa __P((struct at_addr));
+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();
+#ifdef CCITT
+extern int ccitt_addr __P((char *, struct sockaddr_x25 *));
+#endif
+
+void usage __P((const char *)) __dead2;
+
+void
+usage(cp)
+ const char *cp;
+{
+ if (cp)
+ warnx("bad keyword: %s", cp);
+ (void) fprintf(stderr,
+ "usage: route [ -nqv ] cmd [[ -<qualifers> ] args ]\n");
+ exit(EX_USAGE);
+ /* 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)
+ err(EX_OSERR, "socket");
+ setuid(uid);
+ 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) {
+ errx(EX_NOPERM, "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_ATALK:
+ af = AF_APPLETALK;
+ break;
+#ifdef NS
+ case K_XNS:
+ af = AF_NS;
+ break;
+#endif
+ case K_LINK:
+ af = AF_LINK;
+ break;
+#ifdef ISO
+ case K_ISO:
+ case K_OSI:
+ af = AF_ISO;
+ break;
+#endif
+#ifdef CCITT
+ case K_X25:
+ af = AF_CCITT;
+#endif
+ 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)
+ err(EX_OSERR, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ err(EX_OSERR, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ err(EX_OSERR, "route-sysctl-get");
+ 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) {
+ warn("write to routing socket");
+ (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[MAXHOSTNAMELEN + 1];
+ struct hostent *hp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+#ifdef NS
+ char *ns_print();
+#endif
+
+ if (first) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.'))) {
+ domain[MAXHOSTNAMELEN] = '\0';
+ (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)
+ strncpy(line, cp, sizeof line);
+ else {
+ /* XXX - why not inet_ntoa()? */
+#define C(x) (unsigned)((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_APPLETALK:
+ (void) snprintf(line, sizeof(line), "atalk %s",
+ atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
+ break;
+
+#ifdef NS
+ case AF_NS:
+ return (ns_print((struct sockaddr_ns *)sa));
+#endif
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+#ifdef ISO
+ case AF_ISO:
+ (void) snprintf(line, sizeof line, "iso %s",
+ iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr));
+ break;
+#endif
+ 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);
+ char *cpe = line + sizeof(line);
+
+ while (++s < slim && cp < cpe) /* start with sa->sa_data */
+ cp += snprintf(cp, cpe - 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[MAXHOSTNAMELEN + 1];
+ struct netent *np = 0;
+ u_long net, mask;
+ register u_long i;
+ int subnetshift;
+#ifdef NS
+ char *ns_print();
+#endif
+
+ 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)
+ strncpy(line, cp, sizeof(line));
+ 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_APPLETALK:
+ (void) snprintf(line, sizeof(line), "atalk %s",
+ atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
+ break;
+
+#ifdef NS
+ case AF_NS:
+ return (ns_print((struct sockaddr_ns *)sa));
+ break;
+#endif
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+#ifdef ISO
+ case AF_ISO:
+ (void) snprintf(line, sizeof(line), "iso %s",
+ iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr));
+ break;
+#endif
+
+ 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);
+ char *cpe = line + sizeof(line);
+
+ while (s < slim && cp < cpe)
+ cp += snprintf(cp, cpe - 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) {
+ errx(EX_NOPERM, "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;
+#ifdef ISO
+ case K_OSI:
+ case K_ISO:
+ af = AF_ISO;
+ aflen = sizeof(struct sockaddr_iso);
+ break;
+#endif
+ case K_INET:
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ break;
+ case K_ATALK:
+ af = AF_APPLETALK;
+ aflen = sizeof(struct sockaddr_at);
+ break;
+#ifdef CCITT
+ case K_X25:
+ af = AF_CCITT;
+ aflen = sizeof(struct sockaddr_x25);
+ break;
+#endif
+ case K_SA:
+ af = PF_ROUTE;
+ aflen = sizeof(union sockunion);
+ break;
+#ifdef NS
+ case K_XNS:
+ af = AF_NS;
+ aflen = sizeof(struct sockaddr_ns);
+ break;
+#endif
+ case K_IFACE:
+ case K_INTERFACE:
+ iflag++;
+ break;
+ case K_NOSTATIC:
+ flags &= ~RTF_STATIC;
+ break;
+ case K_LLINFO:
+ flags |= RTF_LLINFO;
+ 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 {
+#ifdef CRUFT
+ 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;
+ }
+#endif
+ (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;
+#ifdef NS
+ struct ns_addr ns_addr();
+#endif
+#ifdef ISO
+ struct iso_addr *iso_addr();
+#endif
+ 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;
+ if (iflag) {
+ #define MAX_IFACES 400
+ int sock;
+ struct ifreq iflist[MAX_IFACES];
+ struct ifconf ifconf;
+ struct ifreq *ifr, *ifr_end;
+ struct sockaddr_dl *dl, *sdl = NULL;
+
+ /* Get socket */
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ /* Get interface list */
+ ifconf.ifc_req = iflist;
+ ifconf.ifc_len = sizeof(iflist);
+ if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0)
+ err(1, "ioctl(SIOCGIFCONF)");
+ close(sock);
+
+ /* Look for this interface in the list */
+ for (ifr = ifconf.ifc_req,
+ ifr_end = (struct ifreq *)
+ (ifconf.ifc_buf + ifconf.ifc_len);
+ ifr < ifr_end;
+ ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
+ + ifr->ifr_addr.sa_len)) {
+ dl = (struct sockaddr_dl *)&ifr->ifr_addr;
+ if (ifr->ifr_addr.sa_family == AF_LINK
+ && (ifr->ifr_flags & IFF_POINTOPOINT)
+ && !strncmp(s, dl->sdl_data, dl->sdl_nlen)
+ && s[dl->sdl_nlen] == 0) {
+ sdl = dl;
+ break;
+ }
+ }
+
+ /* If we found it, then use it */
+ if (sdl) {
+ su->sdl = *sdl;
+ return(1);
+ }
+ }
+ 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) {
+#ifdef NS
+ 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));
+#endif
+
+#ifdef ISO
+ 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);
+#endif
+
+ case AF_APPLETALK:
+ if (!atalk_aton(s, &su->sat.sat_addr))
+ errx(EX_NOHOST, "bad address: %s", s);
+ rtm_addrs |= RTA_NETMASK;
+ return(forcehost || su->sat.sat_addr.s_node != 0);
+
+ case AF_LINK:
+ link_addr(s, &su->sdl);
+ return (1);
+
+#ifdef ISO
+ case AF_CCITT:
+ ccitt_addr(s, &su->sx25);
+ return (which == RTA_DST ? x25_makemask() : 1);
+#endif
+
+ 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)) != INADDR_NONE) &&
+ (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)) != INADDR_NONE ||
+ (forcehost == 0 && (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);
+ }
+ errx(EX_NOHOST, "bad address: %s", s);
+}
+
+#ifdef CCITT
+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;
+}
+#endif
+
+#ifdef NS
+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,"%lxH.%s%s", (unsigned long)ntohl(net.long_e),
+ host, cport);
+ return (mybuf);
+}
+#endif
+
+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)
+ err(EX_OSERR, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ err(EX_OSERR, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ err(EX_OSERR, "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)
+ warn("read from routing socket");
+ 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) {
+#ifdef NS
+ case AF_NS:
+#endif
+ case AF_INET:
+ case AF_APPLETALK:
+#ifdef CCITT
+ case AF_CCITT:
+#endif
+ case 0:
+ return;
+#ifdef ISO
+ case AF_ISO:
+ olen = MIN(so_dst.siso.siso_nlen,
+ MAX(so_mask.sa.sa_len - 6, 0));
+ break;
+#endif
+ }
+ 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;
+#ifdef ISO
+ switch (so_dst.sa.sa_family) {
+ case AF_ISO:
+ so_dst.siso.siso_nlen = olen;
+ break;
+ }
+#endif
+}
+
+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\015BLACKHOLE\016b016"
+"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
+"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
+char ifnetflags[] =
+"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\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: %ld, seq %d, errno %d, flags:",
+ (long)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) {
+ warnx("routing message version %d not understood",
+ rtm->rtm_version);
+ return;
+ }
+ if (rtm->rtm_msglen > msglen) {
+ warnx("message length mismatch, in packet %d, returned %d\n",
+ rtm->rtm_msglen, msglen);
+ }
+ if (rtm->rtm_errno) {
+ errno = rtm->rtm_errno;
+ warn("message indicates error %d", 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("%8ld%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
+ printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
+ printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
+ printf("%8ld%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
+ if (rtm->rtm_rmx.rmx_expire)
+ rtm->rtm_rmx.rmx_expire -= time(0);
+ printf("%8ld%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++) != 0) {
+ 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;
+#ifdef ISO
+ case AF_ISO:
+ (void) printf("%s: iso %s; ",
+ which, iso_ntoa(&su->siso.siso_addr));
+ break;
+#endif
+ case AF_INET:
+ (void) printf("%s: inet %s; ",
+ which, inet_ntoa(su->sin.sin_addr));
+ break;
+ case AF_APPLETALK:
+ (void) printf("%s: atalk %s; ",
+ which, atalk_ntoa(su->sat.sat_addr));
+ break;
+#ifdef NS
+ case AF_NS:
+ (void) printf("%s: xns %s; ",
+ which, ns_ntoa(su->sns.sns_addr));
+ break;
+#endif
+ }
+ (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 = 0 /* foil gcc */;
+
+ 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;
+}
+
+int
+atalk_aton(const char *text, struct at_addr *addr)
+{
+ u_int net, node;
+
+ if (sscanf(text, "%u.%u", &net, &node) != 2
+ || net > 0xffff || node > 0xff)
+ return(0);
+ addr->s_net = htons(net);
+ addr->s_node = node;
+ return(1);
+}
+
+char *
+atalk_ntoa(struct at_addr at)
+{
+ static char buf[20];
+
+ (void) snprintf(buf, sizeof(buf), "%u.%u", ntohs(at.s_net), at.s_node);
+ return(buf);
+}
diff --git a/sbin/routed/Makefile b/sbin/routed/Makefile
new file mode 100644
index 0000000..4d6ea8f
--- /dev/null
+++ b/sbin/routed/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/19/93
+# $Id: Makefile,v 1.2 1996/09/16 17:03:27 wollman Exp $
+
+PROG= routed
+SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c \
+ trace.c
+MAN8= routed.8
+SUBDIR= rtquery
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+#COPTS= -g -DDEBUG -Wall
+
+.include <bsd.prog.mk>
diff --git a/sbin/routed/Makefile.inc b/sbin/routed/Makefile.inc
new file mode 100644
index 0000000..10fa13f
--- /dev/null
+++ b/sbin/routed/Makefile.inc
@@ -0,0 +1 @@
+.include "../../Makefile.inc"
diff --git a/sbin/routed/defs.h b/sbin/routed/defs.h
index 3e52ab7..d87029d 100644
--- a/sbin/routed/defs.h
+++ b/sbin/routed/defs.h
@@ -31,14 +31,9 @@
* SUCH DAMAGE.
*
* @(#)defs.h 8.1 (Berkeley) 6/5/93
- *
- * $NetBSD$
+ * $Id: defs.h,v 1.3 1996/11/19 20:42:11 wollman Exp $
*/
-#ifndef __NetBSD__
-#ident "$Revision: 1.19 $"
-#endif
-
/* Definitions for RIPv2 routing process.
*
* This code is based on the 4.4BSD `routed` daemon, with extensions to
@@ -82,6 +77,7 @@
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
+#include <sys/time.h>
#ifdef sgi
#include <net/radix.h>
#else
@@ -123,8 +119,7 @@
*/
/* #define MCAST_PPP_BUG */
-#define DAY (24*60*60)
-#define NEVER DAY /* a long time */
+#define NEVER (24*60*60) /* a long time */
#define EPOCH NEVER /* bias time by this to avoid <0 */
/* Scan the kernel regularly to see if any interfaces have appeared or been
@@ -293,13 +288,15 @@ struct interface {
#endif
time_t ts; /* timestamp on network stats */
} int_data;
-# define MAX_AUTH_KEYS 5
struct auth { /* authentication info */
u_char type;
- u_char key[RIP_AUTH_PW_LEN];
- u_char keyid;
- time_t start, end;
- } int_auth[MAX_AUTH_KEYS];
+# define MAX_AUTH_KEYS 3
+ struct auth_key {
+ u_char key[RIP_AUTH_PW_LEN];
+ u_char keyid;
+ time_t start, end;
+ } keys[MAX_AUTH_KEYS];
+ } int_auth;
int int_rdisc_pref; /* advertised rdisc preference */
int int_rdisc_int; /* MaxAdvertiseInterval */
int int_rdisc_cnt;
@@ -316,10 +313,10 @@ struct interface {
#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */
#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */
#define IS_DISTRUST 0x0000100 /* ignore untrusted routers */
-#define IS_REDIRECT_OK 0x0000200 /* accept ICMP redirects */
-#define IS_BROKE 0x0000400 /* seems to be broken */
-#define IS_SICK 0x0000800 /* seems to be broken */
-#define IS_DUP 0x0001000 /* has a duplicate address */
+#define IS_BROKE 0x0000200 /* seems to be broken */
+#define IS_SICK 0x0000400 /* seems to be broken */
+#define IS_DUP 0x0000800 /* has a duplicate address */
+/* 0x0001000 spare */
#define IS_NEED_NET_SYN 0x0002000 /* need RS_NET_SYN route */
#define IS_NO_AG 0x0004000 /* do not aggregate subnets */
#define IS_NO_SUPER_AG 0x0008000 /* do not aggregate networks */
@@ -398,7 +395,7 @@ extern struct parm {
u_int parm_int_state;
int parm_rdisc_pref;
int parm_rdisc_int;
- struct auth parm_auth[MAX_AUTH_KEYS];
+ struct auth parm_auth;
} *parms;
/* authority for internal networks */
@@ -474,14 +471,13 @@ extern int need_flash; /* flash update needed */
extern struct timeval need_kern; /* need to update kernel table */
extern int update_seqno; /* a route has changed */
-extern int tracelevel, new_tracelevel;
+extern u_int tracelevel, new_tracelevel;
#define MAX_TRACELEVEL 4
#define TRACEKERNEL (tracelevel >= 4) /* log kernel changes */
#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */
#define TRACEPACKETS (tracelevel >= 2) /* note packets */
#define TRACEACTIONS (tracelevel != 0)
extern FILE *ftrace; /* output trace file */
-extern char inittracename[MAXPATHLEN+1];
extern struct radix_node_head *rhead;
@@ -499,7 +495,7 @@ extern void rip_on(struct interface *);
extern void bufinit(void);
extern int output(enum output_type, struct sockaddr_in *,
struct interface *, struct rip *, int);
-extern void clr_ws_buf(struct ws_buf *, struct auth *);
+extern void clr_ws_buf(struct ws_buf *, struct auth_key *, struct interface *);
extern void rip_query(void);
extern void rip_bcast(int);
extern void supply(struct sockaddr_in *, struct interface *,
@@ -507,12 +503,8 @@ extern void supply(struct sockaddr_in *, struct interface *,
extern void msglog(char *, ...);
struct msg_limit {
- time_t reuse;
- struct msg_sub {
naddr addr;
time_t until;
-# define MSG_SUBJECT_N 8
- } subs[MSG_SUBJECT_N];
};
extern void msglim(struct msg_limit *, naddr, char *, ...);
#define LOGERR(msg) msglog(msg ": %s", strerror(errno))
@@ -532,16 +524,15 @@ extern void intvl_random(struct timeval *, u_long, u_long);
extern int getnet(char *, naddr *, naddr *);
extern int gethost(char *, naddr *);
extern void gwkludge(void);
-extern char *parse_parms(char *, int);
+extern char *parse_parms(char *);
extern char *check_parms(struct parm *);
extern void get_parms(struct interface *);
extern void lastlog(void);
-extern void set_tracefile(char *, char *, int);
-extern void tracelevel_msg(char *, int);
+extern void trace_on(char *, int);
extern void trace_off(char*, ...);
-extern void set_tracelevel(void);
extern void trace_flush(void);
+extern void set_tracelevel(int);
extern void trace_kernel(char *, ...);
extern void trace_act(char *, ...);
extern void trace_pkt(char *, ...);
@@ -624,15 +615,7 @@ extern struct interface *ifwithname(char *, naddr);
extern struct interface *ifwithindex(u_short);
extern struct interface *iflookup(naddr);
-extern struct auth *find_auth(struct interface *);
-extern void end_md5_auth(struct ws_buf *, struct auth *);
-
-#define MD5_DIGEST_LEN 16
-typedef struct {
- u_int32_t state[4]; /* state (ABCD) */
- u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
- unsigned char buffer[64]; /* input buffer */
-} MD5_CTX;
-extern void MD5Init(MD5_CTX*);
-extern void MD5Update(MD5_CTX*, u_char*, u_int);
-extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
+extern struct auth_key *find_auth(struct interface *);
+extern void end_md5_auth(struct ws_buf *, struct auth_key *);
+
+#include <md5.h>
diff --git a/sbin/routed/main.c b/sbin/routed/main.c
index 58cce95..dc0b1d8 100644
--- a/sbin/routed/main.c
+++ b/sbin/routed/main.c
@@ -39,7 +39,7 @@ static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93";
#elif defined(__NetBSD__)
static char rcsid[] = "$NetBSD$";
#endif
-#ident "$Revision: 1.20 $"
+#ident "$Revision: 1.18 $"
#include "defs.h"
#include "pathnames.h"
@@ -201,13 +201,10 @@ main(int argc,
/* handle arbirary, (usually) per-interface
* parameters.
*/
- p = parse_parms(optarg, 0);
- if (p != 0) {
- if (strcasecmp(p,optarg))
- msglog("%s in \"%s\"", p, optarg);
- else
- msglog("bad \"-P %s\"", optarg);
- }
+ p = parse_parms(optarg);
+ if (p != 0)
+ msglog("bad \"%s\" in \"%s\"",
+ p, optarg);
break;
default:
@@ -221,11 +218,9 @@ main(int argc,
tracename = *argv++;
argc--;
}
- if (tracename != 0 && tracename[0] == '\0')
- goto usage;
if (argc != 0) {
usage:
- logbad(0, "usage: routed [-sqdghmpAt] [-T tracefile]"
+ logbad(0, "usage: routed [-sqdghmpAt] [-T /tracefile]"
" [-F net[,metric]] [-P parms]");
}
if (geteuid() != 0)
@@ -268,16 +263,18 @@ usage:
signal(SIGUSR2, sigtrace_off);
/* get into the background */
+ if (background) {
#ifdef sgi
- if (0 > _daemonize(background ? 0 : (_DF_NOCHDIR|_DF_NOFORK),
- new_tracelevel == 0 ? -1 : STDOUT_FILENO,
- new_tracelevel == 0 ? -1 : STDERR_FILENO,
- -1))
- BADERR(0, "_daemonize()");
+ if (0 > _daemonize(_DF_NOCHDIR,
+ new_tracelevel == 0 ? -1 : STDOUT_FILENO,
+ new_tracelevel == 0 ? -1 : STDERR_FILENO,
+ -1))
+ BADERR(0, "_daemonize()");
#else
- if (background && daemon(0, new_tracelevel) < 0)
- BADERR(0,"daemon()");
+ if (daemon(1, 1) < 0)
+ BADERR(0,"daemon()");
#endif
+ }
mypid = getpid();
srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid));
@@ -300,11 +297,11 @@ usage:
if (background && new_tracelevel == 0)
ftrace = 0;
if (tracename != 0) {
- strncpy(inittracename, tracename, sizeof(inittracename)-1);
- set_tracefile(inittracename, "%s\n", -1);
- } else {
- tracelevel_msg("%s\n", -1); /* turn on tracing to stdio */
+ trace_on(tracename, 1);
+ if (new_tracelevel == 0) /* use stdout if file is bad */
+ new_tracelevel = 1;
}
+ set_tracelevel(1);
bufinit();
@@ -357,8 +354,8 @@ usage:
now_expire = now.tv_sec - EXPIRE_TIME;
now_garbage = now.tv_sec - GARBAGE_TIME;
- /* deal with signals that should affect tracing */
- set_tracelevel();
+ /* deal with interrupts that should affect tracing */
+ set_tracelevel(0);
if (stopint != 0) {
rip_bcast(0);
@@ -494,7 +491,7 @@ usage:
/* ARGSUSED */
void
-sigalrm(int s)
+sigalrm(int sig)
{
/* Historically, SIGALRM would cause the daemon to check for
* new and broken interfaces.
@@ -819,53 +816,20 @@ msglog(char *p, ...)
}
-/* Put a message about a bad system into the system log if
+/* Put a message about a bad router into the system log if
* we have not complained about it recently.
- *
- * It is desirable to complain about all bad systems, but not too often.
- * In the worst case, it is not practical to keep track of all bad systems.
- * For example, there can be many systems with the wrong password.
*/
void
msglim(struct msg_limit *lim, naddr addr, char *p, ...)
{
va_list args;
- int i;
- struct msg_sub *ms1, *ms;
char *p1;
va_start(args, p);
- /* look for the oldest slot in the table
- * or the slot for the bad router.
- */
- ms = ms1 = lim->subs;
- for (i = MSG_SUBJECT_N; ; i--, ms1++) {
- if (i == 0) {
- /* Reuse a slot at most once every 10 minutes.
- */
- if (lim->reuse > now.tv_sec) {
- ms = 0;
- } else {
- ms = ms1;
- lim->reuse = now.tv_sec + 10*60;
- }
- break;
- }
- if (ms->addr == addr) {
- /* Repeat a complaint about a given system at
- * most once an hour.
- */
- if (ms->until > now.tv_sec)
- ms = 0;
- break;
- }
- if (ms->until < ms1->until)
- ms = ms1;
- }
- if (ms != 0) {
- ms->addr = addr;
- ms->until = now.tv_sec + 60*60; /* 60 minutes */
+ if ( lim->addr != addr || lim->until <= now.tv_sec) {
+ lim->addr = addr;
+ lim->until = now.tv_sec + 60*60;
trace_flush();
for (p1 = p; *p1 == ' '; p1++)
@@ -873,7 +837,6 @@ msglim(struct msg_limit *lim, naddr addr, char *p, ...)
vsyslog(LOG_ERR, p1, args);
}
- /* always display the message if tracing */
if (ftrace != 0) {
(void)vfprintf(ftrace, p, args);
(void)fputc('\n', ftrace);
diff --git a/sbin/routed/md5.c b/sbin/routed/md5.c
deleted file mode 100644
index c24aa5a..0000000
--- a/sbin/routed/md5.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/* This code could be made a lot faster for PPP */
-
-/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
- * rights reserved.
- *
- * License to copy and use this software is granted provided that it
- * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
- * Algorithm" in all material mentioning or referencing this software
- * or this function.
- *
- * License is also granted to make and use derivative works provided
- * that such works are identified as "derived from the RSA Data
- * Security, Inc. MD5 Message-Digest Algorithm" in all material
- * mentioning or referencing the derived work.
- *
- * RSA Data Security, Inc. makes no representations concerning either
- * the merchantability of this software or the suitability of this
- * software for any particular purpose. It is provided "as is"
- * without express or implied warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this
- * documentation and/or software.
- */
-
-#ident "$Revision: 1.3 $"
-
-#ifdef sgi
-#include <strings.h>
-#include <bstring.h>
-#endif
-#include <sys/types.h>
-
-#define MD5_DIGEST_LEN 16
-typedef struct {
- u_int32_t state[4]; /* state (ABCD) */
- u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
- unsigned char buffer[64]; /* input buffer */
-} MD5_CTX;
-extern void MD5Init(MD5_CTX*);
-extern void MD5Update(MD5_CTX*, u_char*, u_int);
-extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
-
-/* UINT4 defines a four byte word */
-#define UINT4 u_int32_t
-
-
-#define MD5_memcpy(d,s,l) bcopy(s,d,l)
-
-/* Constants for MD5Transform routine.
- */
-#define S11 7
-#define S12 12
-#define S13 17
-#define S14 22
-#define S21 5
-#define S22 9
-#define S23 14
-#define S24 20
-#define S31 4
-#define S32 11
-#define S33 16
-#define S34 23
-#define S41 6
-#define S42 10
-#define S43 15
-#define S44 21
-
-static void MD5Transform(UINT4[4], unsigned char [64]);
-static void Encode(unsigned char *, UINT4 *, unsigned int);
-static void Decode(UINT4 *, unsigned char *, unsigned int);
-
-static unsigned char PADDING[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-
-/* F, G, H and I are basic MD5 functions.
- */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | (~z)))
-
-/* ROTATE_LEFT rotates x left n bits.
- */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
- * Rotation is separate from addition to prevent recomputation.
- */
-#define FF(a, b, c, d, x, s, ac) { \
- (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define GG(a, b, c, d, x, s, ac) { \
- (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define HH(a, b, c, d, x, s, ac) { \
- (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define II(a, b, c, d, x, s, ac) { \
- (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-
-/* MD5 initialization. Begins an MD5 operation, writing a new context.
- */
-void
-MD5Init(MD5_CTX *context)
-{
- context->count[0] = context->count[1] = 0;
- /* Load magic initialization constants.
- */
- context->state[0] = 0x67452301;
- context->state[1] = 0xefcdab89;
- context->state[2] = 0x98badcfe;
- context->state[3] = 0x10325476;
-}
-
-/* MD5 block update operation. Continues an MD5 message-digest
- * operation, processing another message block, and updating the
- * context.
- */
-void
-MD5Update(MD5_CTX *context, /* context */
- unsigned char *input, /* input block */
- unsigned int inputLen) /* length of input block */
-{
- unsigned int i, indx, partLen;
-
- /* Compute number of bytes mod 64 */
- indx = ((context->count[0] >> 3) & 0x3F);
-
- /* Update number of bits */
- if ((context->count[0] += ((UINT4)inputLen << 3))
- < ((UINT4)inputLen << 3))
- context->count[1]++;
- context->count[1] += ((UINT4)inputLen >> 29);
-
- partLen = 64 - indx;
-
- /* Transform as many times as possible.
- */
- if (inputLen >= partLen) {
- bcopy(input, &context->buffer[indx], partLen);
- MD5Transform (context->state, context->buffer);
-
- for (i = partLen; i + 63 < inputLen; i += 64)
- MD5Transform (context->state, &input[i]);
-
- indx = 0;
- } else {
- i = 0;
- }
-
- /* Buffer remaining input */
- bcopy(&input[i], &context->buffer[indx], inputLen-i);
-}
-
-
-/* MD5 finalization. Ends an MD5 message-digest operation, writing the
- the message digest and zeroizing the context.
- */
-void
-MD5Final(unsigned char digest[MD5_DIGEST_LEN], /* message digest */
- MD5_CTX *context) /* context */
-{
- unsigned char bits[8];
- unsigned int indx, padLen;
-
- /* Save number of bits */
- Encode (bits, context->count, 8);
-
- /* Pad out to 56 mod 64.
- */
- indx = (unsigned int)((context->count[0] >> 3) & 0x3f);
- padLen = (indx < 56) ? (56 - indx) : (120 - indx);
- MD5Update(context, PADDING, padLen);
-
- /* Append length (before padding) */
- MD5Update(context, bits, 8);
-
- /* Store state in digest */
- Encode(digest, context->state, MD5_DIGEST_LEN);
-
- /* Zeroize sensitive information.
- */
- bzero(context, sizeof(*context));
-}
-
-
-/* MD5 basic transformation. Transforms state based on block.
- */
-static void
-MD5Transform(UINT4 state[4],
- unsigned char block[64])
-{
- UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
-
- Decode (x, block, 64);
-
- /* Round 1 */
- FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
- FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
- FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
- FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
- FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
- FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
- FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
- FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
- FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
- FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
- FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
- FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
- FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
- FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
- FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
- FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
-
- /* Round 2 */
- GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
- GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
- GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
- GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
- GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
- GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
- GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
- GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
- GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
- GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
- GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
- GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
- GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
- GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
- GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
- GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
-
- /* Round 3 */
- HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
- HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
- HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
- HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
- HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
- HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
- HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
- HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
- HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
- HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
- HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
- HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
- HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
- HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
- HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
- HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
-
- /* Round 4 */
- II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
- II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
- II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
- II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
- II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
- II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
- II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
- II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
- II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
- II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
- II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
- II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
- II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
- II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
- II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
- II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
-
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
-
- /* Zeroize sensitive information.
- */
- bzero(x, sizeof(x));
-}
-
-
-/* Encodes input (UINT4) into output (unsigned char). Assumes len is
- * a multiple of 4.
- */
-static void
-Encode(unsigned char *output,
- UINT4 *input,
- unsigned int len)
-{
- unsigned int i, j;
-
- for (i = 0, j = 0; j < len; i++, j += 4) {
- output[j] = (unsigned char)(input[i] & 0xff);
- output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
- output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
- output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
- }
-}
-
-
-/* Decodes input (unsigned char) into output (UINT4). Assumes len is
- * a multiple of 4.
- */
-static void
-Decode (UINT4 *output,
- unsigned char *input,
- unsigned int len)
-{
- unsigned int i, j;
-
- for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
- (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
-}
diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h
index 14b721c..eb90433 100644
--- a/sbin/routed/pathnames.h
+++ b/sbin/routed/pathnames.h
@@ -47,4 +47,6 @@
* Leave this undefined, and only the trace file originally specified
* when routed was started, if any, will be appended to.
*/
-#define _PATH_TRACE "/etc/routed.trace"
+#if 0
+#define _PATH_TRACE "/var/log/routed.trace"
+#endif
diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8
index 424a851..5eaaff2 100644
--- a/sbin/routed/routed.8
+++ b/sbin/routed/routed.8
@@ -39,7 +39,7 @@
.Nd network RIP and router discovery routing daemon
.Sh SYNOPSIS
.Nm
-.Op Fl sqdghmpAt
+.Op Fl sqdghmAt
.Op Fl T Ar tracefile
.Oo
.Fl F
@@ -514,13 +514,11 @@ specifies a RIPv2 password that will be included on all RIPv2
responses sent and checked on all RIPv2 responses received.
The password must not contain any blanks, tab characters, commas
or '#' characters.
-.It Cm passwd Ns \&= Ns Ar XXX1[|KeyID[start|stop]]
-specifies a RIPv2 cleartext password that will be included on
+.It Cm passwd Ns \&= Ns Ar XXX1[|KeyID[start|stop]][XXX2...]
+specifies one or more RIPv2 cleartext passwords that will be included on
all RIPv2 responses sent, and checked on all RIPv2 responses received.
-Any blanks, tab characters, commas, or '#', '|', or NULL characters in the
+Any blanks, tab characters, commas, or '#' or '|' characters in the
password must be escaped with a backslash (\\).
-The common escape sequences \\n, \\r, \\t, \\b, and \\xxx have their
-usual meanings.
The
.Cm KeyID
must be unique but is ignored for cleartext passwords.
@@ -530,21 +528,15 @@ and
.Cm stop
are timestamps in the form year/month/day@hour:minute.
They specify when the password is valid.
-The valid password with the most future is used on output packets, unless
-all passwords have expired, in which case the password that expired most
-recently is used, or unless no passwords are valid yet, in which case
-no password is output.
+The first valid password is used on output packets.
Incoming packets can carry any password that is valid, will
be valid within 24 hours, or that was valid within 24 hours.
-.It Cm md5_passwd Ns \&= Ns Ar XXX1|KeyID[start|stop]
-specifes a RIPv2 MD5 password.
+.It Cm md5_passwd Ns \&= Ns Ar XXX1|KeyID[start|stop][XXX2...]
+specifes one or more RIPv2 MD5 passwords.
Except that a
.Cm KeyID
-is required, this keyword is similar to
+is required, this keyword is the similar to
.Cm passwd .
-To protect the secrets, this parameter setting is valid only in the
-.Em /etc/gateways
-file and only when that file is readable only by UID 0.
.It Cm no_ag
turns off aggregation of subnets in RIPv1 and RIPv2 responses.
.It Cm no_super_ag
@@ -620,10 +612,6 @@ causes RIP packets from that router and other routers named in
other
.Cm trust_gateway
keywords to be accept, and packets from other routers to be ignored.
-.It Cm redirect_ok
-causes RIP to allow ICMP Redirect messages when the system is acting
-as a router and forwarding packets.
-Otherwise, ICMP Redirect messages are are overridden.
.El
.Pp
.Sh FILES
diff --git a/sbin/routed/routed.h b/sbin/routed/routed.h
deleted file mode 100644
index 5885ac5..0000000
--- a/sbin/routed/routed.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*-
- * Copyright (c) 1983, 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.
- *
- * @(#)routed.h 8.1 (Berkeley) 6/2/93
- *
- * $NetBSD$
- */
-
-#ifndef _ROUTED_H_
-#define _ROUTED_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
-#ident "$Revision: 1.11 $"
-
-/*
- * Routing Information Protocol
- *
- * Derived from Xerox NS Routing Information Protocol
- * by changing 32-bit net numbers to sockaddr's and
- * padding stuff to 32-bit boundaries.
- */
-
-#define RIPv1 1
-#define RIPv2 2
-#ifndef RIPVERSION
-#define RIPVERSION RIPv1
-#endif
-
-#define RIP_PORT 520
-
-#if RIPVERSION == 1
-/* Note that this so called sockaddr has a 2-byte sa_family and no sa_len.
- * It is not a UNIX sockaddr, but the shape of an address as defined
- * in RIPv1. It is still defined to allow old versions of programs
- * such as `gated` to use this file to define RIPv1.
- */
-struct netinfo {
- struct sockaddr rip_dst; /* destination net/host */
- u_int32_t rip_metric; /* cost of route */
-};
-#else
-struct netinfo {
- u_int16_t n_family;
-#define RIP_AF_INET htons(AF_INET)
-#define RIP_AF_UNSPEC 0
-#define RIP_AF_AUTH 0xffff
- u_int16_t n_tag; /* optional in RIPv2 */
- u_int32_t n_dst; /* destination net or host */
-#define RIP_DEFAULT 0
- u_int32_t n_mask; /* netmask in RIPv2 */
- u_int32_t n_nhop; /* optional next hop in RIPv2 */
- u_int32_t n_metric; /* cost of route */
-};
-#endif
-
-/* RIPv2 authentication */
-struct netauth {
- u_int16_t a_family; /* always RIP_AF_AUTH */
- u_int16_t a_type;
-#define RIP_AUTH_NONE 0
-#define RIP_AUTH_PW htons(2) /* password type */
-#define RIP_AUTH_MD5 htons(3) /* Keyed MD5 */
- union {
-#define RIP_AUTH_PW_LEN 16
- u_int8_t au_pw[RIP_AUTH_PW_LEN];
- struct a_md5 {
- int16_t md5_pkt_len; /* RIP-II packet length */
- int8_t md5_keyid; /* key ID and auth data len */
- int8_t md5_auth_len; /* 16 */
- u_int32_t md5_seqno; /* sequence number */
- u_int32_t rsvd[2]; /* must be 0 */
-#define RIP_AUTH_MD5_LEN RIP_AUTH_PW_LEN
- } a_md5;
- } au;
-};
-
-struct rip {
- u_int8_t rip_cmd; /* request/response */
- u_int8_t rip_vers; /* protocol version # */
- u_int16_t rip_res1; /* pad to 32-bit boundary */
- union { /* variable length... */
- struct netinfo ru_nets[1];
- int8_t ru_tracefile[1];
- struct netauth ru_auth[1];
- } ripun;
-#define rip_nets ripun.ru_nets
-#define rip_auths ripun.ru_auth
-#define rip_tracefile ripun.ru_tracefile
-};
-
-/* Packet types.
- */
-#define RIPCMD_REQUEST 1 /* want info */
-#define RIPCMD_RESPONSE 2 /* responding to request */
-#define RIPCMD_TRACEON 3 /* turn tracing on */
-#define RIPCMD_TRACEOFF 4 /* turn it off */
-
-/* Gated extended RIP to include a "poll" command instead of using
- * RIPCMD_REQUEST with (RIP_AF_UNSPEC, RIP_DEFAULT). RFC 1058 says
- * command 5 is used by Sun Microsystems for its own purposes.
- */
-#define RIPCMD_POLL 5
-
-#define RIPCMD_MAX 6
-
-#ifdef RIPCMDS
-char *ripcmds[RIPCMD_MAX] = {
- "#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF"
-};
-#endif
-
-#define HOPCNT_INFINITY 16
-#define MAXPACKETSIZE 512 /* max broadcast size */
-#define NETS_LEN ((MAXPACKETSIZE-sizeof(struct rip)) \
- / sizeof(struct netinfo) +1)
-
-#define INADDR_RIP_GROUP (u_int32_t)0xe0000009 /* 224.0.0.9 */
-
-
-/* Timer values used in managing the routing table.
- *
- * Complete tables are broadcast every SUPPLY_INTERVAL seconds.
- * If changes occur between updates, dynamic updates containing only changes
- * may be sent. When these are sent, a timer is set for a random value
- * between MIN_WAITTIME and MAX_WAITTIME, and no additional dynamic updates
- * are sent until the timer expires.
- *
- * Every update of a routing entry forces an entry's timer to be reset.
- * After EXPIRE_TIME without updates, the entry is marked invalid,
- * but held onto until GARBAGE_TIME so that others may see it, to
- * "poison" the bad route.
- */
-#define SUPPLY_INTERVAL 30 /* time to supply tables */
-#define MIN_WAITTIME 2 /* min sec until next flash updates */
-#define MAX_WAITTIME 5 /* max sec until flash update */
-
-#define STALE_TIME 90 /* switch to a new gateway */
-#define EXPIRE_TIME 180 /* time to mark entry invalid */
-#define GARBAGE_TIME 240 /* time to garbage collect */
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* !_ROUTED_H_ */
diff --git a/sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile
new file mode 100644
index 0000000..e69c3e7
--- /dev/null
+++ b/sbin/routed/rtquery/Makefile
@@ -0,0 +1,10 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id: Makefile,v 1.2 1996/09/16 17:04:22 wollman Exp $
+
+PROG= rtquery
+MAN8= rtquery.8
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+#COPTS= -g -DDEBUG -Wall
+
+.include <bsd.prog.mk>
diff --git a/sbin/routed/rtquery/md5.c b/sbin/routed/rtquery/md5.c
deleted file mode 100644
index c24aa5a..0000000
--- a/sbin/routed/rtquery/md5.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/* This code could be made a lot faster for PPP */
-
-/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
- * rights reserved.
- *
- * License to copy and use this software is granted provided that it
- * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
- * Algorithm" in all material mentioning or referencing this software
- * or this function.
- *
- * License is also granted to make and use derivative works provided
- * that such works are identified as "derived from the RSA Data
- * Security, Inc. MD5 Message-Digest Algorithm" in all material
- * mentioning or referencing the derived work.
- *
- * RSA Data Security, Inc. makes no representations concerning either
- * the merchantability of this software or the suitability of this
- * software for any particular purpose. It is provided "as is"
- * without express or implied warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this
- * documentation and/or software.
- */
-
-#ident "$Revision: 1.3 $"
-
-#ifdef sgi
-#include <strings.h>
-#include <bstring.h>
-#endif
-#include <sys/types.h>
-
-#define MD5_DIGEST_LEN 16
-typedef struct {
- u_int32_t state[4]; /* state (ABCD) */
- u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
- unsigned char buffer[64]; /* input buffer */
-} MD5_CTX;
-extern void MD5Init(MD5_CTX*);
-extern void MD5Update(MD5_CTX*, u_char*, u_int);
-extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
-
-/* UINT4 defines a four byte word */
-#define UINT4 u_int32_t
-
-
-#define MD5_memcpy(d,s,l) bcopy(s,d,l)
-
-/* Constants for MD5Transform routine.
- */
-#define S11 7
-#define S12 12
-#define S13 17
-#define S14 22
-#define S21 5
-#define S22 9
-#define S23 14
-#define S24 20
-#define S31 4
-#define S32 11
-#define S33 16
-#define S34 23
-#define S41 6
-#define S42 10
-#define S43 15
-#define S44 21
-
-static void MD5Transform(UINT4[4], unsigned char [64]);
-static void Encode(unsigned char *, UINT4 *, unsigned int);
-static void Decode(UINT4 *, unsigned char *, unsigned int);
-
-static unsigned char PADDING[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-
-/* F, G, H and I are basic MD5 functions.
- */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | (~z)))
-
-/* ROTATE_LEFT rotates x left n bits.
- */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
- * Rotation is separate from addition to prevent recomputation.
- */
-#define FF(a, b, c, d, x, s, ac) { \
- (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define GG(a, b, c, d, x, s, ac) { \
- (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define HH(a, b, c, d, x, s, ac) { \
- (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-#define II(a, b, c, d, x, s, ac) { \
- (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-}
-
-/* MD5 initialization. Begins an MD5 operation, writing a new context.
- */
-void
-MD5Init(MD5_CTX *context)
-{
- context->count[0] = context->count[1] = 0;
- /* Load magic initialization constants.
- */
- context->state[0] = 0x67452301;
- context->state[1] = 0xefcdab89;
- context->state[2] = 0x98badcfe;
- context->state[3] = 0x10325476;
-}
-
-/* MD5 block update operation. Continues an MD5 message-digest
- * operation, processing another message block, and updating the
- * context.
- */
-void
-MD5Update(MD5_CTX *context, /* context */
- unsigned char *input, /* input block */
- unsigned int inputLen) /* length of input block */
-{
- unsigned int i, indx, partLen;
-
- /* Compute number of bytes mod 64 */
- indx = ((context->count[0] >> 3) & 0x3F);
-
- /* Update number of bits */
- if ((context->count[0] += ((UINT4)inputLen << 3))
- < ((UINT4)inputLen << 3))
- context->count[1]++;
- context->count[1] += ((UINT4)inputLen >> 29);
-
- partLen = 64 - indx;
-
- /* Transform as many times as possible.
- */
- if (inputLen >= partLen) {
- bcopy(input, &context->buffer[indx], partLen);
- MD5Transform (context->state, context->buffer);
-
- for (i = partLen; i + 63 < inputLen; i += 64)
- MD5Transform (context->state, &input[i]);
-
- indx = 0;
- } else {
- i = 0;
- }
-
- /* Buffer remaining input */
- bcopy(&input[i], &context->buffer[indx], inputLen-i);
-}
-
-
-/* MD5 finalization. Ends an MD5 message-digest operation, writing the
- the message digest and zeroizing the context.
- */
-void
-MD5Final(unsigned char digest[MD5_DIGEST_LEN], /* message digest */
- MD5_CTX *context) /* context */
-{
- unsigned char bits[8];
- unsigned int indx, padLen;
-
- /* Save number of bits */
- Encode (bits, context->count, 8);
-
- /* Pad out to 56 mod 64.
- */
- indx = (unsigned int)((context->count[0] >> 3) & 0x3f);
- padLen = (indx < 56) ? (56 - indx) : (120 - indx);
- MD5Update(context, PADDING, padLen);
-
- /* Append length (before padding) */
- MD5Update(context, bits, 8);
-
- /* Store state in digest */
- Encode(digest, context->state, MD5_DIGEST_LEN);
-
- /* Zeroize sensitive information.
- */
- bzero(context, sizeof(*context));
-}
-
-
-/* MD5 basic transformation. Transforms state based on block.
- */
-static void
-MD5Transform(UINT4 state[4],
- unsigned char block[64])
-{
- UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
-
- Decode (x, block, 64);
-
- /* Round 1 */
- FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
- FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
- FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
- FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
- FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
- FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
- FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
- FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
- FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
- FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
- FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
- FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
- FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
- FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
- FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
- FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
-
- /* Round 2 */
- GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
- GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
- GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
- GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
- GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
- GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
- GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
- GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
- GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
- GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
- GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
- GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
- GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
- GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
- GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
- GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
-
- /* Round 3 */
- HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
- HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
- HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
- HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
- HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
- HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
- HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
- HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
- HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
- HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
- HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
- HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
- HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
- HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
- HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
- HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
-
- /* Round 4 */
- II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
- II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
- II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
- II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
- II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
- II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
- II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
- II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
- II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
- II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
- II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
- II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
- II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
- II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
- II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
- II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
-
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
-
- /* Zeroize sensitive information.
- */
- bzero(x, sizeof(x));
-}
-
-
-/* Encodes input (UINT4) into output (unsigned char). Assumes len is
- * a multiple of 4.
- */
-static void
-Encode(unsigned char *output,
- UINT4 *input,
- unsigned int len)
-{
- unsigned int i, j;
-
- for (i = 0, j = 0; j < len; i++, j += 4) {
- output[j] = (unsigned char)(input[i] & 0xff);
- output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
- output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
- output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
- }
-}
-
-
-/* Decodes input (unsigned char) into output (UINT4). Assumes len is
- * a multiple of 4.
- */
-static void
-Decode (UINT4 *output,
- unsigned char *input,
- unsigned int len)
-{
- unsigned int i, j;
-
- for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
- (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
-}
diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c
index 5417d06..42d4d6e 100644
--- a/sbin/routed/rtquery/rtquery.c
+++ b/sbin/routed/rtquery/rtquery.c
@@ -40,7 +40,7 @@ static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93";
#elif defined(__NetBSD__)
static char rcsid[] = "$NetBSD$";
#endif
-#ident "$Revision: 1.10 $"
+#ident "$Revision: 1.1.1.2 $"
#include <sys/param.h>
#include <sys/protosw.h>
@@ -65,17 +65,7 @@ static char rcsid[] = "$NetBSD$";
#define _HAVE_SIN_LEN
#endif
-#define MD5_DIGEST_LEN 16
-typedef struct {
- u_int32_t state[4]; /* state (ABCD) */
- u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */
- unsigned char buffer[64]; /* input buffer */
-} MD5_CTX;
-extern void MD5Init(MD5_CTX*);
-extern void MD5Update(MD5_CTX*, u_char*, u_int);
-extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*);
-
-
+#include <md5.h>
#define WTIME 15 /* Time to wait for all responses */
#define STIME (250*1000) /* usec to wait for another response */
diff --git a/sbin/routed/table.c b/sbin/routed/table.c
index d00279e..dda559a 100644
--- a/sbin/routed/table.c
+++ b/sbin/routed/table.c
@@ -36,7 +36,6 @@ static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93";
#elif defined(__NetBSD__)
static char rcsid[] = "$NetBSD$";
#endif
-#ident "$Revision: 1.28 $"
#include "defs.h"
@@ -746,7 +745,7 @@ static struct khash {
#define KS_DELETED 0x100 /* already deleted */
time_t k_keep;
#define K_KEEP_LIM 30
- time_t k_redirect_time; /* when redirected route 1st seen */
+ time_t k_redirect_time;
} *khash_bins[KHASH_SIZE];
@@ -832,7 +831,8 @@ rtm_add(struct rt_msghdr *rtm,
} else if (INFO_MASK(info) != 0) {
mask = ntohl(S_ADDR(INFO_MASK(info)));
} else {
- msglog("ignore %s without mask", rtm_type_name(rtm->rtm_type));
+ msglog("ignore %s without mask",
+ rtm_type_name(rtm->rtm_type));
return;
}
@@ -859,32 +859,20 @@ rtm_add(struct rt_msghdr *rtm,
k->k_state |= KS_STATIC;
if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) {
- if (INFO_AUTHOR(info) != 0
- && INFO_AUTHOR(info)->sa_family == AF_INET)
- ifp = iflookup(S_ADDR(INFO_AUTHOR(info)));
- else
- ifp = 0;
- if (supplier
- && (ifp == 0 || !(ifp->int_state & IS_REDIRECT_OK))) {
+ if (supplier) {
/* Routers are not supposed to listen to redirects,
- * so delete it if it came via an unknown interface
- * or the interface does not have special permission.
+ * so delete it.
*/
k->k_state &= ~KS_DYNAMIC;
k->k_state |= KS_DELETE;
LIM_SEC(need_kern, 0);
- trace_act("mark for deletion redirected %s --> %s"
- " via %s",
+ trace_act("mark redirected %s --> %s for deletion"
+ " since this is a router",
addrname(k->k_dst, k->k_mask, 0),
- naddr_ntoa(k->k_gate),
- ifp ? ifp->int_name : "unknown interface");
+ naddr_ntoa(k->k_gate));
} else {
k->k_state |= KS_DYNAMIC;
k->k_redirect_time = now.tv_sec;
- trace_act("accept redirected %s --> %s via %s",
- addrname(k->k_dst, k->k_mask, 0),
- naddr_ntoa(k->k_gate),
- ifp ? ifp->int_name : "unknown interface");
}
return;
}
@@ -904,10 +892,17 @@ rtm_add(struct rt_msghdr *rtm,
* Find the interface toward the gateway.
*/
ifp = iflookup(k->k_gate);
- if (ifp == 0)
- msglog("static route %s --> %s impossibly lacks ifp",
- addrname(S_ADDR(INFO_DST(info)), mask, 0),
- naddr_ntoa(k->k_gate));
+ if (ifp == 0) {
+ /* if there is no known interface,
+ * maybe there is a new interface
+ */
+ ifinit();
+ ifp = iflookup(k->k_gate);
+ if (ifp == 0)
+ msglog("static route %s --> %s impossibly lacks ifp",
+ addrname(S_ADDR(INFO_DST(info)), mask, 0),
+ naddr_ntoa(k->k_gate));
+ }
kern_check_static(k, ifp);
}
@@ -921,8 +916,8 @@ rtm_lose(struct rt_msghdr *rtm,
{
if (INFO_GATE(info) == 0
|| INFO_GATE(info)->sa_family != AF_INET) {
- trace_act("ignore %s without gateway",
- rtm_type_name(rtm->rtm_type));
+ msglog("ignore %s without gateway",
+ rtm_type_name(rtm->rtm_type));
return;
}
@@ -1574,8 +1569,15 @@ rtadd(naddr dst,
if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock,
rhead, rt->rt_nodes)) {
+/*
+ * This will happen if RIP1 and RIP2 routeds talk to one another and
+ * there are variable subnets. This is only good for filling up your
+ * syslog. -jkh
+ */
+#if 0
msglog("rnh_addaddr() failed for %s mask=%#x",
naddr_ntoa(dst), mask);
+#endif
}
}
diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile
new file mode 100644
index 0000000..33b2dbe
--- /dev/null
+++ b/sbin/savecore/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.2 (Berkeley) 4/17/94
+
+PROG= savecore
+SRCS= savecore.c zopen.c
+MAN8= savecore.8
+
+ZOPENPATH= ${.CURDIR}/../../usr.bin/compress
+.PATH: ${ZOPENPATH}
+CFLAGS+= -I${ZOPENPATH}
+
+.include <bsd.prog.mk>
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
new file mode 100644
index 0000000..5a613c9
--- /dev/null
+++ b/sbin/savecore/savecore.8
@@ -0,0 +1,121 @@
+.\" 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.
+.\"
+.\" From: @(#)savecore.8 8.1 (Berkeley) 6/5/93
+.\" $Id: savecore.8,v 1.3 1994/09/24 00:08:21 wollman Exp $
+.\"
+.Dd September 23, 1994
+.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 running kernel (as determined from
+.Xr getbootfile 3 ).
+.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 /kernel.#
+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 BUGS
+The minfree code does not consider the effect of compression.
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr getbootfile 3 ,
+.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 0000000..760835a
--- /dev/null
+++ b/sbin/savecore/savecore.c
@@ -0,0 +1,664 @@
+/*-
+ * 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 <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.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 <unistd.h>
+#include "zopen.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 *kernel;
+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 get_dumpsize __P((void));
+void kmem_setup __P((void));
+void log __P((int, char *, ...));
+void Lseek __P((int, off_t, int));
+int Open __P((const 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, "cdfN:vz")) != EOF)
+ switch(ch) {
+ case 'c':
+ clear = 1;
+ break;
+ case 'd': /* Not documented. */
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'N':
+ kernel = 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)
+ kernel = 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");
+
+ get_dumpsize();
+
+ if ((!get_crashtime() || !check_space()) && !force)
+ exit(1);
+
+ save_core();
+
+ clear_dump();
+ exit(0);
+}
+
+void
+kmem_setup()
+{
+ FILE *fp;
+ int kmem, i;
+ const 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(getbootfile(), current_nl)) == -1)
+ syslog(LOG_ERR, "%s: nlist: %s", getbootfile(),
+ 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",
+ getbootfile(), current_nl[cursyms[i]].n_name);
+ exit(1);
+ }
+
+ dump_sys = kernel ? kernel : getbootfile();
+ 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 (kernel)
+ 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) && kernel == 0)
+ syslog(LOG_WARNING,
+ "warning: %s version mismatch:\n\t%s\nand\t%s\n",
+ getbootfile(), 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];
+ mode_t oumask;
+
+ /*
+ * 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);
+ }
+
+ /* Create the core file. */
+ oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to 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);
+ (void)umask(oumask);
+
+ /* 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;
+ }
+
+ /* Seek to the start of the core. */
+ Lseek(ifd, (off_t)dumplo, L_SET);
+
+ /* Copy the core file. */
+ 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)close(ifd);
+ if (compress)
+ (void)fclose(fp);
+ else
+ (void)close(ofd);
+
+ /* Copy the kernel. */
+ ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
+ (void)snprintf(path, sizeof(path), "%s/kernel.%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: kernel may be incomplete");
+ exit(1);
+ }
+ }
+ if (nr < 0) {
+ syslog(LOG_ERR, "%s: %s",
+ kernel ? kernel : getbootfile(), strerror(errno));
+ syslog(LOG_WARNING,
+ "WARNING: kernel 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 * 86400)
+ if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
+ (void)printf("dump time is unreasonable\n");
+ return (0);
+ }
+ return (1);
+}
+
+void
+get_dumpsize()
+{
+ /* Read the dump size. */
+ Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
+ (void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
+ dumpsize *= getpagesize();
+}
+
+int
+check_space()
+{
+ register FILE *fp;
+ const char *tkernel;
+ off_t minfree, spacefree, totfree, kernelsize, needed;
+ struct stat st;
+ struct statfs fsbuf;
+ char buf[100], path[MAXPATHLEN];
+
+ tkernel = kernel ? kernel : getbootfile();
+ if (stat(tkernel, &st) < 0) {
+ syslog(LOG_ERR, "%s: %m", tkernel);
+ exit(1);
+ }
+ kernelsize = st.st_blocks * S_BLKSIZE;
+
+ if (statfs(dirname, &fsbuf) < 0) {
+ syslog(LOG_ERR, "%s: %m", dirname);
+ exit(1);
+ }
+ spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
+ totfree = ((off_t) fsbuf.f_bfree * 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 + kernelsize) / 1024;
+ if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
+ syslog(LOG_WARNING,
+ "no dump, not enough free space on device");
+ return (0);
+ }
+ if (spacefree - needed < 0)
+ syslog(LOG_WARNING,
+ "dump performed, but free space threshold crossed");
+ return (1);
+}
+
+int
+Open(name, rw)
+ const 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/scsi/Makefile b/sbin/scsi/Makefile
new file mode 100644
index 0000000..f084f7c
--- /dev/null
+++ b/sbin/scsi/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= scsi
+MAN8= scsi.8
+LDADD= -lscsi
+DPADD= ${LIBSCSI}
+
+.include <bsd.prog.mk>
diff --git a/sbin/scsi/scsi.8 b/sbin/scsi/scsi.8
new file mode 100644
index 0000000..e0f62f5
--- /dev/null
+++ b/sbin/scsi/scsi.8
@@ -0,0 +1,251 @@
+.\"
+.\" Written By Julian ELischer
+.\" Copyright julian Elischer 1993.
+.\" Permission is granted to use or redistribute this file in any way as long
+.\" as this notice remains. Julian Elischer does not guarantee that this file
+.\" is totally correct for any given task and users of this file must
+.\" accept responsibility for any damage that occurs from the application of this
+.\" file.
+.\"
+.\" (julian@tfs.com julian@dialix.oz.au)
+.\" User SCSI hooks added by Peter Dufault:
+.\"
+.\" Copyright (c) 1994 HD Associates
+.\" (contact: dufault@hda.com)
+.\" 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. The name of HD Associates
+.\" may not be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.\" $Id: scsi.8,v 1.5 1995/05/05 20:41:58 dufault Exp $
+.\"
+.Dd October 11, 1993
+.Dt SCSI 8
+.Os BSD 4
+.Sh NAME
+.Nm scsi
+.Nd program to assist with scsi devices.
+.Sh SYNOPSIS
+.Bd -literal -offset
+Usage:
+scsi -f device -d debug_level # To set debug level
+scsi -f device [-v] -z seconds # To freeze bus
+scsi -f device -m page [-P pc] [-e] # To read mode pages
+scsi -f device -p [-b bus] [-l lun] # To probe all devices
+scsi -f device -r [-b bus] [-t targ] [-l lun] # To reprobe a device
+scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...
+ -o count out_fmt [arg0 ... argn] # EITHER (for data out)
+ -i count in_fmt # OR (for data in)
+.Pp
+"out_fmt" can be "-" to read output data from stdin;
+"in_fmt" can be "-" to write input data to stdout;
+.Pp
+If debugging is not compiled in the kernel, "-d" will have no effect
+.Ed
+.Sh DESCRIPTION
+The
+.Nm scsi
+program is used to send commands to a scsi device. It is also
+a sample usage of the user level SCSI commands.
+.Pp
+The
+.Fr -d
+option sets the SCSI kernel debug level. The kernel must have been compiled
+with the
+.Em SCSIDEBUG
+option. See
+.Fr /sys/scsi/scsi_debug.h
+to figure out what to set the kernel debug level to.
+.Pp
+The
+.Fr -z
+option freezes all activity on all SCSI busses for a given number of
+seconds. If
+.Fr -v
+is also specified then a BEL character is sent to the standard
+output at the start and finish of the bus freeze.
+This requires that the kernel be built with the SCSI_FREEZE kernel option.
+This kernel code is not committed yet.
+.Pp
+The
+.Fr -m
+option is used to read a device mode page. The file
+.Fr /usr/share/misc/scsi_modes
+is read to look at for how to interpret the mode data. The environment
+variable SCSI_MODES can specify a different file to use.
+.Pp
+.in +.25i
+The
+.Fr -P
+option can be used to specify a page control field. The page control
+fields are
+.Bd -literal -offset
+0 Current Values
+1 Changeable Values
+2 Default Values
+3 Saved Values
+.Ed
+.Pp
+The
+.Fr -e
+option permits you to edit the fields. It will use the editor specified
+by your EDITOR environment variable. To store changes permanently,
+edit page control 3 using the
+.Fr -P
+flag.
+.in -.25i
+.Pp
+The
+.Fr -p
+option can be used against the "super scsi" device
+.Fr /dev/scsi/super
+to probe all devices with a given SCSI lun on a given SCSI bus.
+The bus can be selected with the -b option and the default is 0.
+The lun can be selected with the -l option and the default is 0.
+See
+.Xr scsi 4
+for a description of the "super scsi" device.
+.Pp
+The
+.Fr -r
+option can be used in
+.Tn FreeBSD
+1.1 to reprobe a specific SCSI device at a given
+Bus, Target and Lun.
+This is not needed in
+.Fx 2.1 ,
+since opening a fixed SCSI device
+has the side effect of reprobing it, and probing with the bus with the
+-p option should bring on line any newly found devices.
+See
+.Xr scsi 4
+for a description of fixed scsi devices.
+.Pp
+The
+.Fr -c
+option permits you to send user level SCSI commands specified on
+the command line to a
+device. The command is sent using the SCIOCCOMMAND ioctl, so the
+device you are accessing must permit this ioctl. See
+.Xr scsi 4
+for full details of which minor devices permit the ioctl, and
+.Xr scsi 3
+for the full details on how to build up the commands and data phases
+using the format arguments.
+.in +.25i
+.Pp
+.Fr -v
+turns on more verbose information.
+.Pp
+.Fr -s
+sets the command timeout in seconds. The default is two seconds.
+.Pp
+.Fr "-c cmd_fmt"
+specifies the command as described in
+.Xr scsi 3 "."
+The additional arguments provide values for any variables
+specified in the command format.
+.Pp
+.Fr "-o count out_fmt arg0 ... argn"
+indicates that this is a data out command (i.e., data will be sent from
+the system to the device) with
+.Fr count
+bytes of data. The data out is built up using the facilities described in
+.Xr scsi 3
+using the provided arguments to fill in any integer variables.
+.Fr out_fmt
+can be specified as a hyphen ("-") to indicate that the
+.Fr count
+bytes of data should be read from the standard input.
+.Pp
+.Fr "-i count in_fmt"
+indicates that this is a data in command (i.e., data will be read from
+the device into the system) with
+.Fr count
+bytes of data read in. The information is extracted according to
+.Fr in_fmt
+using the facilities described in
+.Xr scsi 3
+and displayed on the standard output.
+.Fr in_fmt
+can be specified as a hyphen ("-") to indicate that the
+.Fr count
+bytes of data input should be written to the standard output.
+.in -.25i
+.Sh EXAMPLES
+To verify that the device type for the disk /dev/rsd0c is 0
+(direct access device):
+.Bd -literal -offset
+root# scsi -f /dev/rsd0c -c "12 0 0 0 64 0" -i 64 "*b3 b5"
+0
+.Ed
+.Pp
+To do an inquiry to /dev/rsd2c:
+.Bd -literal -offset
+root# scsi -f /dev/rsd2c -c "12 0 0 0 64 0" -i 64 "s8 z8 z16 z4"
+FUJITSU M2654S-512 010P
+.Pp
+To edit mode page 1 on /dev/rsd2c, and store it permanently on the
+drive:
+.Bd -literal -offset
+root# scsi -f /dev/rsd2c -m 1 -e -P 3
+.Ed
+.Pp
+.Sh ENVIRONMENT
+The SU_DEBUG_OUTPUT variable can be set to a file to send debugging
+output to that file.
+.Pp
+The SU_DEBUG_LEVEL variable can be set to a non-zero integer to increase
+the level of debugging. Currently this is a on or off thing; it should
+perhaps use the ioctl to set the debug level in the kernel and then set
+it back to zero at program exit.
+.Pp
+The SU_DEBUG_TRUNCATE variable can be set to an integer to limit the
+amount of data phase output sent to the debugging file.
+.Pp
+The EDITOR variable determines the editor to use for the mode editor.
+.Sh SEE ALSO
+.Xr scsi 4 ,
+.Xr scsi 3
+.Sh BUGS
+.Pp
+This command wasn't ready for inclusion in 2.0R and so is missing in
+that release.
+.Pp
+Some devices respond to an inquiry for all LUNS. This will cause them
+to come on line to 8 times during reprobe to different logical units.
+.Pp
+The "-i" option to do an inquiry went away in 2.1. The new facilities
+provided by "-c" supercede that.
+.Pp
+Check your permissions carefully.
+"scsi -f /dev/rsd0c -c "4 0 0 0 0 0" permits anyone who can open
+/dev/rsd0c to format the disk drive. This must be changed to
+at least require write access to the drive.
+.Sh HISTORY
+The
+.Nm scsi
+command appeared in 386BSD 0.1.2.4/FreeBSD to support the new reprobe
+and user SCSI commands.
diff --git a/sbin/scsi/scsi.c b/sbin/scsi/scsi.c
new file mode 100644
index 0000000..eb399a1
--- /dev/null
+++ b/sbin/scsi/scsi.c
@@ -0,0 +1,974 @@
+/*
+ * Written By Julian ELischer
+ * Copyright julian Elischer 1993.
+ * Permission is granted to use or redistribute this file in any way as long
+ * as this notice remains. Julian Elischer does not guarantee that this file
+ * is totally correct for any given task and users of this file must
+ * accept responsibility for any damage that occurs from the application of this
+ * file.
+ *
+ * (julian@tfs.com julian@dialix.oz.au)
+ *
+ * User SCSI hooks added by Peter Dufault:
+ *
+ * Copyright (c) 1994 HD Associates
+ * (contact: dufault@hda.com)
+ * 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. The name of HD Associates
+ * may not be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/scsiio.h>
+#include <sys/file.h>
+#include <scsi.h>
+#include <ctype.h>
+#include <signal.h>
+
+int fd;
+int debuglevel;
+int debugflag;
+int commandflag;
+int reprobe;
+int probe_all;
+int verbose = 0;
+int bus = -1; /* all busses */
+int targ = -1; /* all targs */
+int lun = 0; /* just lun 0 */
+int freeze = 0; /* Freeze this many seconds */
+
+int modeflag;
+int editflag;
+int modepage = 0; /* Read this mode page */
+int pagectl = 0; /* Mode sense page control */
+int seconds = 2;
+
+void usage(void)
+{
+ printf(
+
+"Usage:\n"
+"\n"
+" scsi -f device -d debug_level # To set debug level\n"
+" scsi -f device [-v] -z seconds # To freeze bus\n"
+" scsi -f device -m page [-P pc] # To read mode pages\n"
+" scsi -f device -p [-b bus] [-l lun] # To probe all devices\n"
+" scsi -f device -r [-b bus] [-t targ] [-l lun] # To reprobe a device\n"
+" scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...\n"
+" -o count out_fmt [arg0 ... argn] # EITHER (data out)\n"
+" -i count in_fmt # OR (data in)\n"
+"\n"
+"\"out_fmt\" can be \"-\" to read output data from stdin;\n"
+"\"in_fmt\" can be \"-\" to write input data to stdout;\n"
+"\n"
+"If debugging is not compiled in the kernel, \"-d\" will have no effect\n"
+
+);
+
+ exit (1);
+}
+
+void procargs(int *argc_p, char ***argv_p)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ extern char *optarg;
+ extern int optind;
+ int fflag,
+ ch;
+
+ fflag = 0;
+ commandflag = 0;
+ debugflag = 0;
+ while ((ch = getopt(argc, argv, "ceprvf:d:b:t:l:z:m:P:s:")) != EOF) {
+ switch (ch) {
+ case 'p':
+ probe_all = 1;
+ break;
+ case 'r':
+ reprobe = 1;
+ break;
+ case 'c':
+ commandflag = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'e':
+ editflag = 1;
+ break;
+ case 'f':
+ if ((fd = scsi_open(optarg, O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ "%s: unable to open device %s: %s\n",
+ argv[0], optarg, strerror(errno));
+ exit(errno);
+ }
+ fflag = 1;
+ break;
+ case 'd':
+ debuglevel = strtol(optarg, 0, 0);
+ debugflag = 1;
+ break;
+ case 'b':
+ bus = strtol(optarg, 0, 0);
+ break;
+ case 't':
+ targ = strtol(optarg, 0, 0);
+ break;
+ case 'l':
+ lun = strtol(optarg, 0, 0);
+ break;
+ case 'z':
+ freeze = strtol(optarg, 0, 0);
+ break;
+ case 'P':
+ pagectl = strtol(optarg, 0, 0);
+ break;
+ case 's':
+ seconds = strtol(optarg, 0, 0);
+ break;
+ case 'm':
+ modeflag = 1;
+ modepage = strtol(optarg, 0, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ *argc_p = argc - optind;
+ *argv_p = argv + optind;
+
+ if (!fflag) usage();
+}
+
+/* get_hook: Structure for evaluating args in a callback.
+ */
+struct get_hook
+{
+ int argc;
+ char **argv;
+ int got;
+};
+
+/* iget: Integer argument callback
+ */
+int iget(void *hook, char *name)
+{
+ struct get_hook *h = (struct get_hook *)hook;
+ int arg;
+
+ if (h->got >= h->argc)
+ {
+ fprintf(stderr, "Expecting an integer argument.\n");
+ usage();
+ }
+ arg = strtol(h->argv[h->got], 0, 0);
+ h->got++;
+
+ if (verbose && name && *name)
+ printf("%s: %d\n", name, arg);
+
+ return arg;
+}
+
+/* cget: char * argument callback
+ */
+char *cget(void *hook, char *name)
+{
+ struct get_hook *h = (struct get_hook *)hook;
+ char *arg;
+
+ if (h->got >= h->argc)
+ {
+ fprintf(stderr, "Expecting a character pointer argument.\n");
+ usage();
+ }
+ arg = h->argv[h->got];
+ h->got++;
+
+ if (verbose && name)
+ printf("cget: %s: %s", name, arg);
+
+ return arg;
+}
+
+/* arg_put: "put argument" callback
+ */
+void arg_put(void *hook, int letter, void *arg, int count, char *name)
+{
+ if (verbose && name && *name)
+ printf("%s: ", name);
+
+ switch(letter)
+ {
+ case 'i':
+ case 'b':
+ printf("%d ", (int)arg);
+ break;
+
+ case 'c':
+ case 'z':
+ {
+ char *p = malloc(count + 1);
+ p[count] = 0;
+ strncpy(p, (char *)arg, count);
+ if (letter == 'z')
+ {
+ int i;
+ for (i = count - 1; i >= 0; i--)
+ if (p[i] == ' ')
+ p[i] = 0;
+ else
+ break;
+ }
+ printf("%s ", p);
+ }
+
+ break;
+
+ default:
+ printf("Unknown format letter: '%c'\n", letter);
+ }
+ if (verbose)
+ putchar('\n');
+}
+
+int arg_get (void *hook, char *field_name)
+{
+ printf("get \"%s\".\n", field_name);
+ return 0;
+}
+
+/* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer.
+ */
+enum data_phase {none = 0, in, out};
+
+/* do_cmd: Send a command to a SCSI device
+ */
+static void
+do_cmd(int fd, char *fmt, int argc, char **argv)
+{
+ struct get_hook h;
+ scsireq_t *scsireq = scsireq_new();
+ enum data_phase data_phase;
+ int count, amount;
+ char *data_fmt, *bp;
+
+ h.argc = argc;
+ h.argv = argv;
+ h.got = 0;
+
+ scsireq_reset(scsireq);
+
+ scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h);
+
+ /* Three choices here:
+ * 1. We've used up all the args and have no data phase.
+ * 2. We have input data ("-i")
+ * 3. We have output data ("-o")
+ */
+
+ if (h.got >= h.argc)
+ {
+ data_phase = none;
+ count = scsireq->datalen = 0;
+ }
+ else
+ {
+ char *flag = cget(&h, 0);
+
+ if (strcmp(flag, "-o") == 0)
+ {
+ data_phase = out;
+ scsireq->flags = SCCMD_WRITE;
+ }
+ else if (strcmp(flag, "-i") == 0)
+ {
+ data_phase = in;
+ scsireq->flags = SCCMD_READ;
+ }
+ else
+ {
+ fprintf(stderr,
+ "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag);
+ usage();
+ }
+
+ count = scsireq->datalen = iget(&h, 0);
+ if (count)
+ {
+ data_fmt = cget(&h, 0);
+
+ scsireq->databuf = malloc(count);
+
+ if (data_phase == out)
+ {
+ if (strcmp(data_fmt, "-") == 0) /* Read data from stdin */
+ {
+ bp = (char *)scsireq->databuf;
+ while (count > 0 && (amount = read(0, bp, count)) > 0)
+ {
+ count -= amount;
+ bp += amount;
+ }
+ if (amount == -1)
+ {
+ perror("read");
+ exit(errno);
+ }
+ else if (amount == 0)
+ {
+ /* early EOF */
+ fprintf(stderr,
+ "Warning: only read %lu bytes out of %lu.\n",
+ scsireq->datalen - (u_long)count,
+ scsireq->datalen);
+ scsireq->datalen -= (u_long)count;
+ }
+ }
+ else
+ {
+ bzero(scsireq->databuf, count);
+ scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h);
+ }
+ }
+ }
+ }
+
+
+ scsireq->timeout = seconds * 1000;
+
+ if (scsireq_enter(fd, scsireq) == -1)
+ {
+ scsi_debug(stderr, -1, scsireq);
+ exit(errno);
+ }
+
+ if (SCSIREQ_ERROR(scsireq))
+ scsi_debug(stderr, 0, scsireq);
+
+ if (count && data_phase == in)
+ {
+ if (strcmp(data_fmt, "-") == 0) /* stdout */
+ {
+ bp = (char *)scsireq->databuf;
+ while (count > 0 && (amount = write(1, bp, count)) > 0)
+ {
+ count -= amount;
+ bp += amount;
+ }
+ if (amount < 0)
+ {
+ perror("write");
+ exit(errno);
+ }
+ else if (amount == 0)
+ fprintf(stderr, "Warning: wrote only %d bytes out of %d.\n",
+ scsireq->datalen - count,
+ scsireq->datalen);
+
+ }
+ else
+ {
+ scsireq_decode_visit(scsireq, data_fmt, arg_put, 0);
+ putchar('\n');
+ }
+ }
+}
+
+static void
+freeze_ioctl(int fd, int op, void *data)
+{
+ if (ioctl(fd, SCIOCFREEZE, 0) == -1) {
+ if (errno == ENODEV) {
+ fprintf(stderr,
+ "Your kernel must be configured with option SCSI_FREEZE.\n");
+ }
+ else
+ perror("SCIOCFREEZE");
+ exit(errno);
+ }
+}
+
+/* do_freeze: Freeze the bus for a given number of seconds.
+ */
+static void do_freeze(int seconds)
+{
+ if (seconds == -1) {
+ printf("Hit return to thaw: ");
+ fflush(stdout);
+ sync();
+
+ freeze_ioctl(fd, SCIOCFREEZE, 0);
+
+ (void)getchar();
+
+ freeze_ioctl(fd, SCIOCTHAW, 0);
+ }
+ else {
+ sync();
+ freeze_ioctl(fd, SCIOCFREEZETHAW, &seconds);
+ if (verbose) {
+ putchar('\007');
+ fflush(stdout);
+ }
+
+ freeze_ioctl(fd, SCIOCWAITTHAW, 0);
+ if (verbose) {
+ putchar('\007');
+ fflush(stdout);
+ }
+ }
+}
+
+void mode_sense(int fd, u_char *data, int len, int pc, int page)
+{
+ scsireq_t *scsireq;
+
+ bzero(data, len);
+
+ scsireq = scsireq_new();
+
+ if (scsireq_enter(fd, scsireq_build(scsireq,
+ len, data, SCCMD_READ,
+ "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0",
+ pc, page, len)) == -1) /* Mode sense */
+ {
+ scsi_debug(stderr, -1, scsireq);
+ exit(errno);
+ }
+
+ if (SCSIREQ_ERROR(scsireq))
+ {
+ scsi_debug(stderr, 0, scsireq);
+ exit(-1);
+ }
+
+ free(scsireq);
+}
+
+void mode_select(int fd, u_char *data, int len, int perm)
+{
+ scsireq_t *scsireq;
+
+ scsireq = scsireq_new();
+
+ if (scsireq_enter(fd, scsireq_build(scsireq,
+ len, data, SCCMD_WRITE,
+ "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1) /* Mode select */
+ {
+ scsi_debug(stderr, -1, scsireq);
+ exit(errno);
+ }
+
+ if (SCSIREQ_ERROR(scsireq))
+ {
+ scsi_debug(stderr, 0, scsireq);
+ exit(-1);
+ }
+
+ free(scsireq);
+}
+
+
+#define START_ENTRY '{'
+#define END_ENTRY '}'
+
+static void
+skipwhite(FILE *f)
+{
+ int c;
+
+skip_again:
+
+ while (isspace(c = getc(f)))
+ ;
+
+ if (c == '#') {
+ while ((c = getc(f)) != '\n' && c != EOF)
+ ;
+ goto skip_again;
+ }
+
+ ungetc(c, f);
+}
+
+/* mode_lookup: Lookup a format description for a given page.
+ */
+char *mode_db = "/usr/share/misc/scsi_modes";
+static char *mode_lookup(int page)
+{
+ char *new_db;
+ FILE *modes;
+ int match, next, found, c;
+ static char fmt[1024]; /* XXX This should be with strealloc */
+ int page_desc;
+ new_db = getenv("SCSI_MODES");
+
+ if (new_db)
+ mode_db = new_db;
+
+ modes = fopen(mode_db, "r");
+ if (modes == 0)
+ return 0;
+
+ next = 0;
+ found = 0;
+
+ while (!found) {
+
+ skipwhite(modes);
+
+ if (fscanf(modes, "%i", &page_desc) != 1)
+ break;
+
+ if (page_desc == page)
+ found = 1;
+
+ skipwhite(modes);
+ if (getc(modes) != START_ENTRY) {
+ fprintf(stderr, "Expected %c.\n", START_ENTRY);
+ exit(-1);
+ }
+
+ match = 1;
+ while (match != 0) {
+ c = getc(modes);
+ if (c == EOF) {
+ fprintf(stderr, "Expected %c.\n", END_ENTRY);
+ }
+
+ if (c == START_ENTRY) {
+ match++;
+ }
+ if (c == END_ENTRY) {
+ match--;
+ if (match == 0)
+ break;
+ }
+ if (found && c != '\n') {
+ if (next >= sizeof(fmt)) {
+ fprintf(stderr, "Stupid program: Buffer overflow.\n");
+ exit(ENOMEM);
+ }
+
+ fmt[next++] = (u_char)c;
+ }
+ }
+ }
+ fmt[next] = 0;
+
+ return (found) ? fmt : 0;
+}
+
+/* -------- edit: Mode Select Editor ---------
+ */
+struct editinfo
+{
+ int can_edit;
+ int default_value;
+} editinfo[64]; /* XXX Bogus fixed size */
+
+static int editind;
+volatile int edit_opened;
+static FILE *edit_file;
+static char edit_name[L_tmpnam];
+
+static inline void
+edit_rewind(void)
+{
+ editind = 0;
+}
+
+static void
+edit_done(void)
+{
+ int opened;
+
+ sigset_t all, prev;
+ sigfillset(&all);
+
+ (void)sigprocmask(SIG_SETMASK, &all, &prev);
+
+ opened = (int)edit_opened;
+ edit_opened = 0;
+
+ (void)sigprocmask(SIG_SETMASK, &prev, 0);
+
+ if (opened)
+ {
+ if (fclose(edit_file))
+ perror(edit_name);
+ if (unlink(edit_name))
+ perror(edit_name);
+ }
+}
+
+static void
+edit_init(void)
+{
+ edit_rewind();
+ if (tmpnam(edit_name) == 0) {
+ perror("tmpnam failed");
+ exit(errno);
+ }
+ if ( (edit_file = fopen(edit_name, "w")) == 0) {
+ perror(edit_name);
+ exit(errno);
+ }
+ edit_opened = 1;
+
+ atexit(edit_done);
+}
+
+static void
+edit_check(void *hook, int letter, void *arg, int count, char *name)
+{
+ if (letter != 'i' && letter != 'b') {
+ fprintf(stderr, "Can't edit format %c.\n", letter);
+ exit(-1);
+ }
+
+ if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) {
+ fprintf(stderr, "edit table overflow\n");
+ exit(ENOMEM);
+ }
+ editinfo[editind].can_edit = ((int)arg != 0);
+ editind++;
+}
+
+static void
+edit_defaults(void *hook, int letter, void *arg, int count, char *name)
+{
+ if (letter != 'i' && letter != 'b') {
+ fprintf(stderr, "Can't edit format %c.\n", letter);
+ exit(-1);
+ }
+
+ editinfo[editind].default_value = ((int)arg);
+ editind++;
+}
+
+static void
+edit_report(void *hook, int letter, void *arg, int count, char *name)
+{
+ if (editinfo[editind].can_edit) {
+ if (letter != 'i' && letter != 'b') {
+ fprintf(stderr, "Can't report format %c.\n", letter);
+ exit(-1);
+ }
+
+ fprintf(edit_file, "%s: %d\n", name, (int)arg);
+ }
+
+ editind++;
+}
+
+static int
+edit_get(void *hook, char *name)
+{
+ struct get_hook *h = (struct get_hook *)hook;
+ int arg = editinfo[editind].default_value;
+
+ if (editinfo[editind].can_edit) {
+ char line[80];
+ if (fgets(line, sizeof(line), edit_file) == 0) {
+ perror("fgets");
+ exit(errno);
+ }
+
+ line[strlen(line) - 1] = 0;
+
+ if (strncmp(name, line, strlen(name)) != 0) {
+ fprintf(stderr, "Expected \"%s\" and read \"%s\"\n",
+ name, line);
+ exit(-1);
+ }
+
+ arg = strtoul(line + strlen(name) + 2, 0, 0);
+ }
+
+ editind++;
+ return arg;
+}
+
+static void
+edit_edit(void)
+{
+ char *system_line;
+ char *editor = getenv("EDITOR");
+ if (!editor)
+ editor = "vi";
+
+ fclose(edit_file);
+
+ system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
+ sprintf(system_line, "%s %s", editor, edit_name);
+ system(system_line);
+ free(system_line);
+
+ if ( (edit_file = fopen(edit_name, "r")) == 0) {
+ perror(edit_name);
+ exit(errno);
+ }
+}
+
+static void
+mode_edit(int fd, int page, int edit, int argc, char *argv[])
+{
+ int i;
+ u_char data[255];
+ u_char *mode_pars;
+ struct mode_header
+ {
+ u_char mdl; /* Mode data length */
+ u_char medium_type;
+ u_char dev_spec_par;
+ u_char bdl; /* Block descriptor length */
+ };
+
+ struct mode_page_header
+ {
+ u_char page_code;
+ u_char page_length;
+ };
+
+ struct mode_header *mh;
+ struct mode_page_header *mph;
+
+ char *fmt = mode_lookup(page);
+ if (!fmt && verbose) {
+ fprintf(stderr,
+ "No mode data base entry in \"%s\" for page %d; binary %s only.\n",
+ mode_db, page, (edit ? "edit" : "display"));
+ }
+
+ if (edit) {
+ if (!fmt) {
+ fprintf(stderr, "Sorry: can't edit without a format.\n");
+ exit(-1);
+ }
+
+ if (pagectl != 0 && pagectl != 3) {
+ fprintf(stderr,
+"It only makes sense to edit page 0 (current) or page 3 (saved values)\n");
+ exit(-1);
+ }
+
+ verbose = 1;
+
+ mode_sense(fd, data, sizeof(data), 1, page);
+
+ mh = (struct mode_header *)data;
+ mph = (struct mode_page_header *)
+ (((char *)mh) + sizeof(*mh) + mh->bdl);
+
+ mode_pars = (char *)mph + sizeof(*mph);
+
+ edit_init();
+ scsireq_buff_decode_visit(mode_pars, mh->mdl,
+ fmt, edit_check, 0);
+
+ mode_sense(fd, data, sizeof(data), 0, page);
+
+ edit_rewind();
+ scsireq_buff_decode_visit(mode_pars, mh->mdl,
+ fmt, edit_defaults, 0);
+
+ edit_rewind();
+ scsireq_buff_decode_visit(mode_pars, mh->mdl,
+ fmt, edit_report, 0);
+
+ edit_edit();
+
+ edit_rewind();
+ scsireq_buff_encode_visit(mode_pars, mh->mdl,
+ fmt, edit_get, 0);
+
+ /* Eliminate block descriptors:
+ */
+ bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
+ sizeof(*mph) + mph->page_length);
+
+ mh->bdl = mh->dev_spec_par = 0;
+ mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
+ mode_pars = ((char *)mph) + 2;
+
+#if 0
+ /* Turn this on to see what you're sending to the
+ * device:
+ */
+ edit_rewind();
+ scsireq_buff_decode_visit(mode_pars,
+ mh->mdl, fmt, arg_put, 0);
+#endif
+
+ edit_done();
+
+ /* Make it permanent if pageselect is three.
+ */
+
+ mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
+ mh->mdl = 0; /* Reserved for mode select */
+
+ mode_select(fd, (char *)mh,
+ sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length,
+ (pagectl == 3));
+
+ exit(0);
+ }
+
+ mode_sense(fd, data, sizeof(data), pagectl, page);
+
+ /* Skip over the block descriptors.
+ */
+ mh = (struct mode_header *)data;
+ mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
+ mode_pars = (char *)mph + sizeof(*mph);
+
+ if (!fmt) {
+ for (i = 0; i < mh->mdl; i++) {
+ printf("%02x%c",mode_pars[i],
+ (((i + 1) % 8) == 0) ? '\n' : ' ');
+ }
+ putc('\n', stdout);
+ } else {
+ verbose = 1;
+ scsireq_buff_decode_visit(mode_pars,
+ mh->mdl, fmt, arg_put, 0);
+ }
+}
+
+/* do_probe_all: Loop over all SCSI IDs and see if something is
+ * there. This only does BUS 0 LUN 0.
+ */
+void do_probe_all(void)
+{
+ scsireq_t *scsireq;
+
+ char vendor_id[8 + 1], product_id[16 + 1], revision[4 + 1];
+ int id;
+ u_char *inq_buf = malloc(96);
+ struct scsi_addr addr;
+
+ scsireq = scsireq_build(scsireq_new(),
+ 96, inq_buf, SCCMD_READ,
+ "12 0 0 0 v 0", 96);
+
+ addr.scbus = (bus == -1) ? 0 : bus;
+ addr.lun = lun;
+
+ if (addr.scbus || addr.lun)
+ {
+ printf("For bus %d lun %d:\n", addr.scbus, addr.lun);
+ }
+
+ for (id = 0; id < 8; id++)
+ {
+ addr.target = id;
+
+ printf("%d: ", id);
+ if (ioctl(fd, SCIOCADDR, &addr) == -1) {
+ if (errno == ENXIO)
+ {
+ errno = 0;
+ printf("nothing.\n");
+ }
+ else
+ printf("SCIOCADDR: %s\n", strerror(errno));
+
+ continue;
+ }
+
+ if (scsireq_enter(fd, scsireq) == -1) {
+ printf("scsireq_enter: %s\n", strerror(errno));
+ continue;
+ }
+
+ vendor_id[sizeof(vendor_id) - 1] = 0;
+ product_id[sizeof(product_id) - 1] = 0;
+ revision[sizeof(revision) - 1] = 0;
+
+ scsireq_decode(scsireq, "s8 c8 c16 c4",
+ vendor_id, product_id, revision);
+
+ printf("%s %s %s\n", vendor_id, product_id, revision);
+ }
+}
+
+void main(int argc, char **argv)
+{
+ struct scsi_addr scaddr;
+
+ procargs(&argc,&argv);
+
+ /* XXX This has grown to the point that it should be cleaned up.
+ */
+ if (freeze) {
+ do_freeze(freeze);
+ } else if (probe_all) {
+ do_probe_all();
+ } else if(reprobe) {
+ scaddr.scbus = bus;
+ scaddr.target = targ;
+ scaddr.lun = lun;
+
+ if (ioctl(fd,SCIOCREPROBE,&scaddr) == -1)
+ perror("ioctl");
+ } else if(debugflag) {
+ if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1)
+ {
+ perror("ioctl [SCIODEBUG]");
+ exit(1);
+ }
+ } else if (commandflag) {
+ int i;
+ char *fmt;
+
+ if (argc < 1) {
+ fprintf(stderr, "Need the command format string.\n");
+ usage();
+ }
+
+
+ fmt = argv[0];
+
+ argc -= 1;
+ argv += 1;
+
+ do_cmd(fd, fmt, argc, argv);
+ } else if (modeflag) {
+ mode_edit(fd, modepage, editflag, argc, argv);
+ }
+ exit(0);
+}
diff --git a/sbin/scsiformat/Makefile b/sbin/scsiformat/Makefile
new file mode 100644
index 0000000..d121e73
--- /dev/null
+++ b/sbin/scsiformat/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 5.3 (Berkeley) 6/5/93
+
+MAN8= scsiformat.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/scsiformat.sh ${DESTDIR}${BINDIR}/scsiformat
+
+.include <bsd.prog.mk>
diff --git a/sbin/scsiformat/scsiformat.8 b/sbin/scsiformat/scsiformat.8
new file mode 100644
index 0000000..d0f18c1
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.8
@@ -0,0 +1,102 @@
+.\" 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
+.\" $Id$
+.\"
+.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 qw
+.Op Fl p Ar page-control
+.Ar device-name
+.Sh DESCRIPTION
+The
+.Nm scsiformat
+utility can be used to format SCSI disks
+on systems that support on-line SCSI formatting.
+It will also show the various parameters set in the SCSI controller.
+The
+.Ar device-name
+should be either a disk name like
+.Ql sd0 ,
+or the path name of the control device for a disk, as in
+.Pa /dev/rsd0.ctl .
+Using a regular disk device may not work, for example in case of a
+device where the medium format is corrupted.
+.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 q
+Quiet; suppress the mode page output. Only the inquiry string will be
+printed, so the operator can ensure he's going to format the intended
+drive.
+.It Fl w
+Do actually format; by default, only the disk parameters will be
+displayed.
+.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 SEE ALSO
+.Xr scsi 8 .
+.Sh HISTORY
+The
+.Nm scsiformat
+utility first appeared in
+.Bx 4.4 .
+This version has been reworked as a wrapper around
+.Xr scsi 8
+by Peter Dufault for
+.Fx 2.1 .
diff --git a/sbin/scsiformat/scsiformat.sh b/sbin/scsiformat/scsiformat.sh
new file mode 100644
index 0000000..9d2c132
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+#
+# Copyright (c) 1995 Peter Dufault
+#
+# All rights reserved.
+#
+# This program is free software.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Id: scsiformat.sh,v 1.1 1995/09/17 12:47:01 joerg Exp $
+#
+# scsiformat [-qw] [-p page-control] raw-device-name
+#
+
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"; export PATH
+
+READONLY=yes
+QUIET=no
+RAW=
+PAGE=0
+
+usage()
+{
+ echo "usage: scsiformat [-qw] [-p page-control] raw-device-name" 1>&2
+ exit 2
+}
+
+while getopts "qwp:" option
+do
+ case $option in
+ q)
+ QUIET=yes
+ ;;
+ w)
+ READONLY=no
+ ;;
+ p)
+ case $OPTARG in
+ c)
+ PAGE=0
+ ;;
+ d)
+ PAGE=2
+ ;;
+ s)
+ PAGE=3
+ ;;
+ v)
+ PAGE=1
+echo "*** note: for variable parameters, 1-bit means 'can write here'"
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ ;;
+ ?)
+ usage
+ ;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+if [ $# -ne 1 ] ; then
+ usage
+fi
+
+RAW=$1
+
+if [ "x$RAW" = "x" ] ; then
+ usage
+fi
+
+if expr "$RAW" : 'sd[0-9][0-9]*$' > /dev/null ; then
+ # generic disk name given, convert to control device name
+ RAW="/dev/r${RAW}.ctl"
+fi
+
+scsi -f $RAW -v -c "12 0 0 0 v 0" 96 -i 96 "s8 z8 z16 z4" || exit $?
+
+if [ "$QUIET" = "no" ] ; then
+ scsi -f $RAW \
+-v -c "1A 0 v:2 4:6 0 64 0" $PAGE \
+-i 72 "{Mode data length} i1 \
+{Medium type} i1 \
+{Device Specific Parameter} i1 \
+{Block descriptor length} i1 \
+{Density code} i1 \
+{Number of blocks} i3 \
+{Reserved} i1 \
+{Block length} i3 \
+{PS} b1 \
+{Reserved} b1 \
+{Page code} b6 \
+{Page length} i1 \
+{Number of Cylinders} i3 \
+{Number of Heads} i1 \
+{Starting Cylinder-Write Precompensation} i3 \
+{Starting Cylinder-Reduced Write Current} i3 \
+{Drive Step Rate} i2 \
+{Landing Zone Cylinder} i3 \
+{Reserved} b6 \
+{RPL} b2 \
+{Rotational Offset} i1 \
+{Reserved} i1 \
+{Medium Rotation Rate} i2 \
+{Reserved} i1 \
+{Reserved} i1 " || exit $?
+fi # !quiet
+
+if [ "$READONLY" = "no" ]
+then
+ # grace period, last chance to hit INTR
+ echo -n "Three seconds until format begins."
+ sleep 1
+ echo -n "."
+ sleep 1
+ echo -n "."
+ sleep 1
+ # formatting may take a huge amount of time, set timeout to 2 hours
+ echo " Formatting... this may take a while."
+ scsi -s 7200 -f $RAW -c "4 0 0 0 0 0"
+fi
diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile
new file mode 100644
index 0000000..48847e4
--- /dev/null
+++ b/sbin/shutdown/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= shutdown
+MAN8= shutdown.8
+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 0000000..9d05838
--- /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 0000000..4819c8a
--- /dev/null
+++ b/sbin/shutdown/shutdown.8
@@ -0,0 +1,150 @@
+.\" 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 hkrn
+.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 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
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr wall 1 ,
+.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 0000000..e94e096
--- /dev/null
+++ b/sbin/shutdown/shutdown.c
@@ -0,0 +1,468 @@
+/*
+ * 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 <unistd.h>
+
+#include "pathnames.h"
+
+#ifdef DEBUG
+#undef _PATH_NOLOGIN
+#define _PATH_NOLOGIN "./nologin"
+#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 dohalt, doreboot, killflg, mbuflen;
+static char *nosync, *whom, mbuf[BUFSIZ];
+
+void badtime __P((void));
+void die_you_gravy_sucking_pig_dog __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, "-hknr")) != EOF)
+ switch (ch) {
+ case '-':
+ readstdin = 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 (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");
+ exit(0);
+ }
+#ifdef DEBUG
+ if (doreboot)
+ (void)printf("reboot");
+ else if (dohalt)
+ (void)printf("halt");
+ if (nosync)
+ (void)printf(" no sync");
+ (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 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 [-hknr] shutdowntime [ message ]\n");
+ exit(1);
+}
diff --git a/sbin/slattach/Makefile b/sbin/slattach/Makefile
new file mode 100644
index 0000000..4d288ec
--- /dev/null
+++ b/sbin/slattach/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 5.4 (Berkeley) 5/11/90
+#
+# $Header: /home/ncvs/src/sbin/slattach/Makefile,v 1.5 1995/09/19 03:27:23 ache Exp $
+
+PROG= slattach
+SRCS= slattach.c uucplock.c
+MAN8= slattach.8
+MLINKS= slattach.8 slip.8
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+.PATH: ${.CURDIR}/../startslip
+
+.include <bsd.prog.mk>
diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8
new file mode 100644
index 0000000..ef0d52a
--- /dev/null
+++ b/sbin/slattach/slattach.8
@@ -0,0 +1,263 @@
+.\" Copyright (c) 1986, 1991 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 6.4 (Berkeley) 3/16/91
+.\"
+.\" $Header: /home/ncvs/src/sbin/slattach/slattach.8,v 1.10 1996/02/17 19:21:40 ache Exp $
+.\"
+.Dd April 4, 1993
+.Dt SLATTACH 8
+.Os BSD 4.3
+.Sh NAME
+.Nm slattach
+.Nd attach serial lines as network interfaces
+.Sh SYNOPSIS
+.Nm slattach
+.Op Fl a
+.Op Fl c
+.Op Fl e Ar exit-command
+.Op Fl f
+.Op Fl h
+.Op Fl l
+.Op Fl n
+.Op Fl z
+.Op Fl L
+.Op Fl r Ar redial-command
+.Op Fl s Ar baudrate
+.Op Fl u Ar unit-command
+.Op Fl K Ar keepalive
+.Op Fl O Ar outfill
+.Op Fl S Ar unit
+.Ar ttyname
+.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 Fl a
+Autoenable the VJ header compression option, if the other end of the link
+is capable of VJ header compression then it will be used otherwise normal
+headers will be used.
+.It Fl c
+Enables the VJ header compression option. Note that both ends of the link
+must be able to use VJ header compression for this to work.
+.It Fl e Ar exit-command
+Specifies a command to be invoked within a shell
+.Ql sh \-c Ar exit-command
+before
+.Nm slattach
+exits.
+.It Fl f
+Disables the invocation of daemon() to run
+.Nm slattach
+in the background.
+.It Fl h
+Turn on cts/rts style flow control on the slip port, by default no flow
+control is done.
+.It Fl l
+disable modem control (CLOCAL) and ignore carrier detect on the slip
+port. By default the
+.Ar redial-command
+is invoked upon carrier drop and
+.Nm slattach
+aborts if no
+.Ar redial-command
+is specified.
+.It Fl n
+Throw away ICMP packets. The slip interface will ignore ICMP packets
+to prevent slow lines being saturated by ICMP responses.
+.It Fl r Ar redial-command
+Specifies a command to be invoked within a shell
+.Ql sh \-c Ar redial-command
+whenever carrier is lost on the modem line.
+Empty
+.Ar redial-command
+i.e.
+.Fl r Qq ""
+cause connection reestablishing on leased line
+without any external command invoked.
+.It Fl s Ar baudrate
+Specifies the speed of the connection. If not specified, the
+default of 9600 is used.
+.It Fl u Ar unit-command
+When the line is switched to slip discipline, run
+.Ql Nm "sh -c" Ar unit-command <old> <new>
+where
+.Ar <old>
+and
+.Ar <new>
+are the slip unit numbers when the line was
+last opened and the unit number of the current slip connection
+respectively. The unit number can change after redialing if you are
+using more than one slip line.
+When
+.Nm slattach
+connected first time, it run
+.Ql Nm "sh -c" Ar unit-command Nm \-1 Ar <new> .
+When
+.Nm slattach
+disconnected, it run
+.Ql Nm "sh -c" Ar unit-command <old> Nm \-1 .
+.Nm Slattach
+will abort if the unit number
+changes and
+.Ql Fl u Ar \%unit-command
+was not specified.
+.It Fl z
+forces redial
+.Ar redial-command
+upon startup irrespective of carrier.
+.It Fl L
+Use uucp-style device locking. You need it unless you start
+.Nm slattach
+from external program which already does uucp locking.
+Default case is no uucp locking to satisfy such programs.
+.It Fl K Ar keepalive
+Set SLIP "keep alive" timeout in seconds. If FRAME_END is not received in
+this amount of time, re-connect occurs.
+The default value is no timeout.
+.It Fl O Ar outfill
+Set SLIP "out fill" timeout in seconds. It forces at least one FRAME_END
+to be sent during this time period, which is necessary for the "keep alive"
+timeout on the remote side.
+The default value is no timeout.
+.It Fl S Ar unit
+Set the SLIP unit number directly. Use with caution, because no check is made
+for two interfaces with same number.
+By default sliplogin dynamically assigns the unit number.
+.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 .
+.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 using
+.Ql kill -INT .
+.Ar Interface-name
+is the name that is shown by
+.Xr netstat 1
+.Pp
+To setup
+.Nm slattach
+to redial the phone when carrier is lost, use the
+.Fl r Ar redial-command
+option to specify a script or executable that will reconnect the
+serial line to the slip server. For example, the script could redial
+the server and log in, etc.
+.Pp
+To reconfigure the network interface in case the slip unit number
+changes, use the
+.Fl u Ar unit-command
+option to specify a script or executable that will be invoked as
+.Ql sh \-c Ar unit-command old new
+where
+.Ar old
+and
+.Ar new
+are the slip unit numbers before and after
+reconnecting the line. The unit number can change if you have more
+than one line disconnect at the same time. The first to succeed in
+reconnecting will get the lowest unit number.
+.Pp
+To kill
+.Nm slattach
+use
+.Ql kill -INT
+(SIGINT) which causes it to close the tty and exit.
+.Pp
+To force a redial, use
+.Ql kill -HUP
+(SIGHUP) which causes
+.Nm slattach
+to think carrier was lost and thus invoke
+.Ql sh \-c Ar redial-command
+to reconnect to the server.
+.Pp
+If you use a hard-wired connection rather than a modem, invoke
+.Nm slattach
+with the
+.Fl l
+option in order to ignore carrier on the slip line.
+.Sh EXAMPLES
+.Bd -literal -offset indent -compact
+slattach ttyd8
+slattach \-s 4800 /dev/ttyd1
+slattach \-c \-s 38400 /dev/cuaa1
+slattach \-r 'kermit -y dial.script >kermit.log 2>&1'
+.Ed
+.Sh DIAGNOSTICS
+Look for error messages in /var/log/messages (
+.Nm slattach
+is a daemon).
+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 are logged there.
+.Nm Slattach
+also logs failure to set the controlling terminal or failure to install
+signal handlers. Upon connection and redial the ttyname and baud rate
+are logged and on shutdown the ttyname is logged.
+.Pp
+.Sh FILES
+.Pa /var/run/slattach.<tty>.pid ,
+.Pp
+with
+.Ar tty
+replaced by the terminal path name component of
+.Ar ttyname .
+This file contains the numerical process ID of the
+.Nm slattach
+process and can be examined by scripts in oder to send a signal to
+.Nm slattach .
+.Sh SEE ALSO
+.Xr startslip 1 ,
+.Xr sliplogin 8 ,
+.Xr netstat 1 ,
+.Xr netintro 4 ,
+.Xr ifconfig 8 ,
+.Xr rc 8 ,
+.Xr uustat 1 ,
+/usr/share/examples/slattach
+.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 0000000..9ab7b31
--- /dev/null
+++ b/sbin/slattach/slattach.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 1988 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 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)slattach.c 4.6 (Berkeley) 6/1/90";*/
+static char rcsid[] = "$Id: slattach.c,v 1.21 1996/12/10 17:07:44 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/slip.h>
+#include <net/if_slvar.h>
+
+#define DEFAULT_BAUD 9600
+
+void sighup_handler(); /* SIGHUP handler */
+void sigint_handler(); /* SIGINT handler */
+void sigterm_handler(); /* SIGTERM handler */
+void sigurg_handler(); /* SIGURG handler */
+void exit_handler(int ret); /* run exit_cmd iff specified upon exit. */
+void setup_line(int cflag); /* configure terminal settings */
+void slip_discipline(); /* switch to slip line discipline */
+void configure_network(); /* configure slip interface */
+void acquire_line(); /* get tty device as controling terminal */
+
+int fd = -1;
+char *dev = (char *)0; /* path name of the tty (e.g. /dev/tty01) */
+char *dvname; /* basename of dev */
+int locked = 0; /* uucp lock active */
+int flow_control = 0; /* non-zero to enable hardware flow control. */
+int modem_control = HUPCL; /* !CLOCAL+HUPCL iff we watch carrier. */
+int comstate; /* TIOCMGET current state of serial driver */
+int redial_on_startup = 0; /* iff non-zero execute redial_cmd on startup */
+speed_t speed = DEFAULT_BAUD; /* baud rate of tty */
+int slflags = 0; /* compression flags */
+int unit = -1; /* slip device unit number */
+int foreground = 0; /* act as demon if zero, else don't fork. */
+int keepal = 0; /* keepalive timeout */
+int outfill = 0; /* outfill timeout */
+int sl_unit = -1; /* unit number */
+int uucp_lock = 0; /* do uucp locking */
+int exiting = 0; /* allready running exit_handler */
+
+struct termios tty; /* tty configuration/state */
+
+char tty_path[32]; /* path name of the tty (e.g. /dev/tty01) */
+char pidfilename[40]; /* e.g. /var/run/slattach.tty01.pid */
+char *redial_cmd = 0; /* command to exec upon shutdown. */
+char *config_cmd = 0; /* command to exec if slip unit changes. */
+char *exit_cmd = 0; /* command to exec before exiting. */
+
+static char usage_str[] = "\
+usage: %s [-acfhlnz] [-e command] [-r command] [-s speed] [-u command] \\\n\
+ [-L] [-K timeout] [-O timeout] [-S unit] device\n\
+ -a -- autoenable VJ compression\n\
+ -c -- enable VJ compression\n\
+ -e ECMD -- run ECMD before exiting\n\
+ -f -- run in foreground (don't detach from controlling tty)\n\
+ -h -- turn on cts/rts style flow control\n\
+ -l -- disable modem control (CLOCAL) and ignore carrier detect\n\
+ -n -- throw out ICMP packets\n\
+ -r RCMD -- run RCMD upon loss of carrier\n\
+ -s # -- set baud rate (default 9600)\n\
+ -u UCMD -- run 'UCMD <old sl#> <new sl#>' before switch to slip discipline\n\
+ -z -- run RCMD upon startup irrespective of carrier\n\
+ -L -- do uucp-style device locking\n\
+ -K # -- set SLIP \"keep alive\" timeout (default 0)\n\
+ -O # -- set SLIP \"out fill\" timeout (default 0)\n\
+ -S # -- set SLIP unit number (default is dynamic)\n\
+";
+
+int main(int argc, char **argv)
+{
+ int option;
+ extern char *optarg;
+ extern int optind;
+
+ while ((option = getopt(argc, argv, "ace:fhlnr:s:u:zLK:O:S:")) != EOF) {
+ switch (option) {
+ case 'a':
+ slflags |= IFF_LINK2;
+ slflags &= ~IFF_LINK0;
+ break;
+ case 'c':
+ slflags |= IFF_LINK0;
+ slflags &= ~IFF_LINK2;
+ break;
+ case 'e':
+ exit_cmd = (char*) strdup (optarg);
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'h':
+ flow_control |= CRTSCTS;
+ break;
+ case 'l':
+ modem_control = CLOCAL; /* clear HUPCL too */
+ break;
+ case 'n':
+ slflags |= IFF_LINK1;
+ break;
+ case 'r':
+ redial_cmd = (char*) strdup (optarg);
+ break;
+ case 's':
+ speed = atoi(optarg);
+ break;
+ case 'u':
+ config_cmd = (char*) strdup (optarg);
+ break;
+ case 'z':
+ redial_on_startup = 1;
+ break;
+ case 'L':
+ uucp_lock = 1;
+ break;
+ case 'K':
+ keepal = atoi(optarg);
+ break;
+ case 'O':
+ outfill = atoi(optarg);
+ break;
+ case 'S':
+ sl_unit = atoi(optarg);
+ break;
+ default:
+ fprintf(stderr, "%s: Invalid option -- '%c'\n",
+ argv[0], option);
+ case '?':
+ fprintf(stderr, usage_str, argv[0]);
+ exit_handler(1);
+ }
+ }
+
+ if (optind == argc - 1)
+ dev = argv[optind];
+
+ if (optind < (argc - 1)) {
+ fprintf(stderr, "%s: Too many args, first='%s'\n",
+ argv[0], argv[optind]);
+ }
+ if (optind > (argc - 1)) {
+ fprintf(stderr, "%s: Not enough args\n", argv[0]);
+ }
+ if (dev == (char *)0) {
+ fprintf(stderr, usage_str, argv[0]);
+ exit_handler(2);
+ }
+ if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
+ strcpy(tty_path, _PATH_DEV);
+ strcat(tty_path, "/");
+ strncat(tty_path, dev, 10);
+ dev = tty_path;
+ }
+ dvname = strrchr(dev, '/'); /* always succeeds */
+ dvname++; /* trailing tty pathname component */
+ sprintf(pidfilename, "%sslattach.%s.pid", _PATH_VARRUN, dvname);
+ printf("%s\n",pidfilename);
+
+ if (!foreground)
+ daemon(0,0); /* fork, setsid, chdir /, and close std*. */
+ /* daemon() closed stderr, so log errors from here on. */
+ openlog("slattach",LOG_CONS|LOG_PID,LOG_DAEMON);
+
+ acquire_line(); /* get tty device as controling terminal */
+ setup_line(0); /* configure for slip line discipline */
+ slip_discipline(); /* switch to slip line discipline */
+
+ /* upon INT log a timestamp and exit. */
+ if ((int)signal(SIGINT,sigint_handler) < 0)
+ syslog(LOG_NOTICE,"cannot install SIGINT handler: %m");
+ /* upon TERM log a timestamp and exit. */
+ if ((int)signal(SIGTERM,sigterm_handler) < 0)
+ syslog(LOG_NOTICE,"cannot install SIGTERM handler: %m");
+ /* upon HUP redial and reconnect. */
+ if ((int)signal(SIGHUP,sighup_handler) < 0)
+ syslog(LOG_NOTICE,"cannot install SIGHUP handler: %m");
+
+ if (redial_on_startup)
+ sighup_handler();
+ else if (!(modem_control & CLOCAL)) {
+ if (ioctl(fd, TIOCMGET, &comstate) < 0)
+ syslog(LOG_NOTICE,"cannot get carrier state: %m");
+ if (!(comstate & TIOCM_CD)) { /* check for carrier */
+ /* force a redial if no carrier */
+ kill (getpid(), SIGHUP);
+ } else
+ configure_network();
+ } else
+ configure_network(); /* configure the network if needed. */
+
+ for (;;) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+ }
+}
+
+/* Close all FDs, fork, reopen tty port as 0-2, and make it the
+ controlling terminal for our process group. */
+void acquire_line()
+{
+ int ttydisc = TTYDISC;
+ int oflags;
+ FILE *pidfile;
+
+ /* reset to tty discipline */
+ if (fd >= 0 && ioctl(fd, TIOCSETD, &ttydisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+
+ (void)close(STDIN_FILENO); /* close FDs before forking. */
+ (void)close(STDOUT_FILENO);
+ (void)close(STDERR_FILENO);
+ if (fd > 2)
+ (void)close(fd);
+
+ signal(SIGHUP, SIG_IGN); /* ignore HUP signal when parent dies. */
+ if (daemon(0,0)) { /* fork, setsid, chdir /, and close std*. */
+ syslog(LOG_ERR, "daemon(0,0): %m");
+ exit_handler(1);
+ }
+
+ while (getppid () != 1)
+ sleep (1); /* Wait for parent to die. */
+
+ /* create PID file */
+ if((pidfile = fopen(pidfilename, "w"))) {
+ fprintf(pidfile, "%ld\n", getpid());
+ fclose(pidfile);
+ }
+
+ if ((int)signal(SIGHUP,sighup_handler) < 0) /* Re-enable HUP signal */
+ syslog(LOG_NOTICE,"cannot install SIGHUP handler: %m");
+
+ if (uucp_lock) {
+ /* unlock not needed here, always re-lock with new pid */
+ if (uu_lock(dvname)) {
+ syslog(LOG_ERR, "can't lock %s", dev);
+ exit_handler(1);
+ }
+ locked = 1;
+ }
+
+ if ((fd = open(dev, O_RDWR | O_NONBLOCK, 0)) < 0) {
+ syslog(LOG_ERR, "open(%s) %m", dev);
+ exit_handler(1);
+ }
+ /* Turn off O_NONBLOCK for dumb redialers, if any. */
+ if ((oflags = fcntl(fd, F_GETFL)) == -1) {
+ syslog(LOG_ERR, "fcntl(F_GETFL) failed: %m");
+ exit_handler(1);
+ }
+ if (fcntl(fd, F_SETFL, oflags & ~O_NONBLOCK) == -1) {
+ syslog(LOG_ERR, "fcntl(F_SETFL) failed: %m");
+ exit_handler(1);
+ }
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ fd = STDIN_FILENO;
+
+ /* acquire the serial line as a controling terminal. */
+ if (ioctl(fd, TIOCSCTTY, 0) < 0) {
+ syslog(LOG_ERR,"ioctl(TIOCSCTTY): %m");
+ exit_handler(1);
+ }
+ /* Make us the foreground process group associated with the
+ slip line which is our controlling terminal. */
+ if (tcsetpgrp(fd, getpid()) < 0)
+ syslog(LOG_NOTICE,"tcsetpgrp failed: %m");
+}
+
+/* Set the tty flags and set DTR. */
+/* Call as setup_line(CLOCAL) to force clocal assertion. */
+void setup_line(int cflag)
+{
+ tty.c_lflag = tty.c_iflag = tty.c_oflag = 0;
+ tty.c_cflag = CREAD | CS8 | flow_control | modem_control | cflag;
+ cfsetispeed(&tty, speed);
+ cfsetospeed(&tty, speed);
+ /* set the line speed and flow control */
+ if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSAFLUSH): %m");
+ exit_handler(1);
+ }
+ /* set data terminal ready */
+ if (ioctl(fd, TIOCSDTR) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSDTR): %m");
+ exit_handler(1);
+ }
+}
+
+/* Put the line in slip discipline. */
+void slip_discipline()
+{
+ struct ifreq ifr;
+ int slipdisc = SLIPDISC;
+ int s, tmp_unit = -1;
+
+ /* Switch to slip line discipline. */
+ if (ioctl(fd, TIOCSETD, &slipdisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+
+ if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSUNIT): %m");
+ exit_handler(1);
+ }
+
+ /* find out what unit number we were assigned */
+ if (ioctl(fd, SLIOCGUNIT, (caddr_t)&tmp_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCGUNIT): %m");
+ exit_handler(1);
+ }
+
+ if (tmp_unit < 0) {
+ syslog(LOG_ERR, "bad unit (%d) from ioctl(SLIOCGUNIT)",tmp_unit);
+ exit_handler(1);
+ }
+
+ if (keepal > 0) {
+ signal(SIGURG, sigurg_handler);
+ if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
+ exit_handler(1);
+ }
+ }
+ if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
+ exit_handler(1);
+ }
+
+ /* open a socket as the handle to the interface */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit_handler(1);
+ }
+ sprintf(ifr.ifr_name, "sl%d", tmp_unit);
+
+ /* get the flags for the interface */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ exit_handler(1);
+ }
+
+ /* Assert any compression or no-icmp flags. */
+#define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
+ ifr.ifr_flags &= SLMASK;
+ ifr.ifr_flags |= slflags;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
+ exit_handler(1);
+ }
+ close(s);
+}
+
+/* configure the interface, eg. by passing the unit number to a script. */
+void configure_network()
+{
+ int new_unit;
+
+ /* find out what unit number we were assigned */
+ if (ioctl(fd, SLIOCGUNIT, (caddr_t)&new_unit) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCGUNIT): %m");
+ exit_handler(1);
+ }
+ /* iff the unit number changes either invoke config_cmd or punt. */
+ if (config_cmd) {
+ char *s;
+ s = (char*) malloc(strlen(config_cmd) + 32);
+ sprintf (s, "%s %d %d", config_cmd, unit, new_unit);
+ syslog(LOG_NOTICE, "Configuring %s (sl%d):", dev, unit);
+ syslog(LOG_NOTICE, " '%s'", s);
+ system(s);
+ free (s);
+ unit = new_unit;
+ } else {
+ /* don't compare unit numbers if this is the first time to attach. */
+ if (unit < 0)
+ unit = new_unit;
+ if (new_unit != unit) {
+ syslog(LOG_ERR, "slip unit changed from sl%d to sl%d, but no -u CMD was specified!");
+ exit_handler(1);
+ }
+ syslog(LOG_NOTICE,"sl%d connected to %s at %d baud",unit,dev,speed);
+ }
+}
+
+/* sighup_handler() is invoked when carrier drops, eg. before redial. */
+void sighup_handler()
+{
+ if(exiting) return;
+
+ if (redial_cmd == NULL) {
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); exiting", dev, unit);
+ exit_handler(1);
+ }
+again:
+ /* invoke a shell for redial_cmd or punt. */
+ if (*redial_cmd) {
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); running '%s'",
+ dev, unit, redial_cmd);
+ acquire_line(); /* reopen dead line */
+ setup_line(CLOCAL);
+ if (locked) {
+ if (uucp_lock)
+ uu_unlock(dvname); /* for redial */
+ locked = 0;
+ }
+ if (system(redial_cmd))
+ goto again;
+ if (uucp_lock) {
+ if (uu_lock(dvname)) {
+ syslog(LOG_ERR, "can't relock %s after %s, aborting",
+ dev, redial_cmd);
+ exit_handler(1);
+ }
+ locked = 1;
+ }
+ /* Now check again for carrier (dial command is done): */
+ if (!(modem_control & CLOCAL)) {
+ tty.c_cflag &= ~CLOCAL;
+ if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSAFLUSH): %m");
+ exit_handler(1);
+ }
+ ioctl(fd, TIOCMGET, &comstate);
+ if (!(comstate & TIOCM_CD)) { /* check for carrier */
+ /* force a redial if no carrier */
+ goto again;
+ }
+ } else
+ setup_line(0);
+ } else { /* Empty redial command */
+ syslog(LOG_NOTICE,"SIGHUP on %s (sl%d); reestablish connection",
+ dev, unit);
+ acquire_line(); /* reopen dead line */
+ setup_line(0); /* restore ospeed from hangup (B0) */
+ /* If modem control, just wait for carrier before attaching.
+ If no modem control, just fall through immediately. */
+ if (!(modem_control & CLOCAL)) {
+ int carrier = 0;
+
+ syslog(LOG_NOTICE, "Waiting for carrier on %s (sl%d)",
+ dev, unit);
+ /* Now wait for carrier before attaching line. */
+ /* We must poll since CLOCAL prevents signal. */
+ while (! carrier) {
+ sleep(2);
+ ioctl(fd, TIOCMGET, &comstate);
+ if (comstate & TIOCM_CD)
+ carrier = 1;
+ }
+ syslog(LOG_NOTICE, "Carrier now present on %s (sl%d)",
+ dev, unit);
+ }
+ }
+ slip_discipline();
+ configure_network();
+}
+/* Signal handler for SIGINT. We just log and exit. */
+void sigint_handler()
+{
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGINT on %s (sl%d); exiting",dev,unit);
+ exit_handler(0);
+}
+/* Signal handler for SIGURG. */
+void sigurg_handler()
+{
+ int ttydisc = TTYDISC;
+
+ signal(SIGURG, SIG_IGN);
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGURG on %s (sl%d); hangup",dev,unit);
+ if (ioctl(fd, TIOCSETD, &ttydisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit_handler(1);
+ }
+ cfsetospeed(&tty, B0);
+ if (tcsetattr(fd, TCSANOW, &tty) < 0) {
+ syslog(LOG_ERR, "tcsetattr(TCSANOW): %m");
+ exit_handler(1);
+ }
+ /* Need to go to sighup handler in any case */
+ if (modem_control & CLOCAL)
+ kill (getpid(), SIGHUP);
+
+}
+/* Signal handler for SIGTERM. We just log and exit. */
+void sigterm_handler()
+{
+ if(exiting) return;
+ syslog(LOG_NOTICE,"SIGTERM on %s (sl%d); exiting",dev,unit);
+ exit_handler(0);
+}
+/* Run config_cmd if specified before exiting. */
+void exit_handler(int ret)
+{
+ if(exiting) return;
+ exiting = 1;
+ /*
+ * First close the slip line in case exit_cmd wants it (like to hang
+ * up a modem or something).
+ */
+ if (fd != -1)
+ close(fd);
+ if (uucp_lock && locked)
+ uu_unlock(dvname);
+
+ /* Remove the PID file */
+ (void)unlink(pidfilename);
+
+ if (config_cmd) {
+ char *s;
+ s = (char*) malloc(strlen(config_cmd) + 32);
+ sprintf (s, "%s %d -1", config_cmd, unit);
+ syslog(LOG_NOTICE, "Deconfiguring %s (sl%d):", dev, unit);
+ syslog(LOG_NOTICE, " '%s'", s);
+ system(s);
+ free (s);
+ }
+ /* invoke a shell for exit_cmd. */
+ if (exit_cmd) {
+ syslog(LOG_NOTICE,"exiting after running %s", exit_cmd);
+ system(exit_cmd);
+ }
+ exit(ret);
+}
+
+/* local variables: */
+/* c-indent-level: 8 */
+/* c-argdecl-indent: 0 */
+/* c-label-offset: -8 */
+/* c-continued-statement-offset: 8 */
+/* c-brace-offset: 0 */
+/* comment-column: 32 */
+/* end: */
diff --git a/sbin/startslip/Makefile b/sbin/startslip/Makefile
new file mode 100644
index 0000000..f010138
--- /dev/null
+++ b/sbin/startslip/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= startslip
+SRCS= startslip.c uucplock.c
+
+.include <bsd.prog.mk>
diff --git a/sbin/startslip/startslip.1 b/sbin/startslip/startslip.1
new file mode 100644
index 0000000..8ca28de
--- /dev/null
+++ b/sbin/startslip/startslip.1
@@ -0,0 +1,200 @@
+.\" 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 b Ar speed
+[
+.Fl s Ar string1
+.Op Fl s Ar string2 Op Ar ...
+]
+.Op Fl h
+.Op Fl l
+.Op Fl L
+.Op Fl A Ar annexname
+.Op Fl U Ar upscript
+.Op Fl D Ar downscript
+.Op Fl t Ar script_timeout
+.Op Fl W Ar maxtries
+.Op Fl w Ar retry_pause
+.Op Fl K Ar keepalive
+.Op Fl O Ar outfill
+.Op Fl S Ar unit
+.Ar device user passwd
+.Sh DESCRIPTION
+.Pp
+.Nm Startslip
+opens the specified
+.Ar device .
+Once carrier is asserted (if modem control is enabled)
+.Nm startslip
+attempts to login as the specified
+.Ar user
+with the given
+.Ar password .
+Standard login: & Password: scheme assumed unless Annex option
+specified (see
+.Fl A ) .
+If successful, it puts the device into the slip line discipline,
+calls
+.Ar upscript
+and goes to wait mode.
+If carrier drops (modem control enabled) or a
+.Dv SIGHUP
+is sent to
+.Nm startslip ,
+it closes the device, calls
+.Ar downscript
+and attempts to repeat the dialup and login sequence.
+If a
+.Dv SIGTERM
+is send to
+.Nm startslip ,
+it closes the device, calls
+.Ar downscript
+and exits.
+When
+.Nm startslip
+called twice on the same device, previous copy killed by a
+.Dv SIGTERM
+before any operation.
+.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 b Ar speed
+Determines the baud rate used for
+.Ar device .
+Default value is 9600.
+.It Fl t Ar script_timeout
+Set login script timeout in seconds, default value is 90.
+.It Fl w Ar retry_pause
+Set connecting retry pause in seconds (increased from one retry to another),
+default value is 60.
+.It Fl W Ar maxtries
+Set maximum number of connection retries for one session,
+default value is 6.
+Value 0 means infinite count.
+.It Fl s Ar stringN
+The optional
+.Ar stringN
+is written to
+.Ar device .
+Return character is added automatically.
+For a dialup modem,
+the string is used to specify a dial sequence.
+No string written by default.
+You can specify several
+.Fl s Ar stringN
+arguments to use with each try, f.e. several host phone numbers.
+.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 h
+Use hardware (CTS/RTS) flow control for
+.Ar device .
+Default value is no flow control.
+.It Fl l
+Disable modem control (waiting for carrier and carrier drop sense) for
+.Ar device .
+Modem control is enabled by default.
+.It Fl U Ar upscript
+Specifies a script to run when a SLIP interface becomes connected. This may
+contain ifconfig, route, and other appropriate commands. The arguments that
+are passed to the script are "slX up".
+Default value is "/sbin/ifconfig".
+Dial sequence number (see
+.Fl s )
+passed via
+.Dv LINE
+environment variable.
+.It Fl D Ar downscript
+Specifies a script to run when a SLIP connection goes away. The arguments that
+are passed to the script are "slX down". Default value is "/sbin/ifconfig".
+Dial sequence number (see
+.Fl s )
+passed via
+.Dv LINE
+environment variable.
+.It Fl K Ar keepalive
+Set SLIP "keep alive" timeout in seconds. If FRAME_END not received during this
+time period, a reconnect will occur. Active "out fill" timeout expected from other
+side.
+Default value is no timeout.
+.It Fl O Ar outfill
+Set SLIP "out fill" timeout in seconds. It causes at least one FRAME_END
+to be sent during this timeout.
+Needed for "keep alive" timeout on other side.
+Default value is no timeout.
+.It Fl S Ar unit
+Set SLIP unit number directly. Use with caution, no check for two
+interfaces with same number made.
+Default is dynamic assignment.
+.It Fl L
+Use uucp-style device locking. You need it unless you start
+.Nm startslip
+from external program which already does uucp locking.
+Default case is no uucp locking to satisfy such programs.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/startslip.<device>.pid -compact
+.It Pa /var/run/startslip.<device>.pid
+pid stored here
+.Sh SEE ALSO
+.Xr sliplogin 8 ,
+.Xr slattach 8 ,
+.Xr uustat 1 ,
+/usr/share/examples/startslip
+.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 0000000..222d163
--- /dev/null
+++ b/sbin/startslip/startslip.c
@@ -0,0 +1,590 @@
+/*-
+ * 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.
+ *
+ * $Id: startslip.c,v 1.18 1995/09/27 17:15:37 ache Exp $
+ */
+
+#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/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_slvar.h>
+#include <net/slip.h>
+
+#define DEFAULT_BAUD B9600
+int speed = DEFAULT_BAUD;
+#define FC_NONE 0 /* flow control: none */
+#define FC_HW 1 /* flow control: hardware (RTS/CTS) */
+int flowcontrol = FC_NONE;
+int modem_control = 1; /* !CLOCAL+HUPCL iff we watch carrier. */
+int sl_unit = -1;
+int uucp_lock = 0; /* uucp locking */
+char *annex;
+char *username;
+int hup;
+int terminate;
+int locked = 0; /* uucp lock active */
+int logged_in = 0;
+int wait_time = 60; /* then back off */
+int script_timeout = 90; /* connect script default timeout */
+time_t conn_time, start_time;
+int MAXTRIES = 6; /* w/60 sec and doubling, takes an hour */
+#define PIDFILE "%sstartslip.%s.pid"
+
+#define MAXDIALS 20
+char *dials[MAXDIALS];
+int diali, dialc;
+
+int fd = -1;
+FILE *pfd;
+char *dvname, *devicename;
+char pidfile[80];
+
+#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;
+ void sighup(), sigterm(), sigurg();
+ FILE *wfd = NULL;
+ char *dialerstring = 0, buf[BUFSIZ];
+ int unitnum, keepal = 0, outfill = 0;
+ char unitname[32];
+ char *password;
+ char *upscript = NULL, *downscript = NULL;
+ int first = 1, tries = 0;
+ time_t fintimeout;
+ pid_t pid;
+ struct termios t;
+
+ while ((ch = getopt(argc, argv, "dhlb:s:t:w:A:U:D:W:K:O:S:L")) != EOF)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ speed = atoi(optarg);
+ break;
+ case 's':
+ if (diali >= MAXDIALS) {
+ (void)fprintf(stderr,
+ "max dial strings number (%d) exceeded\n", MAXDIALS);
+ exit(1);
+ }
+ dials[diali++] = strdup(optarg);
+ break;
+ case 't':
+ script_timeout = atoi(optarg);
+ break;
+ case 'w':
+ wait_time = atoi(optarg);
+ break;
+ case 'W':
+ MAXTRIES = atoi(optarg);
+ break;
+ case 'A':
+ annex = strdup(optarg);
+ break;
+ case 'U':
+ upscript = strdup(optarg);
+ break;
+ case 'D':
+ downscript = strdup(optarg);
+ break;
+ case 'L':
+ uucp_lock = 1;
+ break;
+ case 'l':
+ modem_control = 0;
+ break;
+ case 'h':
+ flowcontrol = FC_HW;
+ break;
+ case 'K':
+ keepal = atoi(optarg);
+ break;
+ case 'O':
+ outfill = atoi(optarg);
+ break;
+ case 'S':
+ sl_unit = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 3)
+ usage();
+
+ /*
+ * Copy these so they exist after we clobber them.
+ */
+ devicename = strdup(argv[0]);
+ username = strdup(argv[1]);
+ password = strdup(argv[2]);
+
+ /*
+ * 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';
+
+ openlog("startslip", LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+ if (debug)
+ setbuf(stdout, NULL);
+
+ signal(SIGTERM, sigterm);
+ if ((dvname = strrchr(devicename, '/')) == NULL)
+ dvname = devicename;
+ else
+ dvname++;
+ sprintf(pidfile, PIDFILE, _PATH_VARRUN, dvname);
+ if ((pfd = fopen(pidfile, "r")) != NULL) {
+ pid = 0;
+ fscanf(pfd, "%ld\n", &pid);
+ if (pid > 0)
+ kill(pid, SIGTERM);
+ fclose(pfd);
+ pfd = NULL; /* not remove pidfile yet */
+ sleep(5); /* allow down script to be completed */
+ } else
+restart:
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGURG, SIG_IGN);
+ hup = 0;
+ if (wfd) {
+ printd("fclose, ");
+ fclose(wfd);
+ conn_time = time(NULL) - start_time;
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ wfd = NULL;
+ fd = -1;
+ sleep(5);
+ } else if (fd >= 0) {
+ printd("close, ");
+ close(fd);
+ conn_time = time(NULL) - start_time;
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ fd = -1;
+ sleep(5);
+ }
+ if (logged_in) {
+ syslog(LOG_INFO, "%s: connection time elapsed: %ld secs", username, conn_time);
+ sprintf(buf, "LINE=%d %s %s down",
+ diali ? (dialc - 1) % diali : 0,
+ downscript ? downscript : "/sbin/ifconfig" , unitname);
+ (void) system(buf);
+ logged_in = 0;
+ }
+ if (terminate)
+ down(0);
+ tries++;
+ if (MAXTRIES > 0 && tries > MAXTRIES) {
+ syslog(LOG_ERR, "%s: exiting login after %d tries", username, tries);
+ /* ???
+ if (first)
+ */
+ down(3);
+ }
+ if (tries > 1) {
+ syslog(LOG_INFO, "%s: sleeping %d seconds (%d tries)",
+ username, wait_time * (tries - 1), tries);
+ sleep(wait_time * (tries - 1));
+ if (terminate)
+ goto restart;
+ }
+
+ if (daemon(1, debug) < 0) {
+ syslog(LOG_ERR, "%s: daemon: %m", username);
+ down(2);
+ }
+
+ pid = getpid();
+ printd("restart: pid %ld: ", pid);
+ if ((pfd = fopen(pidfile, "w")) != NULL) {
+ fprintf(pfd, "%ld\n", pid);
+ fclose(pfd);
+ }
+ printd("open");
+ if (uucp_lock) {
+ if (uu_lock(dvname)) {
+ syslog(LOG_ERR, "%s: can't lock %s", username, devicename);
+ goto restart;
+ }
+ locked = 1;
+ }
+ if ((fd = open(devicename, O_RDWR | O_NONBLOCK)) < 0) {
+ syslog(LOG_ERR, "%s: open %s: %m", username, devicename);
+ if (first)
+ down(1);
+ else {
+ if (uucp_lock)
+ uu_unlock(dvname);
+ locked = 0;
+ goto restart;
+ }
+ }
+ printd(" %d", fd);
+ signal(SIGHUP, sighup);
+ if (ioctl(fd, TIOCSCTTY, 0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (TIOCSCTTY): %m", username);
+ down(2);
+ }
+ if (tcsetpgrp(fd, getpid()) < 0) {
+ syslog(LOG_ERR, "%s: tcsetpgrp failed: %m", username);
+ down(2);
+ }
+ printd(", ioctl\n");
+ if (tcgetattr(fd, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ cfmakeraw(&t);
+ switch (flowcontrol) {
+ case FC_HW:
+ t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW);
+ break;
+ case FC_NONE:
+ t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW);
+ break;
+ }
+ if (modem_control)
+ t.c_cflag |= HUPCL;
+ else
+ t.c_cflag &= ~(HUPCL);
+ t.c_cflag |= CLOCAL; /* until modem commands passes */
+ cfsetispeed(&t, speed);
+ cfsetospeed(&t, speed);
+ if (tcsetattr(fd, TCSAFLUSH, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ sleep(2); /* wait for flakey line to settle */
+ if (hup || terminate)
+ goto restart;
+
+ wfd = fdopen(fd, "w+");
+ if (wfd == NULL) {
+ syslog(LOG_ERR, "%s: can't fdopen %s: %m", username, devicename);
+ down(2);
+ }
+ setbuf(wfd, NULL);
+
+ if (diali > 0)
+ dialerstring = dials[dialc++ % diali];
+ if (dialerstring) {
+ syslog(LOG_INFO, "%s: dialer string: %s\\r", username, dialerstring);
+ fprintf(wfd, "%s\r", dialerstring);
+ }
+ printd("\n");
+
+ fintimeout = time(NULL) + script_timeout;
+ if (modem_control) {
+ printd("waiting for carrier\n");
+ while (time(NULL) < fintimeout && !carrier()) {
+ sleep(1);
+ if (hup || terminate)
+ goto restart;
+ }
+ if (!carrier())
+ goto restart;
+ t.c_cflag &= ~(CLOCAL);
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
+ down(2);
+ }
+ /* Only now we able to receive HUP on carier drop! */
+ }
+
+ /*
+ * Log in
+ */
+ printd("look for login: ");
+ for (;;) {
+ if (getline(buf, BUFSIZ, fd, fintimeout) == 0 || hup || terminate)
+ 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", username);
+ printd("Sent login: %s\n", username);
+ continue;
+ }
+ if (bcmp(&buf[1], "assword:", 8) == 0) {
+ fprintf(wfd, "%s\r", password);
+ printd("Sent password: %s\n", password);
+ break;
+ }
+ } else {
+ if (strstr(&buf[1], "ogin:") != NULL) {
+ fprintf(wfd, "%s\r", username);
+ printd("Sent login: %s\n", username);
+ continue;
+ }
+ if (strstr(&buf[1], "assword:") != NULL) {
+ fprintf(wfd, "%s\r", password);
+ printd("Sent password: %s\n", password);
+ break;
+ }
+ }
+ }
+
+ sleep(5); /* Wait until login completed */
+ if (hup || terminate)
+ goto restart;
+ start_time = time(NULL);
+ /*
+ * Attach
+ */
+ printd("setd");
+ disc = SLIPDISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (%s, TIOCSETD): %m",
+ username, devicename);
+ down(2);
+ }
+ if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSUNIT): %m", username);
+ down(2);
+ }
+ if (ioctl(fd, SLIOCGUNIT, &unitnum) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCGUNIT): %m", username);
+ down(2);
+ }
+ sprintf(unitname, "sl%d", unitnum);
+
+ if (keepal > 0) {
+ signal(SIGURG, sigurg);
+ if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSKEEPAL): %m", username);
+ down(2);
+ }
+ }
+ if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(SLIOCSOUTFILL): %m", username);
+ down(2);
+ }
+
+ sprintf(buf, "LINE=%d %s %s up",
+ diali ? (dialc - 1) % diali : 0,
+ upscript ? upscript : "/sbin/ifconfig" , unitname);
+ (void) system(buf);
+
+ printd(", ready\n");
+ if (!first)
+ syslog(LOG_INFO, "%s: reconnected on %s (%d tries)", username, unitname, tries);
+ else
+ syslog(LOG_INFO, "%s: connected on %s", username, unitname);
+ first = 0;
+ tries = 0;
+ logged_in = 1;
+ while (hup == 0 && terminate == 0) {
+ sigpause(0L);
+ printd("sigpause return\n");
+ }
+ goto restart;
+}
+
+void
+sighup()
+{
+
+ printd("hup\n");
+ if (hup == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got hangup signal", username);
+ hup = 1;
+}
+
+void
+sigurg()
+{
+
+ printd("urg\n");
+ if (hup == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got dead line signal", username);
+ hup = 1;
+}
+
+void
+sigterm()
+{
+
+ printd("terminate\n");
+ if (terminate == 0 && logged_in)
+ syslog(LOG_INFO, "%s: got terminate signal", username);
+ terminate = 1;
+}
+
+getline(buf, size, fd, fintimeout)
+ char *buf;
+ int size, fd;
+ time_t fintimeout;
+{
+ register int i;
+ int ret;
+ fd_set readfds;
+ struct timeval tv;
+ time_t timeout;
+
+ size--;
+ for (i = 0; i < size; i++) {
+ if (hup || terminate)
+ return (0);
+ if ((timeout = fintimeout - time(NULL)) <= 0)
+ goto tout;
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if ((ret = select(fd + 1, &readfds, NULL, NULL, &tv)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "%s: getline: select: %m", username);
+ } else {
+ if (! ret) {
+ tout:
+ printd("getline: timed out\n");
+ return (0);
+ }
+ if ((ret = read(fd, &buf[i], 1)) == 1) {
+ buf[i] &= 0177;
+ if (buf[i] == '\r' || buf[i] == '\0') {
+ i--;
+ continue;
+ }
+ if (buf[i] != '\n' && buf[i] != ':')
+ continue;
+ buf[i + 1] = '\0';
+ printd("Got %d: %s", i + 1, buf);
+ return (i+1);
+ }
+ if (ret <= 0) {
+ if (ret < 0) {
+ syslog(LOG_ERR, "%s: getline: read: %m", username);
+ } else
+ syslog(LOG_ERR, "%s: read returned 0", username);
+ buf[i] = '\0';
+ printd("returning %d after %d: %s\n", ret, i, buf);
+ return (0);
+ }
+ }
+ }
+ return (0);
+}
+
+carrier()
+{
+ int comstate;
+
+ if (ioctl(fd, TIOCMGET, &comstate) < 0) {
+ syslog(LOG_ERR, "%s: ioctl (%s, TIOCMGET): %m",
+ username, devicename);
+ down(2);
+ }
+ return !!(comstate & TIOCM_CD);
+}
+
+down(code)
+{
+ if (fd > -1)
+ close(fd);
+ if (pfd)
+ unlink(pidfile);
+ if (uucp_lock && locked)
+ uu_unlock(dvname);
+ exit(code);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "\
+usage: startslip [-d] [-b speed] [-s string1 [-s string2 [...]]] [-A annexname] \\\n\
+ [-h] [-l] [-U upscript] [-D downscript] [-t script_timeout] [-L]\\\n\
+ [-w retry_pause] [-W maxtries] [-K keepalive] [-O outfill] [-S unit] \\\n\
+ device user passwd\n");
+ exit(1);
+}
diff --git a/sbin/startslip/uucplock.c b/sbin/startslip/uucplock.c
new file mode 100644
index 0000000..20eafe4
--- /dev/null
+++ b/sbin/startslip/uucplock.c
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)uucplock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <dirent.h>
+#include <errno.h>
+#ifndef USE_PERROR
+#include <syslog.h>
+#endif
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+
+#define LOCKFMT "LCK..%s"
+
+/* Forward declarations */
+static int put_pid (int fd, pid_t pid);
+static pid_t get_pid (int fd);
+
+/*
+ * uucp style locking routines
+ * return: 0 - success
+ * -1 - failure
+ */
+
+int uu_lock (char *ttyname)
+{
+ int fd;
+ pid_t pid;
+ char tbuf[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ (void)sprintf(tbuf, _PATH_UUCPLOCK LOCKFMT, ttyname);
+ fd = open(tbuf, O_RDWR|O_CREAT|O_EXCL, 0660);
+ if (fd < 0) {
+ /*
+ * file is already locked
+ * check to see if the process holding the lock still exists
+ */
+ fd = open(tbuf, O_RDWR, 0);
+ if (fd < 0) {
+#ifndef USE_PERROR
+ syslog(LOG_ERR, "lock open: %m");
+#else
+ perror("lock open");
+#endif
+ return(-1);
+ }
+ if ((pid = get_pid (fd)) == -1) {
+#ifndef USE_PERROR
+ syslog(LOG_ERR, "lock read: %m");
+#else
+ perror("lock read");
+#endif
+ (void)close(fd);
+ return(-1);
+ }
+
+ if (kill(pid, 0) == 0 || errno != ESRCH) {
+ (void)close(fd); /* process is still running */
+ return(-1);
+ }
+ /*
+ * The process that locked the file isn't running, so
+ * we'll lock it ourselves
+ */
+ if (lseek(fd, (off_t) 0, L_SET) < 0) {
+#ifndef USE_PERROR
+ syslog(LOG_ERR, "lock lseek: %m");
+#else
+ perror("lock lseek");
+#endif
+ (void)close(fd);
+ return(-1);
+ }
+ /* fall out and finish the locking process */
+ }
+ pid = getpid();
+ if (!put_pid (fd, pid)) {
+#ifndef USE_PERROR
+ syslog(LOG_ERR, "lock write: %m");
+#else
+ perror("lock write");
+#endif
+ (void)close(fd);
+ (void)unlink(tbuf);
+ return(-1);
+ }
+ (void)close(fd);
+ return(0);
+}
+
+int uu_unlock (char *ttyname)
+{
+ char tbuf[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ (void)sprintf(tbuf, _PATH_UUCPLOCK LOCKFMT, ttyname);
+ return(unlink(tbuf));
+}
+
+static int put_pid (int fd, pid_t pid)
+{
+ char buf[32];
+ int len;
+
+ len = sprintf (buf, "%10d\n", pid);
+ return write (fd, buf, len) == len;
+}
+
+static pid_t get_pid (int fd)
+{
+ int bytes_read;
+ char buf[32];
+ pid_t pid;
+
+ bytes_read = read (fd, buf, sizeof (buf) - 1);
+ if (bytes_read > 0) {
+ buf[bytes_read] = '\0';
+ pid = strtol (buf, (char **) NULL, 10);
+ } else
+ pid = -1;
+ return pid;
+}
+
+/* end of uucplock.c */
diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile
new file mode 100644
index 0000000..ed5aebc
--- /dev/null
+++ b/sbin/swapon/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= swapon
+MAN8= swapon.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/swapon/swapon.8 b/sbin/swapon/swapon.8
new file mode 100644
index 0000000..979f77a
--- /dev/null
+++ b/sbin/swapon/swapon.8
@@ -0,0 +1,91 @@
+.\" 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
+.\" $Id$
+.\"
+.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 unless their ``noauto'' option is also set.
+.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 5 ,
+.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 0000000..2a07b53
--- /dev/null
+++ b/sbin/swapon/swapon.c
@@ -0,0 +1,122 @@
+/*
+ * 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>
+#include <stdlib.h>
+#include <unistd.h>
+
+void usage __P((void));
+int add __P((char *name, int ignoreebusy));
+
+int
+main(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()) != NULL) {
+ if (strcmp(fsp->fs_type, FSTAB_SW))
+ continue;
+ if (strstr(fsp->fs_mntops, "noauto"))
+ 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);
+}
+
+int
+add(char *name, int ignoreebusy)
+{
+ extern int errno;
+
+ if (swapon(name) == -1) {
+ switch (errno) {
+ 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);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: swapon [-a] [special_file ...]\n");
+ exit(1);
+}
diff --git a/sbin/sysctl/Makefile b/sbin/sysctl/Makefile
new file mode 100644
index 0000000..8d94c66
--- /dev/null
+++ b/sbin/sysctl/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sysctl
+MAN8= sysctl.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/sysctl/pathconf.c b/sbin/sysctl/pathconf.c
new file mode 100644
index 0000000..4b60ccf
--- /dev/null
+++ b/sbin/sysctl/pathconf.c
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)pathconf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PC_NAMES { \
+ { 0, 0 }, \
+ { "link_max", CTLTYPE_INT }, \
+ { "max_canon", CTLTYPE_INT }, \
+ { "max_input", CTLTYPE_INT }, \
+ { "name_max", CTLTYPE_INT }, \
+ { "path_max", CTLTYPE_INT }, \
+ { "pipe_buf", CTLTYPE_INT }, \
+ { "chown_restricted", CTLTYPE_INT }, \
+ { "no_trunc", CTLTYPE_INT }, \
+ { "vdisable", CTLTYPE_INT }, \
+}
+#define PC_MAXID 10
+
+struct ctlname pcnames[] = PC_NAMES;
+char names[BUFSIZ];
+
+struct list {
+ struct ctlname *list;
+ int size;
+};
+struct list pclist = { pcnames, PC_MAXID };
+
+int Aflag, aflag, nflag, wflag, stdinflag;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ char *path;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "Aan")) != EOF) {
+ switch (ch) {
+
+ case 'A':
+ Aflag = 1;
+ break;
+
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'n':
+ nflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+ path = *argv++;
+ if (strcmp(path, "-") == 0)
+ stdinflag = 1;
+ argc--;
+ if (Aflag || aflag) {
+ listall(path, &pclist);
+ exit(0);
+ }
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(path, *argv, 1);
+ exit(0);
+}
+
+/*
+ * List all variables known to the system.
+ */
+listall(path, lp)
+ char *path;
+ struct list *lp;
+{
+ int lvl2;
+
+ if (lp->list == 0)
+ return;
+ for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
+ if (lp->list[lvl2].ctl_name == 0)
+ continue;
+ parse(path, lp->list[lvl2].ctl_name, Aflag);
+ }
+}
+
+/*
+ * Parse a name into an index.
+ * Lookup and print out the attribute if it exists.
+ */
+parse(pathname, string, flags)
+ char *pathname;
+ char *string;
+ int flags;
+{
+ int indx, value;
+ char *bufp, buf[BUFSIZ];
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((indx = findname(string, "top", &bufp, &pclist)) == -1)
+ return;
+ if (bufp) {
+ fprintf(stderr, "name %s in %s is unknown\n", *bufp, string);
+ return;
+ }
+ if (stdinflag)
+ value = fpathconf(0, indx);
+ else
+ value = pathconf(pathname, indx);
+ if (value == -1) {
+ if (flags == 0)
+ return;
+ switch (errno) {
+ case EOPNOTSUPP:
+ fprintf(stderr, "%s: value is not available\n", string);
+ return;
+ case ENOTDIR:
+ fprintf(stderr, "%s: specification is incomplete\n",
+ string);
+ return;
+ case ENOMEM:
+ fprintf(stderr, "%s: type is unknown to this program\n",
+ string);
+ return;
+ default:
+ perror(string);
+ return;
+ }
+ }
+ if (!nflag)
+ fprintf(stdout, "%s = ", string);
+ fprintf(stdout, "%d\n", value);
+}
+
+/*
+ * Scan a list of names searching for a particular name.
+ */
+findname(string, level, bufp, namelist)
+ char *string;
+ char *level;
+ char **bufp;
+ struct list *namelist;
+{
+ char *name;
+ int i;
+
+ if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
+ fprintf(stderr, "%s: incomplete specification\n", string);
+ return (-1);
+ }
+ for (i = 0; i < namelist->size; i++)
+ if (namelist->list[i].ctl_name != NULL &&
+ strcmp(name, namelist->list[i].ctl_name) == 0)
+ break;
+ if (i == namelist->size) {
+ fprintf(stderr, "%s level name %s in %s is invalid\n",
+ level, name, string);
+ return (-1);
+ }
+ return (i);
+}
+
+usage()
+{
+
+ (void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n",
+ "pathname [-n] variable ...",
+ "pathname [-n] -a", "pathname [-n] -A");
+ exit(1);
+}
diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8
new file mode 100644
index 0000000..3c20dd8
--- /dev/null
+++ b/sbin/sysctl/sysctl.8
@@ -0,0 +1,242 @@
+.\" 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.
+.\"
+.\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93
+.\" $Id: sysctl.8,v 1.9 1996/08/23 00:56:57 mpp Exp $
+.\"
+.Dd September 23, 1994
+.Dt SYSCTL 8
+.Os
+.Sh NAME
+.Nm sysctl
+.Nd get or set kernel state
+.Sh SYNOPSIS
+.Nm sysctl
+.Op Fl bn
+.Ar name ...
+.Nm sysctl
+.Op Fl bn
+.Fl w
+.Ar name=value ...
+.Nm sysctl
+.Op Fl bn
+.Fl aAX
+.Sh DESCRIPTION
+The
+.Nm sysctl
+utility retrieves kernel state and allows processes with
+appropriate privilege to set kernel state.
+The state to be retrieved or set is described using a
+``Management Information Base'' (``MIB'') style name,
+described as a dotted set of components.
+.Pp
+The
+.Fl a
+flag can be used to list all the currently available string or integer values.
+.Pp
+The
+.Fl A
+flag will list all the known MIB names including opaques.
+Those with string or integer values will be printed as with the
+.Fl a
+flag; for the opaque values,
+information about the format and the length is printed in addition the first
+few bytes is dumped in hex.
+.Pp
+The
+.Fl X
+flag is the same as
+.Fl A
+except the entire value of opaque variables is hexdumped.
+.Pp
+The
+.Fl n
+flag specifies that the printing of the field name should be
+suppressed and that only its value should be output.
+This flag is useful for setting shell variables.
+For example, to save the pagesize in variable psize, use:
+.Bd -literal -offset indent -compact
+set psize=`sysctl -n hw.pagesize`
+.Ed
+.Pp
+The
+.Fl b
+flag forces the value of the variable(s) to be output in raw, binary
+format. No names are printed and no terminating newlines are output.
+This is mostly useful with a single variable.
+.Pp
+If just a MIB style name is given,
+the corresponding value is retrieved.
+If a value is to be set, the
+.Fl w
+flag must be specified and the MIB name followed
+by an equal sign and the new value to be used.
+.Pp
+The information available from
+.Nm sysctl
+consists of integers, strings, and opaques.
+.Nm sysctl
+only knows about a couple of opaque types, and will resort to hexdumps
+for the rest.
+The opaque information is much more useful if retrieved by special
+purpose programs such as
+.Nm ps ,
+.Nm systat ,
+and
+.Nm netstat .
+.Pp
+The string and integer information is summarized below.
+For a detailed description of these variable see
+.Xr sysctl 3 .
+.Pp
+The changeable column indicates whether a process with appropriate
+privilege can change the value.
+.Bl -column net.inet.ip.forwardingxxxxxx integerxxx
+.It Sy Name Type Changeable
+.It kern.ostype string no
+.It kern.osrelease string no
+.It kern.osrevision integer no
+.It kern.version string no
+.It kern.maxvnodes integer yes
+.It kern.maxproc integer yes
+.It kern.maxprocperuid integer yes
+.It kern.maxfiles integer yes
+.It kern.maxfilesperproc integer yes
+.It kern.argmax integer no
+.It kern.securelevel integer raise only
+.It kern.hostname string yes
+.It kern.hostid integer yes
+.It kern.clockrate struct no
+.It kern.posix1version integer no
+.It kern.ngroups integer no
+.It kern.job_control integer no
+.It kern.saved_ids integer no
+.It kern.boottime struct no
+.It kern.domainname string yes
+.It kern.update integer yes
+.It kern.osreldate string no
+.It kern.bootfile string yes
+.It vm.loadavg struct no
+.It hw.machine string no
+.It hw.model string no
+.It hw.ncpu integer no
+.It hw.byteorder integer no
+.It hw.physmem integer no
+.It hw.usermem integer no
+.It hw.pagesize integer no
+.It hw.floatingpoint integer no
+.It machdep.console_device dev_t no
+.It machdep.adjkerntz integer yes
+.It machdep.disable_rtc_set integer yes
+.It user.cs_path string no
+.It user.bc_base_max integer no
+.It user.bc_dim_max integer no
+.It user.bc_scale_max integer no
+.It user.bc_string_max integer no
+.It user.coll_weights_max integer no
+.It user.expr_nest_max integer no
+.It user.line_max integer no
+.It user.re_dup_max integer no
+.It user.posix2_version integer no
+.It user.posix2_c_bind integer no
+.It user.posix2_c_dev integer no
+.It user.posix2_char_term integer no
+.It user.posix2_fort_dev integer no
+.It user.posix2_fort_run integer no
+.It user.posix2_localedef integer no
+.It user.posix2_sw_dev integer no
+.It user.posix2_upe integer no
+.It user.stream_max integer no
+.It user.tzname_max integer no
+.El
+.Sh EXAMPLES
+.Pp
+For example, to retrieve the maximum number of processes allowed
+in the system, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl kern.maxproc
+.Ed
+.Pp
+To set the maximum number of processes allowed
+in the system to 1000, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl -w kern.maxproc=1000
+.Ed
+.Pp
+Information about the system clock rate may be obtained with:
+.Bd -literal -offset indent -compact
+sysctl kern.clockrate
+.Ed
+.Pp
+Information about the load average history may be obtained with
+.Bd -literal -offset indent -compact
+sysctl vm.loadavg
+.Ed
+.Pp
+More variables than these exist, and the best and likely only place
+to search for their deeper meaning is undoubtedly the source where
+they are defined.
+.Sh FILES
+.Bl -tag -width <netinet/icmpXvar.h> -compact
+.It Pa <sys/sysctl.h>
+definitions for top level identifiers, second level kernel and hardware
+identifiers, and user level identifiers
+.It Pa <sys/socket.h>
+definitions for second level network identifiers
+.It Pa <sys/gmon.h>
+definitions for third level profiling identifiers
+.It Pa <vm/vm_param.h>
+definitions for second level virtual memory identifiers
+.It Pa <netinet/in.h>
+definitions for third level Internet identifiers and
+fourth level IP identifiers
+.It Pa <netinet/icmp_var.h>
+definitions for fourth level ICMP identifiers
+.It Pa <netinet/udp_var.h>
+definitions for fourth level UDP identifiers
+.El
+.Sh SEE ALSO
+.Xr sysctl 3
+.Sh BUGS
+.Nm sysctl
+presently exploits an undocumented interface to the kernel
+sysctl facility to traverse the sysctl tree and to retrieve format
+and name information.
+This correct interface is being thought about for the time being.
+.Sh HISTORY
+.Nm sysctl
+first appeared in
+.Bx 4.4 .
+.Pp
+In
+.Fx 2.2
+.Nm sysctl
+was significantly remodeled.
diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c
new file mode 100644
index 0000000..373d42e
--- /dev/null
+++ b/sbin/sysctl/sysctl.c
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "From: @(#)sysctl.c 8.1 (Berkeley) 6/6/93"; */
+static const char rcsid[] =
+ "$Id: sysctl.c,v 1.9 1995/12/21 12:39:25 phk Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+
+static int Aflag, aflag, nflag, wflag, Xflag, bflag;
+
+static int oidfmt(int *, int, char *, u_int *);
+static void parse(char *);
+static int show_var(int *, int);
+static int sysctl_all (int *oid, int len);
+static int name2oid(char *, int *);
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage:\n%s",
+ "\tsysctl [-bnX] variable ...\n"
+ "\tsysctl [-bnX] -w variable=value ...\n"
+ "\tsysctl [-bnX] -a\n"
+ "\tsysctl [-bnX] -A\n"
+ );
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ setbuf(stdout,0);
+ setbuf(stderr,0);
+
+ while ((ch = getopt(argc, argv, "AabnwX")) != EOF) {
+ switch (ch) {
+ case 'A': Aflag = 1; break;
+ case 'a': aflag = 1; break;
+ case 'b': bflag = 1; break;
+ case 'n': nflag = 1; break;
+ case 'w': wflag = 1; break;
+ case 'X': Xflag = Aflag = 1; break;
+ default: usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (Aflag || aflag)
+ exit (sysctl_all(0, 0));
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(*argv++);
+ exit(0);
+}
+
+/*
+ * Parse a name into a MIB entry.
+ * Lookup and print out the MIB entry if it exists.
+ * Set a new value if requested.
+ */
+static void
+parse(char *string)
+{
+ int len, i, j;
+ void *newval = 0;
+ int intval, newsize = 0;
+ quad_t quadval;
+ int mib[CTL_MAXNAME];
+ char *cp, *bufp, buf[BUFSIZ];
+ u_int kind;
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((cp = strchr(string, '=')) != NULL) {
+ if (!wflag) {
+ fprintf(stderr, "Must specify -w to set variables\n");
+ exit(2);
+ }
+ *strchr(buf, '=') = '\0';
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ newval = cp;
+ newsize = strlen(cp);
+ } else {
+ if (wflag)
+ usage();
+ }
+ len = name2oid(bufp, mib);
+
+ if (len < 0)
+ errx(1, "Unknown oid '%s'", bufp);
+
+ if (oidfmt(mib, len, 0, &kind))
+ err(1, "Couldn't find format of oid '%s'", bufp);
+
+ if (!wflag) {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE) {
+ sysctl_all(mib, len);
+ } else {
+ i = show_var(mib, len);
+ if (!i && !bflag)
+ putchar('\n');
+ }
+ } else {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE)
+ errx(1, "oid '%s' isn't a leaf node", bufp);
+
+ if (!(kind&CTLFLAG_WR))
+ errx(1, "oid '%s' is read only", bufp);
+
+ switch (kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ intval = atoi(newval);
+ newval = &intval;
+ newsize = sizeof intval;
+ break;
+ break;
+ case CTLTYPE_STRING:
+ break;
+ case CTLTYPE_QUAD:
+ break;
+ sscanf(newval, "%qd", &quadval);
+ newval = &quadval;
+ newsize = sizeof quadval;
+ break;
+ default:
+ errx(1, "oid '%s' is type %d,"
+ " cannot set that", bufp);
+ }
+
+ i = show_var(mib, len);
+ if (sysctl(mib, len, 0, 0, newval, newsize) == -1) {
+ if (!i && !bflag)
+ putchar('\n');
+ switch (errno) {
+ case EOPNOTSUPP:
+ errx(1, "%s: value is not available\n",
+ string);
+ case ENOTDIR:
+ errx(1, "%s: specification is incomplete\n",
+ string);
+ case ENOMEM:
+ errx(1, "%s: type is unknown to this program\n",
+ string);
+ default:
+ perror(string);
+ return;
+ }
+ }
+ if (!bflag)
+ printf(" -> ");
+ i = nflag;
+ nflag = 1;
+ j = show_var(mib, len);
+ if (!j && !bflag)
+ putchar('\n');
+ nflag = i;
+ }
+}
+
+/* These functions will dump out various interesting structures. */
+
+static int
+S_clockinfo(int l2, void *p)
+{
+ struct clockinfo *ci = (struct clockinfo*)p;
+ if (l2 != sizeof *ci)
+ err(-1, "S_clockinfo %d != %d", l2, sizeof *ci);
+ printf("{ hz = %d, tick = %d, profhz = %d, stathz = %d }",
+ ci->hz, ci->tick, ci->profhz, ci->stathz);
+ return (0);
+}
+
+static int
+S_loadavg(int l2, void *p)
+{
+ struct loadavg *tv = (struct loadavg*)p;
+
+ if (l2 != sizeof *tv)
+ err(-1, "S_loadavg %d != %d", l2, sizeof *tv);
+
+ printf("{ %.2f %.2f %.2f }",
+ (double)tv->ldavg[0]/(double)tv->fscale,
+ (double)tv->ldavg[1]/(double)tv->fscale,
+ (double)tv->ldavg[2]/(double)tv->fscale);
+ return (0);
+}
+
+static int
+S_timeval(int l2, void *p)
+{
+ struct timeval *tv = (struct timeval*)p;
+ char *p1, *p2;
+
+ if (l2 != sizeof *tv)
+ err(-1, "S_timeval %d != %d", l2, sizeof *tv);
+ printf("{ sec = %ld, usec = %ld } ",
+ tv->tv_sec, tv->tv_usec);
+ p1 = strdup(ctime(&tv->tv_sec));
+ for (p2=p1; *p2 ; p2++)
+ if (*p2 == '\n')
+ *p2 = '\0';
+ fputs(p1, stdout);
+ return (0);
+}
+
+static int
+T_dev_t(int l2, void *p)
+{
+ dev_t *d = (dev_t *)p;
+ if (l2 != sizeof *d)
+ err(-1, "T_dev_T %d != %d", l2, sizeof *d);
+ printf("{ major = %d, minor = %d }",
+ major(*d), minor(*d));
+ return (0);
+}
+
+/*
+ * These functions uses a presently undocumented interface to the kernel
+ * to walk the tree and get the type so it can print the value.
+ * This interface is under work and consideration, and should probably
+ * be killed with a big axe by the first person who can find the time.
+ * (be aware though, that the proper interface isn't as obvious as it
+ * may seem, there are various conflicting requirements.
+ */
+
+static int
+name2oid(char *name, int *oidp)
+{
+ int oid[2];
+ int i, j;
+
+ oid[0] = 0;
+ oid[1] = 3;
+
+ j = CTL_MAXNAME * sizeof (int);
+ i = sysctl(oid, 2, oidp, &j, name, strlen(name));
+ if (i < 0)
+ return i;
+ j /= sizeof (int);
+ return (j);
+}
+
+static int
+oidfmt(int *oid, int len, char *fmt, u_int *kind)
+{
+ int qoid[CTL_MAXNAME+2];
+ u_char buf[BUFSIZ];
+ int i, j;
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, len * sizeof(int));
+
+ j = sizeof buf;
+ i = sysctl(qoid, len + 2, buf, &j, 0, 0);
+ if (i)
+ err(-1, "sysctl fmt %d %d %d", i, j, errno);
+
+ if (kind)
+ *kind = *(u_int *)buf;
+
+ if (fmt)
+ strcpy(fmt, (char *)(buf + sizeof(u_int)));
+ return 0;
+}
+
+/*
+ * This formats and outputs the value of one variable
+ *
+ * Returns zero if anything was actually output.
+ * Returns one if didn't know what to do with this.
+ * Return minus one if we had errors.
+ */
+
+static int
+show_var(int *oid, int nlen)
+{
+ u_char buf[BUFSIZ], *val, *p;
+ char name[BUFSIZ], *fmt;
+ int qoid[CTL_MAXNAME+2];
+ int i, j, len;
+ u_int kind;
+ int (*func)(int, void *) = 0;
+
+ /* find an estimate of how much we need for this var */
+ j = 0;
+ i = sysctl(oid, nlen, 0, &j, 0, 0);
+ j += j; /* we want to be sure :-) */
+
+ val = alloca(j);
+ len = j;
+ i = sysctl(oid, nlen, val, &len, 0, 0);
+ if (i || !len)
+ return (1);
+
+ if (bflag) {
+ fwrite(val, 1, len, stdout);
+ return (0);
+ }
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, nlen * sizeof(int));
+
+ j = sizeof buf;
+ i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
+ if (i || !j)
+ err(-1, "sysctl fmt %d %d %d", i, j, errno);
+
+ kind = *(u_int *)buf;
+
+ fmt = (char *)(buf + sizeof(u_int));
+
+ qoid[1] = 1;
+ j = sizeof name;
+ i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
+ if (i || !j)
+ err(-1, "sysctl name %d %d %d", i, j, errno);
+
+ p = val;
+ switch (*fmt) {
+ case 'A':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%s", p);
+ return (0);
+
+ case 'I':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%d", *(int *)p);
+ return (0);
+
+ case 'T':
+ case 'S':
+ i = 0;
+ if (!strcmp(fmt, "S,clockinfo")) func = S_clockinfo;
+ else if (!strcmp(fmt, "S,timeval")) func = S_timeval;
+ else if (!strcmp(fmt, "S,loadavg")) func = S_loadavg;
+ else if (!strcmp(fmt, "T,dev_t")) func = T_dev_t;
+ if (func) {
+ if (!nflag)
+ printf("%s: ", name);
+ return ((*func)(len, p));
+ }
+ /* FALL THROUGH */
+ default:
+ if (!Aflag)
+ return (1);
+ if (!nflag)
+ printf("%s: ", name);
+ printf("Format:%s Length:%d Dump:0x", fmt, len);
+ while (len--) {
+ printf("%02x", *p++);
+ if (Xflag || p < val+16)
+ continue;
+ printf("...");
+ break;
+ }
+ return (0);
+ }
+ return (1);
+}
+
+static int
+sysctl_all (int *oid, int len)
+{
+ int name1[22], name2[22];
+ int i, j, l1, l2;
+
+ name1[0] = 0;
+ name1[1] = 2;
+ l1 = 2;
+ if (len) {
+ memcpy(name1+2, oid, len*sizeof (int));
+ l1 += len;
+ } else {
+ name1[2] = 1;
+ l1++;
+ }
+ while (1) {
+ l2 = sizeof name2;
+ j = sysctl(name1, l1, name2, &l2, 0, 0);
+ if (j < 0)
+ if (errno == ENOENT)
+ return 0;
+ else
+ err(-1, "sysctl(getnext) %d %d", j, l2);
+
+ l2 /= sizeof (int);
+
+ if (l2 < len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (name2[i] != oid[i])
+ return 0;
+
+ i = show_var(name2, l2);
+ if (!i && !bflag)
+ putchar('\n');
+
+ memcpy(name1+2, name2, l2*sizeof (int));
+ l1 = 2 + l2;
+ }
+}
diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile
new file mode 100644
index 0000000..ec9e8c9
--- /dev/null
+++ b/sbin/tunefs/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= tunefs
+MAN8= tunefs.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8
new file mode 100644
index 0000000..df161a9
--- /dev/null
+++ b/sbin/tunefs/tunefs.8
@@ -0,0 +1,150 @@
+.\" 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
+.Op Fl a Ar maxcontig
+.Op Fl d Ar rotdelay
+.Op Fl e Ar maxbpg
+.Op Fl m Ar minfree
+.Op Fl p
+.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
+The file system has several backups of the super-block. Specifying
+this option will cause all backups to be modified as well as the
+primary super-block. This is potentially dangerous - use with caution.
+.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 8%.
+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. Settings of 5% and less force space optimization to
+always be used which will greatly increase the overhead for file
+writes.
+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. Optimization for space has much
+higher overhead for file writes.
+The kernel normally changes the preference automatically as
+the percent fragmentation changes on the file system.
+.It Fl p
+This option shows a summary of what the current tuneable settings
+are on the selected file system. More detailed information can be
+obtained in the
+.Xr dumpfs 8
+manual page.
+.El
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr dumpfs 8 ,
+.Xr newfs 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.
+.\" Take this out and a Unix Demon will dog your steps from now until
+.\" the time_t's wrap around.
+.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 0000000..3677d89
--- /dev/null
+++ b/sbin/tunefs/tunefs.c
@@ -0,0 +1,311 @@
+/*
+ * 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));
+void printfs __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 'p':
+ printfs();
+ exit(0);
+
+ 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");
+ fprintf(stderr, "\t-p no change - just prints current tuneable settings\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
+printfs()
+{
+ warnx("maximum contiguous block count: (-a) %d",
+ sblock.fs_maxcontig);
+ warnx("rotational delay between contiguous blocks: (-d) %d ms",
+ sblock.fs_rotdelay);
+ warnx("maximum blocks per file in a cylinder group: (-e) %d",
+ sblock.fs_maxbpg);
+ warnx("minimum percentage of free space: (-m) %d%%",
+ sblock.fs_minfree);
+ warnx("optimization preference: (-o) %s",
+ sblock.fs_optim == FS_OPTSPACE ? "space" : "time");
+ if (sblock.fs_minfree >= MINFREE &&
+ sblock.fs_optim == FS_OPTSPACE)
+ warnx(OPTWARN, "time", ">=", MINFREE);
+ if (sblock.fs_minfree < MINFREE &&
+ sblock.fs_optim == FS_OPTTIME)
+ warnx(OPTWARN, "space", "<", MINFREE);
+}
+
+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 0000000..120b360
--- /dev/null
+++ b/sbin/umount/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.2 (Berkeley) 2/20/94
+
+PROG= umount
+MAN8= umount.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8
new file mode 100644
index 0000000..d1d02d0
--- /dev/null
+++ b/sbin/umount/umount.8
@@ -0,0 +1,145 @@
+.\" 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 BUGS
+When using union filesystems,
+.Xr umount 8
+cannot always determine the node which is the mountpoint.
+In this case,
+it is necessary to specify the relevant directory to be unmounted
+in the same form as that displayed by
+.Xr mount 8 .
+For example, given a mount entry like this:
+.Bd -literal -offset indent
+<above>/tmpdir on /cdrom (local, user mount)
+.Ed
+.Pp
+then the command:
+.Bd -literal -offset indent
+umount '<above>/tmpdir'
+.Ed
+.Pp
+would unmount
+.Ar /tmpdir
+from the mountpoint
+.Ar /cdrom .
+.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 0000000..ad64d53
--- /dev/null
+++ b/sbin/umount/umount.c
@@ -0,0 +1,424 @@
+/*-
+ * 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)
+ errs += umountfs(*argv);
+ 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);
+}
+
+/* Returns 1 on failure, 0 on success */
+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) {
+ /* Continue and let the system call check it... */
+ strcpy(rname, name);
+ }
+
+ 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;
+{
+ int *tmp_typelist;
+
+ tmp_typelist=typelist;
+ /* If no type specified, it's always selected. */
+ if (tmp_typelist == NULL)
+ return (1);
+ for (; *tmp_typelist != MOUNT_NONE; ++tmp_typelist)
+ if (type == *tmp_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);
+}
OpenPOWER on IntegriCloud