summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile17
-rw-r--r--sbin/Makefile.inc4
-rw-r--r--sbin/adjkerntz/Makefile5
-rw-r--r--sbin/adjkerntz/adjkerntz.8181
-rw-r--r--sbin/adjkerntz/adjkerntz.c361
-rw-r--r--sbin/adjkerntz/pathnames.h29
-rw-r--r--sbin/badsect/Makefile6
-rw-r--r--sbin/badsect/badsect.8133
-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.8385
-rw-r--r--sbin/bsdlabel/bsdlabel.c1452
-rw-r--r--sbin/bsdlabel/dkcksum.c55
-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.c710
-rw-r--r--sbin/ccdconfig/pathnames.h35
-rw-r--r--sbin/clri/Makefile6
-rw-r--r--sbin/clri/clri.878
-rw-r--r--sbin/clri/clri.c131
-rw-r--r--sbin/comcontrol/Makefile6
-rw-r--r--sbin/comcontrol/comcontrol.864
-rw-r--r--sbin/comcontrol/comcontrol.c108
-rw-r--r--sbin/cxconfig/Makefile4
-rw-r--r--sbin/cxconfig/cxconfig.8323
-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.8385
-rw-r--r--sbin/disklabel/disklabel.c1452
-rw-r--r--sbin/disklabel/dkcksum.c55
-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.c345
-rw-r--r--sbin/dump/Makefile32
-rw-r--r--sbin/dump/dump.8400
-rw-r--r--sbin/dump/dump.h215
-rw-r--r--sbin/dump/dumprmt.c420
-rw-r--r--sbin/dump/itime.c271
-rw-r--r--sbin/dump/main.c647
-rw-r--r--sbin/dump/optr.c530
-rw-r--r--sbin/dump/pathnames.h42
-rw-r--r--sbin/dump/tape.c861
-rw-r--r--sbin/dump/traverse.c609
-rw-r--r--sbin/dump/unctime.c106
-rw-r--r--sbin/dumpfs/Makefile6
-rw-r--r--sbin/dumpfs/dumpfs.863
-rw-r--r--sbin/dumpfs/dumpfs.c322
-rw-r--r--sbin/dumplfs/Makefile9
-rw-r--r--sbin/dumplfs/dumplfs.860
-rw-r--r--sbin/dumplfs/dumplfs.c617
-rw-r--r--sbin/dumplfs/extern.h38
-rw-r--r--sbin/dumplfs/misc.c63
-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.c1326
-rw-r--r--sbin/fsck/Makefile10
-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.c734
-rw-r--r--sbin/fsck/fsck.8309
-rw-r--r--sbin/fsck/fsck.h281
-rw-r--r--sbin/fsck/inode.c621
-rw-r--r--sbin/fsck/main.c352
-rw-r--r--sbin/fsck/pass1.c322
-rw-r--r--sbin/fsck/pass1b.c104
-rw-r--r--sbin/fsck/pass2.c467
-rw-r--r--sbin/fsck/pass3.c74
-rw-r--r--sbin/fsck/pass4.c136
-rw-r--r--sbin/fsck/pass5.c345
-rw-r--r--sbin/fsck/preen.c384
-rw-r--r--sbin/fsck/setup.c505
-rw-r--r--sbin/fsck/utilities.c625
-rw-r--r--sbin/fsck_ffs/Makefile10
-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.c734
-rw-r--r--sbin/fsck_ffs/fsck.h281
-rw-r--r--sbin/fsck_ffs/fsck_ffs.8309
-rw-r--r--sbin/fsck_ffs/inode.c621
-rw-r--r--sbin/fsck_ffs/main.c352
-rw-r--r--sbin/fsck_ffs/pass1.c322
-rw-r--r--sbin/fsck_ffs/pass1b.c104
-rw-r--r--sbin/fsck_ffs/pass2.c467
-rw-r--r--sbin/fsck_ffs/pass3.c74
-rw-r--r--sbin/fsck_ffs/pass4.c136
-rw-r--r--sbin/fsck_ffs/pass5.c345
-rw-r--r--sbin/fsck_ffs/preen.c384
-rw-r--r--sbin/fsck_ffs/setup.c505
-rw-r--r--sbin/fsck_ffs/utilities.c625
-rw-r--r--sbin/fsck_ifs/Makefile10
-rw-r--r--sbin/fsck_ifs/dir.c734
-rw-r--r--sbin/fsck_ifs/fsck.h281
-rw-r--r--sbin/fsck_ifs/fsck_ifs.8309
-rw-r--r--sbin/fsck_ifs/inode.c621
-rw-r--r--sbin/fsck_ifs/main.c352
-rw-r--r--sbin/fsck_ifs/pass1.c322
-rw-r--r--sbin/fsck_ifs/pass1b.c104
-rw-r--r--sbin/fsck_ifs/pass2.c467
-rw-r--r--sbin/fsck_ifs/pass3.c74
-rw-r--r--sbin/fsck_ifs/pass4.c136
-rw-r--r--sbin/fsck_ifs/pass5.c345
-rw-r--r--sbin/fsck_ifs/preen.c384
-rw-r--r--sbin/fsck_ifs/setup.c505
-rw-r--r--sbin/fsck_ifs/utilities.c625
-rw-r--r--sbin/fsdb/Makefile15
-rw-r--r--sbin/fsdb/fsdb.8243
-rw-r--r--sbin/fsdb/fsdb.c901
-rw-r--r--sbin/fsdb/fsdb.h61
-rw-r--r--sbin/fsdb/fsdbutil.c208
-rw-r--r--sbin/fsirand/Makefile8
-rw-r--r--sbin/fsirand/fsirand.8113
-rw-r--r--sbin/fsirand/fsirand.c293
-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.864
-rw-r--r--sbin/i386/comcontrol/comcontrol.c108
-rw-r--r--sbin/i386/cxconfig/Makefile4
-rw-r--r--sbin/i386/cxconfig/cxconfig.8323
-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.c1326
-rw-r--r--sbin/i386/ft/Makefile7
-rw-r--r--sbin/i386/ft/ft.888
-rw-r--r--sbin/i386/ft/ft.c517
-rw-r--r--sbin/i386/ft/ftecc.c479
-rw-r--r--sbin/i386/mount_msdos/Makefile17
-rw-r--r--sbin/i386/mount_msdos/mount_msdos.8119
-rw-r--r--sbin/i386/mount_msdos/mount_msdos.c213
-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/Makefile17
-rw-r--r--sbin/ifconfig/ifconfig.8339
-rw-r--r--sbin/ifconfig/ifconfig.c1247
-rw-r--r--sbin/ifconfig/ifconfig.h46
-rw-r--r--sbin/ifconfig/ifmedia.c530
-rw-r--r--sbin/init/Makefile35
-rw-r--r--sbin/init/NOTES112
-rw-r--r--sbin/init/init.8303
-rw-r--r--sbin/init/init.c1466
-rw-r--r--sbin/init/pathnames.h42
-rw-r--r--sbin/ipfw/Makefile7
-rw-r--r--sbin/ipfw/ipfw.8496
-rw-r--r--sbin/ipfw/ipfw.c1171
-rw-r--r--sbin/kldload/Makefile33
-rw-r--r--sbin/kldload/kldload.854
-rw-r--r--sbin/kldload/kldload.c71
-rw-r--r--sbin/kldstat/Makefile33
-rw-r--r--sbin/kldstat/kldstat.860
-rw-r--r--sbin/kldstat/kldstat.c119
-rw-r--r--sbin/kldunload/Makefile33
-rw-r--r--sbin/kldunload/kldunload.860
-rw-r--r--sbin/kldunload/kldunload.c92
-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.160
-rw-r--r--sbin/md5/md5.c177
-rw-r--r--sbin/mknod/Makefile6
-rw-r--r--sbin/mknod/mknod.8109
-rw-r--r--sbin/mknod/mknod.c104
-rw-r--r--sbin/modload/Makefile42
-rw-r--r--sbin/modload/modload.8123
-rw-r--r--sbin/modload/modload.c401
-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.c126
-rw-r--r--sbin/modunload/pathnames.h3
-rw-r--r--sbin/mount/Makefile9
-rw-r--r--sbin/mount/getmntopts.3179
-rw-r--r--sbin/mount/getmntopts.c106
-rw-r--r--sbin/mount/mntopts.h82
-rw-r--r--sbin/mount/mount.8310
-rw-r--r--sbin/mount/mount.c557
-rw-r--r--sbin/mount/mount_ufs.c151
-rw-r--r--sbin/mount/pathnames.h38
-rw-r--r--sbin/mount/vfslist.c92
-rw-r--r--sbin/mount_cd9660/Makefile12
-rw-r--r--sbin/mount_cd9660/mount_cd9660.8118
-rw-r--r--sbin/mount_cd9660/mount_cd9660.c220
-rw-r--r--sbin/mount_ext2fs/Makefile12
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.875
-rw-r--r--sbin/mount_ext2fs/mount_ext2fs.c130
-rw-r--r--sbin/mount_ifs/Makefile9
-rw-r--r--sbin/mount_ifs/getmntopts.3179
-rw-r--r--sbin/mount_ifs/getmntopts.c106
-rw-r--r--sbin/mount_ifs/mntopts.h82
-rw-r--r--sbin/mount_ifs/mount.8310
-rw-r--r--sbin/mount_ifs/mount.c557
-rw-r--r--sbin/mount_ifs/mount_ufs.c151
-rw-r--r--sbin/mount_ifs/pathnames.h38
-rw-r--r--sbin/mount_ifs/vfslist.c92
-rw-r--r--sbin/mount_lfs/Makefile12
-rw-r--r--sbin/mount_lfs/mount_lfs.8129
-rw-r--r--sbin/mount_lfs/mount_lfs.c167
-rw-r--r--sbin/mount_lfs/pathnames.h36
-rw-r--r--sbin/mount_msdos/Makefile17
-rw-r--r--sbin/mount_msdos/mount_msdos.8119
-rw-r--r--sbin/mount_msdos/mount_msdos.c213
-rw-r--r--sbin/mount_msdosfs/Makefile17
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.8119
-rw-r--r--sbin/mount_msdosfs/mount_msdosfs.c213
-rw-r--r--sbin/mount_nfs/Makefile20
-rw-r--r--sbin/mount_nfs/mount_nfs.8301
-rw-r--r--sbin/mount_nfs/mount_nfs.c895
-rw-r--r--sbin/mount_null/Makefile12
-rw-r--r--sbin/mount_null/mount_null.8233
-rw-r--r--sbin/mount_null/mount_null.c150
-rw-r--r--sbin/mount_nullfs/Makefile12
-rw-r--r--sbin/mount_nullfs/mount_nullfs.8233
-rw-r--r--sbin/mount_nullfs/mount_nullfs.c150
-rw-r--r--sbin/mount_portal/Makefile14
-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.c292
-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.c165
-rw-r--r--sbin/mount_portalfs/Makefile14
-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.c292
-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.c165
-rw-r--r--sbin/mount_std/Makefile19
-rw-r--r--sbin/mount_std/mount_std.8170
-rw-r--r--sbin/mount_std/mount_std.c132
-rw-r--r--sbin/mount_umap/Makefile12
-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/Makefile12
-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/Makefile12
-rw-r--r--sbin/mount_union/mount_union.8201
-rw-r--r--sbin/mount_union/mount_union.c157
-rw-r--r--sbin/mount_unionfs/Makefile12
-rw-r--r--sbin/mount_unionfs/mount_unionfs.8201
-rw-r--r--sbin/mount_unionfs/mount_unionfs.c157
-rw-r--r--sbin/mountd/Makefile10
-rw-r--r--sbin/mountd/exports.5255
-rw-r--r--sbin/mountd/mountd.8143
-rw-r--r--sbin/mountd/mountd.c2132
-rw-r--r--sbin/mountd/netgroup.5160
-rw-r--r--sbin/mountd/pathnames.h39
-rw-r--r--sbin/newfs/Makefile15
-rw-r--r--sbin/newfs/mkfs.c1401
-rw-r--r--sbin/newfs/newfs.8324
-rw-r--r--sbin/newfs/newfs.c695
-rw-r--r--sbin/newlfs/Makefile9
-rw-r--r--sbin/newlfs/config.h140
-rw-r--r--sbin/newlfs/extern.h45
-rw-r--r--sbin/newlfs/lfs.c684
-rw-r--r--sbin/newlfs/misc.c83
-rw-r--r--sbin/newlfs/newfs.c483
-rw-r--r--sbin/newlfs/newlfs.899
-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/Makefile18
-rw-r--r--sbin/nfsd/nfsd.8134
-rw-r--r--sbin/nfsd/nfsd.c673
-rw-r--r--sbin/nfsiod/Makefile8
-rw-r--r--sbin/nfsiod/nfsiod.893
-rw-r--r--sbin/nfsiod/nfsiod.c184
-rw-r--r--sbin/nologin/Makefile12
-rw-r--r--sbin/nologin/nologin.564
-rw-r--r--sbin/nologin/nologin.854
-rw-r--r--sbin/nologin/nologin.sh38
-rw-r--r--sbin/ping/Makefile9
-rw-r--r--sbin/ping/ping.8412
-rw-r--r--sbin/ping/ping.c1182
-rw-r--r--sbin/quotacheck/Makefile8
-rw-r--r--sbin/quotacheck/preen.c384
-rw-r--r--sbin/quotacheck/quotacheck.8157
-rw-r--r--sbin/quotacheck/quotacheck.c648
-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.c214
-rw-r--r--sbin/restore/Makefile22
-rw-r--r--sbin/restore/dirs.c770
-rw-r--r--sbin/restore/extern.h108
-rw-r--r--sbin/restore/interactive.c781
-rw-r--r--sbin/restore/main.c368
-rw-r--r--sbin/restore/pathnames.h43
-rw-r--r--sbin/restore/restore.8438
-rw-r--r--sbin/restore/restore.c851
-rw-r--r--sbin/restore/restore.h139
-rw-r--r--sbin/restore/symtab.c631
-rw-r--r--sbin/restore/tape.c1385
-rw-r--r--sbin/restore/utilities.c434
-rw-r--r--sbin/route/Makefile25
-rw-r--r--sbin/route/keywords46
-rw-r--r--sbin/route/route.8333
-rw-r--r--sbin/route/route.c1483
-rw-r--r--sbin/routed/Makefile13
-rw-r--r--sbin/routed/Makefile.inc1
-rw-r--r--sbin/routed/defs.h622
-rw-r--r--sbin/routed/if.c1341
-rw-r--r--sbin/routed/input.c920
-rw-r--r--sbin/routed/main.c901
-rw-r--r--sbin/routed/output.c952
-rw-r--r--sbin/routed/parms.c916
-rw-r--r--sbin/routed/pathnames.h52
-rw-r--r--sbin/routed/radix.c895
-rw-r--r--sbin/routed/radix.h161
-rw-r--r--sbin/routed/rdisc.c1041
-rw-r--r--sbin/routed/routed.8650
-rw-r--r--sbin/routed/rtquery/Makefile10
-rw-r--r--sbin/routed/rtquery/rtquery.8130
-rw-r--r--sbin/routed/rtquery/rtquery.c843
-rw-r--r--sbin/routed/table.c2001
-rw-r--r--sbin/routed/trace.c1015
-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.8300
-rw-r--r--sbin/scsi/scsi.c958
-rw-r--r--sbin/scsiformat/Makefile9
-rw-r--r--sbin/scsiformat/scsiformat.8109
-rw-r--r--sbin/scsiformat/scsiformat.sh144
-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.c455
-rw-r--r--sbin/slattach/Makefile12
-rw-r--r--sbin/slattach/slattach.8263
-rw-r--r--sbin/slattach/slattach.c596
-rw-r--r--sbin/startslip/Makefile7
-rw-r--r--sbin/startslip/startslip.1201
-rw-r--r--sbin/startslip/startslip.c604
-rw-r--r--sbin/swapon/Makefile6
-rw-r--r--sbin/swapon/swapon.891
-rw-r--r--sbin/swapon/swapon.c123
-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/Makefile13
-rw-r--r--sbin/umount/umount.8148
-rw-r--r--sbin/umount/umount.c384
386 files changed, 102208 insertions, 0 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
new file mode 100644
index 0000000..9a781ca
--- /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 fsirand 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..470d869
--- /dev/null
+++ b/sbin/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93
+
+BINDIR?= /sbin
+NOSHARED?= YES
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..cf1c767
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.8
@@ -0,0 +1,181 @@
+.\" 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.
+.\"
+.\" $Id$
+.\"
+.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 mount_msdos 8 ,
+.Xr rc 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..f92ea56
--- /dev/null
+++ b/sbin/adjkerntz/adjkerntz.c
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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)
+
+static void usage __P((void));
+
+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")) != -1)
+ switch((char)ch) {
+ case 'i': /* initial call, save offset */
+ if (init != Unknown)
+ usage();
+ init = True;
+ break;
+ case 'a': /* adjustment call, use saved offset */
+ if (init != Unknown)
+ usage();
+ init = False;
+ break;
+ case 's':
+ sleep_mode = True;
+ break;
+ default:
+ usage();
+ }
+ if (init == Unknown)
+ 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;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: adjkerntz -i",
+ "\t\t(initial call from /etc/rc)",
+ " adjkerntz -a [-s]",
+ "\t\t(adjustment call, -s for sleep/retry mode)");
+ exit(2);
+}
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..0f4c649
--- /dev/null
+++ b/sbin/badsect/badsect.8
@@ -0,0 +1,133 @@
+.\" 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
+.\" $Id: badsect.8,v 1.4 1997/02/22 14:32:09 peter Exp $
+.\"
+.Dd June 5, 1993
+.Dt BADSECT 8
+.Os BSD 4
+.Sh NAME
+.Nm badsect
+.Nd create files to contain bad sectors
+.Sh SYNOPSIS
+.Nm /sbin/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 format 8 ,
+.Xr fsck 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..eab396d
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.8
@@ -0,0 +1,385 @@
+.\" 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
+.\" $Id$
+.\"
+.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 disklabel 5 ,
+.Xr disktab 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..00c09b1
--- /dev/null
+++ b/sbin/bsdlabel/bsdlabel.c
@@ -0,0 +1,1452 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 <err.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 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)) != -1)
+ 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)
+ err(4, "%s", 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)
+ err(4, "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")))
+ err(4, "%s", 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)
+ err(4, "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)
+ err(4, "%s", 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)
+ err(4, "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)
+ err(4, "%s", 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)
+ err(4, "%s", 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)
+ err(4, "%s", xxboot);
+ memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
+#endif /* i386 */
+ if (read(b, boot, (int)dp->d_secsize) < 0)
+ err(4, "%s", 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)
+ err(4, "%s", bootxx);
+ if (read(b, &boot[dp->d_secsize],
+ (int)(dp->d_bbsize-dp->d_secsize)) < 0)
+ err(4, "%s", bootxx);
+#else
+ if (read(b, boot, (int)dp->d_bbsize) < 0)
+ err(4, "%s", 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)
+ err(4, "%s", xxboot);
+ if (read(b, bootbuf, bootsize) < 0) {
+ free(bootbuf);
+ err(4, "%s", 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, fd;
+ struct disklabel label;
+ FILE *fp;
+
+ if ((fd = mkstemp(tmpfil)) == -1 ||
+ (fp = fdopen(fd, "w")) == NULL) {
+ fprintf(stderr, "%s: Can't create\n", tmpfil);
+ return (1);
+ }
+ display(fp, lp);
+ fclose(fp);
+ for (;;) {
+ if (!editit())
+ break;
+ fp = fopen(tmpfil, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't reopen for reading\n",
+ tmpfil);
+ break;
+ }
+ bzero((char *)&label, sizeof(label));
+ if (getasciilabel(fp, &label)) {
+ *lp = label;
+ if (writelabel(f, bootarea, lp) == 0) {
+ fclose(fp);
+ (void) unlink(tmpfil);
+ return (0);
+ }
+ }
+ fclose(fp);
+ 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) {
+ err(4, "open()");
+ return (NULL);
+ }
+ if (ioctl(f, DIOCGDINFO, &lab) < 0) {
+ err(4, "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
+usage()
+{
+#if NUMBOOT > 0
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: disklabel [-r] disk",
+ "\t\t(to read label)",
+ " disklabel -w [-r] disk type [ packid ]",
+ "\t\t(to write label with existing boot program)",
+ " disklabel -e [-r] disk",
+ "\t\t(to edit label)",
+ " disklabel -R [-r] disk protofile",
+ "\t\t(to restore label with existing boot program)",
+#if NUMBOOT > 1
+ " disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]",
+ "\t\t(to install boot program with existing label)",
+ " disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
+ "\t\t(to write label and boot program)",
+ " disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
+ "\t\t(to restore label and boot program)",
+#else
+ " disklabel -B [ -b bootprog ] disk [ type ]",
+ "\t\t(to install boot program with existing on-disk label)",
+ " disklabel -w -B [ -b bootprog ] disk type [ packid ]",
+ "\t\t(to write label and install boot program)",
+ " disklabel -R -B [ -b bootprog ] disk protofile [ type ]",
+ "\t\t(to restore label and install boot program)",
+#endif
+ " disklabel [-NW] disk",
+ "\t\t(to write disable/enable label)");
+#else
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: disklabel [-r] disk", "(to read label)",
+ " disklabel -w [-r] disk type [ packid ]",
+ "\t\t(to write label)",
+ " disklabel -e [-r] disk",
+ "\t\t(to edit label)",
+ " disklabel -R [-r] disk protofile",
+ "\t\t(to restore label)",
+ " disklabel [-NW] disk",
+ "\t\t(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..78489e5
--- /dev/null
+++ b/sbin/bsdlabel/dkcksum.c
@@ -0,0 +1,55 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..1045a9b
--- /dev/null
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -0,0 +1,710 @@
+/* $Id: ccdconfig.c,v 1.6 1997/02/22 14:32:10 peter 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"
+
+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, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
+ " ccdconfig -C [-v] [-f config_file]",
+ " ccdconfig -u [-v] ccd [...]",
+ " ccdconfig -U [-v] [-f config_file]",
+ " ccdconfig -g [-M core] [-N system] [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..187c790
--- /dev/null
+++ b/sbin/clri/clri.8
@@ -0,0 +1,78 @@
+.\" 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
+.\" $Id$
+.\"
+.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..9750587
--- /dev/null
+++ b/sbin/clri/clri.c
@@ -0,0 +1,131 @@
+/*
+ * 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))
+ errx(1, "%s: can't read superblock", fs);
+
+ sbp = (struct fs *)sblock;
+ if (sbp->fs_magic != FS_MAGIC)
+ errx(1, "%s: superblock magic number 0x%x, not 0x%x.",
+ fs, sbp->fs_magic, FS_MAGIC);
+ bsize = sbp->fs_bsize;
+
+ /* remaining arguments are inode numbers. */
+ while (*++argv) {
+ /* get the inode number. */
+ if ((inonum = atoi(*argv)) <= 0)
+ errx(1, "%s is not a valid inode number.", *argv);
+ (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..e9d35c6
--- /dev/null
+++ b/sbin/comcontrol/comcontrol.8
@@ -0,0 +1,64 @@
+.\" $Id$
+.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 stty 1 ,
+.Xr sio 4
+.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..e250483
--- /dev/null
+++ b/sbin/cxconfig/cxconfig.8
@@ -0,0 +1,323 @@
+.Dd "December 2, 1994"
+.Dt CXCONFIG 8
+.Os FreeBSD
+.Sh NAME
+.Nm cxconfig
+.Nd channel options management utility for Cronyx-Sigma adapter
+.Sh DESCRIPTION
+.Pp
+The
+.Nm
+utility is used for configuring the channel options of
+the Cronyx-Sigma adapter.
+.Pp
+To change channel options the channel should be free: the corresponding
+network interface in ``down'' state, the asynchronous terminal device
+.Pa /dev/tty*
+closed.
+Generally, the channel options are set up during the operating
+system startup, for example from the
+.Pa /etc/rc
+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"
+.Bl -tag -width 10n
+.It "cxconfig"
+The brief information about all channels.
+.It "cxconfig -a"
+The full information about all channels.
+.It "cxconfig <channel>"
+The brief information about the channel.
+.It "cxconfig -a <channel>"
+The full information about the channel.
+.It "cxconfig <channel> <option>..."
+Setting the channel options.
+.El
+.Sh "Channel options"
+.Bl -tag -width 10n
+.It 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.
+.It 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.
+.It async
+Set the asynchronous channel mode.
+.It "hdlc, bisync, bsc, x.21, x21
+Set the synchronous channel mode: HDLC, Bisync (BSC) or X.21.
+.It ppp
+Set the link-level protocol: PpP/HDLC. The built-in simplified synchronous PPP
+implementation is used (see RFC-1548, RFC-1549).
+.It 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.
+.It ext
+Use the external link-level protocol suite (for BSD/386 only).
+.It "+keepalive, -keepalive"
+Enable the automatic line state control sub-protocol.
+This setting is not significant when the external link-level protocol is used.
+.It "+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.
+.It "port=rs232, port=rs449, port=v35
+Set the zero channel hardware interface type.
+.El
+.Sh "Common options"
+.Bl -tag -width 10n
+.It "nrz, nrzi, manchester"
+Set the data line signal encoding.
+In the case of
+.Em NRZ
+encoding the zero bit is transmitted by the zero signal
+level, the one bit - by the positive signal level.
+In the case of
+.Em NRZI
+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
+.Em Manchester
+encoding the zero bit is encoded as 01 value,
+the one bit - as 10 value.
+.It "+dpll, -dpll"
+Enable the digital phase locked loop mode (DPLL).
+When enabled, the receiver timing clock signal
+is derived from the received data.
+.It "+lloop, -lloop"
+Set the local loopback mode.
+.It "+extclock, -extclock"
+Set the timing clock source of synchronous channels. There are
+two possible variants:
+.Em "external clock"
+source or
+.Em "internal clock"
+generation.
+.Pp
+.Em"External clock"
+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).
+.Pp
+In the case of
+.Em "internal clock"
+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.
+.It fifo=#
+FIFO threshold level setup for receiver and transmitter.
+.It rfifo=#
+Hardware RTS/CTS flow control FIFO threshold setup.
+.It "+ctsup, -ctsup"
+Enable/disable interrupts on CTS (Clear To Send) signal setup (0 to 1 transition).
+.It "+ctsdown, -ctsdown"
+Enable/disable interrupts on CTS (Clear To Send) signal clear (1 to 0 transition).
+.It "+cdup, -cdup"
+Enable/disable interrupts on CD (Carrier Detect) signal setup (0 to 1 transition).
+.It "+cddown, -cddown"
+Enable/disable interrupts on CD (Carrier Detect) signal clear (1 to 0 transition).
+.It "+dsrup, -dsrup"
+Enable/disable interrupts on DSR (Data Set Ready) signal setup (0 to 1 transition).
+.It "+dsrdown, -dsrdown"
+Enable/disable interrupts on DSR (Data Set Ready) signal clear (1 to 0 transition).
+.El
+.Sh "Asynchronous mode options"
+.Bl -tag -width 10n
+.It cs#
+Select character size: 5, 6, 7 or 8 bits.
+.It "parodd, pareven
+Parity mode: odd or even.
+.It "+ignpar, -ignpar
+Disable/enable parity detection.
+.It nopar
+Disable parity bit generation.
+.It forcepar
+Force parity: even - 0, odd - 1.
+.It "stopb1, stopb1.5, stopb2
+Use 1 or 1.5 or 2 stop bits per character.
+.It "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.It "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.It "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.It "+rloop, -rloop"
+Set the remote loopback mode.
+.It "+etc, -etc"
+Enable the embedded transmit commands mode.
+.It "+ixon, -ixon"
+Enable the hardware XON/XOFF flow control support.
+.It "+ixany, -ixany"
+Use the hardware IXANY mode support.
+.It "+sdt, -sdt"
+Detect the spec. characters SCHR1 and SCHR2 in the receive data.
+.It "+flowct, -flowct"
+Receive the flow control spec. characters as data.
+.It "+rdt, -rdt"
+Detect the spec. characters in range SCRL..SCRH in the receive data.
+.It "+exdt, -exdt"
+Detect the spec. characters SCHR3 and SCHR4 in the receive data.
+.It "parintr, parnull, parign, pardisc, parffnull
+Action on parity errors:
+.Pp
+.Bl -tag -width parffnullxxx -compact
+.It Mode
+Action
+.It parintr
+Generate the receiver error interrupt
+.It parnull
+Input the NULL character
+.It parign
+Ignore the error, receive as good data
+.It pardisc
+Ignore the character
+.It parffnull
+Input the sequence <0xFF, NULL, character>
+.El
+.It "brkintr, brknull, brkdisc
+Line break state action:
+.Pp
+.Bl -tag -width parffnullxxx -compact
+.It Mode
+Action
+.It brkintr
+Generate the receiver error interrupt
+.It brknull
+Input the NULL character
+.It brkdisc
+Ignore the line break state
+.El
+.It "+inlcr, -inlcr"
+Translate received NL characters to CR.
+.It "+icrnl, -icrnl"
+Translate received CR characters to NL.
+.It "+igncr, -igncr"
+Ignore received CR characters.
+.It "+ocrnl, -ocrnl"
+Translate transmitted CR characters to NL.
+.It "+onlcr, -onlcr"
+Translate transmitted NL characters to CR.
+.It "+fcerr, -fcerr"
+Process (don't process) the characters, received with errors,
+for special character/flow control matching.
+.It "+lnext, -lnext"
+Enable the LNEXT character option: the character following
+the LNEXT character is not processed for special character/flow
+control matching.
+.It "+istrip, -istrip"
+Strip input characters to seven bits.
+.It schr1=#
+The XON flow control character value.
+.It schr2=#
+The XOFF flow control character value.
+.It schr3=#
+The SCHR3 spec. character value.
+.It schr4=#
+The SCHR4 spec. character value.
+.It "scrl=#, scrh=#
+The spec. character range (inclusive).
+.It lnext=#
+The LNEXT spec. character value.
+.El
+.Sh "HDLC mode options"
+.Bl -tag -width 10n
+.It if#
+The minimum number of flags transmitted before a frame is started.
+.It noaddr
+No frame address recognition.
+.It "addrlen1, addrlen2"
+Address field length: 1 or 2 bytes.
+.It "addr1, addr2"
+Addressing mode: 4x1 bytes or 2x2 bytes.
+Registers RFAR1..RFAR4 should contain the address to be matched.
+.It "+clrdet, -clrdet"
+Enable/disable clear detect for X.21 protocol support.
+.It "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.It "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.It "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.It "+fcs, -fcs"
+Enable/disable the frame checksum generation and checking.
+.It "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).
+.It "fcs-crc-16, fcs-v.41
+Frame checksum preset: all zeros (CRC-16) or all ones (CRC V.41).
+.It "+crcinv, -crcinv"
+Invert (ie. CRC V.41) or don't invert (ie. CRC-16) the transmitted frame checksum.
+.It "+fcsapd, -fcsapd"
+Pass the received CRC to the host at the end of receiver data buffer.
+.It "idlemark, idleflag
+Idle mode: idle in mark (transmit all ones) or idle in flag (transmit flag).
+.It "+syn, -syn"
+Enable/disable sending pad characters before sending flag when coming out
+of the idle mode.
+.It pad#
+The number of synchronous characters sent (0..4).
+.It "syn=0xaa, syn=0x00
+Send sync pattern.
+.It "rfar1=#, rfar2=#, rfar3=#, rfar4=#
+Frame address registers for address recognition.
+.El
+.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:
+.Bd -literal
+cxconfig cx7 128000 hdlc ppp -keepalive nrzi -cts +dpll -extclock
+ifconfig cx7 158.250.244.2 158.250.244.1 up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx0 hdlc 256000 cisco +keepalive -extclock
+ifconfig cx0 200.1.1.1 200.1.1.2 up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx1 hdlc ext
+ifconfig cx1 193.124.254.50 193.124.254.49 multicast up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx0 hdlc 64000 port=rs232 ppp +keepalive -extclock +cts
+ifconfig cx0 100.0.0.2 100.0.0.1 debug up
+.Ed
+.Sh FILES
+.Pa /dev/cronyx
+The special device file for adapter options management.
+.Sh SEE ALSO
+.Xr 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..eab396d
--- /dev/null
+++ b/sbin/disklabel/disklabel.8
@@ -0,0 +1,385 @@
+.\" 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
+.\" $Id$
+.\"
+.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 disklabel 5 ,
+.Xr disktab 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..00c09b1
--- /dev/null
+++ b/sbin/disklabel/disklabel.c
@@ -0,0 +1,1452 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 <err.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 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)) != -1)
+ 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)
+ err(4, "%s", 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)
+ err(4, "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")))
+ err(4, "%s", 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)
+ err(4, "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)
+ err(4, "%s", 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)
+ err(4, "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)
+ err(4, "%s", 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)
+ err(4, "%s", 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)
+ err(4, "%s", xxboot);
+ memcpy((void *)tmpbuf, (void *)boot, (int)dp->d_secsize);
+#endif /* i386 */
+ if (read(b, boot, (int)dp->d_secsize) < 0)
+ err(4, "%s", 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)
+ err(4, "%s", bootxx);
+ if (read(b, &boot[dp->d_secsize],
+ (int)(dp->d_bbsize-dp->d_secsize)) < 0)
+ err(4, "%s", bootxx);
+#else
+ if (read(b, boot, (int)dp->d_bbsize) < 0)
+ err(4, "%s", 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)
+ err(4, "%s", xxboot);
+ if (read(b, bootbuf, bootsize) < 0) {
+ free(bootbuf);
+ err(4, "%s", 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, fd;
+ struct disklabel label;
+ FILE *fp;
+
+ if ((fd = mkstemp(tmpfil)) == -1 ||
+ (fp = fdopen(fd, "w")) == NULL) {
+ fprintf(stderr, "%s: Can't create\n", tmpfil);
+ return (1);
+ }
+ display(fp, lp);
+ fclose(fp);
+ for (;;) {
+ if (!editit())
+ break;
+ fp = fopen(tmpfil, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Can't reopen for reading\n",
+ tmpfil);
+ break;
+ }
+ bzero((char *)&label, sizeof(label));
+ if (getasciilabel(fp, &label)) {
+ *lp = label;
+ if (writelabel(f, bootarea, lp) == 0) {
+ fclose(fp);
+ (void) unlink(tmpfil);
+ return (0);
+ }
+ }
+ fclose(fp);
+ 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) {
+ err(4, "open()");
+ return (NULL);
+ }
+ if (ioctl(f, DIOCGDINFO, &lab) < 0) {
+ err(4, "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
+usage()
+{
+#if NUMBOOT > 0
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: disklabel [-r] disk",
+ "\t\t(to read label)",
+ " disklabel -w [-r] disk type [ packid ]",
+ "\t\t(to write label with existing boot program)",
+ " disklabel -e [-r] disk",
+ "\t\t(to edit label)",
+ " disklabel -R [-r] disk protofile",
+ "\t\t(to restore label with existing boot program)",
+#if NUMBOOT > 1
+ " disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]",
+ "\t\t(to install boot program with existing label)",
+ " disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]",
+ "\t\t(to write label and boot program)",
+ " disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]",
+ "\t\t(to restore label and boot program)",
+#else
+ " disklabel -B [ -b bootprog ] disk [ type ]",
+ "\t\t(to install boot program with existing on-disk label)",
+ " disklabel -w -B [ -b bootprog ] disk type [ packid ]",
+ "\t\t(to write label and install boot program)",
+ " disklabel -R -B [ -b bootprog ] disk protofile [ type ]",
+ "\t\t(to restore label and install boot program)",
+#endif
+ " disklabel [-NW] disk",
+ "\t\t(to write disable/enable label)");
+#else
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: disklabel [-r] disk", "(to read label)",
+ " disklabel -w [-r] disk type [ packid ]",
+ "\t\t(to write label)",
+ " disklabel -e [-r] disk",
+ "\t\t(to edit label)",
+ " disklabel -R [-r] disk protofile",
+ "\t\t(to restore label)",
+ " disklabel [-NW] disk",
+ "\t\t(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..78489e5
--- /dev/null
+++ b/sbin/disklabel/dkcksum.c
@@ -0,0 +1,55 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..2746e35
--- /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: dmesg.c,v 1.6 1997/02/22 14:32:13 peter Exp $";
+#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:")) != -1)
+ 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..8d96075
--- /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$
+.\" "
+.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..7905619
--- /dev/null
+++ b/sbin/dset/dset.c
@@ -0,0 +1,345 @@
+/*
+ * 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"},
+ "",
+};
+
+struct nlist nlaux[] = {
+ {"_num_eisa_slots"},
+ {""},
+};
+
+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, i;
+ 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")) != -1)
+ 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 (kvm_nlist(kd, nlaux) != 0) {
+ /* num_eisa_conf need not exist, only handle it if found */
+ if (verbose)
+ printf("num_eisa_slots not found, ignoring.\n");
+ } else {
+ if (nlaux[0].n_type == 0)
+ fatal("kvm_nlist", "bad symbol type");
+ pos1 = nlaux[0].n_value;
+ if (kvm_read(kd, pos1, &s, sizeof(int)) < 0)
+ fatal("kvmread", NULL);
+
+ if (nlist(kernel, nlaux) != 0)
+ fatal("nlist", NULL);
+ if (nlaux[0].n_type == 0)
+ fatal("nlist", "bad symbol type");
+ pos = nlaux[0].n_value + getpagesize() - entry;
+ if (lseek(f, pos, SEEK_SET) != pos)
+ fatal("seek", NULL);
+ if ((res = read(f, (char *) &i, sizeof(int)))
+ <= 0)
+ fatal("read", NULL);
+
+ if (i != s) {
+ if (verbose)
+ printf("\nChanging num_eisa_slots from %d to %d.\n",
+ i, s);
+ if (!testonly) {
+ res = lseek(f, -(off_t) sizeof(int),
+ SEEK_CUR);
+ if (write(f, &s, sizeof(int)) <= 0)
+ fatal("write", NULL);
+ }
+ }
+ }
+
+ 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..764a7d6
--- /dev/null
+++ b/sbin/dump/Makefile
@@ -0,0 +1,32 @@
+# @(#)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=2555
+MAN8= dump.8
+MLINKS+=dump.8 rdump.8
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && defined(MAKE_EBONES)
+.PATH: ${.CURDIR}/../../usr.bin/rlogin
+SRCS+= krcmd.c kcmd.c
+LDADD+= -lkrb -ldes
+CFLAGS+=-DKERBEROS
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8
new file mode 100644
index 0000000..b05d1da
--- /dev/null
+++ b/sbin/dump/dump.8
@@ -0,0 +1,400 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dump.8 8.3 (Berkeley) 5/1/95
+.\" $Id: dump.8,v 1.14 1997/03/15 06:23:57 peter Exp $
+.\"
+.Dd May 1, 1995
+.Dt DUMP 8
+.Os BSD 4
+.Sh NAME
+.Nm dump
+.Nd filesystem backup
+.Sh SYNOPSIS
+.Nm dump
+.Op Fl 0123456789acknu
+.Op Fl B Ar records
+.Op Fl b Ar blocksize
+.Op Fl d Ar density
+.Op Fl f Ar file
+.Op Fl h Ar level
+.Op Fl s Ar feet
+.Op Fl T Ar date
+.Ar filesystem
+.Nm dump
+.Op Fl W Li \&| Fl w
+.Pp
+.in -\\n(iSu
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+.Nm Dump
+examines files
+on a filesystem
+and determines which files
+need to be backed up. These files
+are copied to the given disk, tape or other
+storage medium for safe keeping (see the
+.Fl f
+option below for doing remote backups).
+A dump that is larger than the output medium is broken into
+multiple volumes.
+On most media the size is determined by writing until an
+end-of-media indication is returned. This can be enforced
+by using the
+.Fl a
+option.
+.Pp
+On media that cannot reliably return an end-of-media indication
+(such as some cartridge tape drives)
+each volume is of a fixed size;
+the actual size is determined by the tape size and density and/or
+block count options below.
+By default, the same output file name is used for each volume
+after prompting the operator to change media.
+.Pp
+The following options are supported by
+.Nm dump :
+.Bl -tag -width Ds
+.It Fl 0\-9
+Dump levels.
+A level 0, full backup,
+guarantees the entire file system is copied
+(but see also the
+.Fl h
+option below).
+A level number above 0,
+incremental backup,
+tells dump to
+copy all files new or modified since the
+last dump of the same or lower level.
+The default level is 9.
+.It Fl B Ar records
+The number of dump records per volume.
+This option overrides the calculation of tape size
+based on length and density.
+.It Fl a
+.Dq auto-size .
+Bypass all tape length considerations, and enforce writing
+until an end-of-media indication is returned. This fits best
+for most modern tape drives. Use of this option is particularly
+recommended when appending to an existing tape, or using a tape
+drive with hardware compression (where you can never be sure about
+the compression ratio).
+.It Fl b Ar blocksize
+The number of kilobytes per dump record.
+.It Fl c
+Change the defaults for use with a cartridge tape drive, with a density
+of 8000 bpi, and a length of 1700 feet.
+.It Fl h Ar level
+Honor the user
+.Dq nodump
+flag
+.Dp Dv UF_NODUMP
+only for dumps at or above the given
+.Ar level .
+The default honor level is 1,
+so that incremental backups omit such files
+but full backups retain them.
+.It Fl d Ar density
+Set tape density to
+.Ar density .
+The default is 1600BPI.
+.It Fl f Ar file
+Write the backup to
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/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
+.\" rmt path, is the path on the remote host
+.Pa /etc/rmt ;
+this can be overridden by the environment variable
+.Ev RMT .
+.It Fl k
+Use Kerberos authentication to talk to remote tape servers. (Only
+available if this option was enabled when
+.Nm dump
+was compiled.)
+.It Fl n
+Whenever
+.Nm dump
+requires operator attention,
+notify all operators in the group
+.Dq operator
+by means similar to a
+.Xr wall 1 .
+.It Fl s Ar feet
+Attempt to calculate the amount of tape needed
+at a particular density.
+If this amount is exceeded,
+.Nm dump
+prompts for a new tape.
+It is recommended to be a bit conservative on this option.
+The default tape length is 2300 feet.
+.ne 1i
+.It Fl T Ar date
+Use the specified date as the starting time for the dump
+instead of the time determined from looking in
+.Pa /etc/dumpdates .
+The format of date is the same as that of
+.Xr ctime 3 .
+This option is useful for automated dump scripts that wish to
+dump over a specific period of time.
+The
+.Fl T
+option is mutually exclusive from the
+.Fl u
+option.
+.It Fl u
+Update the file
+.Pa /etc/dumpdates
+after a successful dump.
+The format of
+.Pa /etc/dumpdates
+is readable by people, consisting of one
+free format record per line:
+filesystem name,
+increment level
+and
+.Xr ctime 3
+format dump date.
+There may be only one entry per filesystem at each level.
+The file
+.Pa /etc/dumpdates
+may be edited to change any of the fields,
+if necessary.
+.It Fl W
+.Nm Dump
+tells the operator what file systems need to be dumped.
+This information is gleaned from the files
+.Pa /etc/dumpdates
+and
+.Pa /etc/fstab .
+The
+.Fl W
+option causes
+.Nm dump
+to print out, for each file system in
+.Pa /etc/dumpdates
+the most recent dump date and level,
+and highlights those file systems that should be dumped.
+If the
+.Fl W
+option is set, all other options are ignored, and
+.Nm dump
+exits immediately.
+.It Fl w
+Is like W, but prints only those filesystems which need to be dumped.
+.El
+.Pp
+.Nm Dump
+requires operator intervention on these conditions:
+end of tape,
+end of dump,
+tape write error,
+tape open error or
+disk read error (if there are more than a threshold of 32).
+In addition to alerting all operators implied by the
+.Fl n
+key,
+.Nm dump
+interacts with the operator on
+.Em dump's
+control terminal at times when
+.Nm dump
+can no longer proceed,
+or if something is grossly wrong.
+All questions
+.Nm dump
+poses
+.Em must
+be answered by typing
+.Dq yes
+or
+.Dq no ,
+appropriately.
+.Pp
+Since making a dump involves a lot of time and effort for full dumps,
+.Nm dump
+checkpoints itself at the start of each tape volume.
+If writing that volume fails for some reason,
+.Nm dump
+will,
+with operator permission,
+restart itself from the checkpoint
+after the old tape has been rewound and removed,
+and a new tape has been mounted.
+.Pp
+.Nm Dump
+tells the operator what is going on at periodic intervals,
+including usually low estimates of the number of blocks to write,
+the number of tapes it will take, the time to completion, and
+the time to the tape change.
+The output is verbose,
+so that others know that the terminal
+controlling
+.Nm dump
+is busy,
+and will be for some time.
+.Pp
+In the event of a catastrophic disk event, the time required
+to restore all the necessary backup tapes or files to disk
+can be kept to a minimum by staggering the incremental dumps.
+An efficient method of staggering incremental dumps
+to minimize the number of tapes follows:
+.Bl -bullet -offset indent
+.It
+Always start with a level 0 backup, for example:
+.Bd -literal -offset indent
+/sbin/dump -0u -f /dev/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 dump 5 ,
+.Xr fstab 5 ,
+.Xr ft 8 ,
+.Xr restore 8 ,
+.Xr rmt 8
+.Sh DIAGNOSTICS
+Many, and verbose.
+.Pp
+Dump exits with zero status on success.
+Startup errors are indicated with an exit code of 1;
+abnormal termination is indicated with an exit code of 3.
+.Sh BUGS
+Fewer than 32 read errors on the filesystem are ignored.
+.Pp
+Each reel requires a new process, so parent processes for
+reels already written just hang around until the entire tape
+is written.
+.Pp
+Currently,
+.Xr physio 9
+slices all requests into chunks of 64 KB. Therefore, it is
+impossible to use a larger tape blocksize, so
+.Nm dump
+will prevent this from happening.
+.Pp
+.Nm Dump
+with the
+.Fl W
+or
+.Fl w
+options does not report filesystems that have never been recorded
+in
+.Pa /etc/dumpdates ,
+even if listed in
+.Pa /etc/fstab .
+.Pp
+It would be nice if
+.Nm dump
+knew about the dump sequence,
+kept track of the tapes scribbled on,
+told the operator which tape to mount when,
+and provided more assistance
+for the operator running
+.Xr restore .
+.Pp
+.Nm dump
+cannot do remote backups without being run as root, due to its
+security history. This will be fixed in a later version of FreeBSD.
+Presently, it work if you set it setuid (like it used to be), but this
+might constitute a security risk.
+.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..6053198
--- /dev/null
+++ b/sbin/dump/dump.h
@@ -0,0 +1,215 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dump.h 8.2 (Berkeley) 4/28/95
+ */
+
+#define MAXINOPB (MAXBSIZE / sizeof(struct dinode))
+#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t))
+
+/*
+ * Dump maps used to describe what is to be dumped.
+ */
+int mapsize; /* size of the state maps */
+char *usedinomap; /* map of allocated inodes */
+char *dumpdirmap; /* map of directories to be dumped */
+char *dumpinomap; /* map of files to be dumped */
+/*
+ * Map manipulation macros.
+ */
+#define SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY)
+#define CLRINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] &= ~(1 << ((u_int)((ino) - 1) % NBBY))
+#define TSTINO(ino, map) \
+ (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY)))
+
+/*
+ * All calculations done in 0.1" units!
+ */
+char *disk; /* name of the disk file */
+char *tape; /* name of the tape file */
+char *dumpdates; /* name of the file containing dump date information*/
+char *temp; /* name of the file for doing rewrite of dumpdates */
+char lastlevel; /* dump level of previous dump */
+char level; /* dump level of this dump */
+int uflag; /* update flag */
+int diskfd; /* disk file descriptor */
+int tapefd; /* tape file descriptor */
+int pipeout; /* true => output to standard output */
+ino_t curino; /* current inumber; used globally */
+int newtape; /* new tape flag */
+int density; /* density in 0.1" units */
+long tapesize; /* estimated tape size, blocks */
+long tsize; /* tape size in 0.1" units */
+long asize; /* number of 0.1" units written on current tape */
+int etapes; /* estimated number of tapes */
+int nonodump; /* if set, do not honor UF_NODUMP user flags */
+int unlimited; /* if set, write to end of medium */
+
+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>
+#define strchr(a,b) index(a,b)
+#define strrchr(a,b) rindex(a,b)
+extern char *strdup(), *ctime();
+extern int read(), write();
+extern int errno;
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP "/etc/utmp"
+#endif
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
+
+#ifdef sunos
+extern char *calloc();
+extern char *malloc();
+extern long atol();
+extern char *strcpy();
+extern char *strncpy();
+extern char *strcat();
+extern time_t time();
+extern void endgrent();
+extern 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..0be7866
--- /dev/null
+++ b/sbin/dump/dumprmt.c
@@ -0,0 +1,420 @@
+/*-
+ * 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.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mtio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef 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 <err.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "pathnames.h"
+#include "dump.h"
+
+#define TS_CLOSED 0
+#define TS_OPEN 1
+
+static int rmtstate = TS_CLOSED;
+static int rmtape;
+static char *rmtpeer;
+
+static int okname __P((char *));
+static int rmtcall __P((char *, char *));
+static void rmtconnaborted __P((/* int, int */));
+static int rmtgetb __P((void));
+static void rmtgetconn __P((void));
+static void rmtgets __P((char *, int));
+static int rmtreply __P((char *));
+#ifdef KERBEROS
+int krcmd __P((char **, int /*u_short*/, char *, char *, int *, char *));
+#endif
+
+static int errfd = -1;
+extern int dokerberos;
+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(dokerberos ? "kshell" : "shell", "tcp");
+ if (sp == NULL) {
+ msg("%s/tcp: unknown service\n",
+ dokerberos ? "kshell" : "shell");
+ exit(X_ABORT);
+ }
+ pwd = getpwuid(getuid());
+ if (pwd == NULL) {
+ msg("who are you?\n");
+ exit(X_ABORT);
+ }
+ }
+ if ((cp = strchr(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("");
+#ifdef KERBEROS
+ if (dokerberos)
+ rmtape = krcmd(&rmtpeer, sp->s_port, tuser, rmt, &errfd,
+ (char *)0);
+ else
+#endif
+ 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)snprintf(buf, sizeof (buf), "O%.226s\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)snprintf(line, sizeof (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)snprintf(line, sizeof (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)snprintf(line, sizeof (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)snprintf(line, sizeof (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)snprintf(buf, sizeof (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..d838160
--- /dev/null
+++ b/sbin/dump/main.c
@@ -0,0 +1,647 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/inode.h>
+#include <ufs/fs.h>
+#else
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dump.h"
+#include "pathnames.h"
+
+#ifndef SBOFF
+#define SBOFF (SBLOCK * DEV_BSIZE)
+#endif
+
+int notify = 0; /* notify operator flag */
+int blockswritten = 0; /* number of blocks written on current tape */
+int tapeno = 0; /* current tape number */
+int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */
+int ntrec = NTREC; /* # tape blocks in each tape record */
+int cartridge = 0; /* Assume non-cartridge tape */
+int dokerberos = 0; /* Use Kerberos authentication */
+long dev_bsize = 1; /* recalculated below */
+long blocksperfile; /* output blocks per file */
+char *host = NULL; /* remote host (if any) */
+
+static long numarg __P((char *, long, long));
+static void obsolete __P((int *, char **[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register ino_t ino;
+ register int dirty;
+ register struct dinode *dp;
+ register struct fstab *dt;
+ register char *map;
+ register int ch;
+ int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1;
+ ino_t maxino;
+
+ spcl.c_date = 0;
+ (void)time((time_t *)&spcl.c_date);
+
+ tsize = 0; /* Default later, based on 'c' option for cart tapes */
+ if ((tape = getenv("TAPE")) == NULL)
+ tape = _PATH_DEFTAPE;
+ dumpdates = _PATH_DUMPDATES;
+ temp = _PATH_DTMP;
+ if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
+ quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
+ level = '0';
+
+ if (argc < 2)
+ usage();
+
+ obsolete(&argc, &argv);
+#ifdef KERBEROS
+#define optstring "0123456789aB:b:cd:f:h:kns:T:uWw"
+#else
+#define optstring "0123456789aB:b:cd:f:h:ns:T:uWw"
+#endif
+ while ((ch = getopt(argc, argv, optstring)) != -1)
+#undef optstring
+ switch (ch) {
+ /* dump level */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ level = ch;
+ break;
+
+ case 'a': /* `auto-size', Write to EOM. */
+ unlimited = 1;
+ break;
+
+ case 'B': /* blocks per output file */
+ blocksperfile = numarg("number of blocks per file",
+ 1L, 0L);
+ break;
+
+ case 'b': /* blocks per tape write */
+ ntrec = numarg("number of blocks per write",
+ 1L, 1000L);
+ /*
+ * XXX
+ * physio(9) currently slices all requests to
+ * 64 KB chunks. So now, if somebody entered
+ * e.g. 96 KB block size here, he would effectively
+ * yield one 64 KB and one 32 KB block, which
+ * restore cannot handle.
+ * Thus we currently enforce pyhsio(9)'s limit
+ * here, too.
+ */
+ if ( ntrec > 64 ) {
+ msg("please choose a blocksize <= 64\n");
+ exit(X_ABORT);
+ }
+ break;
+
+ case 'c': /* Tape is cart. not 9-track */
+ cartridge = 1;
+ break;
+
+ case 'd': /* density, in bits per inch */
+ density = numarg("density", 10L, 327670L) / 10;
+ if (density >= 625 && !bflag)
+ ntrec = HIGHDENSITYTREC;
+ break;
+
+ case 'f': /* output file */
+ tape = optarg;
+ break;
+
+ case 'h':
+ honorlevel = numarg("honor level", 0L, 10L);
+ break;
+
+#ifdef KERBEROS
+ case 'k':
+ dokerberos = 1;
+ break;
+#endif
+
+ case 'n': /* notify operators */
+ notify = 1;
+ break;
+
+ case 's': /* tape size, feet */
+ tsize = numarg("tape size", 1L, 0L) * 12 * 10;
+ break;
+
+ case 'T': /* time of last dump */
+ spcl.c_ddate = unctime(optarg);
+ if (spcl.c_ddate < 0) {
+ (void)fprintf(stderr, "bad time \"%s\"\n",
+ optarg);
+ exit(X_ABORT);
+ }
+ Tflag = 1;
+ lastlevel = '?';
+ argc--;
+ argv++;
+ break;
+
+ case 'u': /* update /etc/dumpdates */
+ uflag = 1;
+ break;
+
+ case 'W': /* what to do */
+ case 'w':
+ lastdump(ch);
+ exit(0); /* do nothing else */
+
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void)fprintf(stderr, "Must specify disk or filesystem\n");
+ exit(X_ABORT);
+ }
+ disk = *argv++;
+ argc--;
+ if (argc >= 1) {
+ (void)fprintf(stderr, "Unknown arguments to dump:");
+ while (argc--)
+ (void)fprintf(stderr, " %s", *argv++);
+ (void)fprintf(stderr, "\n");
+ exit(X_ABORT);
+ }
+ if (Tflag && uflag) {
+ (void)fprintf(stderr,
+ "You cannot use the T and u flags together.\n");
+ exit(X_ABORT);
+ }
+ if (strcmp(tape, "-") == 0) {
+ pipeout++;
+ tape = "standard output";
+ }
+
+ if (blocksperfile)
+ blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
+ else if (!unlimited) {
+ /*
+ * 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 (strchr(tape, ':')) {
+ host = tape;
+ tape = strchr(host, ':');
+ *tape++ = '\0';
+#ifdef RDUMP
+ if (index(tape, '\n')) {
+ (void)fprintf(stderr, "invalid characters in tape\n");
+ exit(X_ABORT);
+ }
+ 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);
+ }
+ spcl.c_dev[NAMELEN-1]='\0';
+ spcl.c_filesys[NAMELEN-1]='\0';
+ (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 || unlimited) {
+ 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 =
+ ( (double) tapesize /* blocks */
+ * TP_BSIZE /* bytes/block */
+ * (1.0/density) /* 0.1" / byte " */
+ +
+ (double) 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 =
+ ( (double) tapesize /* blocks */
+ * TP_BSIZE /* bytes / block */
+ * (1.0/density) /* 0.1" / byte " */
+ +
+ (double) 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 */
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: dump [-0123456789ac"
+#ifdef KERBEROS
+ "k"
+#endif
+ "nu] [-B records] [-b blocksize] [-d density] [-f file]\n"
+ " [-h level] [-s feet] [-T date] filesystem\n"
+ " dump [-W | -w]\n");
+ exit(1);
+}
+
+/*
+ * Pick up a numeric argument. It must be nonnegative and in the given
+ * range (except that a vmax of 0 means unlimited).
+ */
+static long
+numarg(meaning, vmin, vmax)
+ char *meaning;
+ long vmin, vmax;
+{
+ char *p;
+ long val;
+
+ val = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal %s -- %s", meaning, optarg);
+ if (val < vmin || (vmax && val > vmax))
+ errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax);
+ return (val);
+}
+
+void
+sig(signo)
+ int signo;
+{
+ switch(signo) {
+ case SIGALRM:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGHUP:
+ case SIGTERM:
+ case SIGTRAP:
+ if (pipeout)
+ quit("Signal on pipe: cannot recover\n");
+ msg("Rewriting attempted as response to unknown signal.\n");
+ (void)fflush(stderr);
+ (void)fflush(stdout);
+ close_rewind();
+ exit(X_REWRITE);
+ /* NOTREACHED */
+ case SIGSEGV:
+ msg("SIGSEGV: ABORTING!\n");
+ (void)signal(SIGSEGV, SIG_DFL);
+ (void)kill(0, SIGSEGV);
+ /* NOTREACHED */
+ }
+}
+
+char *
+rawname(cp)
+ char *cp;
+{
+ static char rawbuf[MAXPATHLEN];
+ char *dp = strrchr(cp, '/');
+
+ if (dp == NULL)
+ return (NULL);
+ *dp = '\0';
+ (void)strncpy(rawbuf, cp, MAXPATHLEN - 1);
+ rawbuf[MAXPATHLEN-1] = '\0';
+ *dp = '/';
+ (void)strncat(rawbuf, "/r", MAXPATHLEN - 1 - strlen(rawbuf));
+ (void)strncat(rawbuf, dp + 1, MAXPATHLEN - 1 - strlen(rawbuf));
+ return (rawbuf);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(argcp, argvp)
+ int *argcp;
+ char **argvp[];
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch (*ap) {
+ case 'B':
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'h':
+ case 's':
+ case 'T':
+ if (*argv == NULL) {
+ warnx("option requires an argument -- %c", *ap);
+ usage();
+ }
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while (*nargv++ = *argv++);
+
+ /* Update argument count. */
+ *argcp = nargv - *argvp - 1;
+}
diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c
new file mode 100644
index 0000000..60a6796
--- /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[MAXPATHLEN], buf[BUFSIZ];
+ register char *cp;
+ int lmsg = 1;
+ FILE *f_tty;
+
+ (void) strcpy(t, _PATH_DEV);
+ (void) strncat(t, tty, sizeof t - strlen(_PATH_DEV) - 1);
+
+ 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..65f6b19
--- /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" /* path on remote host */
diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c
new file mode 100644
index 0000000..91f5dc4
--- /dev/null
+++ b/sbin/dump/tape.c
@@ -0,0 +1,861 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+
+#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 && !unlimited)
+ 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 && !unlimited && (blocksperfile ?
+ (blocksthisvol >= blocksperfile) : (asize > tsize))) {
+ close_rewind();
+ startnewtape(0);
+ }
+ timeest();
+}
+
+void
+trewind()
+{
+ int f;
+ int got;
+
+ for (f = 0; f < SLAVES; f++) {
+ /*
+ * Drain the results, but unlike EOT we DO (or should) care
+ * what the return values were, since if we detect EOT after
+ * we think we've written the last blocks to the tape anyway,
+ * we have to replay those blocks with rollforward.
+ *
+ * fixme: punt for now.
+ */
+ if (slaves[f].sent) {
+ if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slaves[f].sent = 0;
+ if (got != writesize) {
+ msg("EOT detected in last 2 tape records!\n");
+ msg("Use a longer tape, decrease the size estimate\n");
+ quit("or use no size estimate at all.\n");
+ }
+ }
+ (void) close(slaves[f].fd);
+ }
+ while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
+ /* void */;
+
+ if (pipeout)
+ return;
+
+ msg("Closing %s\n", tape);
+
+#ifdef RDUMP
+ if (host) {
+ rmtclose();
+ while (rmtopen(tape, 0) < 0)
+ sleep(10);
+ rmtclose();
+ return;
+ }
+#endif
+ (void) close(tapefd);
+ while ((f = open(tape, 0)) < 0)
+ sleep (10);
+ (void) close(f);
+}
+
+void
+close_rewind()
+{
+ trewind();
+ if (nexttape)
+ return;
+ if (!nogripe) {
+ msg("Change Volumes: Mount volume #%d\n", tapeno+1);
+ broadcast("CHANGE DUMP VOLUMES!\7\7\n");
+ }
+ while (!query("Is the new volume mounted and ready to go?"))
+ if (query("Do you want to abort?")) {
+ dumpabort(0);
+ /*NOTREACHED*/
+ }
+}
+
+void
+rollforward()
+{
+ register struct req *p, *q, *prev;
+ register struct slave *tslp;
+ int i, size, savedtapea, got;
+ union u_spcl *ntb, *otb;
+ tslp = &slaves[SLAVES];
+ ntb = (union u_spcl *)tslp->tblock[1];
+
+ /*
+ * Each of the N slaves should have requests that need to
+ * be replayed on the next tape. Use the extra slave buffers
+ * (slaves[SLAVES]) to construct request lists to be sent to
+ * each slave in turn.
+ */
+ for (i = 0; i < SLAVES; i++) {
+ q = &tslp->req[1];
+ otb = (union u_spcl *)slp->tblock;
+
+ /*
+ * For each request in the current slave, copy it to tslp.
+ */
+
+ prev = NULL;
+ for (p = slp->req; p->count > 0; p += p->count) {
+ *q = *p;
+ if (p->dblk == 0)
+ *ntb++ = *otb++; /* copy the datablock also */
+ prev = q;
+ q += q->count;
+ }
+ if (prev == NULL)
+ quit("rollforward: protocol botch");
+ if (prev->dblk != 0)
+ prev->count -= 1;
+ else
+ ntb--;
+ q -= 1;
+ q->count = 0;
+ q = &tslp->req[0];
+ if (i == 0) {
+ q->dblk = 0;
+ q->count = 1;
+ trecno = 0;
+ nextblock = tslp->tblock;
+ savedtapea = spcl.c_tapea;
+ spcl.c_tapea = slp->tapea;
+ startnewtape(0);
+ spcl.c_tapea = savedtapea;
+ lastspclrec = savedtapea - 1;
+ }
+ size = (char *)ntb - (char *)q;
+ if (atomic(write, slp->fd, (char *)q, size) != size) {
+ perror(" DUMP: error writing command pipe");
+ dumpabort(0);
+ }
+ slp->sent = 1;
+ if (++slp >= &slaves[SLAVES])
+ slp = &slaves[0];
+
+ q->count = 1;
+
+ if (prev->dblk != 0) {
+ /*
+ * If the last one was a disk block, make the
+ * first of this one be the last bit of that disk
+ * block...
+ */
+ q->dblk = prev->dblk +
+ prev->count * (TP_BSIZE / DEV_BSIZE);
+ ntb = (union u_spcl *)tslp->tblock;
+ } else {
+ /*
+ * It wasn't a disk block. Copy the data to its
+ * new location in the buffer.
+ */
+ q->dblk = 0;
+ *((union u_spcl *)tslp->tblock) = *ntb;
+ ntb = (union u_spcl *)tslp->tblock[1];
+ }
+ }
+ slp->req[0] = *q;
+ nextblock = slp->tblock;
+ if (q->dblk == 0)
+ nextblock++;
+ trecno = 1;
+
+ /*
+ * Clear the first slaves' response. One hopes that it
+ * worked ok, otherwise the tape is much too short!
+ */
+ if (slp->sent) {
+ if (atomic(read, slp->fd, (char *)&got, sizeof got)
+ != sizeof got) {
+ perror(" DUMP: error reading command pipe in master");
+ dumpabort(0);
+ }
+ slp->sent = 0;
+
+ if (got != writesize) {
+ quit("EOT detected at start of the tape!\n");
+ }
+ }
+}
+
+/*
+ * We implement taking and restoring checkpoints on the tape level.
+ * When each tape is opened, a new process is created by forking; this
+ * saves all of the necessary context in the parent. The child
+ * continues the dump; the parent waits around, saving the context.
+ * If the child returns X_REWRITE, then it had problems writing that tape;
+ * this causes the parent to fork again, duplicating the context, and
+ * everything continues as if nothing had happened.
+ */
+void
+startnewtape(top)
+ int top;
+{
+ int parentpid;
+ int childpid;
+ int status;
+ int waitpid;
+ char *p;
+#ifdef sunos
+ void (*interrupt_save)();
+#else
+ sig_t interrupt_save;
+#endif
+
+ interrupt_save = signal(SIGINT, SIG_IGN);
+ parentpid = getpid();
+
+restore_check_point:
+ (void)signal(SIGINT, interrupt_save);
+ /*
+ * All signals are inherited...
+ */
+ childpid = fork();
+ if (childpid < 0) {
+ msg("Context save fork fails in parent %d\n", parentpid);
+ Exit(X_ABORT);
+ }
+ if (childpid != 0) {
+ /*
+ * PARENT:
+ * save the context by waiting
+ * until the child doing all of the work returns.
+ * don't catch the interrupt
+ */
+ signal(SIGINT, SIG_IGN);
+#ifdef TDEBUG
+ msg("Tape: %d; parent process: %d child process %d\n",
+ tapeno+1, parentpid, childpid);
+#endif /* TDEBUG */
+ while ((waitpid = wait(&status)) != childpid)
+ msg("Parent %d waiting for child %d has another child %d return\n",
+ parentpid, childpid, waitpid);
+ if (status & 0xFF) {
+ msg("Child %d returns LOB status %o\n",
+ childpid, status&0xFF);
+ }
+ status = (status >> 8) & 0xFF;
+#ifdef TDEBUG
+ switch(status) {
+ case X_FINOK:
+ msg("Child %d finishes X_FINOK\n", childpid);
+ break;
+ case X_ABORT:
+ msg("Child %d finishes X_ABORT\n", childpid);
+ break;
+ case X_REWRITE:
+ msg("Child %d finishes X_REWRITE\n", childpid);
+ break;
+ default:
+ msg("Child %d finishes unknown %d\n",
+ childpid, status);
+ break;
+ }
+#endif /* TDEBUG */
+ switch(status) {
+ case X_FINOK:
+ Exit(X_FINOK);
+ case X_ABORT:
+ Exit(X_ABORT);
+ case X_REWRITE:
+ goto restore_check_point;
+ default:
+ msg("Bad return code from dump: %d\n", status);
+ Exit(X_ABORT);
+ }
+ /*NOTREACHED*/
+ } else { /* we are the child; just continue */
+#ifdef TDEBUG
+ sleep(4); /* allow time for parent's message to get out */
+ msg("Child on Tape %d has parent %d, my pid = %d\n",
+ tapeno+1, parentpid, getpid());
+#endif /* TDEBUG */
+ /*
+ * If we have a name like "/dev/rmt0,/dev/rmt1",
+ * use the name before the comma first, and save
+ * the remaining names for subsequent volumes.
+ */
+ tapeno++; /* current tape sequence */
+ if (nexttape || strchr(tape, ',')) {
+ if (nexttape && *nexttape)
+ tape = nexttape;
+ if ((p = strchr(tape, ',')) != NULL) {
+ *p = '\0';
+ nexttape = p + 1;
+ } else
+ nexttape = NULL;
+ msg("Dumping volume %d on %s\n", tapeno, tape);
+ }
+#ifdef RDUMP
+ while ((tapefd = (host ? rmtopen(tape, 2) :
+ pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#else
+ while ((tapefd = (pipeout ? 1 :
+ open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
+#endif
+ {
+ msg("Cannot open output \"%s\".\n", tape);
+ if (!query("Do you want to retry the open?"))
+ dumpabort(0);
+ }
+
+ enslave(); /* Share open tape file descriptor with slaves */
+
+ asize = 0;
+ blocksthisvol = 0;
+ if (top)
+ newtape++; /* new tape signal */
+ spcl.c_count = slp->count;
+ /*
+ * measure firstrec in TP_BSIZE units since restore doesn't
+ * know the correct ntrec value...
+ */
+ spcl.c_firstrec = slp->firstrec;
+ spcl.c_volume++;
+ spcl.c_type = TS_TAPE;
+ spcl.c_flags |= DR_NEWHEADER;
+ writeheader((ino_t)slp->inode);
+ spcl.c_flags &=~ DR_NEWHEADER;
+ if (tapeno > 1)
+ msg("Volume %d begins with blocks from inode %d\n",
+ tapeno, slp->inode);
+ }
+}
+
+void
+dumpabort(signo)
+ int signo;
+{
+
+ if (master != 0 && master != getpid())
+ /* Signals master to call dumpabort */
+ (void) kill(master, SIGTERM);
+ else {
+ killall();
+ msg("The ENTIRE dump is aborted.\n");
+ }
+#ifdef RDUMP
+ rmtclose();
+#endif
+ Exit(X_ABORT);
+}
+
+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);
+ slaves[i].sent = 0;
+ }
+}
+
+/*
+ * 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 (wrote < 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..70c0414
--- /dev/null
+++ b/sbin/dump/traverse.c
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 1980, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef sunos
+#include <sys/vnode.h>
+
+#include <ufs/fs.h>
+#include <ufs/fsdir.h>
+#include <ufs/inode.h>
+#else
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#endif
+
+#include <protocols/dumprestore.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef __STDC__
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include "dump.h"
+
+#define HASDUMPEDFILE 0x1
+#define HASSUBDIRS 0x2
+
+#ifdef FS_44INODEFMT
+typedef quad_t fsizeT;
+#else
+typedef long fsizeT;
+#endif
+
+static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
+static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
+static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
+
+/*
+ * This is an estimation of the number of TP_BSIZE blocks in the file.
+ * It estimates the number of blocks in files with holes by assuming
+ * that all of the blocks accounted for by di_blocks are data blocks
+ * (when some of the blocks are usually used for indirect pointers);
+ * hence the estimate may be high.
+ */
+long
+blockest(dp)
+ register struct dinode *dp;
+{
+ long blkest, sizeest;
+
+ /*
+ * dp->di_size is the size of the file in bytes.
+ * dp->di_blocks stores the number of sectors actually in the file.
+ * If there are more sectors than the size would indicate, this just
+ * means that there are indirect blocks in the file or unused
+ * sectors in the last file block; we can safely ignore these
+ * (blkest = sizeest below).
+ * If the file is bigger than the number of sectors would indicate,
+ * then the file has holes in it. In this case we must use the
+ * block count to estimate the number of data blocks used, but
+ * we use the actual size for estimating the number of indirect
+ * dump blocks (sizeest vs. blkest in the indirect block
+ * calculation).
+ */
+ blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
+ sizeest = howmany(dp->di_size, TP_BSIZE);
+ if (blkest > sizeest)
+ blkest = sizeest;
+ if (dp->di_size > sblock->fs_bsize * NDADDR) {
+ /* calculate the number of indirect blocks on the dump tape */
+ blkest +=
+ howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
+ TP_NINDIR);
+ }
+ return (blkest + 1);
+}
+
+/* Auxiliary macro to pick up files changed since previous dump. */
+#define CHANGEDSINCE(dp, t) \
+ ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
+
+/* The WANTTODUMP macro decides whether a file should be dumped. */
+#ifdef UF_NODUMP
+#define WANTTODUMP(dp) \
+ (CHANGEDSINCE(dp, spcl.c_ddate) && \
+ (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP))
+#else
+#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
+#endif
+
+/*
+ * Dump pass 1.
+ *
+ * Walk the inode list for a filesystem to find all allocated inodes
+ * that have been modified since the previous dump time. Also, find all
+ * the directories in the filesystem.
+ */
+int
+mapfiles(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register int mode;
+ register ino_t ino;
+ register struct dinode *dp;
+ int anydirskipped = 0;
+
+ for (ino = ROOTINO; ino < maxino; ino++) {
+ dp = getino(ino);
+ if ((mode = (dp->di_mode & IFMT)) == 0)
+ continue;
+ SETINO(ino, usedinomap);
+ if (mode == IFDIR)
+ SETINO(ino, dumpdirmap);
+ if (WANTTODUMP(dp)) {
+ SETINO(ino, dumpinomap);
+ if (mode != IFREG && mode != IFDIR && mode != IFLNK)
+ *tapesize += 1;
+ else
+ *tapesize += blockest(dp);
+ continue;
+ }
+ if (mode == IFDIR)
+ anydirskipped = 1;
+ }
+ /*
+ * Restore gets very upset if the root is not dumped,
+ * so ensure that it always is dumped.
+ */
+ SETINO(ROOTINO, dumpinomap);
+ return (anydirskipped);
+}
+
+/*
+ * Dump pass 2.
+ *
+ * Scan each directory on the filesystem to see if it has any modified
+ * files in it. If it does, and has not already been added to the dump
+ * list (because it was itself modified), then add it. If a directory
+ * has not been modified itself, contains no modified files and has no
+ * subdirectories, then it can be deleted from the dump list and from
+ * the list of directories. By deleting it from the list of directories,
+ * its parent may now qualify for the same treatment on this or a later
+ * pass using this algorithm.
+ */
+int
+mapdirs(maxino, tapesize)
+ ino_t maxino;
+ long *tapesize;
+{
+ register struct dinode *dp;
+ register int i, isdir;
+ register char *map;
+ register ino_t ino;
+ long filesize;
+ int ret, change = 0;
+
+ isdir = 0; /* XXX just to get gcc to shut up */
+ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
+ if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */
+ isdir = *map++;
+ else
+ isdir >>= 1;
+ if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap))
+ continue;
+ dp = getino(ino);
+ filesize = dp->di_size;
+ for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
+ if (dp->di_db[i] != 0)
+ ret |= searchdir(ino, dp->di_db[i],
+ (long)dblksize(sblock, dp, i),
+ filesize);
+ if (ret & HASDUMPEDFILE)
+ filesize = 0;
+ else
+ filesize -= sblock->fs_bsize;
+ }
+ for (i = 0; filesize > 0 && i < NIADDR; i++) {
+ if (dp->di_ib[i] == 0)
+ continue;
+ ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
+ }
+ if (ret & HASDUMPEDFILE) {
+ SETINO(ino, dumpinomap);
+ *tapesize += blockest(dp);
+ change = 1;
+ continue;
+ }
+ if ((ret & HASSUBDIRS) == 0) {
+ if (!TSTINO(ino, dumpinomap)) {
+ CLRINO(ino, dumpdirmap);
+ change = 1;
+ }
+ }
+ }
+ return (change);
+}
+
+/*
+ * Read indirect blocks, and pass the data blocks to be searched
+ * as directories. Quit as soon as any entry is found that will
+ * require the directory to be dumped.
+ */
+static int
+dirindir(ino, blkno, ind_level, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ int ind_level;
+ long *filesize;
+{
+ int ret = 0;
+ register int i;
+ daddr_t idblk[MAXNINDIR];
+
+ bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= searchdir(ino, blkno, sblock->fs_bsize,
+ *filesize);
+ if (ret & HASDUMPEDFILE)
+ *filesize = 0;
+ else
+ *filesize -= sblock->fs_bsize;
+ }
+ return (ret);
+ }
+ ind_level--;
+ for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
+ blkno = idblk[i];
+ if (blkno != 0)
+ ret |= dirindir(ino, blkno, ind_level, filesize);
+ }
+ return (ret);
+}
+
+/*
+ * Scan a disk block containing directory information looking to see if
+ * any of the entries are on the dump list and to see if the directory
+ * contains any subdirectories.
+ */
+static int
+searchdir(ino, blkno, size, filesize)
+ ino_t ino;
+ daddr_t blkno;
+ register long size;
+ long filesize;
+{
+ register struct direct *dp;
+ register long loc, ret = 0;
+ char dblk[MAXBSIZE];
+
+ bread(fsbtodb(sblock, blkno), dblk, (int)size);
+ if (filesize < size)
+ size = filesize;
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(dblk + loc);
+ if (dp->d_reclen == 0) {
+ msg("corrupted directory, inumber %d\n", ino);
+ break;
+ }
+ loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_name[0] == '.') {
+ if (dp->d_name[1] == '\0')
+ continue;
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
+ continue;
+ }
+ if (TSTINO(dp->d_ino, dumpinomap)) {
+ ret |= HASDUMPEDFILE;
+ if (ret & HASSUBDIRS)
+ break;
+ }
+ if (TSTINO(dp->d_ino, dumpdirmap)) {
+ ret |= HASSUBDIRS;
+ if (ret & HASDUMPEDFILE)
+ break;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Dump passes 3 and 4.
+ *
+ * Dump the contents of an inode to tape.
+ */
+void
+dumpino(dp, ino)
+ register struct dinode *dp;
+ ino_t ino;
+{
+ int ind_level, cnt;
+ fsizeT size;
+ char buf[TP_BSIZE];
+
+ if (newtape) {
+ newtape = 0;
+ dumpmap(dumpinomap, TS_BITS, ino);
+ }
+ CLRINO(ino, dumpinomap);
+ spcl.c_dinode = *dp;
+ spcl.c_type = TS_INODE;
+ spcl.c_count = 0;
+ switch (dp->di_mode & 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);
+ memmove(buf, dp->di_shortlink, (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
+ memset(idblk, 0, (int)sblock->fs_bsize);
+ if (ind_level <= 0) {
+ if (*size < NINDIR(sblock) * sblock->fs_bsize)
+ cnt = howmany(*size, sblock->fs_fsize);
+ else
+ cnt = NINDIR(sblock) * sblock->fs_frag;
+ *size -= NINDIR(sblock) * sblock->fs_bsize;
+ blksout(&idblk[0], cnt, ino);
+ return;
+ }
+ ind_level--;
+ for (i = 0; i < NINDIR(sblock); i++) {
+ dmpindir(ino, idblk[i], ind_level, size);
+ if (*size <= 0)
+ return;
+ }
+}
+
+/*
+ * Collect up the data into tape record sized buffers and output them.
+ */
+void
+blksout(blkp, frags, ino)
+ daddr_t *blkp;
+ int frags;
+ ino_t ino;
+{
+ register daddr_t *bp;
+ int i, j, count, blks, tbperdb;
+
+ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
+ tbperdb = sblock->fs_bsize >> tp_bshift;
+ for (i = 0; i < blks; i += TP_NINDIR) {
+ if (i + TP_NINDIR > blks)
+ count = blks;
+ else
+ count = i + TP_NINDIR;
+ for (j = i; j < count; j++)
+ if (blkp[j / tbperdb] != 0)
+ spcl.c_addr[j - i] = 1;
+ else
+ spcl.c_addr[j - i] = 0;
+ spcl.c_count = count - i;
+ writeheader(ino);
+ bp = &blkp[i / tbperdb];
+ for (j = i; j < count; j += tbperdb, bp++)
+ if (*bp != 0)
+ if (j + tbperdb <= count)
+ dumpblock(*bp, (int)sblock->fs_bsize);
+ else
+ dumpblock(*bp, (count - j) * TP_BSIZE);
+ spcl.c_type = TS_ADDR;
+ }
+}
+
+/*
+ * Dump a map to the tape.
+ */
+void
+dumpmap(map, type, ino)
+ char *map;
+ int type;
+ ino_t ino;
+{
+ register int i;
+ char *cp;
+
+ spcl.c_type = type;
+ spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
+ writeheader(ino);
+ for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
+ writerec(cp, 0);
+}
+
+/*
+ * Write a header record to the dump tape.
+ */
+void
+writeheader(ino)
+ ino_t ino;
+{
+ register long sum, cnt, *lp;
+
+ spcl.c_inumber = ino;
+ spcl.c_magic = NFS_MAGIC;
+ spcl.c_checksum = 0;
+ lp = (long *)&spcl;
+ sum = 0;
+ cnt = sizeof(union u_spcl) / (4 * sizeof(long));
+ while (--cnt >= 0) {
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ sum += *lp++;
+ }
+ spcl.c_checksum = CHECKSUM - sum;
+ writerec((char *)&spcl, 1);
+}
+
+struct dinode *
+getino(inum)
+ ino_t inum;
+{
+ static daddr_t minino, maxino;
+ static struct dinode inoblock[MAXINOPB];
+
+ curino = inum;
+ if (inum >= minino && inum < maxino)
+ return (&inoblock[inum - minino]);
+ bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock,
+ (int)sblock->fs_bsize);
+ minino = inum - (inum % INOPB(sblock));
+ maxino = minino + INOPB(sblock);
+ return (&inoblock[inum - minino]);
+}
+
+/*
+ * Read a chunk of data from the disk.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+int breaderrors = 0;
+#define BREADEMAX 32
+
+void
+bread(blkno, buf, size)
+ daddr_t blkno;
+ char *buf;
+ int size;
+{
+ int cnt, i;
+ extern int errno;
+
+loop:
+ if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
+ ((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.
+ */
+ memset(buf, 0, size);
+ for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+ if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
+ ((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..bacb469
--- /dev/null
+++ b/sbin/dump/unctime.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <time.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#ifndef __P
+#include <sys/cdefs.h>
+#endif
+
+/*
+ * Convert a ctime(3) format string into a system format date.
+ * Return the date thus calculated.
+ *
+ * Return -1 if the string is not in ctime format.
+ */
+
+/*
+ * Offsets into the ctime string to various parts.
+ */
+
+#define E_MONTH 4
+#define E_DAY 8
+#define E_HOUR 11
+#define E_MINUTE 14
+#define E_SECOND 17
+#define E_YEAR 20
+
+static int lookup __P((char *));
+
+
+time_t
+unctime(str)
+ char *str;
+{
+ struct tm then;
+ char dbuf[26];
+
+ (void) strncpy(dbuf, str, sizeof(dbuf) - 1);
+ dbuf[sizeof(dbuf) - 1] = '\0';
+ dbuf[E_MONTH+3] = '\0';
+ if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
+ return (-1);
+ then.tm_mday = atoi(&dbuf[E_DAY]);
+ then.tm_hour = atoi(&dbuf[E_HOUR]);
+ then.tm_min = atoi(&dbuf[E_MINUTE]);
+ then.tm_sec = atoi(&dbuf[E_SECOND]);
+ then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
+ then.tm_isdst = -1;
+ return(mktime(&then));
+}
+
+static char months[] =
+ "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+static int
+lookup(str)
+ char *str;
+{
+ register char *cp, *cp2;
+
+ for (cp = months, cp2 = str; *cp != '\0'; cp += 3)
+ if (strncmp(cp, cp2, 3) == 0)
+ return((cp-months) / 3);
+ return(-1);
+}
diff --git a/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..7f52246
--- /dev/null
+++ b/sbin/dumpfs/dumpfs.8
@@ -0,0 +1,63 @@
+.\" 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
+.\" $Id$
+.\"
+.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 disktab 5 ,
+.Xr fs 5 ,
+.Xr disklabel 8 ,
+.Xr fsck 8 ,
+.Xr newfs 8 ,
+.Xr tunefs 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..dd53cd4
--- /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.5 (Berkeley) 4/29/95";
+#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 <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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, "")) != -1)
+ 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, skipped", 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\trps\t%d\n",
+ afs.fs_rotdelay, 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);
+ warn("%s", name);
+ 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) {
+ warnx("%s: error reading cg", 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..445ff7b
--- /dev/null
+++ b/sbin/dumplfs/dumplfs.8
@@ -0,0 +1,60 @@
+.\" 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
+.\" $Id$
+.\"
+.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 disktab 5 ,
+.Xr fs 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..0949480
--- /dev/null
+++ b/sbin/dumplfs/dumplfs.c
@@ -0,0 +1,617 @@
+/*-
+ * 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.5 (Berkeley) 5/24/95";
+#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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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:")) != -1)
+ 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(1, "%s", special);
+
+ /* 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(1, NULL);
+ 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)
+ errx(1, "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(1, NULL);
+ 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(1, NULL);
+ 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(1, NULL);
+ 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),
+ "mtime ", ctime(&dip->di_mtime),
+ "ctime ", ctime(&dip->di_ctime));
+ (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 numbytes;
+ struct dinode *inop;
+
+ if (sp->ss_magic != SS_MAGIC ||
+ 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\tmagic no: 0x%x\n",
+ addr, sp->ss_magic);
+ (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));
+
+ /* 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:");
+ numbytes = 0;
+ for (dp--, i = 0; i < sp->ss_ninos; dp--) {
+ numbytes += lfsp->lfs_bsize; /* add bytes for inode block */
+ 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++) {
+ (void)printf(" FINFO for inode: %d version %d nblocks %d lastlength %d\n",
+ fp->fi_ino, fp->fi_version, fp->fi_nblocks, fp->fi_lastlength);
+ 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 == fp->fi_nblocks - 1)
+ numbytes += fp->fi_lastlength;
+ else
+ numbytes += lfsp->lfs_bsize;
+ }
+ if ((j % 8) != 0)
+ (void)printf("\n");
+ fp = (FINFO *)dp;
+ }
+ return (numbytes);
+}
+
+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, nbytes, 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 {
+ nbytes = dump_sum(fd, lfsp, sump, segnum, sum_offset >>
+ (lfsp->lfs_bshift - lfsp->lfs_fsbtodb));
+ if (nbytes)
+ sum_offset += LFS_SUMMARY_SIZE + nbytes;
+ 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%qX\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s0x%qX\t\t%s%d\t%s0x%qX\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(1, NULL);
+ 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..d49a6de
--- /dev/null
+++ b/sbin/dumplfs/extern.h
@@ -0,0 +1,38 @@
+/*-
+ * 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.2 (Berkeley) 4/28/95
+ */
+
+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..34c5982
--- /dev/null
+++ b/sbin/dumplfs/misc.c
@@ -0,0 +1,63 @@
+/*-
+ * 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.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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(1, "%s", special);
+ if ((rbytes = read(fd, p, len)) < 0)
+ err(1, "%s", special);
+ if (rbytes != len)
+ errx(1, "%s: short read (%d, not %d)", special, rbytes, len);
+}
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..2850b43
--- /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$
+.\"
+.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..8da85cd
--- /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.4 1997/02/22 14:32:22 peter 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")) != -1)
+ 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..70b0f8c
--- /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 1234
+.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 1234
+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 1 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 2 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 3 is:
+ <UNUSED>
+ The data for partition 4 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
+(1-4) 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 4 and mark it as unused:
+.Pp
+.nf
+ p 4 0 0 0
+.fi
+.Pp
+Example: to set partition 1 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 1 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 1 the active partition:
+.Pp
+.nf
+ a 1
+.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..7a4c819
--- /dev/null
+++ b/sbin/fdisk/fdisk.c
@@ -0,0 +1,1326 @@
+/*
+ * 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"}
+ ,{0xA6, "OpenBSD"}
+ ,{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 '1':
+ partition = 1;
+ break;
+ case '2':
+ partition = 2;
+ break;
+ case '3':
+ partition = 3;
+ break;
+ case '4':
+ partition = 4;
+ 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 = 1; 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]] [-{1,2,3,4}] [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 = 1; 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;
+ u_int64_t part_mb;
+
+ partp = ((struct dos_partition *) &mboot.parts) + i - 1;
+
+ if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ /*
+ * Be careful not to overflow.
+ */
+ part_mb = partp->dp_size;
+ part_mb *= secsize;
+ part_mb /= (1024 * 1024);
+ printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
+ printf(" start %ld, size %ld (%qd Meg), flag %x\n",
+ partp->dp_start,
+ partp->dp_size,
+ part_mb,
+ 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 - 1;
+
+ 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 == 4) {
+ init_sector0(1);
+ printf("\nThe static data for the DOS partition 4 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 = 4, 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-1].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 < 1 || partition > 4)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
+ 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 < 1 || partition > 4)
+ {
+ 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-1].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..3155b1a
--- /dev/null
+++ b/sbin/fsck/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.2 (Berkeley) 4/27/95
+
+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
+CFLAGS+=-W
+.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..4b6999b
--- /dev/null
+++ b/sbin/fsck/dir.c
@@ -0,0 +1,734 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.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 *));
+static int dircheck __P((struct inodesc *, struct direct *));
+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 *));
+static struct bufarea *getdirblk __P((ufs_daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *));
+
+/*
+ * 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)
+ errx(EEXIT, "wrong type to dirscan %d", 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;
+ memmove(dbuf, dp, (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);
+ memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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.
+ */
+static int
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ if (dp->d_ino >= maxino ||
+ dp->d_reclen == 0 ||
+ dp->d_reclen > spaceleft ||
+ (dp->d_reclen & 0x3) != 0)
+ return (0);
+ if (dp->d_ino == 0)
+ return (1);
+ size = DIRSIZ(!newinofmt, dp);
+# 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_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > MAXNAMLEN ||
+ type > 15)
+ return (0);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ return (0);
+ if (*cp != '\0')
+ return (0);
+ return (1);
+}
+
+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;
+ int 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();
+ }
+ }
+}
+
+static 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 */
+ dirp->d_reclen = newent.d_reclen;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+ memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ /*
+ * If the entry was split, dirscan() will only reverse the byte
+ * order of the original entry, and not the new one, before
+ * writing it back out. So, we reverse the byte order here if
+ * necessary.
+ */
+ if (oldlen != 0 && !newinofmt && !doinglevel2) {
+ u_char tmp;
+
+ tmp = dirp->d_namlen;
+ dirp->d_namlen = dirp->d_type;
+ dirp->d_type = tmp;
+ }
+# endif
+ return (ALTERED|STOP);
+}
+
+static int
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (memcmp(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];
+
+ memset(&idesc, 0, 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;
+
+ memset(&idesc, 0, 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);
+ memset(&idesc, 0, 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
+ */
+static int
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ ufs_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;
+ memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, &emptydir, 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);
+ }
+ memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, 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.
+ */
+static 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.
+ */
+static struct bufarea *
+getdirblk(blkno, size)
+ ufs_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..9221ea8
--- /dev/null
+++ b/sbin/fsck/fsck.8
@@ -0,0 +1,309 @@
+.\" 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.4 (Berkeley) 5/9/95
+.\" $Id: fsck.8,v 1.9 1997/03/11 12:19:36 peter Exp $
+.\"
+.Dd May 9, 1995
+.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.
+Filesystems are marked clean when they are unmounted,
+when they have been mounted read-only, or when
+.Nm fsck
+runs on them successfully.
+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 four 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.
+.It 3
+If maxcontig is greater than one,
+build the free segment maps to aid in finding contiguous sets of blocks.
+If maxcontig is equal to one, delete any existing segment maps.
+.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 fs 5 ,
+.Xr fstab 5 ,
+.Xr fsdb 8 ,
+.Xr newfs 8 ,
+.Xr reboot 8
diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h
new file mode 100644
index 0000000..1967691
--- /dev/null
+++ b/sbin/fsck/fsck.h
@@ -0,0 +1,281 @@
+/*
+ * 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.4 (Berkeley) 5/9/95
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 */
+ ufs_daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ ufs_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 */
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (ufs_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 */
+ ufs_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;
+ ufs_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 */
+ ufs_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 */
+
+ufs_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 */
+u_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 */
+
+ufs_daddr_t n_blks; /* number of blocks in use */
+ufs_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
+
+#define EEXIT 8 /* Standard error exit. */
+
+struct fstab;
+
+void adjust __P((struct inodesc *, int lcnt));
+ufs_daddr_t allocblk __P((long frags));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, ufs_daddr_t blk));
+char *blockcheck __P((char *name));
+int bread __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+void catch __P((int));
+void catchquit __P((int));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+int checkfstab __P((int preen, int maxrun,
+ int (*docheck)(struct fstab *),
+ int (*chkit)(char *, char *, long, int)));
+int chkrange __P((ufs_daddr_t blk, int cnt));
+void ckfini __P((int markclean));
+int ckinode __P((struct dinode *dp, struct inodesc *));
+void clri __P((struct inodesc *, char *type, int flag));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *));
+int dofix __P((struct inodesc *, char *msg));
+void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_fragacct __P((struct fs *, int, int32_t [], int));
+int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_setblock __P((struct fs *, u_char *, ufs_daddr_t));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int findino __P((struct inodesc *));
+int findname __P((struct inodesc *));
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((ufs_daddr_t blkno, long frags));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, ufs_daddr_t blk, long size));
+struct bufarea *getdatablk __P((ufs_daddr_t blkno, long size));
+struct inoinfo *getinoinfo __P((ino_t inumber));
+struct dinode *getnextinode __P((ino_t inumber));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+struct dinode *ginode __P((ino_t inumber));
+void inocleanup __P((void));
+void inodirty __P((void));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void panic __P((const char *fmt, ...));
+void pass1 __P((void));
+void pass1b __P((void));
+int pass1check __P((struct inodesc *));
+void pass2 __P((void));
+void pass3 __P((void));
+void pass4 __P((void));
+int pass4check __P((struct inodesc *));
+void pass5 __P((void));
+void pfatal __P((const char *fmt, ...));
+void pinode __P((ino_t ino));
+void propagate __P((void));
+void pwarn __P((const char *fmt, ...));
+int reply __P((char *question));
+void resetinodebuf __P((void));
+int setup __P((char *dev));
+void voidquit __P((int));
diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c
new file mode 100644
index 0000000..c16571c
--- /dev/null
+++ b/sbin/fsck/inode.c
@@ -0,0 +1,621 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ ufs_daddr_t *ap;
+ long ret, 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;
+{
+ ufs_daddr_t *ap;
+ ufs_daddr_t *aplim;
+ 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)
+ ufs_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;
+{
+ ufs_daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errx(EEXIT, "bad inode number %d to ginode", 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;
+ ufs_daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errx(EEXIT, "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;
+ }
+ (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)
+ errx(EEXIT, "Cannot allocate space for inode buffer");
+ 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(ufs_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(ufs_daddr_t);
+ memmove(&inp->i_blks[0], &dp->di_db[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)
+ errx(EEXIT, "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);
+ }
+ errx(EEXIT, "cannot find inode %d", 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);
+ memmove(idesc->id_name, dirp->d_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;
+ time_t t;
+
+ 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);
+ t = dp->di_mtime;
+ p = ctime(&t);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ ufs_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:
+ errx(EEXIT, "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;
+ dp->di_atime = time(NULL);
+ 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;
+
+ memset(&idesc, 0, 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..fbeb3ee
--- /dev/null
+++ b/sbin/fsck/main.c
@@ -0,0 +1,352 @@
+/*
+ * 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.6 (Berkeley) 5/14/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fstab.h>
+#include <string.h>
+
+#include "fsck.h"
+
+int returntosingle;
+
+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 __P((int argc, char *argv[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb:c:l:m:")) != -1) {
+ 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)
+ errx(EEXIT, "bad mode to -m: %o", 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:
+ errx(EEXIT, "%c option?", 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);
+}
+
+static 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)
+ errx(EEXIT, "-%c flag requires a %s", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+static 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 */
+static int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ ufs_daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno, flags;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ switch (setup(filesys)) {
+ case 0:
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ /* fall through */
+ case -1:
+ 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);
+ }
+ if (!hotroot) {
+ ckfini(1);
+ } else {
+ struct statfs stfs_buf;
+ /*
+ * Check to see if root is mounted read-write.
+ */
+ if (statfs("/", &stfs_buf) == 0)
+ flags = stfs_buf.f_flags;
+ else
+ flags = 0;
+ ckfini(flags & MNT_RDONLY);
+ }
+ 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 ufs_args args;
+ int ret;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = 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..9958277
--- /dev/null
+++ b/sbin/fsck/pass1.c
@@ -0,0 +1,322 @@
+/*
+ * 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.6 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ufs_daddr_t badblk;
+static ufs_daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *));
+
+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.
+ */
+ memset(&idesc, 0, 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();
+}
+
+static 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 (memcmp(dp->di_db, zino.di_db,
+ NDADDR * sizeof(ufs_daddr_t)) ||
+ memcmp(dp->di_ib, zino.di_ib,
+ NIADDR * sizeof(ufs_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 ||
+ (mode == IFDIR && dp->di_size > MAXDIRSIZE)) {
+ 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)
+ errx(EEXIT, "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);
+ memmove(dp->di_shortlink, symbuf, (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(ufs_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)
+ exit(EEXIT);
+ } 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;
+ ufs_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)
+ exit(EEXIT);
+ 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)
+ exit(EEXIT);
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ 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..e5036c7
--- /dev/null
+++ b/sbin/fsck/pass1b.c
@@ -0,0 +1,104 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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"
+
+static struct dups *duphead;
+static int pass1bcheck __P((struct inodesc *));
+
+void
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ memset(&idesc, 0, 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;
+ }
+ }
+}
+
+static int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ ufs_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..445f6f1
--- /dev/null
+++ b/sbin/fsck/pass2.c
@@ -0,0 +1,467 @@
+/*
+ * 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.9 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+static int blksort __P((const void *, const void *));
+static int pass2check __P((struct inodesc *));
+
+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)
+ exit(EEXIT);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("FIX") == 0)
+ exit(EEXIT);
+ dp = ginode(ROOTINO);
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ if (newinofmt) {
+ statemap[WINO] = FSTATE;
+ typemap[WINO] = DT_WHT;
+ }
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ memset(&curino, 0, 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;
+ }
+ }
+ memset(&dino, 0, sizeof(struct dinode));
+ dino.di_mode = IFDIR;
+ dp->di_size = inp->i_isize;
+ memmove(&dp->di_db[0], &inp->i_blks[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();
+}
+
+static 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, ".");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ memmove(dirp, &proto, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ memset(dirp, 0, (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, "..");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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);
+ memset(dirp, 0, (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;
+ memmove(dirp, &proto, (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 if (newinofmt &&
+ ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
+ (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
+ fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
+ dirp->d_ino = WINO;
+ dirp->d_type = DT_WHT;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } 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:
+ errx(EEXIT, "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.
+ */
+static int
+blksort(arg1, arg2)
+ const void *arg1, *arg2;
+{
+
+ return ((*(struct inoinfo **)arg1)->i_blks[0] -
+ (*(struct inoinfo **)arg2)->i_blks[0]);
+}
diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c
new file mode 100644
index 0000000..89aff79
--- /dev/null
+++ b/sbin/fsck/pass3.c
@@ -0,0 +1,74 @@
+/*
+ * 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.2 (Berkeley) 4/27/95";
+#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..e20f6fa
--- /dev/null
+++ b/sbin/fsck/pass4.c
@@ -0,0 +1,136 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ memset(&idesc, 0, 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:
+ errx(EEXIT, "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;
+ ufs_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..3dd0c1a
--- /dev/null
+++ b/sbin/fsck/pass5.c
@@ -0,0 +1,345 @@
+/*
+ * 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.9 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ struct fs *fs = &sblock;
+ struct cg *cg = &cgrp;
+ ufs_daddr_t dbase, dmax;
+ ufs_daddr_t d;
+ 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;
+
+ statemap[WINO] = USTATE;
+ memset(newcg, 0, (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_firstfield);
+ sumsize = &ocg->cg_iused[0] - (u_int8_t *)(&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_firstfield);
+ 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_firstfield);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ sumsize = 0; /* keep lint happy */
+ errx(EEXIT, "UNKNOWN ROTATIONAL TABLE FORMAT %d",
+ fs->fs_postblformat);
+ }
+ memset(&idesc[0], 0, sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ memset(&cstotal, 0, 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;
+ memset(&newcg->cg_frsum[0], 0, sizeof newcg->cg_frsum);
+ memset(&cg_blktot(newcg)[0], 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;
+ errx(EEXIT, "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) {
+ int32_t *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 (memcmp(&newcg->cg_cs, cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(cs, &newcg->cg_cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ memmove(cg, newcg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (memcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ memmove(cg_inosused(cg), cg_inosused(newcg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((memcmp(newcg, cg, basesize) != 0 ||
+ memcmp(&cg_blktot(newcg)[0],
+ &cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ memmove(cg, newcg, (size_t)basesize);
+ memmove(&cg_blktot(cg)[0],
+ &cg_blktot(newcg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (memcmp(&cstotal, &fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(&fs->fs_cstotal, &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..383467b
--- /dev/null
+++ b/sbin/fsck/preen.c
@@ -0,0 +1,384 @@
+/*
+ * 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.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <ctype.h>
+#include <fstab.h>
+#include <string.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;
+
+int nrun, ndisks;
+char hotroot;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static struct disk *finddisk __P((char *name));
+static char *rawname __P((char *name));
+static int startdisk __P((struct disk *dk,
+ int (*checkit)(char *, char *, long, int)));
+static char *unrawname __P((char *name));
+
+int
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen;
+ int maxrun;
+ int (*docheck)(struct fstab *);
+ int (*chkit)(char *, char *, long, int);
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 ||
+ (passno == 1 && fsp->fs_passno == 1)) {
+ if ((name = blockcheck(fsp->fs_spec)) != 0) {
+ if ((sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0)) != 0)
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while ((ret = startdisk(nextdisk, chkit)) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while ((ret = startdisk(dk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while ((ret = startdisk(nextdisk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+static struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (len = strlen(name), p = name + len - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+
+ 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);
+}
+
+static 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;
+}
+
+static int
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)(char *, char *, long, int);
+{
+ 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(origname)
+ char *origname;
+{
+ struct stat stslash, stblock, stchar;
+ char *newname, *raw;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ newname = origname;
+retry:
+ if (stat(newname, &stblock) < 0) {
+ perror(newname);
+ printf("Can't stat %s\n", newname);
+ return (origname);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(newname);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (origname);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (origname);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ newname = unrawname(origname);
+ retried++;
+ goto retry;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(origname) - 1;
+ if (l > 0 && origname[l] == '/')
+ /* remove trailing slash */
+ origname[l] = '\0';
+ if(!(fsinfo=getfsfile(origname))) {
+ printf("Can't resolve %s to character special device",
+ origname);
+ return (0);
+ }
+ newname = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+static char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = strrchr(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);
+}
+
+static char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = strrchr(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..bec9a31
--- /dev/null
+++ b/sbin/fsck/setup.c
@@ -0,0 +1,505 @@
+/*
+ * 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.10 (Berkeley) 5/9/95";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+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));
+static int readsb __P((int listerr));
+
+/*
+ * Read in a superblock finding an alternate if necessary.
+ * Return 1 if successful, 0 if unsuccessful, -1 if filesystem
+ * is already clean (preen mode only).
+ */
+int
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long skipclean, bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ skipclean = preen;
+ 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)
+ errx(EEXIT, "cannot allocate space for superblock");
+ 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) {
+ skipclean = 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_firstfield);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_firstfield);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty && !bflag) {
+ memmove(&altsblock, &sblock, (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)
+ exit(EEXIT);
+ asked++;
+ }
+ }
+ /*
+ * If we survive the above basic checks and are preening,
+ * quit here unless forced.
+ */
+ if (skipclean && sblock.fs_clean && !fflag)
+ return (-1);
+ /*
+ * 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(0);
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+static int
+readsb(listerr)
+ int listerr;
+{
+ ufs_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_firstfield = sblock.fs_firstfield;
+ altsblock.fs_unused_1 = sblock.fs_unused_1;
+ 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;
+ memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp);
+ altsblock.fs_maxcluster = sblock.fs_maxcluster;
+ memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt);
+ memmove(altsblock.fs_sparecon,
+ sblock.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_state = sblock.fs_state;
+ 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 (memcmp(&sblock, &altsblock, (int)sblock.fs_sbsize)) {
+ if (debug) {
+ long *nlp, *olp, *endlp;
+
+ printf("superblock mismatches\n");
+ nlp = (long *)&altsblock;
+ olp = (long *)&sblock;
+ endlp = olp + (sblock.fs_sbsize / sizeof *olp);
+ for ( ; olp < endlp; olp++, nlp++) {
+ if (*olp == *nlp)
+ continue;
+ printf("offset %d, original %d, alternate %d\n",
+ olp - (long *)&sblock, *olp, *nlp);
+ }
+ }
+ 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!
+ */
+static 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 = strchr(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);
+ }
+ memset(fs, 0, 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);
+}
+
+static 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));
+ errx(EEXIT, "%s: can't read disk label", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c
new file mode 100644
index 0000000..30c31cf
--- /dev/null
+++ b/sbin/fsck/utilities.c
@@ -0,0 +1,625 @@
+/*
+ * 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.6 (Berkeley) 5/19/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 <ctype.h>
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, ufs_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)
+ errx(EEXIT, "cannot allocate buffer pool");
+ 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;
+ errx(EEXIT, "cannot allocate buffer pool");
+ }
+ 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)
+ ufs_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)
+ errx(EEXIT, "deadlocked buffer pool");
+ 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;
+ ufs_daddr_t blk;
+ long size;
+{
+ ufs_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);
+ }
+}
+
+static void
+rwerror(mesg, blk)
+ char *mesg;
+ ufs_daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+}
+
+void
+ckfini(markclean)
+ int markclean;
+{
+ register struct bufarea *bp, *nbp;
+ int ofsmodified, 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)
+ errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (markclean && sblock.fs_clean == 0) {
+ sblock.fs_clean = 1;
+ sbdirty();
+ ofsmodified = fsmodified;
+ flush(fswritefd, &sblk);
+ fsmodified = ofsmodified;
+ if (!preen)
+ printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
+ }
+ 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;
+ ufs_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;
+ memset(buf, 0, (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;
+ ufs_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
+ */
+ufs_daddr_t
+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)
+ ufs_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;
+ memset(&idesc, 0, 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;
+ memmove(cp, namebuf, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch(sig)
+ int sig;
+{
+ if (!doinglevel2)
+ ckfini(0);
+ 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(sig)
+ int sig;
+{
+ 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(sig)
+ int sig;
+{
+
+ 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:
+ errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+void
+#if __STDC__
+pfatal(const char *fmt, ...)
+#else
+pfatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (!preen) {
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return;
+ }
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr,
+ "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(EEXIT);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+void
+#if __STDC__
+pwarn(const char *fmt, ...)
+#else
+pwarn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (preen)
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Stub for routines from kernel.
+ */
+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
+ pfatal("INTERNAL INCONSISTENCY:");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EEXIT);
+}
diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile
new file mode 100644
index 0000000..3155b1a
--- /dev/null
+++ b/sbin/fsck_ffs/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.2 (Berkeley) 4/27/95
+
+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
+CFLAGS+=-W
+.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..4b6999b
--- /dev/null
+++ b/sbin/fsck_ffs/dir.c
@@ -0,0 +1,734 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.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 *));
+static int dircheck __P((struct inodesc *, struct direct *));
+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 *));
+static struct bufarea *getdirblk __P((ufs_daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *));
+
+/*
+ * 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)
+ errx(EEXIT, "wrong type to dirscan %d", 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;
+ memmove(dbuf, dp, (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);
+ memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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.
+ */
+static int
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ if (dp->d_ino >= maxino ||
+ dp->d_reclen == 0 ||
+ dp->d_reclen > spaceleft ||
+ (dp->d_reclen & 0x3) != 0)
+ return (0);
+ if (dp->d_ino == 0)
+ return (1);
+ size = DIRSIZ(!newinofmt, dp);
+# 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_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > MAXNAMLEN ||
+ type > 15)
+ return (0);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ return (0);
+ if (*cp != '\0')
+ return (0);
+ return (1);
+}
+
+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;
+ int 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();
+ }
+ }
+}
+
+static 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 */
+ dirp->d_reclen = newent.d_reclen;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+ memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ /*
+ * If the entry was split, dirscan() will only reverse the byte
+ * order of the original entry, and not the new one, before
+ * writing it back out. So, we reverse the byte order here if
+ * necessary.
+ */
+ if (oldlen != 0 && !newinofmt && !doinglevel2) {
+ u_char tmp;
+
+ tmp = dirp->d_namlen;
+ dirp->d_namlen = dirp->d_type;
+ dirp->d_type = tmp;
+ }
+# endif
+ return (ALTERED|STOP);
+}
+
+static int
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (memcmp(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];
+
+ memset(&idesc, 0, 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;
+
+ memset(&idesc, 0, 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);
+ memset(&idesc, 0, 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
+ */
+static int
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ ufs_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;
+ memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, &emptydir, 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);
+ }
+ memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, 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.
+ */
+static 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.
+ */
+static struct bufarea *
+getdirblk(blkno, size)
+ ufs_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..1967691
--- /dev/null
+++ b/sbin/fsck_ffs/fsck.h
@@ -0,0 +1,281 @@
+/*
+ * 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.4 (Berkeley) 5/9/95
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 */
+ ufs_daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ ufs_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 */
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (ufs_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 */
+ ufs_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;
+ ufs_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 */
+ ufs_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 */
+
+ufs_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 */
+u_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 */
+
+ufs_daddr_t n_blks; /* number of blocks in use */
+ufs_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
+
+#define EEXIT 8 /* Standard error exit. */
+
+struct fstab;
+
+void adjust __P((struct inodesc *, int lcnt));
+ufs_daddr_t allocblk __P((long frags));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, ufs_daddr_t blk));
+char *blockcheck __P((char *name));
+int bread __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+void catch __P((int));
+void catchquit __P((int));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+int checkfstab __P((int preen, int maxrun,
+ int (*docheck)(struct fstab *),
+ int (*chkit)(char *, char *, long, int)));
+int chkrange __P((ufs_daddr_t blk, int cnt));
+void ckfini __P((int markclean));
+int ckinode __P((struct dinode *dp, struct inodesc *));
+void clri __P((struct inodesc *, char *type, int flag));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *));
+int dofix __P((struct inodesc *, char *msg));
+void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_fragacct __P((struct fs *, int, int32_t [], int));
+int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_setblock __P((struct fs *, u_char *, ufs_daddr_t));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int findino __P((struct inodesc *));
+int findname __P((struct inodesc *));
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((ufs_daddr_t blkno, long frags));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, ufs_daddr_t blk, long size));
+struct bufarea *getdatablk __P((ufs_daddr_t blkno, long size));
+struct inoinfo *getinoinfo __P((ino_t inumber));
+struct dinode *getnextinode __P((ino_t inumber));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+struct dinode *ginode __P((ino_t inumber));
+void inocleanup __P((void));
+void inodirty __P((void));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void panic __P((const char *fmt, ...));
+void pass1 __P((void));
+void pass1b __P((void));
+int pass1check __P((struct inodesc *));
+void pass2 __P((void));
+void pass3 __P((void));
+void pass4 __P((void));
+int pass4check __P((struct inodesc *));
+void pass5 __P((void));
+void pfatal __P((const char *fmt, ...));
+void pinode __P((ino_t ino));
+void propagate __P((void));
+void pwarn __P((const char *fmt, ...));
+int reply __P((char *question));
+void resetinodebuf __P((void));
+int setup __P((char *dev));
+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..9221ea8
--- /dev/null
+++ b/sbin/fsck_ffs/fsck_ffs.8
@@ -0,0 +1,309 @@
+.\" 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.4 (Berkeley) 5/9/95
+.\" $Id: fsck.8,v 1.9 1997/03/11 12:19:36 peter Exp $
+.\"
+.Dd May 9, 1995
+.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.
+Filesystems are marked clean when they are unmounted,
+when they have been mounted read-only, or when
+.Nm fsck
+runs on them successfully.
+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 four 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.
+.It 3
+If maxcontig is greater than one,
+build the free segment maps to aid in finding contiguous sets of blocks.
+If maxcontig is equal to one, delete any existing segment maps.
+.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 fs 5 ,
+.Xr fstab 5 ,
+.Xr fsdb 8 ,
+.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..c16571c
--- /dev/null
+++ b/sbin/fsck_ffs/inode.c
@@ -0,0 +1,621 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ ufs_daddr_t *ap;
+ long ret, 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;
+{
+ ufs_daddr_t *ap;
+ ufs_daddr_t *aplim;
+ 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)
+ ufs_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;
+{
+ ufs_daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errx(EEXIT, "bad inode number %d to ginode", 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;
+ ufs_daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errx(EEXIT, "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;
+ }
+ (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)
+ errx(EEXIT, "Cannot allocate space for inode buffer");
+ 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(ufs_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(ufs_daddr_t);
+ memmove(&inp->i_blks[0], &dp->di_db[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)
+ errx(EEXIT, "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);
+ }
+ errx(EEXIT, "cannot find inode %d", 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);
+ memmove(idesc->id_name, dirp->d_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;
+ time_t t;
+
+ 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);
+ t = dp->di_mtime;
+ p = ctime(&t);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ ufs_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:
+ errx(EEXIT, "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;
+ dp->di_atime = time(NULL);
+ 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;
+
+ memset(&idesc, 0, 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..fbeb3ee
--- /dev/null
+++ b/sbin/fsck_ffs/main.c
@@ -0,0 +1,352 @@
+/*
+ * 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.6 (Berkeley) 5/14/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fstab.h>
+#include <string.h>
+
+#include "fsck.h"
+
+int returntosingle;
+
+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 __P((int argc, char *argv[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb:c:l:m:")) != -1) {
+ 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)
+ errx(EEXIT, "bad mode to -m: %o", 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:
+ errx(EEXIT, "%c option?", 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);
+}
+
+static 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)
+ errx(EEXIT, "-%c flag requires a %s", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+static 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 */
+static int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ ufs_daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno, flags;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ switch (setup(filesys)) {
+ case 0:
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ /* fall through */
+ case -1:
+ 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);
+ }
+ if (!hotroot) {
+ ckfini(1);
+ } else {
+ struct statfs stfs_buf;
+ /*
+ * Check to see if root is mounted read-write.
+ */
+ if (statfs("/", &stfs_buf) == 0)
+ flags = stfs_buf.f_flags;
+ else
+ flags = 0;
+ ckfini(flags & MNT_RDONLY);
+ }
+ 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 ufs_args args;
+ int ret;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = 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..9958277
--- /dev/null
+++ b/sbin/fsck_ffs/pass1.c
@@ -0,0 +1,322 @@
+/*
+ * 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.6 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ufs_daddr_t badblk;
+static ufs_daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *));
+
+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.
+ */
+ memset(&idesc, 0, 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();
+}
+
+static 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 (memcmp(dp->di_db, zino.di_db,
+ NDADDR * sizeof(ufs_daddr_t)) ||
+ memcmp(dp->di_ib, zino.di_ib,
+ NIADDR * sizeof(ufs_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 ||
+ (mode == IFDIR && dp->di_size > MAXDIRSIZE)) {
+ 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)
+ errx(EEXIT, "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);
+ memmove(dp->di_shortlink, symbuf, (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(ufs_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)
+ exit(EEXIT);
+ } 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;
+ ufs_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)
+ exit(EEXIT);
+ 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)
+ exit(EEXIT);
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ 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..e5036c7
--- /dev/null
+++ b/sbin/fsck_ffs/pass1b.c
@@ -0,0 +1,104 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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"
+
+static struct dups *duphead;
+static int pass1bcheck __P((struct inodesc *));
+
+void
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ memset(&idesc, 0, 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;
+ }
+ }
+}
+
+static int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ ufs_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..445f6f1
--- /dev/null
+++ b/sbin/fsck_ffs/pass2.c
@@ -0,0 +1,467 @@
+/*
+ * 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.9 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+static int blksort __P((const void *, const void *));
+static int pass2check __P((struct inodesc *));
+
+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)
+ exit(EEXIT);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("FIX") == 0)
+ exit(EEXIT);
+ dp = ginode(ROOTINO);
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ if (newinofmt) {
+ statemap[WINO] = FSTATE;
+ typemap[WINO] = DT_WHT;
+ }
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ memset(&curino, 0, 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;
+ }
+ }
+ memset(&dino, 0, sizeof(struct dinode));
+ dino.di_mode = IFDIR;
+ dp->di_size = inp->i_isize;
+ memmove(&dp->di_db[0], &inp->i_blks[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();
+}
+
+static 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, ".");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ memmove(dirp, &proto, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ memset(dirp, 0, (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, "..");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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);
+ memset(dirp, 0, (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;
+ memmove(dirp, &proto, (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 if (newinofmt &&
+ ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
+ (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
+ fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
+ dirp->d_ino = WINO;
+ dirp->d_type = DT_WHT;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } 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:
+ errx(EEXIT, "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.
+ */
+static int
+blksort(arg1, arg2)
+ const void *arg1, *arg2;
+{
+
+ return ((*(struct inoinfo **)arg1)->i_blks[0] -
+ (*(struct inoinfo **)arg2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c
new file mode 100644
index 0000000..89aff79
--- /dev/null
+++ b/sbin/fsck_ffs/pass3.c
@@ -0,0 +1,74 @@
+/*
+ * 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.2 (Berkeley) 4/27/95";
+#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..e20f6fa
--- /dev/null
+++ b/sbin/fsck_ffs/pass4.c
@@ -0,0 +1,136 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ memset(&idesc, 0, 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:
+ errx(EEXIT, "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;
+ ufs_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..3dd0c1a
--- /dev/null
+++ b/sbin/fsck_ffs/pass5.c
@@ -0,0 +1,345 @@
+/*
+ * 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.9 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ struct fs *fs = &sblock;
+ struct cg *cg = &cgrp;
+ ufs_daddr_t dbase, dmax;
+ ufs_daddr_t d;
+ 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;
+
+ statemap[WINO] = USTATE;
+ memset(newcg, 0, (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_firstfield);
+ sumsize = &ocg->cg_iused[0] - (u_int8_t *)(&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_firstfield);
+ 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_firstfield);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ sumsize = 0; /* keep lint happy */
+ errx(EEXIT, "UNKNOWN ROTATIONAL TABLE FORMAT %d",
+ fs->fs_postblformat);
+ }
+ memset(&idesc[0], 0, sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ memset(&cstotal, 0, 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;
+ memset(&newcg->cg_frsum[0], 0, sizeof newcg->cg_frsum);
+ memset(&cg_blktot(newcg)[0], 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;
+ errx(EEXIT, "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) {
+ int32_t *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 (memcmp(&newcg->cg_cs, cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(cs, &newcg->cg_cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ memmove(cg, newcg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (memcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ memmove(cg_inosused(cg), cg_inosused(newcg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((memcmp(newcg, cg, basesize) != 0 ||
+ memcmp(&cg_blktot(newcg)[0],
+ &cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ memmove(cg, newcg, (size_t)basesize);
+ memmove(&cg_blktot(cg)[0],
+ &cg_blktot(newcg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (memcmp(&cstotal, &fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(&fs->fs_cstotal, &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..383467b
--- /dev/null
+++ b/sbin/fsck_ffs/preen.c
@@ -0,0 +1,384 @@
+/*
+ * 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.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <ctype.h>
+#include <fstab.h>
+#include <string.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;
+
+int nrun, ndisks;
+char hotroot;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static struct disk *finddisk __P((char *name));
+static char *rawname __P((char *name));
+static int startdisk __P((struct disk *dk,
+ int (*checkit)(char *, char *, long, int)));
+static char *unrawname __P((char *name));
+
+int
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen;
+ int maxrun;
+ int (*docheck)(struct fstab *);
+ int (*chkit)(char *, char *, long, int);
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 ||
+ (passno == 1 && fsp->fs_passno == 1)) {
+ if ((name = blockcheck(fsp->fs_spec)) != 0) {
+ if ((sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0)) != 0)
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while ((ret = startdisk(nextdisk, chkit)) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while ((ret = startdisk(dk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while ((ret = startdisk(nextdisk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+static struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (len = strlen(name), p = name + len - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+
+ 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);
+}
+
+static 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;
+}
+
+static int
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)(char *, char *, long, int);
+{
+ 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(origname)
+ char *origname;
+{
+ struct stat stslash, stblock, stchar;
+ char *newname, *raw;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ newname = origname;
+retry:
+ if (stat(newname, &stblock) < 0) {
+ perror(newname);
+ printf("Can't stat %s\n", newname);
+ return (origname);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(newname);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (origname);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (origname);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ newname = unrawname(origname);
+ retried++;
+ goto retry;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(origname) - 1;
+ if (l > 0 && origname[l] == '/')
+ /* remove trailing slash */
+ origname[l] = '\0';
+ if(!(fsinfo=getfsfile(origname))) {
+ printf("Can't resolve %s to character special device",
+ origname);
+ return (0);
+ }
+ newname = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+static char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = strrchr(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);
+}
+
+static char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = strrchr(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..bec9a31
--- /dev/null
+++ b/sbin/fsck_ffs/setup.c
@@ -0,0 +1,505 @@
+/*
+ * 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.10 (Berkeley) 5/9/95";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+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));
+static int readsb __P((int listerr));
+
+/*
+ * Read in a superblock finding an alternate if necessary.
+ * Return 1 if successful, 0 if unsuccessful, -1 if filesystem
+ * is already clean (preen mode only).
+ */
+int
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long skipclean, bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ skipclean = preen;
+ 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)
+ errx(EEXIT, "cannot allocate space for superblock");
+ 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) {
+ skipclean = 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_firstfield);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_firstfield);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty && !bflag) {
+ memmove(&altsblock, &sblock, (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)
+ exit(EEXIT);
+ asked++;
+ }
+ }
+ /*
+ * If we survive the above basic checks and are preening,
+ * quit here unless forced.
+ */
+ if (skipclean && sblock.fs_clean && !fflag)
+ return (-1);
+ /*
+ * 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(0);
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+static int
+readsb(listerr)
+ int listerr;
+{
+ ufs_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_firstfield = sblock.fs_firstfield;
+ altsblock.fs_unused_1 = sblock.fs_unused_1;
+ 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;
+ memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp);
+ altsblock.fs_maxcluster = sblock.fs_maxcluster;
+ memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt);
+ memmove(altsblock.fs_sparecon,
+ sblock.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_state = sblock.fs_state;
+ 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 (memcmp(&sblock, &altsblock, (int)sblock.fs_sbsize)) {
+ if (debug) {
+ long *nlp, *olp, *endlp;
+
+ printf("superblock mismatches\n");
+ nlp = (long *)&altsblock;
+ olp = (long *)&sblock;
+ endlp = olp + (sblock.fs_sbsize / sizeof *olp);
+ for ( ; olp < endlp; olp++, nlp++) {
+ if (*olp == *nlp)
+ continue;
+ printf("offset %d, original %d, alternate %d\n",
+ olp - (long *)&sblock, *olp, *nlp);
+ }
+ }
+ 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!
+ */
+static 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 = strchr(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);
+ }
+ memset(fs, 0, 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);
+}
+
+static 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));
+ errx(EEXIT, "%s: can't read disk label", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c
new file mode 100644
index 0000000..30c31cf
--- /dev/null
+++ b/sbin/fsck_ffs/utilities.c
@@ -0,0 +1,625 @@
+/*
+ * 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.6 (Berkeley) 5/19/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 <ctype.h>
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, ufs_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)
+ errx(EEXIT, "cannot allocate buffer pool");
+ 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;
+ errx(EEXIT, "cannot allocate buffer pool");
+ }
+ 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)
+ ufs_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)
+ errx(EEXIT, "deadlocked buffer pool");
+ 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;
+ ufs_daddr_t blk;
+ long size;
+{
+ ufs_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);
+ }
+}
+
+static void
+rwerror(mesg, blk)
+ char *mesg;
+ ufs_daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+}
+
+void
+ckfini(markclean)
+ int markclean;
+{
+ register struct bufarea *bp, *nbp;
+ int ofsmodified, 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)
+ errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (markclean && sblock.fs_clean == 0) {
+ sblock.fs_clean = 1;
+ sbdirty();
+ ofsmodified = fsmodified;
+ flush(fswritefd, &sblk);
+ fsmodified = ofsmodified;
+ if (!preen)
+ printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
+ }
+ 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;
+ ufs_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;
+ memset(buf, 0, (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;
+ ufs_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
+ */
+ufs_daddr_t
+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)
+ ufs_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;
+ memset(&idesc, 0, 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;
+ memmove(cp, namebuf, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch(sig)
+ int sig;
+{
+ if (!doinglevel2)
+ ckfini(0);
+ 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(sig)
+ int sig;
+{
+ 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(sig)
+ int sig;
+{
+
+ 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:
+ errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+void
+#if __STDC__
+pfatal(const char *fmt, ...)
+#else
+pfatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (!preen) {
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return;
+ }
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr,
+ "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(EEXIT);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+void
+#if __STDC__
+pwarn(const char *fmt, ...)
+#else
+pwarn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (preen)
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Stub for routines from kernel.
+ */
+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
+ pfatal("INTERNAL INCONSISTENCY:");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EEXIT);
+}
diff --git a/sbin/fsck_ifs/Makefile b/sbin/fsck_ifs/Makefile
new file mode 100644
index 0000000..3155b1a
--- /dev/null
+++ b/sbin/fsck_ifs/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.2 (Berkeley) 4/27/95
+
+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
+CFLAGS+=-W
+.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..4b6999b
--- /dev/null
+++ b/sbin/fsck_ifs/dir.c
@@ -0,0 +1,734 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.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 *));
+static int dircheck __P((struct inodesc *, struct direct *));
+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 *));
+static struct bufarea *getdirblk __P((ufs_daddr_t blkno, long size));
+static int lftempname __P((char *bufp, ino_t ino));
+static int mkentry __P((struct inodesc *));
+
+/*
+ * 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)
+ errx(EEXIT, "wrong type to dirscan %d", 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;
+ memmove(dbuf, dp, (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);
+ memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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;
+ if (idesc->id_fix == IGNORE)
+ return (0);
+ 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.
+ */
+static int
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ if (dp->d_ino >= maxino ||
+ dp->d_reclen == 0 ||
+ dp->d_reclen > spaceleft ||
+ (dp->d_reclen & 0x3) != 0)
+ return (0);
+ if (dp->d_ino == 0)
+ return (1);
+ size = DIRSIZ(!newinofmt, dp);
+# 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_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > MAXNAMLEN ||
+ type > 15)
+ return (0);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ return (0);
+ if (*cp != '\0')
+ return (0);
+ return (1);
+}
+
+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;
+ int 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();
+ }
+ }
+}
+
+static 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 */
+ dirp->d_reclen = newent.d_reclen;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+ memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ /*
+ * If the entry was split, dirscan() will only reverse the byte
+ * order of the original entry, and not the new one, before
+ * writing it back out. So, we reverse the byte order here if
+ * necessary.
+ */
+ if (oldlen != 0 && !newinofmt && !doinglevel2) {
+ u_char tmp;
+
+ tmp = dirp->d_namlen;
+ dirp->d_namlen = dirp->d_type;
+ dirp->d_type = tmp;
+ }
+# endif
+ return (ALTERED|STOP);
+}
+
+static int
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (memcmp(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];
+
+ memset(&idesc, 0, 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;
+
+ memset(&idesc, 0, 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);
+ memset(&idesc, 0, 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
+ */
+static int
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ ufs_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;
+ memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ memmove(bp->b_un.b_buf, &emptydir, 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);
+ }
+ memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ memmove(cp, &emptydir, 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.
+ */
+static 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.
+ */
+static struct bufarea *
+getdirblk(blkno, size)
+ ufs_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..1967691
--- /dev/null
+++ b/sbin/fsck_ifs/fsck.h
@@ -0,0 +1,281 @@
+/*
+ * 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.4 (Berkeley) 5/9/95
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 */
+ ufs_daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ ufs_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 */
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (ufs_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 */
+ ufs_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;
+ ufs_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 */
+ ufs_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 */
+
+ufs_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 */
+u_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 */
+
+ufs_daddr_t n_blks; /* number of blocks in use */
+ufs_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
+
+#define EEXIT 8 /* Standard error exit. */
+
+struct fstab;
+
+void adjust __P((struct inodesc *, int lcnt));
+ufs_daddr_t allocblk __P((long frags));
+ino_t allocdir __P((ino_t parent, ino_t request, int mode));
+ino_t allocino __P((ino_t request, int type));
+void blkerror __P((ino_t ino, char *type, ufs_daddr_t blk));
+char *blockcheck __P((char *name));
+int bread __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void bufinit __P((void));
+void bwrite __P((int fd, char *buf, ufs_daddr_t blk, long size));
+void cacheino __P((struct dinode *dp, ino_t inumber));
+void catch __P((int));
+void catchquit __P((int));
+int changeino __P((ino_t dir, char *name, ino_t newnum));
+int checkfstab __P((int preen, int maxrun,
+ int (*docheck)(struct fstab *),
+ int (*chkit)(char *, char *, long, int)));
+int chkrange __P((ufs_daddr_t blk, int cnt));
+void ckfini __P((int markclean));
+int ckinode __P((struct dinode *dp, struct inodesc *));
+void clri __P((struct inodesc *, char *type, int flag));
+void direrror __P((ino_t ino, char *errmesg));
+int dirscan __P((struct inodesc *));
+int dofix __P((struct inodesc *, char *msg));
+void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_fragacct __P((struct fs *, int, int32_t [], int));
+int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t));
+void ffs_setblock __P((struct fs *, u_char *, ufs_daddr_t));
+void fileerror __P((ino_t cwd, ino_t ino, char *errmesg));
+int findino __P((struct inodesc *));
+int findname __P((struct inodesc *));
+void flush __P((int fd, struct bufarea *bp));
+void freeblk __P((ufs_daddr_t blkno, long frags));
+void freeino __P((ino_t ino));
+void freeinodebuf __P((void));
+int ftypeok __P((struct dinode *dp));
+void getblk __P((struct bufarea *bp, ufs_daddr_t blk, long size));
+struct bufarea *getdatablk __P((ufs_daddr_t blkno, long size));
+struct inoinfo *getinoinfo __P((ino_t inumber));
+struct dinode *getnextinode __P((ino_t inumber));
+void getpathname __P((char *namebuf, ino_t curdir, ino_t ino));
+struct dinode *ginode __P((ino_t inumber));
+void inocleanup __P((void));
+void inodirty __P((void));
+int linkup __P((ino_t orphan, ino_t parentdir));
+int makeentry __P((ino_t parent, ino_t ino, char *name));
+void panic __P((const char *fmt, ...));
+void pass1 __P((void));
+void pass1b __P((void));
+int pass1check __P((struct inodesc *));
+void pass2 __P((void));
+void pass3 __P((void));
+void pass4 __P((void));
+int pass4check __P((struct inodesc *));
+void pass5 __P((void));
+void pfatal __P((const char *fmt, ...));
+void pinode __P((ino_t ino));
+void propagate __P((void));
+void pwarn __P((const char *fmt, ...));
+int reply __P((char *question));
+void resetinodebuf __P((void));
+int setup __P((char *dev));
+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..9221ea8
--- /dev/null
+++ b/sbin/fsck_ifs/fsck_ifs.8
@@ -0,0 +1,309 @@
+.\" 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.4 (Berkeley) 5/9/95
+.\" $Id: fsck.8,v 1.9 1997/03/11 12:19:36 peter Exp $
+.\"
+.Dd May 9, 1995
+.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.
+Filesystems are marked clean when they are unmounted,
+when they have been mounted read-only, or when
+.Nm fsck
+runs on them successfully.
+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 four 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.
+.It 3
+If maxcontig is greater than one,
+build the free segment maps to aid in finding contiguous sets of blocks.
+If maxcontig is equal to one, delete any existing segment maps.
+.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 fs 5 ,
+.Xr fstab 5 ,
+.Xr fsdb 8 ,
+.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..c16571c
--- /dev/null
+++ b/sbin/fsck_ifs/inode.c
@@ -0,0 +1,621 @@
+/*
+ * 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.8 (Berkeley) 4/28/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 <err.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ino_t startinum;
+
+static int iblock __P((struct inodesc *, long ilevel, quad_t isize));
+
+int
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ ufs_daddr_t *ap;
+ long ret, 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;
+{
+ ufs_daddr_t *ap;
+ ufs_daddr_t *aplim;
+ 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)
+ ufs_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;
+{
+ ufs_daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errx(EEXIT, "bad inode number %d to ginode", 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;
+ ufs_daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errx(EEXIT, "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;
+ }
+ (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)
+ errx(EEXIT, "Cannot allocate space for inode buffer");
+ 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(ufs_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(ufs_daddr_t);
+ memmove(&inp->i_blks[0], &dp->di_db[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)
+ errx(EEXIT, "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);
+ }
+ errx(EEXIT, "cannot find inode %d", 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);
+ memmove(idesc->id_name, dirp->d_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;
+ time_t t;
+
+ 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);
+ t = dp->di_mtime;
+ p = ctime(&t);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+void
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ ufs_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:
+ errx(EEXIT, "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;
+ dp->di_atime = time(NULL);
+ 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;
+
+ memset(&idesc, 0, 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..fbeb3ee
--- /dev/null
+++ b/sbin/fsck_ifs/main.c
@@ -0,0 +1,352 @@
+/*
+ * 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.6 (Berkeley) 5/14/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fstab.h>
+#include <string.h>
+
+#include "fsck.h"
+
+int returntosingle;
+
+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 __P((int argc, char *argv[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dfpnNyYb:c:l:m:")) != -1) {
+ 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)
+ errx(EEXIT, "bad mode to -m: %o", 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:
+ errx(EEXIT, "%c option?", 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);
+}
+
+static 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)
+ errx(EEXIT, "-%c flag requires a %s", flag, req);
+ return (ret);
+}
+
+/*
+ * Determine whether a filesystem should be checked.
+ */
+static 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 */
+static int
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+ int child;
+{
+ ufs_daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno, flags;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ switch (setup(filesys)) {
+ case 0:
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ /* fall through */
+ case -1:
+ 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);
+ }
+ if (!hotroot) {
+ ckfini(1);
+ } else {
+ struct statfs stfs_buf;
+ /*
+ * Check to see if root is mounted read-write.
+ */
+ if (statfs("/", &stfs_buf) == 0)
+ flags = stfs_buf.f_flags;
+ else
+ flags = 0;
+ ckfini(flags & MNT_RDONLY);
+ }
+ 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 ufs_args args;
+ int ret;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = 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..9958277
--- /dev/null
+++ b/sbin/fsck_ifs/pass1.c
@@ -0,0 +1,322 @@
+/*
+ * 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.6 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+static ufs_daddr_t badblk;
+static ufs_daddr_t dupblk;
+
+static void checkinode __P((ino_t inumber, struct inodesc *));
+
+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.
+ */
+ memset(&idesc, 0, 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();
+}
+
+static 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 (memcmp(dp->di_db, zino.di_db,
+ NDADDR * sizeof(ufs_daddr_t)) ||
+ memcmp(dp->di_ib, zino.di_ib,
+ NIADDR * sizeof(ufs_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 ||
+ (mode == IFDIR && dp->di_size > MAXDIRSIZE)) {
+ 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)
+ errx(EEXIT, "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);
+ memmove(dp->di_shortlink, symbuf, (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(ufs_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)
+ exit(EEXIT);
+ } 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;
+ ufs_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)
+ exit(EEXIT);
+ 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)
+ exit(EEXIT);
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ 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..e5036c7
--- /dev/null
+++ b/sbin/fsck_ifs/pass1b.c
@@ -0,0 +1,104 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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"
+
+static struct dups *duphead;
+static int pass1bcheck __P((struct inodesc *));
+
+void
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ memset(&idesc, 0, 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;
+ }
+ }
+}
+
+static int
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ ufs_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..445f6f1
--- /dev/null
+++ b/sbin/fsck_ifs/pass2.c
@@ -0,0 +1,467 @@
+/*
+ * 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.9 (Berkeley) 4/28/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 <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+static int blksort __P((const void *, const void *));
+static int pass2check __P((struct inodesc *));
+
+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)
+ exit(EEXIT);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
+ break;
+ }
+ if (reply("FIX") == 0)
+ exit(EEXIT);
+ dp = ginode(ROOTINO);
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ if (newinofmt) {
+ statemap[WINO] = FSTATE;
+ typemap[WINO] = DT_WHT;
+ }
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ memset(&curino, 0, 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;
+ }
+ }
+ memset(&dino, 0, sizeof(struct dinode));
+ dino.di_mode = IFDIR;
+ dp->di_size = inp->i_isize;
+ memmove(&dp->di_db[0], &inp->i_blks[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();
+}
+
+static 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, ".");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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;
+ memmove(dirp, &proto, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ memmove(dirp, &proto, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ memset(dirp, 0, (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, "..");
+# if BYTE_ORDER == LITTLE_ENDIAN
+ if (!newinofmt) {
+ u_char tmp;
+
+ tmp = proto.d_type;
+ proto.d_type = proto.d_namlen;
+ proto.d_namlen = tmp;
+ }
+# endif
+ 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);
+ memset(dirp, 0, (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;
+ memmove(dirp, &proto, (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 if (newinofmt &&
+ ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
+ (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
+ fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
+ dirp->d_ino = WINO;
+ dirp->d_type = DT_WHT;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } 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:
+ errx(EEXIT, "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.
+ */
+static int
+blksort(arg1, arg2)
+ const void *arg1, *arg2;
+{
+
+ return ((*(struct inoinfo **)arg1)->i_blks[0] -
+ (*(struct inoinfo **)arg2)->i_blks[0]);
+}
diff --git a/sbin/fsck_ifs/pass3.c b/sbin/fsck_ifs/pass3.c
new file mode 100644
index 0000000..89aff79
--- /dev/null
+++ b/sbin/fsck_ifs/pass3.c
@@ -0,0 +1,74 @@
+/*
+ * 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.2 (Berkeley) 4/27/95";
+#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..e20f6fa
--- /dev/null
+++ b/sbin/fsck_ifs/pass4.c
@@ -0,0 +1,136 @@
+/*
+ * 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.4 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ memset(&idesc, 0, 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:
+ errx(EEXIT, "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;
+ ufs_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..3dd0c1a
--- /dev/null
+++ b/sbin/fsck_ifs/pass5.c
@@ -0,0 +1,345 @@
+/*
+ * 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.9 (Berkeley) 4/28/95";
+#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 <string.h>
+
+#include "fsck.h"
+
+void
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ struct fs *fs = &sblock;
+ struct cg *cg = &cgrp;
+ ufs_daddr_t dbase, dmax;
+ ufs_daddr_t d;
+ 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;
+
+ statemap[WINO] = USTATE;
+ memset(newcg, 0, (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_firstfield);
+ sumsize = &ocg->cg_iused[0] - (u_int8_t *)(&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_firstfield);
+ 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_firstfield);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ sumsize = 0; /* keep lint happy */
+ errx(EEXIT, "UNKNOWN ROTATIONAL TABLE FORMAT %d",
+ fs->fs_postblformat);
+ }
+ memset(&idesc[0], 0, sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ memset(&cstotal, 0, 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;
+ memset(&newcg->cg_frsum[0], 0, sizeof newcg->cg_frsum);
+ memset(&cg_blktot(newcg)[0], 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;
+ errx(EEXIT, "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) {
+ int32_t *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 (memcmp(&newcg->cg_cs, cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(cs, &newcg->cg_cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ memmove(cg, newcg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (memcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ memmove(cg_inosused(cg), cg_inosused(newcg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((memcmp(newcg, cg, basesize) != 0 ||
+ memcmp(&cg_blktot(newcg)[0],
+ &cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ memmove(cg, newcg, (size_t)basesize);
+ memmove(&cg_blktot(cg)[0],
+ &cg_blktot(newcg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (memcmp(&cstotal, &fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ memmove(&fs->fs_cstotal, &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..383467b
--- /dev/null
+++ b/sbin/fsck_ifs/preen.c
@@ -0,0 +1,384 @@
+/*
+ * 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.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <ctype.h>
+#include <fstab.h>
+#include <string.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;
+
+int nrun, ndisks;
+char hotroot;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static struct disk *finddisk __P((char *name));
+static char *rawname __P((char *name));
+static int startdisk __P((struct disk *dk,
+ int (*checkit)(char *, char *, long, int)));
+static char *unrawname __P((char *name));
+
+int
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen;
+ int maxrun;
+ int (*docheck)(struct fstab *);
+ int (*chkit)(char *, char *, long, int);
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 ||
+ (passno == 1 && fsp->fs_passno == 1)) {
+ if ((name = blockcheck(fsp->fs_spec)) != 0) {
+ if ((sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0)) != 0)
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while ((ret = startdisk(nextdisk, chkit)) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while ((ret = startdisk(dk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while ((ret = startdisk(nextdisk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+static struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (len = strlen(name), p = name + len - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+
+ 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);
+}
+
+static 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;
+}
+
+static int
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)(char *, char *, long, int);
+{
+ 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(origname)
+ char *origname;
+{
+ struct stat stslash, stblock, stchar;
+ char *newname, *raw;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ newname = origname;
+retry:
+ if (stat(newname, &stblock) < 0) {
+ perror(newname);
+ printf("Can't stat %s\n", newname);
+ return (origname);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(newname);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (origname);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (origname);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ newname = unrawname(origname);
+ retried++;
+ goto retry;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(origname) - 1;
+ if (l > 0 && origname[l] == '/')
+ /* remove trailing slash */
+ origname[l] = '\0';
+ if(!(fsinfo=getfsfile(origname))) {
+ printf("Can't resolve %s to character special device",
+ origname);
+ return (0);
+ }
+ newname = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+static char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = strrchr(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);
+}
+
+static char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = strrchr(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..bec9a31
--- /dev/null
+++ b/sbin/fsck_ifs/setup.c
@@ -0,0 +1,505 @@
+/*
+ * 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.10 (Berkeley) 5/9/95";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+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));
+static int readsb __P((int listerr));
+
+/*
+ * Read in a superblock finding an alternate if necessary.
+ * Return 1 if successful, 0 if unsuccessful, -1 if filesystem
+ * is already clean (preen mode only).
+ */
+int
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long skipclean, bmapsize;
+ struct disklabel *lp;
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ skipclean = preen;
+ 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)
+ errx(EEXIT, "cannot allocate space for superblock");
+ 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) {
+ skipclean = 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_firstfield);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_firstfield);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty && !bflag) {
+ memmove(&altsblock, &sblock, (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)
+ exit(EEXIT);
+ asked++;
+ }
+ }
+ /*
+ * If we survive the above basic checks and are preening,
+ * quit here unless forced.
+ */
+ if (skipclean && sblock.fs_clean && !fflag)
+ return (-1);
+ /*
+ * 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(0);
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+static int
+readsb(listerr)
+ int listerr;
+{
+ ufs_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_firstfield = sblock.fs_firstfield;
+ altsblock.fs_unused_1 = sblock.fs_unused_1;
+ 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;
+ memmove(altsblock.fs_csp, sblock.fs_csp, sizeof sblock.fs_csp);
+ altsblock.fs_maxcluster = sblock.fs_maxcluster;
+ memmove(altsblock.fs_fsmnt, sblock.fs_fsmnt, sizeof sblock.fs_fsmnt);
+ memmove(altsblock.fs_sparecon,
+ sblock.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_state = sblock.fs_state;
+ 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 (memcmp(&sblock, &altsblock, (int)sblock.fs_sbsize)) {
+ if (debug) {
+ long *nlp, *olp, *endlp;
+
+ printf("superblock mismatches\n");
+ nlp = (long *)&altsblock;
+ olp = (long *)&sblock;
+ endlp = olp + (sblock.fs_sbsize / sizeof *olp);
+ for ( ; olp < endlp; olp++, nlp++) {
+ if (*olp == *nlp)
+ continue;
+ printf("offset %d, original %d, alternate %d\n",
+ olp - (long *)&sblock, *olp, *nlp);
+ }
+ }
+ 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!
+ */
+static 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 = strchr(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);
+ }
+ memset(fs, 0, 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);
+}
+
+static 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));
+ errx(EEXIT, "%s: can't read disk label", s);
+ }
+ return (&lab);
+}
diff --git a/sbin/fsck_ifs/utilities.c b/sbin/fsck_ifs/utilities.c
new file mode 100644
index 0000000..30c31cf
--- /dev/null
+++ b/sbin/fsck_ifs/utilities.c
@@ -0,0 +1,625 @@
+/*
+ * 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.6 (Berkeley) 5/19/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 <ctype.h>
+#include <err.h>
+#include <string.h>
+
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+static void rwerror __P((char *mesg, ufs_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)
+ errx(EEXIT, "cannot allocate buffer pool");
+ 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;
+ errx(EEXIT, "cannot allocate buffer pool");
+ }
+ 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)
+ ufs_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)
+ errx(EEXIT, "deadlocked buffer pool");
+ 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;
+ ufs_daddr_t blk;
+ long size;
+{
+ ufs_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);
+ }
+}
+
+static void
+rwerror(mesg, blk)
+ char *mesg;
+ ufs_daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ exit(EEXIT);
+}
+
+void
+ckfini(markclean)
+ int markclean;
+{
+ register struct bufarea *bp, *nbp;
+ int ofsmodified, 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)
+ errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (markclean && sblock.fs_clean == 0) {
+ sblock.fs_clean = 1;
+ sbdirty();
+ ofsmodified = fsmodified;
+ flush(fswritefd, &sblk);
+ fsmodified = ofsmodified;
+ if (!preen)
+ printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
+ }
+ 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;
+ ufs_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;
+ memset(buf, 0, (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;
+ ufs_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
+ */
+ufs_daddr_t
+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)
+ ufs_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;
+ memset(&idesc, 0, 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;
+ memmove(cp, namebuf, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch(sig)
+ int sig;
+{
+ if (!doinglevel2)
+ ckfini(0);
+ 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(sig)
+ int sig;
+{
+ 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(sig)
+ int sig;
+{
+
+ 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:
+ errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+void
+#if __STDC__
+pfatal(const char *fmt, ...)
+#else
+pfatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (!preen) {
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return;
+ }
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr,
+ "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(EEXIT);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+void
+#if __STDC__
+pwarn(const char *fmt, ...)
+#else
+pwarn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (preen)
+ (void)fprintf(stderr, "%s: ", cdevname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Stub for routines from kernel.
+ */
+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
+ pfatal("INTERNAL INCONSISTENCY:");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EEXIT);
+}
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..c1b9b59
--- /dev/null
+++ b/sbin/fsdb/fsdb.8
@@ -0,0 +1,243 @@
+.\" $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.
+.\"
+.\" $Id: fsdb.8,v 1.6 1997/02/22 14:32:25 peter Exp $
+.\"
+.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
+.Op Fl r
+.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.
+.Pp
+Option
+.Fl r
+opens the filesystem read/only, and disables all commands that would
+write to it.
+.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 clri 8 ,
+.Xr fsck 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..7ab1e05
--- /dev/null
+++ b/sbin/fsdb/fsdb.c
@@ -0,0 +1,901 @@
+/* $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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: fsdb.c,v 1.8 1997/04/15 09:02:45 joerg 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 <err.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include "fsdb.h"
+#include "fsck.h"
+
+static void usage __P((void));
+int cmdloop __P((void));
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
+ exit(1);
+}
+
+int returntosingle = 0;
+char nflag = 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;
+
+ while (-1 != (ch = getopt(argc, argv, "fdr"))) {
+ switch (ch) {
+ case 'f':
+ /* The -f option is left for historical
+ * reasons and has no meaning.
+ */
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'r':
+ nflag++; /* "no" in fsck, readonly for us */
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ else
+ fsys = argv[0];
+
+ if (!setup(fsys))
+ errx(1, "cannot set up file system `%s'", fsys);
+ printf("%s file system `%s'\nLast Mounted on %s\n",
+ nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
+ rval = cmdloop();
+ if (!nflag) {
+ sblock.fs_clean = 0; /* mark it dirty */
+ sbdirty();
+ ckfini(0);
+ 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, FL_RO, helpfn },
+ { "?", "Print out help", 1, 1, FL_RO, helpfn },
+ { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
+ { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
+ { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
+ { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
+ { "back", "Go to previous active inode", 1, 1, FL_RO, back },
+ { "active", "Print active inode", 1, 1, FL_RO, active },
+ { "print", "Print active inode", 1, 1, FL_RO, active },
+ { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
+ { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
+ { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
+ { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
+ { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
+ { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
+ { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
+ { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
+ { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
+ { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
+ { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
+ { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
+ { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
+ { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
+ { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
+ { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
+ { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
+ { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
+ { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
+ { "quit", "Exit", 1, 1, FL_RO, quit },
+ { "q", "Exit", 1, 1, FL_RO, quit },
+ { "exit", "Exit", 1, 1, FL_RO, 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("fsdb", 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 ((cmdp->flags & FL_WR) == FL_WR && nflag)
+ warnx("`%s' requires write access", cmd_argv[0]),
+ rval = 1;
+ else 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;
+ 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)
+{
+ 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;
+ 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 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..b7c3057
--- /dev/null
+++ b/sbin/fsdb/fsdb.h
@@ -0,0 +1,61 @@
+/* $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.
+ *
+ * $Id: fsdb.h,v 1.2 1997/03/13 12:44:52 peter Exp $
+ */
+
+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;
+ unsigned int flags;
+#define FL_RO 0x0000 /* for symmetry */
+#define FL_WR 0x0001 /* wants to write */
+ 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..3c23fd6
--- /dev/null
+++ b/sbin/fsdb/fsdbutil.c
@@ -0,0 +1,208 @@
+/* $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[] = "$Id: fsdbutil.c,v 1.5 1997/03/13 12:44:53 peter 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;
+ time_t t;
+
+ 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);
+ t = dp->di_mtime;
+ p = ctime(&t);
+ printf("\n\tMTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ dp->di_mtimensec);
+ t = dp->di_ctime;
+ p = ctime(&t);
+ printf("\n\tCTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20],
+ dp->di_ctimensec);
+ t = dp->di_atime;
+ p = ctime(&t);
+ printf("\n\tATIME=%15.15s %4.4s [%d nsec]\n", &p[4], &p[20],
+ dp->di_atimensec);
+
+ 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/fsirand/Makefile b/sbin/fsirand/Makefile
new file mode 100644
index 0000000..1349bb1
--- /dev/null
+++ b/sbin/fsirand/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD: Makefile,v 1.1 1997/01/26 02:23:20 millert Exp $
+
+PROG= fsirand
+MAN8= fsirand.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsirand/fsirand.8 b/sbin/fsirand/fsirand.8
new file mode 100644
index 0000000..1b3b4d4
--- /dev/null
+++ b/sbin/fsirand/fsirand.8
@@ -0,0 +1,113 @@
+.\" Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Todd C. Miller.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED 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.
+.\"
+.\" $OpenBSD: fsirand.8,v 1.6 1997/02/23 03:58:26 millert Exp $
+.\" $Id: fsirand.8,v 1.5 1997/06/11 07:18:18 charnier Exp $
+.\"
+.Dd January 25, 1997
+.Dt FSIRAND 8
+.Os
+.Sh NAME
+.Nm fsirand
+.Nd randomize inode generation numbers
+.Sh SYNOPSIS
+.Nm fsirand
+.Op Fl b
+.Op Fl f
+.Op Fl p
+.Ar special
+.Op Ar "special ..."
+.Sh DESCRIPTION
+The
+.Nm fsirand
+command installs random generation numbers on all the inodes for
+each filesystem specified on the command line by
+.Ar special .
+This increases the security of NFS-exported filesystems by making
+it difficult to ``guess'' filehandles.
+.Pp
+.Em Note:
+.Xr newfs 8
+now does the equivalent of
+.Nm
+itself so it is no longer necessary to
+run
+.Nm
+by hand on a new filesystem. It is only used to
+re-randomize or report on an existing filesystem.
+.Pp
+.Nm Fsirand
+should only be used on an unmounted filesystem that
+has been checked with
+.Xr fsck 8
+or a filesystem that is mounted read-only.
+.Nm Fsirand
+may be used on the root filesystem in single-user mode
+but the system should be rebooted via ``reboot -n'' afterwards.
+.Sh OPTIONS
+.Bl -tag -width indent
+The available options are as follows:
+.It Fl b
+Use the default block size (usually 512 bytes) instead
+of the value gleaned from the disklabel.
+.It Fl f
+Force
+.Nm
+to run even if the filesystem on
+.Ar special
+is not marked as clean.
+.It Fl p
+Print the current generation numbers for all inodes instead of
+generating new ones.
+.Sh CAVEATS
+Since
+.Nm
+allocates enough memory to hold all the inodes in
+a given cylinder group it may use a large amount
+of memory for large disks with few cylinder groups.
+.Sh SEE ALSO
+.Xr fs 5 ,
+.Xr fsck 8 ,
+.Xr newfs 8 .
+.Sh HISTORY
+The
+.Nm
+command appeared in SunOS 3.x.
+.br
+This version of
+.Nm
+first appeared in
+.Ox 2.1 .
+A
+.Tn FreeBSD
+version first appeared in
+.Fx 2.2.5 .
+.Sh AUTHOR
+.nf
+Todd C. Miller <Todd.Miller@courtesan.com>
+.fi
diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c
new file mode 100644
index 0000000..4e2ea7e
--- /dev/null
+++ b/sbin/fsirand/fsirand.c
@@ -0,0 +1,293 @@
+/* $OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $ */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Todd C. Miller.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED 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[] = "$OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static void usage __P((int));
+int fsirand __P((char *));
+
+int printonly = 0, force = 0, ignorelabel = 0;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int n, ex = 0;
+ struct rlimit rl;
+
+ while ((n = getopt(argc, argv, "bfp")) != -1) {
+ switch (n) {
+ case 'b':
+ ignorelabel++;
+ break;
+ case 'p':
+ printonly++;
+ break;
+ case 'f':
+ force++;
+ break;
+ default:
+ usage(1);
+ }
+ }
+ if (argc - optind < 1)
+ usage(1);
+
+ srandomdev();
+
+ /* Increase our data size to the max */
+ if (getrlimit(RLIMIT_DATA, &rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_DATA, &rl) < 0)
+ warn("Can't get resource limit to max data size");
+ } else
+ warn("Can't get resource limit for data size");
+
+ for (n = optind; n < argc; n++) {
+ if (argc - optind != 1)
+ (void)puts(argv[n]);
+ ex += fsirand(argv[n]);
+ if (n < argc - 1)
+ putchar('\n');
+ }
+
+ exit(ex);
+}
+
+int
+fsirand(device)
+ char *device;
+{
+ static struct dinode *inodebuf;
+ static size_t oldibufsize;
+ size_t ibufsize;
+ struct fs *sblock;
+ ino_t inumber, maxino;
+ daddr_t dblk;
+ char sbuf[SBSIZE], sbuftmp[SBSIZE];
+ int devfd, n, cg;
+ u_int32_t bsize = DEV_BSIZE;
+ struct disklabel label;
+
+ if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) {
+ warn("Can't open %s", device);
+ return (1);
+ }
+
+ /* Get block size (usually 512) from disklabel if possible */
+ if (!ignorelabel) {
+ if (ioctl(devfd, DIOCGDINFO, &label) < 0)
+ warn("Can't read disklabel, using sector size of %d",
+ bsize);
+ else
+ bsize = label.d_secsize;
+ }
+
+ /* Read in master superblock */
+ (void)memset(&sbuf, 0, sizeof(sbuf));
+ sblock = (struct fs *)&sbuf;
+ if (lseek(devfd, SBOFF, SEEK_SET) == -1) {
+ warn("Can't seek to superblock (%qd) on %s", SBOFF, device);
+ return (1);
+ }
+ if ((n = read(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
+ warnx("Can't read superblock on %s: %s", device,
+ (n < SBSIZE) ? "short read" : strerror(errno));
+ return (1);
+ }
+ maxino = sblock->fs_ncg * sblock->fs_ipg;
+
+ /* Simple sanity checks on the superblock */
+ if (sblock->fs_magic != FS_MAGIC) {
+ warnx("Bad magic number in superblock");
+ return (1);
+ }
+ if (sblock->fs_sbsize > SBSIZE) {
+ warnx("Superblock size is preposterous");
+ return (1);
+ }
+ if (sblock->fs_postblformat == FS_42POSTBLFMT) {
+ warnx("Filesystem format is too old, sorry");
+ return (1);
+ }
+ if (!force && !printonly && sblock->fs_clean != 1) {
+ warnx("Filesystem is not clean, fsck %s first.", device);
+ return (1);
+ }
+
+ /* Make sure backup superblocks are sane. */
+ sblock = (struct fs *)&sbuftmp;
+ for (cg = 0; cg < sblock->fs_ncg; cg++) {
+ dblk = fsbtodb(sblock, cgsblock(sblock, cg));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("Can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
+ warn("Can't read backup superblock %d on %s: %s",
+ cg + 1, device, (n < SBSIZE) ? "short write"
+ : strerror(errno));
+ return (1);
+ }
+ if (sblock->fs_magic != FS_MAGIC) {
+ warnx("Bad magic number in backup superblock %d on %s",
+ cg + 1, device);
+ return (1);
+ }
+ if (sblock->fs_sbsize > SBSIZE) {
+ warnx("Size of backup superblock %d on %s is preposterous",
+ cg + 1, device);
+ return (1);
+ }
+ }
+ sblock = (struct fs *)&sbuf;
+
+ /* XXX - should really cap buffer at 512kb or so */
+ ibufsize = sizeof(struct dinode) * sblock->fs_ipg;
+ if (oldibufsize < ibufsize) {
+ if ((inodebuf = realloc(inodebuf, ibufsize)) == NULL)
+ errx(1, "Can't allocate memory for inode buffer");
+ oldibufsize = ibufsize;
+ }
+
+ if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) {
+ if (sblock->fs_inodefmt >= FS_44INODEFMT && sblock->fs_id[0])
+ (void)printf("%s was randomized on %s", device,
+ ctime((const time_t *)&(sblock->fs_id[0])));
+ (void)printf("fsid: %x %x\n", sblock->fs_id[0],
+ sblock->fs_id[1]);
+ }
+
+ /* Randomize fs_id unless old 4.2BSD filesystem */
+ if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
+ /* Randomize fs_id and write out new sblock and backups */
+ sblock->fs_id[0] = (u_int32_t)time(NULL);
+ sblock->fs_id[1] = random();
+
+ if (lseek(devfd, SBOFF, SEEK_SET) == -1) {
+ warn("Can't seek to superblock (%qd) on %s", SBOFF,
+ device);
+ return (1);
+ }
+ if ((n = write(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
+ warn("Can't read superblock on %s: %s", device,
+ (n < SBSIZE) ? "short write" : strerror(errno));
+ return (1);
+ }
+ }
+
+ /* For each cylinder group, randomize inodes and update backup sblock */
+ for (cg = 0, inumber = 0; cg < sblock->fs_ncg; cg++) {
+ /* Update superblock if appropriate */
+ if ((sblock->fs_inodefmt >= FS_44INODEFMT) && !printonly) {
+ dblk = fsbtodb(sblock, cgsblock(sblock, cg));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("Can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, (void *)sblock, SBSIZE)) != SBSIZE) {
+ warn("Can't read backup superblock %d on %s: %s",
+ cg + 1, device, (n < SBSIZE) ? "short write"
+ : strerror(errno));
+ return (1);
+ }
+ }
+
+ /* Read in inodes, then print or randomize generation nums */
+ dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber));
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("Can't seek to %qd", (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) {
+ warnx("Can't read inodes: %s",
+ (n < ibufsize) ? "short read" : strerror(errno));
+ return (1);
+ }
+
+ for (n = 0; n < sblock->fs_ipg; n++, inumber++) {
+ if (inumber >= ROOTINO) {
+ if (printonly)
+ (void)printf("ino %d gen %x\n", inumber,
+ inodebuf[n].di_gen);
+ else
+ inodebuf[n].di_gen = random();
+ }
+ }
+
+ /* Write out modified inodes */
+ if (!printonly) {
+ if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) {
+ warn("Can't seek to %qd",
+ (off_t)dblk * bsize);
+ return (1);
+ } else if ((n = write(devfd, inodebuf, ibufsize)) !=
+ ibufsize) {
+ warnx("Can't write inodes: %s",
+ (n != ibufsize) ? "short write" :
+ strerror(errno));
+ return (1);
+ }
+ }
+ }
+ (void)close(devfd);
+
+ return(0);
+}
+
+static void
+usage(ex)
+ int ex;
+{
+ (void)fprintf(stderr,
+"usage: fsirand [ -b ] [ -f ] [ -p ] special [special ...]\n");
+ exit(ex);
+}
diff --git a/sbin/i386/Makefile b/sbin/i386/Makefile
new file mode 100644
index 0000000..58ca50c
--- /dev/null
+++ b/sbin/i386/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+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..e9d35c6
--- /dev/null
+++ b/sbin/i386/comcontrol/comcontrol.8
@@ -0,0 +1,64 @@
+.\" $Id$
+.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 stty 1 ,
+.Xr sio 4
+.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..e250483
--- /dev/null
+++ b/sbin/i386/cxconfig/cxconfig.8
@@ -0,0 +1,323 @@
+.Dd "December 2, 1994"
+.Dt CXCONFIG 8
+.Os FreeBSD
+.Sh NAME
+.Nm cxconfig
+.Nd channel options management utility for Cronyx-Sigma adapter
+.Sh DESCRIPTION
+.Pp
+The
+.Nm
+utility is used for configuring the channel options of
+the Cronyx-Sigma adapter.
+.Pp
+To change channel options the channel should be free: the corresponding
+network interface in ``down'' state, the asynchronous terminal device
+.Pa /dev/tty*
+closed.
+Generally, the channel options are set up during the operating
+system startup, for example from the
+.Pa /etc/rc
+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"
+.Bl -tag -width 10n
+.It "cxconfig"
+The brief information about all channels.
+.It "cxconfig -a"
+The full information about all channels.
+.It "cxconfig <channel>"
+The brief information about the channel.
+.It "cxconfig -a <channel>"
+The full information about the channel.
+.It "cxconfig <channel> <option>..."
+Setting the channel options.
+.El
+.Sh "Channel options"
+.Bl -tag -width 10n
+.It 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.
+.It 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.
+.It async
+Set the asynchronous channel mode.
+.It "hdlc, bisync, bsc, x.21, x21
+Set the synchronous channel mode: HDLC, Bisync (BSC) or X.21.
+.It ppp
+Set the link-level protocol: PpP/HDLC. The built-in simplified synchronous PPP
+implementation is used (see RFC-1548, RFC-1549).
+.It 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.
+.It ext
+Use the external link-level protocol suite (for BSD/386 only).
+.It "+keepalive, -keepalive"
+Enable the automatic line state control sub-protocol.
+This setting is not significant when the external link-level protocol is used.
+.It "+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.
+.It "port=rs232, port=rs449, port=v35
+Set the zero channel hardware interface type.
+.El
+.Sh "Common options"
+.Bl -tag -width 10n
+.It "nrz, nrzi, manchester"
+Set the data line signal encoding.
+In the case of
+.Em NRZ
+encoding the zero bit is transmitted by the zero signal
+level, the one bit - by the positive signal level.
+In the case of
+.Em NRZI
+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
+.Em Manchester
+encoding the zero bit is encoded as 01 value,
+the one bit - as 10 value.
+.It "+dpll, -dpll"
+Enable the digital phase locked loop mode (DPLL).
+When enabled, the receiver timing clock signal
+is derived from the received data.
+.It "+lloop, -lloop"
+Set the local loopback mode.
+.It "+extclock, -extclock"
+Set the timing clock source of synchronous channels. There are
+two possible variants:
+.Em "external clock"
+source or
+.Em "internal clock"
+generation.
+.Pp
+.Em"External clock"
+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).
+.Pp
+In the case of
+.Em "internal clock"
+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.
+.It fifo=#
+FIFO threshold level setup for receiver and transmitter.
+.It rfifo=#
+Hardware RTS/CTS flow control FIFO threshold setup.
+.It "+ctsup, -ctsup"
+Enable/disable interrupts on CTS (Clear To Send) signal setup (0 to 1 transition).
+.It "+ctsdown, -ctsdown"
+Enable/disable interrupts on CTS (Clear To Send) signal clear (1 to 0 transition).
+.It "+cdup, -cdup"
+Enable/disable interrupts on CD (Carrier Detect) signal setup (0 to 1 transition).
+.It "+cddown, -cddown"
+Enable/disable interrupts on CD (Carrier Detect) signal clear (1 to 0 transition).
+.It "+dsrup, -dsrup"
+Enable/disable interrupts on DSR (Data Set Ready) signal setup (0 to 1 transition).
+.It "+dsrdown, -dsrdown"
+Enable/disable interrupts on DSR (Data Set Ready) signal clear (1 to 0 transition).
+.El
+.Sh "Asynchronous mode options"
+.Bl -tag -width 10n
+.It cs#
+Select character size: 5, 6, 7 or 8 bits.
+.It "parodd, pareven
+Parity mode: odd or even.
+.It "+ignpar, -ignpar
+Disable/enable parity detection.
+.It nopar
+Disable parity bit generation.
+.It forcepar
+Force parity: even - 0, odd - 1.
+.It "stopb1, stopb1.5, stopb2
+Use 1 or 1.5 or 2 stop bits per character.
+.It "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.It "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.It "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.It "+rloop, -rloop"
+Set the remote loopback mode.
+.It "+etc, -etc"
+Enable the embedded transmit commands mode.
+.It "+ixon, -ixon"
+Enable the hardware XON/XOFF flow control support.
+.It "+ixany, -ixany"
+Use the hardware IXANY mode support.
+.It "+sdt, -sdt"
+Detect the spec. characters SCHR1 and SCHR2 in the receive data.
+.It "+flowct, -flowct"
+Receive the flow control spec. characters as data.
+.It "+rdt, -rdt"
+Detect the spec. characters in range SCRL..SCRH in the receive data.
+.It "+exdt, -exdt"
+Detect the spec. characters SCHR3 and SCHR4 in the receive data.
+.It "parintr, parnull, parign, pardisc, parffnull
+Action on parity errors:
+.Pp
+.Bl -tag -width parffnullxxx -compact
+.It Mode
+Action
+.It parintr
+Generate the receiver error interrupt
+.It parnull
+Input the NULL character
+.It parign
+Ignore the error, receive as good data
+.It pardisc
+Ignore the character
+.It parffnull
+Input the sequence <0xFF, NULL, character>
+.El
+.It "brkintr, brknull, brkdisc
+Line break state action:
+.Pp
+.Bl -tag -width parffnullxxx -compact
+.It Mode
+Action
+.It brkintr
+Generate the receiver error interrupt
+.It brknull
+Input the NULL character
+.It brkdisc
+Ignore the line break state
+.El
+.It "+inlcr, -inlcr"
+Translate received NL characters to CR.
+.It "+icrnl, -icrnl"
+Translate received CR characters to NL.
+.It "+igncr, -igncr"
+Ignore received CR characters.
+.It "+ocrnl, -ocrnl"
+Translate transmitted CR characters to NL.
+.It "+onlcr, -onlcr"
+Translate transmitted NL characters to CR.
+.It "+fcerr, -fcerr"
+Process (don't process) the characters, received with errors,
+for special character/flow control matching.
+.It "+lnext, -lnext"
+Enable the LNEXT character option: the character following
+the LNEXT character is not processed for special character/flow
+control matching.
+.It "+istrip, -istrip"
+Strip input characters to seven bits.
+.It schr1=#
+The XON flow control character value.
+.It schr2=#
+The XOFF flow control character value.
+.It schr3=#
+The SCHR3 spec. character value.
+.It schr4=#
+The SCHR4 spec. character value.
+.It "scrl=#, scrh=#
+The spec. character range (inclusive).
+.It lnext=#
+The LNEXT spec. character value.
+.El
+.Sh "HDLC mode options"
+.Bl -tag -width 10n
+.It if#
+The minimum number of flags transmitted before a frame is started.
+.It noaddr
+No frame address recognition.
+.It "addrlen1, addrlen2"
+Address field length: 1 or 2 bytes.
+.It "addr1, addr2"
+Addressing mode: 4x1 bytes or 2x2 bytes.
+Registers RFAR1..RFAR4 should contain the address to be matched.
+.It "+clrdet, -clrdet"
+Enable/disable clear detect for X.21 protocol support.
+.It "+dsr, -dsr"
+Use the DSR input signal as receiver enable/disable.
+.It "+cts, -cts"
+Use the CTS input signal as transmitter enable/disable.
+.It "+rts, -rts"
+Drive the RTS output signal as transmitter ready.
+.It "+fcs, -fcs"
+Enable/disable the frame checksum generation and checking.
+.It "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).
+.It "fcs-crc-16, fcs-v.41
+Frame checksum preset: all zeros (CRC-16) or all ones (CRC V.41).
+.It "+crcinv, -crcinv"
+Invert (ie. CRC V.41) or don't invert (ie. CRC-16) the transmitted frame checksum.
+.It "+fcsapd, -fcsapd"
+Pass the received CRC to the host at the end of receiver data buffer.
+.It "idlemark, idleflag
+Idle mode: idle in mark (transmit all ones) or idle in flag (transmit flag).
+.It "+syn, -syn"
+Enable/disable sending pad characters before sending flag when coming out
+of the idle mode.
+.It pad#
+The number of synchronous characters sent (0..4).
+.It "syn=0xaa, syn=0x00
+Send sync pattern.
+.It "rfar1=#, rfar2=#, rfar3=#, rfar4=#
+Frame address registers for address recognition.
+.El
+.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:
+.Bd -literal
+cxconfig cx7 128000 hdlc ppp -keepalive nrzi -cts +dpll -extclock
+ifconfig cx7 158.250.244.2 158.250.244.1 up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx0 hdlc 256000 cisco +keepalive -extclock
+ifconfig cx0 200.1.1.1 200.1.1.2 up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx1 hdlc ext
+ifconfig cx1 193.124.254.50 193.124.254.49 multicast up
+.Ed
+.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:
+.Bd -literal
+cxconfig cx0 hdlc 64000 port=rs232 ppp +keepalive -extclock +cts
+ifconfig cx0 100.0.0.2 100.0.0.1 debug up
+.Ed
+.Sh FILES
+.Pa /dev/cronyx
+The special device file for adapter options management.
+.Sh SEE ALSO
+.Xr 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..70b0f8c
--- /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 1234
+.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 1234
+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 1 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 2 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 3 is:
+ <UNUSED>
+ The data for partition 4 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
+(1-4) 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 4 and mark it as unused:
+.Pp
+.nf
+ p 4 0 0 0
+.fi
+.Pp
+Example: to set partition 1 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 1 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 1 the active partition:
+.Pp
+.nf
+ a 1
+.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..7a4c819
--- /dev/null
+++ b/sbin/i386/fdisk/fdisk.c
@@ -0,0 +1,1326 @@
+/*
+ * 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"}
+ ,{0xA6, "OpenBSD"}
+ ,{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 '1':
+ partition = 1;
+ break;
+ case '2':
+ partition = 2;
+ break;
+ case '3':
+ partition = 3;
+ break;
+ case '4':
+ partition = 4;
+ 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 = 1; 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]] [-{1,2,3,4}] [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 = 1; 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;
+ u_int64_t part_mb;
+
+ partp = ((struct dos_partition *) &mboot.parts) + i - 1;
+
+ if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+ printf("<UNUSED>\n");
+ return;
+ }
+ /*
+ * Be careful not to overflow.
+ */
+ part_mb = partp->dp_size;
+ part_mb *= secsize;
+ part_mb /= (1024 * 1024);
+ printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
+ printf(" start %ld, size %ld (%qd Meg), flag %x\n",
+ partp->dp_start,
+ partp->dp_size,
+ part_mb,
+ 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 - 1;
+
+ 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 == 4) {
+ init_sector0(1);
+ printf("\nThe static data for the DOS partition 4 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 = 4, 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-1].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 < 1 || partition > 4)
+ {
+ fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n",
+ name, current_line_number, partition);
+ break;
+ }
+ partp = ((struct dos_partition *) &mboot.parts) + partition - 1;
+ 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 < 1 || partition > 4)
+ {
+ 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-1].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..3fb1b74
--- /dev/null
+++ b/sbin/i386/ft/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+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..1306010
--- /dev/null
+++ b/sbin/i386/ft/ft.8
@@ -0,0 +1,88 @@
+.\" 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
+.Pa /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..f6c88ab
--- /dev/null
+++ b/sbin/i386/mount_msdos/Makefile
@@ -0,0 +1,17 @@
+#
+# $Id: Makefile,v 1.4 1997/02/22 14:32:29 peter Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..4f6fcd1
--- /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$
+.\"
+.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..53ae83b
--- /dev/null
+++ b/sbin/i386/mount_msdos/mount_msdos.c
@@ -0,0 +1,213 @@
+/*
+ * 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.8 1997/03/03 13:23:54 bde Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <msdosfs/msdosfsmount.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, error, 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:")) != -1) {
+ 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);
+ }
+
+ error = getvfsbyname("msdos", &vfc);
+ if (error && vfsisloadable("msdos")) {
+ if (vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ error = getvfsbyname("msdos", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..4d7da18
--- /dev/null
+++ b/sbin/i386/nextboot/nextboot.8
@@ -0,0 +1,88 @@
+.\" $Id: nextboot.8,v 1.6 1997/02/22 14:32:31 peter 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-existent, 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..5461884
--- /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")) != -1) {
+ 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..0ff6ea0
--- /dev/null
+++ b/sbin/ifconfig/Makefile
@@ -0,0 +1,17 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id: Makefile,v 1.9 1997/02/22 14:32:32 peter Exp $
+
+PROG= ifconfig
+SRCS= ifconfig.c
+
+#comment out to exclude SIOC[GS]IFMEDIA support
+SRCS+= ifmedia.c
+CFLAGS+=-DUSE_IF_MEDIA
+
+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..c2eed30d
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.8
@@ -0,0 +1,339 @@
+.\" 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.12 1997/02/22 14:32:32 peter 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
+.Op Fl m
+.Ar interface
+.Op Ar protocol_family
+.Nm ifconfig
+.Fl a
+.Op Fl m
+.Op Fl d
+.Op Fl u
+.Op Ar protocol_family
+.Nm ifconfig
+.Fl l
+.Op Fl d
+.Op Fl u
+.Sh DESCRIPTION
+.Nm
+is used to assign an address
+to a network interface and/or configure
+network interface parameters.
+.Nm
+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 media Ar type
+Set the media type of the interface to
+.Ar type .
+Some interfaces support the mutually exclusive use of one of several
+different physical media connectors. For example, a 10Mb/s Ethernet
+interface might support the use of either
+.Tn AUI
+or twisted pair connectors. Setting the media type to
+.Dq 10base5/AUI
+would change the currently active connector to the AUI port.
+Setting it to
+.Dq 10baseT/UTP
+would activate twisted pair. Refer to the interfaces' driver
+specific man page for a complete list of the available types.
+.It Cm mediaopt Ar opts
+Set the specified media options on the interface.
+.Ar opts
+is a comma delimited list of options to apply to the interface.
+Refer to the interfaces' driver specific man page for a complete
+list of available options.
+.It Fl mediaopt Ar opts
+Disable the specified media options on the interface.
+.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, or to select the connector type
+for some ethernet cards. Refer to the man page for the specific driver
+for more information.
+.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
+.Nm
+displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+Ifconfig will report only the details specific to that protocol family.
+.Pp
+If the
+.Fl m
+flag is passed before an interface name,
+.Nm
+will display all of the supported media for the specified interface.
+.Pp
+Optionally, the
+.Fl a
+flag may be used instead of an interface name. This flag instructs
+.Nm
+to display information about all interfaces in the system.
+.Fl d
+limits this to interfaces that are down, and
+.Fl u
+limits this to interfaces that are up.
+.Pp
+The
+.Fl l
+flag may be used to list all available interfaces on the system, with
+no other additional information. Use of this flag is mutually exclusive
+with all other flags and commands, except for
+.Fl d
+(only list interfaces that are down)
+and
+.Fl u
+(only list interfaces that are up).
+.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..1d4070d
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.c
@@ -0,0 +1,1247 @@
+/*
+ * 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.29 1997/05/10 14:47:34 peter 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_var.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>
+
+#include "ifconfig.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 allmedia;
+
+struct afswtch;
+
+void Perror __P((const char *cmd));
+void checkatrange __P((struct sockaddr_at *));
+int ifconfig __P((int argc, char *const *argv, const struct afswtch *afp));
+void notealias __P((const char *, int, int, const struct afswtch *afp));
+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((const struct afswtch *afp, int addrcount,
+ struct sockaddr_dl *sdl, struct if_msghdr *ifm,
+ struct ifa_msghdr *ifam));
+void usage __P((void));
+
+typedef void c_func __P((const char *cmd, int arg, int s, const struct afswtch *afp));
+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, int, const struct afswtch *afp));
+} 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 },
+#ifdef USE_IF_MEDIA
+ { "media", NEXTARG, setmedia },
+ { "mediaopt", NEXTARG, setmediaopt },
+ { "-mediaopt", NEXTARG, unsetmediaopt },
+#endif
+ { "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, struct rt_addrinfo *));
+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!! */
+#if 0 /* XXX conflicts with the media command */
+#ifdef USE_IF_MEDIA
+ { "media", AF_INET, media_status, NULL }, /* XXX not real!! */
+#endif
+#endif
+ { 0, 0, 0, 0 }
+};
+
+/*
+ * 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);
+ }
+}
+
+
+void
+usage()
+{
+ fputs("usage: ifconfig -a [ -m ] [ -d ] [ -u ] [ af ]\n", stderr);
+ fputs(" ifconfig -l [ -d ] [ -u ]\n", stderr);
+ fputs(" ifconfig [ -m ] interface\n", stderr);
+ fputs(" [ af [ address [ dest_addr ] ] [ netmask mask ] [ broadcast addr ]\n", stderr);
+ fputs(" [ alias ] [ delete ] ]\n", stderr);
+ fputs(" [ up ] [ down ]\n", stderr);
+ fputs(" [ metric n ]\n", stderr);
+ fputs(" [ mtu n ]\n", stderr);
+ fputs(" [ arp | -arp ]\n", stderr);
+ fputs(" [ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ]\n", stderr);
+#ifdef USE_IF_MEDIA
+ fputs(" [ media mtype ]\n", stderr);
+ fputs(" [ mediaopt mopts ]\n", stderr);
+ fputs(" [ -mediaopt mopts ]\n", stderr);
+#endif
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *const *argv;
+{
+ int c;
+ int all, namesonly, downonly, uponly;
+ int foundit = 0, need_nl = 0;
+ const struct afswtch *afp = 0;
+ int addrcount;
+ struct if_msghdr *ifm, *nextifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+
+
+ size_t needed;
+ int mib[6];
+
+ /* Parse leading line options */
+ all = allmedia = downonly = uponly = namesonly = 0;
+ while ((c = getopt(argc, argv, "adlmu")) != -1) {
+ switch (c) {
+ case 'a': /* scan all interfaces */
+ all++;
+ break;
+ case 'l': /* scan interface names only */
+ namesonly++;
+ break;
+ case 'd': /* restrict scan to "down" interfaces */
+ downonly++;
+ break;
+ case 'u': /* restrict scan to "down" interfaces */
+ uponly++;
+ break;
+ case 'm': /* show media choices in status */
+#ifdef USE_IF_MEDIA
+ allmedia++;
+#else
+ fputs("WARNING: if_media not compiled in!\n", stderr);
+ usage();
+#endif
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* -l cannot be used with -a or -m */
+ if (namesonly && (all || allmedia))
+ usage();
+
+ /* nonsense.. */
+ if (uponly && downonly)
+ usage();
+
+ /* -a and -l allow an address family arg to limit the output */
+ if (all || namesonly) {
+ if (argc > 1)
+ usage();
+
+ if (argc == 1) {
+ for (afp = afs; afp->af_name; afp++)
+ if (strcmp(afp->af_name, *argv) == 0) {
+ argc--, argv++;
+ break;
+ }
+ if (afp->af_name == NULL)
+ usage();
+ /* leave with afp non-zero */
+ }
+ } else {
+ /* not listsing, need an argument */
+ if (argc < 1)
+ usage();
+
+ strncpy(name, *argv, sizeof(name));
+ argc--, argv++;
+ }
+
+ /* Check for address family */
+ if (argc > 0) {
+ for (afp = afs; afp->af_name; afp++)
+ if (strcmp(afp->af_name, *argv) == 0) {
+ argc--, argv++;
+ break;
+ }
+ if (afp->af_name == NULL)
+ afp = NULL; /* not a family, NULL */
+ }
+
+ 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;
+
+ next = buf;
+ while (next < lim) {
+
+ ifm = (struct if_msghdr *)next;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ } else {
+ fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
+ fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
+ ifm->ifm_type);
+ fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
+ fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
+ lim);
+ exit (1);
+ }
+
+ next += ifm->ifm_msglen;
+ ifam = NULL;
+ addrcount = 0;
+ while (next < lim) {
+
+ nextifm = (struct if_msghdr *)next;
+
+ if (nextifm->ifm_type != RTM_NEWADDR)
+ break;
+
+ if (ifam == NULL)
+ ifam = (struct ifa_msghdr *)nextifm;
+
+ addrcount++;
+ next += nextifm->ifm_msglen;
+ }
+
+ if (all || namesonly) {
+ if (uponly)
+ if ((flags & IFF_UP) == 0)
+ continue; /* not up */
+ if (downonly)
+ if (flags & IFF_UP)
+ continue; /* not down */
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ if (namesonly) {
+ if (need_nl)
+ putchar(' ');
+ fputs(name, stdout);
+ need_nl++;
+ continue;
+ }
+ } else {
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
+ continue; /* not same name */
+ }
+
+ if (argc > 0)
+ ifconfig(argc, argv, afp);
+ else
+ status(afp, addrcount, sdl, ifm, ifam);
+
+ if (all == 0 && namesonly == 0) {
+ foundit++; /* flag it as 'done' */
+ break;
+ }
+ }
+ free(buf);
+
+ if (namesonly && need_nl > 0)
+ putchar('\n');
+
+ if (all == 0 && namesonly == 0 && foundit == 0)
+ errx(1, "interface %s does not exist", name);
+
+
+ exit (0);
+}
+
+
+int
+ifconfig(argc, argv, afp)
+ int argc;
+ char *const *argv;
+ const struct afswtch *afp;
+{
+ int s;
+
+ if (afp == NULL)
+ afp = &afs[0];
+ ifr.ifr_addr.sa_family = afp->af_af;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) {
+ perror("ifconfig: socket");
+ exit(1);
+ }
+
+ 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, s, afp);
+ argc--, argv++;
+ } else
+ (*p->c_func)(*argv, p->c_parameter, s, afp);
+ }
+ argc--, argv++;
+ }
+#ifdef ISO
+ if (af == AF_ISO)
+ adjust_nsellength();
+#endif
+ if (setipdst && ifr.ifr_addr.sa_family == 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 (ifr.ifr_addr.sa_family == AF_APPLETALK)
+ checkatrange((struct sockaddr_at *) &addreq.ifra_addr);
+#ifdef NS
+ if (setipdst && ifr.ifr_addr.sa_family == 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 (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, afp->af_name);
+ clearaddr = NULL;
+ }
+ }
+ if (clearaddr) {
+ int ret;
+ strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name);
+ if ((ret = ioctl(s, afp->af_difaddr, afp->af_ridreq)) < 0) {
+ if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
+ /* means no previous address for interface */
+ } else
+ Perror("ioctl (SIOCDIFADDR)");
+ }
+ }
+ if (newaddr) {
+ if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
+ warnx("interface %s cannot change %s addresses!",
+ name, afp->af_name);
+ newaddr = NULL;
+ }
+ }
+ if (newaddr) {
+ strncpy(afp->af_addreq, name, sizeof ifr.ifr_name);
+ if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
+ Perror("ioctl (SIOCAIFADDR)");
+ }
+ close(s);
+ return(0);
+}
+#define RIDADDR 0
+#define ADDR 1
+#define MASK 2
+#define DSTADDR 3
+
+/*ARGSUSED*/
+void
+setifaddr(addr, param, s, afp)
+ const char *addr;
+ int param;
+ int s;
+ const struct afswtch *afp;
+{
+ /*
+ * 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, s, afp)
+ const char *addr;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ (*afp->af_getaddr)(addr, MASK);
+}
+
+void
+setifbroadaddr(addr, dummy, s, afp)
+ const char *addr;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+void
+setifipdst(addr, dummy, s, afp)
+ const char *addr;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ in_getaddr(addr, DSTADDR);
+ setipdst++;
+ clearaddr = 0;
+ newaddr = 0;
+}
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
+
+void
+notealias(addr, param, s, afp)
+ const char *addr;
+ int param;
+ int s;
+ const struct afswtch *afp;
+{
+ 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, s, afp)
+ const char *addr;
+ int param __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ (*afp->af_getaddr)(addr, DSTADDR);
+}
+
+void
+setifflags(vname, value, s, afp)
+ const char *vname;
+ int value;
+ int s;
+ const struct afswtch *afp;
+{
+ 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, s, afp)
+ const char *val;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ 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, s, afp)
+ const char *val;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ 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(afp, addrcount, sdl, ifm, ifam)
+ const struct afswtch *afp;
+ int addrcount;
+ struct sockaddr_dl *sdl;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+{
+ const struct afswtch *p = NULL;
+ struct rt_addrinfo info;
+ int allfamilies, s;
+
+ if (afp == NULL) {
+ allfamilies = 1;
+ afp = &afs[0];
+ } else
+ allfamilies = 0;
+
+ ifr.ifr_addr.sa_family = afp->af_af;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) {
+ perror("ifconfig: socket");
+ exit(1);
+ }
+
+ /*
+ * XXX is it we are doing a SIOCGIFMETRIC etc for one family.
+ * is it possible that the metric and mtu can be different for
+ * each family? If so, we have a format problem, because the
+ * metric and mtu is printed on the global the flags line.
+ */
+ 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;
+
+ printf("%s: ", name);
+ printb("flags", flags, IFFBITS);
+ if (metric)
+ printf(" metric %d", metric);
+ if (mtu)
+ printf(" mtu %d", mtu);
+ putchar('\n');
+
+ while (addrcount > 0) {
+
+ info.rti_addrs = ifam->ifam_addrs;
+
+ /* Expand the compacted addresses */
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ if (!allfamilies) {
+ if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+#ifdef USE_IF_MEDIA
+ afp->af_status != media_status &&
+#endif
+ afp->af_status != ether_status) {
+ p = afp;
+ (*p->af_status)(s, &info);
+ }
+ } else for (p = afs; p->af_name; p++) {
+ if (p->af_af == info.rti_info[RTAX_IFA]->sa_family &&
+#ifdef USE_IF_MEDIA
+ p->af_status != media_status &&
+#endif
+ p->af_status != ether_status)
+ (*p->af_status)(s, &info);
+ }
+ addrcount--;
+ ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
+ }
+ if (allfamilies || afp->af_status == ether_status)
+ ether_status(s, (struct rt_addrinfo *)sdl);
+#ifdef USE_IF_MEDIA
+ if (allfamilies || afp->af_status == media_status)
+ media_status(s, NULL);
+#endif
+ if (!allfamilies && !p && afp->af_status != media_status &&
+ afp->af_status != ether_status)
+ warnx("%s has no %s interface address!", name, afp->af_name);
+
+ close(s);
+ return;
+}
+
+void
+in_status(s, info)
+ int s __unused;
+ struct rt_addrinfo * info;
+{
+ struct sockaddr_in *sin, null_sin;
+
+ memset(&null_sin, 0, sizeof(null_sin));
+
+ sin = (struct sockaddr_in *)info->rti_info[RTAX_IFA];
+ 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(s, info)
+ int s __unused;
+ struct rt_addrinfo * info;
+{
+ struct sockaddr_ipx *sipx, null_sipx;
+
+ memset(&null_sipx, 0, sizeof(null_sipx));
+
+ sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_IFA];
+ 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(s, info)
+ int s __unused;
+ struct rt_addrinfo * info;
+{
+ 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];
+ 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(s, info)
+ int s __unused;
+ struct rt_addrinfo * info;
+{
+ struct sockaddr_ns *sns, null_sns;
+
+ memset(&null_sns, 0, sizeof(null_sns));
+
+ sns = (struct sockaddr_ns *)info->rti_info[RTAX_IFA];
+ 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');
+ close(s);
+}
+#endif
+
+#ifdef ISO
+void
+iso_status(s, info)
+ int s __unused;
+ struct rt_addrinfo * info;
+{
+ struct sockaddr_iso *siso, null_siso;
+
+ memset(&null_siso, 0, sizeof(null_siso));
+
+ siso = (struct sockaddr_iso *)info->rti_info[RTAX_IFA];
+ printf("\tiso %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));
+ }
+
+ siso = (struct sockaddr_iso *)info->rti_info[RTAX_NETMASK];
+ if (siso)
+ printf(" netmask %s ", iso_ntoa(&siso->siso_addr));
+
+ putchar('\n');
+}
+#endif
+
+void
+ether_status(s, info)
+ int s __unused;
+ struct rt_addrinfo *info;
+{
+ char *cp;
+ int n;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)info;
+
+ 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, s, afp)
+ const char *range;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ 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, s, afp)
+ const char *phase;
+ int dummy __unused;
+ int s;
+ const struct afswtch *afp;
+{
+ 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/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
new file mode 100644
index 0000000..85555e3
--- /dev/null
+++ b/sbin/ifconfig/ifconfig.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1997 Peter Wemm.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 FreeBSD Project
+ * by Peter Wemm.
+ * 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.
+ *
+ * so there!
+ *
+ * $Id: ifconfig.h,v 1.2 1997/05/10 14:47:35 peter Exp $
+ */
+
+extern struct ifreq ifr;
+
+extern char name[32]; /* name of interface */
+extern int allmedia;
+struct afswtch;
+
+extern void setmedia(const char *, int, int, const struct afswtch *rafp);
+extern void setmediaopt(const char *, int, int, const struct afswtch *rafp);
+extern void unsetmediaopt(const char *, int, int, const struct afswtch *rafp);
+extern void media_status(int s, struct rt_addrinfo *);
diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c
new file mode 100644
index 0000000..cd1d043
--- /dev/null
+++ b/sbin/ifconfig/ifmedia.c
@@ -0,0 +1,530 @@
+/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */
+/* $Id: ifmedia.c,v 1.2 1997/05/10 14:47:35 peter Exp $ */
+
+/*
+ * Copyright (c) 1997 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.
+ */
+
+/*
+ * 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.
+ */
+
+#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_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void domediaopt __P((const char *, int, int));
+static int get_media_subtype __P((int, const char *));
+static int get_media_options __P((int, const char *));
+static int lookup_media_word __P((struct ifmedia_description *, const char *));
+static void print_media_word __P((int));
+
+void
+media_status(s, info)
+ int s;
+ struct rt_addrinfo *info __unused;
+{
+ struct ifmediareq ifmr;
+ int *media_list, i;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ /*
+ * Interface doesn't support SIOC{G,S}IFMEDIA.
+ */
+ return;
+ }
+
+ if (ifmr.ifm_count == 0) {
+ warnx("%s: no media types?", name);
+ return;
+ }
+
+ media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
+ if (media_list == NULL)
+ err(1, "malloc");
+ ifmr.ifm_ulist = media_list;
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+ err(1, "SIOCGIFMEDIA");
+
+ printf("\tmedia: ");
+ print_media_word(ifmr.ifm_current);
+ if (ifmr.ifm_active != ifmr.ifm_current) {
+ putchar(' ');
+ putchar('(');
+ print_media_word(ifmr.ifm_active);
+ putchar(')');
+ }
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ printf(" status: ");
+ switch (IFM_TYPE(ifmr.ifm_active)) {
+ case IFM_ETHER:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("active");
+ else
+ printf("no carrier");
+ break;
+
+ case IFM_FDDI:
+ case IFM_TOKEN:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ printf("inserted");
+ else
+ printf("no ring");
+ break;
+ }
+ }
+
+ putchar('\n');
+
+ if (allmedia) {
+ printf("\tsupported media:");
+ for (i = 0; i < ifmr.ifm_count; i++) {
+ putchar(' ');
+ print_media_word(media_list[i]);
+ }
+ putchar('\n');
+ }
+
+ free(media_list);
+}
+
+void
+setmedia(val, d, s, afp)
+ const char *val;
+ int d;
+ int s;
+ const struct afswtch *afp;
+{
+ struct ifmediareq ifmr;
+ int first_type, subtype;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+ ifmr.ifm_count = 1;
+ ifmr.ifm_ulist = &first_type;
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ /*
+ * If we get E2BIG, the kernel is telling us
+ * that there are more, so we can ignore it.
+ */
+ if (errno != E2BIG)
+ err(1, "SIOCGIFMEDIA");
+ }
+
+ if (ifmr.ifm_count == 0)
+ errx(1, "%s: no media types?", name);
+
+ /*
+ * We are primarily concerned with the top-level type.
+ * However, "current" may be only IFM_NONE, so we just look
+ * for the top-level type in the first "supported type"
+ * entry.
+ *
+ * (I'm assuming that all supported media types for a given
+ * interface will be the same top-level type..)
+ */
+ subtype = get_media_subtype(IFM_TYPE(first_type), val);
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_media = (ifmr.ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
+ IFM_TYPE(first_type) | subtype;
+
+ if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
+ err(1, "SIOCSIFMEDIA");
+}
+
+void
+setmediaopt(val, d, s, afp)
+ const char *val;
+ int d;
+ int s;
+ const struct afswtch *afp;
+{
+
+ domediaopt(val, 0, s);
+}
+
+void
+unsetmediaopt(val, d, s, afp)
+ const char *val;
+ int d;
+ int s;
+ const struct afswtch *afp;
+{
+
+ domediaopt(val, 1, s);
+}
+
+static void
+domediaopt(val, clear, s)
+ const char *val;
+ int clear;
+ int s;
+{
+ struct ifmediareq ifmr;
+ int *mwords, options;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+ /*
+ * We must go through the motions of reading all
+ * supported media because we need to know both
+ * the current media type and the top-level type.
+ */
+
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+ err(1, "SIOCGIFMEDIA");
+
+ if (ifmr.ifm_count == 0)
+ errx(1, "%s: no media types?", name);
+
+ mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
+ if (mwords == NULL)
+ err(1, "malloc");
+
+ ifmr.ifm_ulist = mwords;
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+ err(1, "SIOCGIFMEDIA");
+
+ options = get_media_options(IFM_TYPE(mwords[0]), val);
+
+ free(mwords);
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ ifr.ifr_media = ifmr.ifm_current;
+ if (clear)
+ ifr.ifr_media &= ~options;
+ else
+ ifr.ifr_media |= options;
+
+ if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
+ err(1, "SIOCSIFMEDIA");
+}
+
+/**********************************************************************
+ * A good chunk of this is duplicated from sys/net/ifmedia.c
+ **********************************************************************/
+
+static struct ifmedia_description ifm_type_descriptions[] =
+ IFM_TYPE_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
+ IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_ethernet_aliases[] =
+ IFM_SUBTYPE_ETHERNET_ALIASES;
+
+static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
+ IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_tokenring_descriptions[] =
+ IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_tokenring_aliases[] =
+ IFM_SUBTYPE_TOKENRING_ALIASES;
+
+static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
+ IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_fddi_descriptions[] =
+ IFM_SUBTYPE_FDDI_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_fddi_aliases[] =
+ IFM_SUBTYPE_FDDI_ALIASES;
+
+static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
+ IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_shared_descriptions[] =
+ IFM_SUBTYPE_SHARED_DESCRIPTIONS;
+
+static struct ifmedia_description ifm_subtype_shared_aliases[] =
+ IFM_SUBTYPE_SHARED_ALIASES;
+
+static struct ifmedia_description ifm_shared_option_descriptions[] =
+ IFM_SHARED_OPTION_DESCRIPTIONS;
+
+struct ifmedia_type_to_subtype {
+ struct {
+ struct ifmedia_description *desc;
+ int alias;
+ } subtypes[5];
+ struct {
+ struct ifmedia_description *desc;
+ int alias;
+ } options[3];
+};
+
+/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
+static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_ethernet_descriptions[0], 0 },
+ { &ifm_subtype_ethernet_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_ethernet_option_descriptions[0], 1 },
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_tokenring_descriptions[0], 0 },
+ { &ifm_subtype_tokenring_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_tokenring_option_descriptions[0], 1 },
+ { NULL, 0 },
+ },
+ },
+ {
+ {
+ { &ifm_subtype_shared_descriptions[0], 0 },
+ { &ifm_subtype_shared_aliases[0], 1 },
+ { &ifm_subtype_fddi_descriptions[0], 0 },
+ { &ifm_subtype_fddi_aliases[0], 1 },
+ { NULL, 0 },
+ },
+ {
+ { &ifm_shared_option_descriptions[0], 0 },
+ { &ifm_subtype_fddi_option_descriptions[0], 1 },
+ { NULL, 0 },
+ },
+ },
+};
+
+static int
+get_media_subtype(type, val)
+ int type;
+ const char *val;
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int rval, i;
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (type == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL)
+ errx(1, "unknown media type 0x%x", type);
+
+ for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
+ rval = lookup_media_word(ttos->subtypes[i].desc, val);
+ if (rval != -1)
+ return (rval);
+ }
+ errx(1, "unknown media subtype: %s", val);
+ /* NOTREACHED */
+}
+
+static int
+get_media_options(type, val)
+ int type;
+ const char *val;
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ char *optlist, *optptr;
+ int option = 0, i, rval = 0;
+
+ /* We muck with the string, so copy it. */
+ optlist = strdup(val);
+ if (optlist == NULL)
+ err(1, "strdup");
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (type == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL)
+ errx(1, "unknown media type 0x%x", type);
+
+ /*
+ * Look up the options in the user-provided comma-separated
+ * list.
+ */
+ optptr = optlist;
+ for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) {
+ for (i = 0; ttos->options[i].desc != NULL; i++) {
+ option = lookup_media_word(ttos->options[i].desc, optptr);
+ if (option != -1)
+ break;
+ }
+ if (option == 0)
+ errx(1, "unknown option: %s", optptr);
+ rval |= option;
+ }
+
+ free(optlist);
+ return (rval);
+}
+
+static int
+lookup_media_word(desc, val)
+ struct ifmedia_description *desc;
+ const char *val;
+{
+
+ for (; desc->ifmt_string != NULL; desc++)
+ if (strcasecmp(desc->ifmt_string, val) == 0)
+ return (desc->ifmt_word);
+
+ return (-1);
+}
+
+static void
+print_media_word(ifmw)
+ int ifmw;
+{
+ struct ifmedia_description *desc;
+ struct ifmedia_type_to_subtype *ttos;
+ int seen_option = 0, i;
+
+ /* Find the top-level interface type. */
+ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
+ desc->ifmt_string != NULL; desc++, ttos++)
+ if (IFM_TYPE(ifmw) == desc->ifmt_word)
+ break;
+ if (desc->ifmt_string == NULL) {
+ printf("<unknown type>");
+ return;
+ }
+
+ /*
+ * Don't print the top-level type; it's not like we can
+ * change it, or anything.
+ */
+
+ /* Find subtype. */
+ for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
+ if (ttos->subtypes[i].alias)
+ continue;
+ for (desc = ttos->subtypes[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
+ goto got_subtype;
+ }
+ }
+
+ /* Falling to here means unknown subtype. */
+ printf("<unknown subtype>");
+ return;
+
+ got_subtype:
+ printf("%s", desc->ifmt_string);
+
+ /* Find options. */
+ for (i = 0; ttos->options[i].desc != NULL; i++) {
+ if (ttos->options[i].alias)
+ continue;
+ for (desc = ttos->options[i].desc;
+ desc->ifmt_string != NULL; desc++) {
+ if (ifmw & desc->ifmt_word) {
+ if (seen_option == 0)
+ printf(" <");
+ printf("%s%s", seen_option++ ? "," : "",
+ desc->ifmt_string);
+ }
+ }
+ }
+ printf("%s", seen_option ? ">" : "");
+}
+
+/**********************************************************************
+ * ...until here.
+ **********************************************************************/
diff --git a/sbin/init/Makefile b/sbin/init/Makefile
new file mode 100644
index 0000000..244ff5e
--- /dev/null
+++ b/sbin/init/Makefile
@@ -0,0 +1,35 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $Id$
+
+PROG= init
+MAN8= init.8
+BINMODE=500
+INSTALLFLAGS=-fschg
+CFLAGS+=-DDEBUGSHELL -DSECURE -DLOGIN_CAP
+
+.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>
+
+.if exists(${.OBJDIR}/../../lib/libcrypt)
+SCRYPTOBJDIR= ${.OBJDIR}/../../lib/libcrypt
+.else
+SCRYPTOBJDIR= ${.CURDIR}/../../lib/libcrypt
+.endif
+
+.if exists (${.CURDIR}/../../secure)
+
+.if exists(${.OBJDIR}/../../secure/lib/libcrypt)
+DESCRYPTOBJDIR= ${.OBJDIR}/../../secure/lib/libcrypt
+.else
+DESCRYPTOBJDIR= ${.CURDIR}/../../secure/lib/libcrypt
+.endif
+
+.endif
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..0591d9d
--- /dev/null
+++ b/sbin/init/init.8
@@ -0,0 +1,303 @@
+.\" 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
+.\" $Id: init.8,v 1.8 1997/02/22 14:32:34 peter Exp $
+.\"
+.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 \- the system immutable and system append-only flags may not
+be turned off;
+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 machine 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 kill 1 ,
+.Xr login 1 ,
+.Xr sh 1 ,
+.Xr ttys 5 ,
+.Xr crash 8 ,
+.Xr getty 8 ,
+.Xr halt 8 ,
+.Xr rc 8 ,
+.Xr reboot 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..ed2da13
--- /dev/null
+++ b/sbin/init/init.c
@@ -0,0 +1,1466 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#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>
+#include <err.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef SECURE
+#include <pwd.h>
+#endif
+
+#ifdef LOGIN_CAP
+#include <login_cap.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 */
+#define RESOURCE_RC "daemon"
+#define RESOURCE_WINDOW "default"
+#define RESOURCE_GETTY "default"
+
+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 *));
+#ifdef LOGIN_CAP
+void setprocresources __P((const char *));
+#endif
+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)
+ errx(1, "%s", strerror(EPERM));
+
+ /* System V users like to reexec init. */
+ if (getpid() != 1)
+ errx(1, "already running");
+
+ /*
+ * 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");
+
+ if (devfs) {
+ 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);
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_RC);
+#endif
+ 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");
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_WINDOW);
+#endif
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strncat(term, sp->se_type, sizeof(term) - 6);
+ 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);
+
+#ifdef LOGIN_CAP
+ setprocresources(RESOURCE_GETTY);
+#endif
+ if (sp->se_type) {
+ /* Don't use malloc after fork */
+ strcpy(term, "TERM=");
+ strncat(term, sp->se_type, sizeof(term) - 6);
+ 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;
+}
+
+#ifdef LOGIN_CAP
+void
+setprocresources(cname)
+ const char *cname;
+{
+ login_cap_t *lc;
+ if ((lc = login_getclassbyname(cname, (char*)NULL)) != NULL) {
+ setusercontext(lc, (struct passwd*)NULL, 0, LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
+ login_close(lc);
+ }
+}
+#endif
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..1bc6fa4
--- /dev/null
+++ b/sbin/ipfw/Makefile
@@ -0,0 +1,7 @@
+PROG= ipfw
+
+COPTS+= -Wall
+
+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..7198d04
--- /dev/null
+++ b/sbin/ipfw/ipfw.8
@@ -0,0 +1,496 @@
+.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
+.Oo
+.Fl f
+|
+.Fl q
+.Oc
+flush
+.Nm ipfw
+.Oo
+.Fl q
+.Oc
+zero
+.Op Ar number ...
+.Nm ipfw
+delete
+.Ar number ...
+.Nm ipfw
+.Op Fl aftN
+list
+.Nm ipfw
+.Oo
+.Fl ftN
+.Oc
+show
+.Nm ipfw
+.Oo
+.Fl q
+.Oc
+add
+.Op Ar number
+.Ar action
+.Op log
+.Ar proto
+from
+.Ar src
+to
+.Ar dst
+.Op via Ar name | ipno
+.Op Ar options
+.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 is 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 show command is equivalent to `ipfw -a list'.
+.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
+.Pp
+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 q
+While adding or flushing, be quiet about actions (implies '-f'). This is
+useful for adjusting rules by executing multiple ipfw commands in a script
+(e.g. sh /etc/rc.firewall), or by processing a file of many ipfw rules,
+across a remote login session. If a flush is performed in normal
+(verbose) mode, it prints a message. Because all rules are flushed, the
+message cannot be delivered to the login session, the login session is
+closed and the remainder of the ruleset is not processed. Access to the
+console is required to recover.
+.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 Ar allow
+Allow packets that match rule.
+The search terminates. Aliases are
+.Ar pass ,
+.Ar permit ,
+and
+.Ar accept .
+.It Ar deny
+Discard packets that match this rule.
+The search terminates.
+.Ar Drop
+is an alias for
+.Ar deny .
+.It Ar reject
+(Deprecated.) Discard packets that match this rule, and try to send an ICMP
+host unreachable notice.
+The search terminates.
+.It Ar unreach code
+Discard packets that match this rule, and try to send an ICMP
+unreachable notice with code
+.Ar code ,
+where
+.Ar code
+is a number from zero to 255, or one of these aliases:
+.Ar net ,
+.Ar host ,
+.Ar protocol ,
+.Ar port ,
+.Ar needfrag ,
+.Ar srcfail ,
+.Ar net-unknown ,
+.Ar host-unknown ,
+.Ar isolated ,
+.Ar net-prohib ,
+.Ar host-prohib ,
+.Ar tosnet ,
+.Ar toshost ,
+.Ar filter-prohib ,
+.Ar host-precedence ,
+or
+.Ar precedence-cutoff .
+The search terminates.
+.It Ar reset
+TCP packets only. Discard packets that match this rule,
+and try to send a TCP reset (RST) notice.
+The search terminates.
+.It Ar count
+Update counters for all packets that match rule.
+The search continues with the next rule.
+.It Ar divert port
+Divert packets that match this rule to the
+.Xr divert 4
+socket bound to port
+.Ar port .
+The search terminates.
+.It Ar tee port
+Send a copy of packets matching this rule to the
+.Xr divert 4
+socket bound to port
+.Ar port .
+The search continues with the next rule.
+.It Ar skipto number
+Skip all subsequent rules numbered less than
+.Ar number .
+The search continues with the first rule numbered
+.Ar number
+or higher.
+.El
+.Pp
+If a packet matches more than one
+.Ar divert
+and/or
+.Ar tee
+rule, all but the last are ignored.
+.Pp
+If the kernel was compiled with
+.Dv IPFIREWALL_VERBOSE ,
+then when a packet matches a rule with the ``log''
+keyword a message will be printed on the console.
+If the kernel was compiled with the
+.Dv IPFIREWALL_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
+Console logging and the log limit are adjustable dynamically
+through the
+.Xr sysctl 8
+interface.
+.Pp
+.Ar proto :
+.Bl -hang -offset flag -width 1234567890123456
+.It Ar ip
+All packets match. The alias
+.Ar all
+has the same effect.
+.It Ar tcp
+Only TCP packets match.
+.It Ar udp
+Only UDP packets match.
+.It Ar icmp
+Only ICMP packets match.
+.It Ar <number|name>
+Only packets for the specified protocol matches (see
+.Pa /etc/protocols
+for a complete list).
+.El
+.Pp
+.Ar src
+and
+.Ar dst :
+.Bl -hang -offset flag
+.It Ar <address/mask>
+.Op Ar 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
+The sense of the match can be inverted by preceding an address with the
+``not'' modifier, causing all other addresses to be matched instead. This
+does not affect the selection of port numbers.
+.Pp
+With the TCP and UDP
+.Em protocols ,
+optional
+.Em ports
+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
+.Dv IP_FW_MAX_PORTS
+(as defined in
+.Pa /usr/src/sys/netinet/ip_fw.h )
+ports.
+.Pp
+Rules can apply to packets when they are incoming, or outgoing, or both.
+The
+.Ar in
+keyword indicates the rule should only match incoming packets.
+The
+.Ar out
+keyword indicates the rule should only match outgoing packets.
+.Pp
+To match packets going through a certain interface, specify
+the interface using
+.Ar via :
+.Bl -hang -offset flag -width 1234567890123456
+.It Ar via ifX
+Packet must be going through interface
+.Ar ifX.
+.It Ar via if*
+Packet must be going through interface
+.Ar ifX ,
+where X is any unit number.
+.It Ar via any
+Packet must be going through
+.Em some
+interface.
+.It Ar via ipno
+Packet must be going through the interface having IP address
+.Ar ipno .
+.El
+.Pp
+The
+.Ar via
+keyword causes the interface to always be checked.
+If
+.Ar recv
+or
+.Ar xmit
+is used instead of
+.Ar via ,
+then the only receive or transmit interface (respectively) is checked.
+By specifying both, it is possible to match packets based on both receive
+and transmit interface, e.g.:
+.Pp
+.Dl "ipfw add 100 deny ip from any to any out recv ed0 xmit ed1"
+.Pp
+The
+.Ar recv
+interface can be tested on either incoming or outgoing packets, while the
+.Ar xmit
+interface can only be tested on outgoing packets. So
+.Ar out
+is required (and
+.Ar in
+invalid) whenver
+.Ar xmit
+is used. Specifying
+.Ar via
+together with
+.Ar xmit
+or
+.Ar recv
+is invalid.
+.Pp
+A packet may not have a receive or transmit interface: packets originating
+from the local host have no receive interface. while packets destined for
+the local host have no transmit interface.
+.Pp
+Additional
+.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:
+.Ar ssrr
+(strict source route),
+.Ar lsrr
+(loose source route),
+.Ar rr
+(record packet route), and
+.Ar 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:
+.Ar fin ,
+.Ar syn ,
+.Ar rst ,
+.Ar psh ,
+.Ar ack ,
+and
+.Ar 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
+.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
+.Pp
+Along the same lines, doing an
+.Bd -literal -offset center
+ipfw flush
+.Ed
+.Pp
+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 a good usage of the list command to see accounting records
+and timestamp information:
+.Pp
+.Dl ipfw -at l
+.Pp
+or in short form without timestamps:
+.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 protocols 5 ,
+.Xr services 5 ,
+.Xr reboot 8 ,
+.Xr syslogd 8 ,
+.Xr sysctl 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.
+.Pp
+Incoming packet fragments diverted by
+.Ar divert
+are reassembled before delivery to the socket, whereas fragments diverted via
+.Ar tee
+are not.
+.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..223e2e8
--- /dev/null
+++ b/sbin/ipfw/ipfw.c
@@ -0,0 +1,1171 @@
+/*
+ * 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.43 1997/06/02 05:02:33 julian 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 <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_fw.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+int lineno = -1;
+
+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_quiet=0; /* Be quiet in add and flush */
+int do_force=0; /* Don't ask for confirmation */
+
+struct icmpcode {
+ int code;
+ char *str;
+};
+
+static struct icmpcode icmpcodes[] = {
+ { ICMP_UNREACH_NET, "net" },
+ { ICMP_UNREACH_HOST, "host" },
+ { ICMP_UNREACH_PROTOCOL, "protocol" },
+ { ICMP_UNREACH_PORT, "port" },
+ { ICMP_UNREACH_NEEDFRAG, "needfrag" },
+ { ICMP_UNREACH_SRCFAIL, "srcfail" },
+ { ICMP_UNREACH_NET_UNKNOWN, "net-unknown" },
+ { ICMP_UNREACH_HOST_UNKNOWN, "host-unknown" },
+ { ICMP_UNREACH_ISOLATED, "isolated" },
+ { ICMP_UNREACH_NET_PROHIB, "net-prohib" },
+ { ICMP_UNREACH_HOST_PROHIB, "host-prohib" },
+ { ICMP_UNREACH_TOSNET, "tosnet" },
+ { ICMP_UNREACH_TOSHOST, "toshost" },
+ { ICMP_UNREACH_FILTER_PROHIB, "filter-prohib" },
+ { ICMP_UNREACH_HOST_PRECEDENCE, "host-precedence" },
+ { ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" },
+ { 0, NULL }
+};
+
+static int
+mask_bits(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);
+}
+
+static void
+print_iface(char *key, union ip_fw_if *un, int byname)
+{
+ char ifnb[FW_IFNLEN+1];
+
+ if (byname) {
+ strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN);
+ ifnb[FW_IFNLEN]='\0';
+ if (un->fu_via_if.unit == -1)
+ printf(" %s %s*", key, ifnb);
+ else
+ printf(" %s %s%d", key, ifnb, un->fu_via_if.unit);
+ } else if (un->fu_via_ip.s_addr != 0) {
+ printf(" %s %s", key, inet_ntoa(un->fu_via_ip));
+ } else
+ printf(" %s any", key);
+}
+
+static void
+print_reject_code(int code)
+{
+ struct icmpcode *ic;
+
+ for (ic = icmpcodes; ic->str; ic++)
+ if (ic->code == code) {
+ printf("%s", ic->str);
+ return;
+ }
+ printf("%u", code);
+}
+
+static void
+show_ipfw(struct ip_fw *chain)
+{
+ char *comma;
+ u_long adrt;
+ struct hostent *he;
+ struct protoent *pe;
+ int i, mb;
+ int nsp = IP_FW_GETNSRCP(chain);
+ int ndp = IP_FW_GETNDSTP(chain);
+
+ 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_DENY:
+ printf("deny");
+ break;
+ case IP_FW_F_COUNT:
+ printf("count");
+ break;
+ case IP_FW_F_DIVERT:
+ printf("divert %u", chain->fw_divert_port);
+ break;
+ case IP_FW_F_TEE:
+ printf("tee %u", chain->fw_divert_port);
+ break;
+ case IP_FW_F_SKIPTO:
+ printf("skipto %u", chain->fw_skipto_rule);
+ break;
+ case IP_FW_F_REJECT:
+ if (chain->fw_reject_code == IP_FW_REJECT_RST)
+ printf("reset");
+ else {
+ printf("unreach ");
+ print_reject_code(chain->fw_reject_code);
+ }
+ 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 %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
+
+ 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_TCP || chain->fw_prot == IPPROTO_UDP) {
+ comma = " ";
+ for (i = 0; i < 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 %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
+
+ 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));
+ }
+
+ if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
+ comma = " ";
+ for (i = 0; i < ndp; i++) {
+ print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma);
+ if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+ }
+
+ /* Direction */
+ if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
+ printf(" in");
+ if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
+ printf(" out");
+
+ /* Handle hack for "via" backwards compatibility */
+ if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
+ print_iface("via",
+ &chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME);
+ } else {
+ /* Receive interface specified */
+ if (chain->fw_flg & IP_FW_F_IIFACE)
+ print_iface("recv", &chain->fw_in_if,
+ chain->fw_flg & IP_FW_F_IIFNAME);
+ /* Transmit interface specified */
+ if (chain->fw_flg & IP_FW_F_OIFACE)
+ print_iface("xmit", &chain->fw_out_if,
+ chain->fw_flg & IP_FW_F_OIFNAME);
+ }
+
+ 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);
+}
+
+static void
+show_usage(const char *fmt, ...)
+{
+ if (fmt) {
+ char buf[100];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ warnx("error: %s", buf);
+ }
+ fprintf(stderr, "usage: ipfw [options]\n"
+" flush\n"
+" add [number] rule\n"
+" delete number ...\n"
+" list [number]\n"
+" show [number]\n"
+" zero [number ...]\n"
+" rule: action proto src dst extras...\n"
+" action:\n"
+" {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
+" reset|count|skipto num|divert port|tee port} [log]\n"
+" proto: {ip|tcp|udp|icmp|<number>}\n"
+" src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+" dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
+" extras:\n"
+" fragment\n"
+" in\n"
+" out\n"
+" {xmit|recv|via} {iface|ip|any}\n"
+" {established|setup}\n"
+" tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
+" ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
+" icmptypes {type[,type]}...\n");
+
+ exit(1);
+}
+
+static 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("hostname ``%s'' unknown", *av);
+ switch (md) {
+ case ':':
+ if (!inet_aton(p,mask))
+ show_usage("bad netmask ``%s''", p);
+ break;
+ case '/':
+ if (atoi(p) == 0) {
+ mask->s_addr = 0;
+ } else if (atoi(p) > 32) {
+ show_usage("bad width ``%s''", p);
+ } else {
+ mask->s_addr =
+ htonl(~0 << (32 - atoi(p)));
+ }
+ break;
+ default:
+ mask->s_addr = htonl(~0);
+ break;
+ }
+ ipno->s_addr &= mask->s_addr;
+ av++;
+ ac--;
+ }
+ *acp = ac;
+ *avp = av;
+}
+
+static void
+fill_reject_code(u_short *codep, char *str)
+{
+ struct icmpcode *ic;
+ u_long val;
+ char *s;
+
+ val = strtoul(str, &s, 0);
+ if (s != str && *s == '\0' && val < 0x100) {
+ *codep = val;
+ return;
+ }
+ for (ic = icmpcodes; ic->str; ic++)
+ if (!strcasecmp(str, ic->str)) {
+ *codep = ic->code;
+ return;
+ }
+ show_usage("unknown ICMP unreachable code ``%s''", str);
+}
+
+static 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 ``%s''", p);
+ p = q;
+ }
+}
+
+static void
+fill_ipopt(u_char *set, u_char *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 */
+ while (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
+ if (i)
+ warn("setsockopt(%s)", "IP_FW_DEL");
+ }
+}
+
+static void
+verify_interface(union ip_fw_if *ifu)
+{
+ 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",
+ ifu->fu_via_if.name,
+ ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
+}
+
+static void
+fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg)
+{
+ if (!ac)
+ show_usage("missing argument for ``%s''", which);
+
+ /* Parse the interface or address */
+ if (!strcmp(arg, "any")) {
+ ifu->fu_via_ip.s_addr = 0;
+ *byname = 0;
+ } else if (!isdigit(*arg)) {
+ char *q;
+
+ *byname = 1;
+ strncpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
+ ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0';
+ for (q = ifu->fu_via_if.name;
+ *q && !isdigit(*q) && *q != '*'; q++)
+ continue;
+ ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q);
+ *q = '\0';
+ verify_interface(ifu);
+ } else if (!inet_aton(arg, &ifu->fu_via_ip)) {
+ show_usage("bad ip address ``%s''", arg);
+ } else
+ *byname = 0;
+}
+
+static void
+add(ac,av)
+ int ac;
+ char **av;
+{
+ struct ip_fw rule;
+ int i;
+ u_char proto;
+ struct protoent *pe;
+ int saw_xmrc = 0, saw_via = 0;
+
+ memset(&rule, 0, sizeof rule);
+
+ av++; ac--;
+
+ /* Rule number */
+ if (ac && isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ }
+
+ /* Action */
+ if (ac == 0)
+ show_usage("missing action");
+ if (!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 (!strncmp(*av,"count",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
+ } else if (!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 (!strncmp(*av,"tee",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_TEE; 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 (!strncmp(*av,"skipto",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
+ if (!ac)
+ show_usage("missing skipto rule number");
+ rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
+ } else if ((!strncmp(*av,"deny",strlen(*av))
+ || !strncmp(*av,"drop",strlen(*av)))) {
+ rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
+ } else if (!strncmp(*av,"reject",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
+ rule.fw_reject_code = ICMP_UNREACH_HOST;
+ } else if (!strncmp(*av,"reset",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
+ rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */
+ } else if (!strncmp(*av,"unreach",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
+ fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
+ } else {
+ show_usage("invalid action ``%s''", *av);
+ }
+
+ /* [log] */
+ if (ac && !strncmp(*av,"log",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
+ }
+
+ /* protocol */
+ if (ac == 0)
+ show_usage("missing protocol");
+ 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 ``%s''", *av);
+ }
+
+ if (rule.fw_prot != IPPROTO_TCP
+ && (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
+ && rule.fw_reject_code == IP_FW_REJECT_RST)
+ show_usage("``reset'' is only valid for tcp packets");
+
+ /* from */
+ if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
+ else
+ show_usage("missing ``from''");
+
+ if (ac && !strncmp(*av,"not",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_INVSRC;
+ av++; ac--;
+ }
+ if (!ac)
+ show_usage("missing arguments");
+
+ fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
+
+ if (ac && isdigit(**av)) {
+ u_short nports = 0;
+
+ if (fill_port(&nports, rule.fw_pts, 0, *av))
+ rule.fw_flg |= IP_FW_F_SRNG;
+ IP_FW_SETNSRCP(&rule, nports);
+ av++; ac--;
+ }
+
+ /* to */
+ if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
+ else
+ show_usage("missing ``to''");
+
+ if (ac && !strncmp(*av,"not",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_INVDST;
+ av++; ac--;
+ }
+ if (!ac)
+ show_usage("missing arguments");
+
+ fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
+
+ if (ac && isdigit(**av)) {
+ u_short nports = 0;
+
+ if (fill_port(&nports,
+ rule.fw_pts, IP_FW_GETNSRCP(&rule), *av))
+ rule.fw_flg |= IP_FW_F_DRNG;
+ IP_FW_SETNDSTP(&rule, nports);
+ av++; ac--;
+ }
+
+ if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
+ && (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) {
+ show_usage("only TCP and UDP protocols are valid"
+ " with port specifications");
+ }
+
+ while (ac) {
+ 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 && !strncmp(*av,"xmit",strlen(*av))) {
+ union ip_fw_if ifu;
+ int byname;
+
+ if (saw_via) {
+badviacombo:
+ show_usage("``via'' is incompatible"
+ " with ``xmit'' and ``recv''");
+ }
+ saw_xmrc = 1;
+ av++; ac--;
+ fill_iface("xmit", &ifu, &byname, ac, *av);
+ rule.fw_out_if = ifu;
+ rule.fw_flg |= IP_FW_F_OIFACE;
+ if (byname)
+ rule.fw_flg |= IP_FW_F_OIFNAME;
+ av++; ac--; continue;
+ }
+ if (ac && !strncmp(*av,"recv",strlen(*av))) {
+ union ip_fw_if ifu;
+ int byname;
+
+ if (saw_via)
+ goto badviacombo;
+ saw_xmrc = 1;
+ av++; ac--;
+ fill_iface("recv", &ifu, &byname, ac, *av);
+ rule.fw_in_if = ifu;
+ rule.fw_flg |= IP_FW_F_IIFACE;
+ if (byname)
+ rule.fw_flg |= IP_FW_F_IIFNAME;
+ av++; ac--; continue;
+ }
+ if (ac && !strncmp(*av,"via",strlen(*av))) {
+ union ip_fw_if ifu;
+ int byname = 0;
+
+ if (saw_xmrc)
+ goto badviacombo;
+ saw_via = 1;
+ av++; ac--;
+ fill_iface("via", &ifu, &byname, ac, *av);
+ rule.fw_out_if = rule.fw_in_if = ifu;
+ if (byname)
+ rule.fw_flg |=
+ (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME);
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"fragment",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_FRAG;
+ av++; ac--; continue;
+ }
+ if (!strncmp(*av,"ipoptions",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``ipoptions''");
+ 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 (!strncmp(*av,"tcpflags",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``tcpflags''");
+ fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
+ av++; ac--; continue;
+ }
+ }
+ if (rule.fw_prot == IPPROTO_ICMP) {
+ if (!strncmp(*av,"icmptypes",strlen(*av))) {
+ av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``icmptypes''");
+ fill_icmptypes(rule.fw_icmptypes,
+ av, &rule.fw_flg);
+ av++; ac--; continue;
+ }
+ }
+ show_usage("unknown argument ``%s''", *av);
+ }
+
+ /* No direction specified -> do both directions */
+ if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN)))
+ rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN);
+
+ /* Sanity check interface check, but handle "via" case separately */
+ if (saw_via) {
+ if (rule.fw_flg & IP_FW_F_IN)
+ rule.fw_flg |= IP_FW_F_IIFACE;
+ if (rule.fw_flg & IP_FW_F_OUT)
+ rule.fw_flg |= IP_FW_F_OIFACE;
+ } else if ((rule.fw_flg & IP_FW_F_OIFACE) && (rule.fw_flg & IP_FW_F_IN))
+ show_usage("can't check xmit interface of incoming packets");
+
+ if (!do_quiet)
+ show_ipfw(&rule);
+ i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
+ if (i)
+ err(1, "setsockopt(%s)", "IP_FW_ADD");
+}
+
+static 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)
+ err(1, "setsockopt(%s)", "IP_FW_ZERO");
+ if (!do_quiet)
+ printf("Accounting cleared.\n");
+ } else {
+ struct ip_fw rule;
+
+ memset(&rule, 0, sizeof rule);
+ while (ac) {
+ /* Rule number */
+ if (isdigit(**av)) {
+ rule.fw_number = atoi(*av); av++; ac--;
+ if (setsockopt(s, IPPROTO_IP,
+ IP_FW_ZERO, &rule, sizeof rule))
+ warn("setsockopt(%s)", "IP_FW_ZERO");
+ else
+ printf("Entry %d cleared\n",
+ rule.fw_number);
+ } else
+ show_usage("invalid rule number ``%s''", *av);
+ }
+ }
+}
+
+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 ,"afqtN")) != -1)
+ switch(ch) {
+ case 'a':
+ do_acct=1;
+ break;
+ case 'f':
+ do_force=1;
+ break;
+ case 'q':
+ do_quiet=1;
+ break;
+ case 't':
+ do_time=1;
+ break;
+ case 'N':
+ do_resolv=1;
+ break;
+ default:
+ show_usage(NULL);
+ }
+
+ 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_quiet )
+ 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)
+ err(1, "setsockopt(%s)", "IP_FW_FLUSH");
+ if (!do_quiet)
+ 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 if (!strncmp(*av, "show", strlen(*av))) {
+ do_acct++;
+ list(--ac,++av);
+ } else {
+ show_usage("Bad arguments");
+ }
+ return 0;
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+#define MAX_ARGS 32
+#define WHITESP " \t\f\v\n\r"
+ char buf[BUFSIZ];
+ char *a, *args[MAX_ARGS];
+ char linename[10];
+ int i;
+ FILE *f;
+
+ s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
+ if ( s < 0 )
+ err(1, "socket");
+
+ setbuf(stdout,0);
+
+ if (av[1] && !access(av[1], R_OK)) {
+ lineno = 0;
+ if ((f = fopen(av[1], "r")) == NULL)
+ err(1, "fopen: %s", av[1]);
+ while (fgets(buf, BUFSIZ, f)) {
+
+ lineno++;
+ sprintf(linename, "Line %d", lineno);
+ args[0] = linename;
+
+ for (i = 1, a = strtok(buf, WHITESP);
+ a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
+ args[i] = a;
+ if (i == MAX_ARGS)
+ errx(1, "%s: too many arguments", linename);
+ args[i] = NULL;
+
+ ipfw_main(i, args);
+ }
+ fclose(f);
+ } else
+ ipfw_main(ac,av);
+ return 0;
+}
diff --git a/sbin/kldload/Makefile b/sbin/kldload/Makefile
new file mode 100644
index 0000000..39033e5
--- /dev/null
+++ b/sbin/kldload/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 Doug Rabson
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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$
+#
+
+PROG= kldload
+SRCS= kldload.c
+MAN8= kldload.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldload/kldload.8 b/sbin/kldload/kldload.8
new file mode 100644
index 0000000..a27ace2
--- /dev/null
+++ b/sbin/kldload/kldload.8
@@ -0,0 +1,54 @@
+.\"
+.\" Copyright (c) 1997 Doug Rabson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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$
+.\"
+.Dd April 25, 1997
+.Dt KLDLOAD 8
+.Os
+.Sh NAME
+.Nm kldload
+.Nd load a file into the kernel
+.Sh SYNOPSIS
+.Nm kldload
+.Op Fl v
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+loads a file into the kernel using the kernel linker.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl v
+Be more verbose.
+.El
+.Sh SEE ALSO
+.Xr kldstat 8 ,
+.Xr kldunload 8
+.Sh AUTHOR
+.Bl -tag
+Doug Rabson, dfr@freebsd.org
+.El.
diff --git a/sbin/kldload/kldload.c b/sbin/kldload/kldload.c
new file mode 100644
index 0000000..e2a58be
--- /dev/null
+++ b/sbin/kldload/kldload.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1997 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+
+static char* progname;
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s filename\n", progname);
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ int c;
+ int verbose = 0;
+ int fileid;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "v")) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ fileid = kldload(argv[0]);
+ if (fileid < 0)
+ err(1, "Can't load %s", argv[0]);
+ else
+ if (verbose)
+ printf("Loaded %s, id=%d\n", argv[0], fileid);
+
+ return 0;
+}
diff --git a/sbin/kldstat/Makefile b/sbin/kldstat/Makefile
new file mode 100644
index 0000000..27cdf27
--- /dev/null
+++ b/sbin/kldstat/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 Doug Rabson
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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$
+#
+
+PROG= kldstat
+SRCS= kldstat.c
+MAN8= kldstat.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldstat/kldstat.8 b/sbin/kldstat/kldstat.8
new file mode 100644
index 0000000..bafdb220
--- /dev/null
+++ b/sbin/kldstat/kldstat.8
@@ -0,0 +1,60 @@
+.\"
+.\" Copyright (c) 1997 Doug Rabson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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$
+.\"
+.Dd April 25, 1997
+.Dt KLDSTAT 8
+.Os
+.Sh NAME
+.Nm kldstat
+.Nd display status of dynamic kernel linker
+.Sh SYNOPSIS
+.Nm kldstat
+.Op Fl v
+.Op Fl i Ar id
+.Op Fl n Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the status of any files dynamically linked into the
+kernel.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl v
+Be more verbose.
+.It Fl i Ar id
+Display the status of only the file with this ID.
+.It Fl n Ar name
+Display the status of only the file with this name.
+.El
+.Sh SEE ALSO
+.Xr kldload 8 ,
+.Xr kldunload 8
+.Sh AUTHOR
+.Bl -tag
+Doug Rabson, dfr@freebsd.org
+.El.
diff --git a/sbin/kldstat/kldstat.c b/sbin/kldstat/kldstat.c
new file mode 100644
index 0000000..edb045f
--- /dev/null
+++ b/sbin/kldstat/kldstat.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1997 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+
+static char* progname;
+
+static void printmod(int modid)
+{
+ struct module_stat stat;
+
+ stat.version = sizeof(struct module_stat);
+ if (modstat(modid, &stat) < 0)
+ warn(1, "Can't state module");
+ else
+ printf("\t\t%2d %s\n", stat.id, stat.name);
+}
+
+static void printfile(int fileid, int verbose)
+{
+ struct kld_file_stat stat;
+ int modid;
+
+ stat.version = sizeof(struct kld_file_stat);
+ if (kldstat(fileid, &stat) < 0)
+ warn(1, "Can't stat file");
+ else
+ printf("%2d %4d %-8x %-8x %s\n",
+ stat.id, stat.refs, stat.address, stat.size, stat.name);
+
+ if (verbose) {
+ printf("\tContains modules:\n");
+ printf("\t\tId Name\n");
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid))
+ printmod(modid);
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-v]\n", progname);
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ int c;
+ int verbose = 0;
+ int fileid = 0;
+ char* filename = 0;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "vi:n:")) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'i':
+ fileid = atoi(optarg);
+ break;
+ case 'n':
+ filename = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (filename) {
+ fileid = kldfind(filename);
+ if (fileid < 0)
+ err(1, "Can't find file %s", filename);
+ }
+
+ printf("Id Refs Address Size Name\n");
+ if (fileid)
+ printfile(fileid, verbose);
+ else
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid))
+ printfile(fileid, verbose);
+
+ return 0;
+}
diff --git a/sbin/kldunload/Makefile b/sbin/kldunload/Makefile
new file mode 100644
index 0000000..7f7fffb
--- /dev/null
+++ b/sbin/kldunload/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 1997 Doug Rabson
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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$
+#
+
+PROG= kldunload
+SRCS= kldunload.c
+MAN8= kldunload.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldunload/kldunload.8 b/sbin/kldunload/kldunload.8
new file mode 100644
index 0000000..f9b806c
--- /dev/null
+++ b/sbin/kldunload/kldunload.8
@@ -0,0 +1,60 @@
+.\"
+.\" Copyright (c) 1997 Doug Rabson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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$
+.\"
+.Dd April 25, 1997
+.Dt KLDUNLOAD 8
+.Os
+.Sh NAME
+.Nm kldunload
+.Nd unload a file from the kernel
+.Sh SYNOPSIS
+.Nm kldunload
+.Op Fl v
+.Op Fl i Ar id
+.Op Fl n Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+utility unloads a file which was previously loaded with
+.Xr kldload 8 .
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl v
+Be more verbose.
+.It Fl i Ar id
+Unload the file with this ID.
+.It Fl n Ar name
+Unload the file with this name.
+.El
+.Sh SEE ALSO
+.Xr kldload 8 ,
+.Xr kldstat 8
+.Sh AUTHOR
+.Bl -tag
+Doug Rabson, dfr@freebsd.org
+.El.
diff --git a/sbin/kldunload/kldunload.c b/sbin/kldunload/kldunload.c
new file mode 100644
index 0000000..5f94ef9
--- /dev/null
+++ b/sbin/kldunload/kldunload.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1997 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+
+static char* progname;
+
+static void usage()
+{
+ fprintf(stderr, "usage: modunload [-i id] [-n filename]\n");
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ int c;
+ int verbose = 0;
+ int fileid = 0;
+ char* filename = 0;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "vi:n:")) != -1)
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'i':
+ fileid = atoi(optarg);
+ break;
+ case 'n':
+ filename = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (fileid == 0 && filename == 0)
+ usage();
+
+ if (filename) {
+ fileid = kldfind(filename);
+ if (fileid < 0)
+ err(1, "Can't find file %s", filename);
+ }
+
+ if (verbose) {
+ struct kld_file_stat stat;
+ stat.version = sizeof stat;
+ if (kldstat(fileid, &stat) < 0)
+ err(1, "Can't stat file");
+ printf("Unloading %s, id=%d\n", stat.name, fileid);
+ }
+
+ if (kldunload(fileid) < 0)
+ err(1, "Can't unload file");
+
+ return 0;
+}
+
diff --git a/sbin/ldconfig/Makefile b/sbin/ldconfig/Makefile
new file mode 100644
index 0000000..4b120b6
--- /dev/null
+++ b/sbin/ldconfig/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+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..07d5d69
--- /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$
+.\"
+.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..c26edcb
--- /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$
+ */
+
+#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..efbd7a4
--- /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+= ${LIBMD}
+
+.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..4a79abb
--- /dev/null
+++ b/sbin/md5/md5.1
@@ -0,0 +1,60 @@
+.Dd Feburary 14, 1994
+.Dt MD5 1
+.Os
+.Sh NAME
+.Nm md5
+.Nd calculate a message-digest fingerprint (checksum) for a file
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Op Fl t
+.Op Fl x
+.Op Fl s Ns Ar string
+.Op Ar filename Ns Pq s
+.Sh DESCRIPTION
+.Nm
+takes as input a message of arbitrary length and produces
+as output a 128-bit
+.Dq fingerprint
+or
+.Dq 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
+.Dq compressed
+in a secure manner before being encrypted with a private
+.Pq secret
+key under a public-key cryptosystem such as
+.Em RSA .
+.Pp
+The following four options may be used in any combination, except
+that
+.Ar filename Ns Pq s
+must be the last objects on the command line.
+.Bl -tag -width Fl
+.It Fl s Ns Ar string
+prints a checksum of the given
+.Dq string .
+.It Fl p
+echos stdin to stdout and appends the MD5 sum to stdout.
+.It Fl t
+runs a built-in time trial.
+.It Fl x
+runs a built-in test script.
+.It Ar filename Ns Pq s
+prints a checksum
+.Pq s
+for each of the files.
+.El
+.Sh SEE ALSO
+.Xr cksum 1
+.Rs
+.%A R. Rivest
+.%T The MD5 Message-Digest Algorithm
+.%O RFC1321
+.Re
+.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..02dbd2d
--- /dev/null
+++ b/sbin/md5/md5.c
@@ -0,0 +1,177 @@
+/*
+ * $Id$
+ *
+ * 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_COUNT, TEST_BLOCK_LEN);
+
+ /* 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..ebcc86f
--- /dev/null
+++ b/sbin/mknod/mknod.8
@@ -0,0 +1,109 @@
+.\" 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 mkfifo 1 ,
+.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..21f63db
--- /dev/null
+++ b/sbin/mknod/mknod.c
@@ -0,0 +1,104 @@
+/*
+ * 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: mknod.c,v 1.6 1997/03/12 19:03:40 bde Exp $";
+#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>
+#include <err.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
+ errx(1, "node must be type 'b' or 'c'");
+
+ errno = 0;
+ major = (long)strtoul(argv[3], &endp, 0);
+ if (endp == argv[3] || *endp != '\0')
+ errx(1, "%s: non-numeric major number", argv[3]);
+ range_error = errno;
+ errno = 0;
+ minor = (long)strtoul(argv[4], &endp, 0);
+ if (endp == argv[4] || *endp != '\0')
+ errx(1, "%s: non-numeric minor number", argv[4]);
+ range_error |= errno;
+ dev = makedev(major, minor);
+ if (range_error || major(dev) != major || minor(dev) != minor)
+ errx(1, "major or minor number too large");
+
+ if (mknod(argv[1], mode, dev) != 0)
+ err(1, "%s", argv[1]);
+ exit(0);
+}
diff --git a/sbin/modload/Makefile b/sbin/modload/Makefile
new file mode 100644
index 0000000..bff8935
--- /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$
+#
+
+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..ff48950
--- /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$
+.\"
+.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 modstat 8 ,
+.Xr modunload 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..5828709
--- /dev/null
+++ b/sbin/modload/modload.c
@@ -0,0 +1,401 @@
+/*
+ * 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.17 1997/03/11 12:23:09 peter Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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;
+
+/*
+ * 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:")) != -1) {
+ 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..9d923a3
--- /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$
+#
+
+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..a4d98e3
--- /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$
+.\"
+.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..4c3b185
--- /dev/null
+++ b/sbin/modunload/modunload.c
@@ -0,0 +1,126 @@
+/*
+ * 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.5 1997/03/11 12:24:00 peter Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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:")) != -1) {
+ 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..a06b102
--- /dev/null
+++ b/sbin/mount/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.6 (Berkeley) 5/8/95
+
+PROG= mount
+SRCS= mount.c mount_ufs.c getmntopts.c vfslist.c
+MAN8= mount.8
+# We do NOT install the getmntopts.3 man page.
+CFLAGS+= -D_NEW_VFSCONF
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount/getmntopts.3 b/sbin/mount/getmntopts.3
new file mode 100644
index 0000000..8424104
--- /dev/null
+++ b/sbin/mount/getmntopts.3
@@ -0,0 +1,179 @@
+.\" 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.3 (Berkeley) 3/30/95
+.\"
+.Dd March 30, 1995
+.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 words are 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 = mntaltflags = 0;
+ ...
+ getmntopts(options, mopts, &mntflags, &mntaltflags);
+ ...
+.Ed
+.Sh DIAGNOSTICS
+If the external integer variable
+.Dv getmnt_silent
+is non-zero then the
+.Nm getmntopts
+function displays an error message and exits if an
+unrecognized option is encountered.
+By default
+.Dv getmnt_silent
+is zero.
+.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..f0cd27c
--- /dev/null
+++ b/sbin/mount/getmntopts.c
@@ -0,0 +1,106 @@
+/*-
+ * 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.3 (Berkeley) 3/29/95";
+#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, *p;
+ 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;
+
+ /*
+ * for options with assignments in them (ie. quotas)
+ * ignore the assignment as it's handled elsewhere
+ */
+ p = strchr(opt, '=');
+ if (p)
+ *++p = '\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..8ca1260
--- /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.7 (Berkeley) 3/29/95
+ */
+
+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; /* 1 => set bit in altflags */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
+#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 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 }
+#define MOPT_USERQUOTA { "userquota", 0, 0, 0 }
+#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
+#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
+#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
+
+/* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */
+#define MOPT_AUTO { "auto", 0, 0, 0 }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW, \
+ MOPT_AUTO
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_USERQUOTA, \
+ MOPT_GROUPQUOTA, \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NOATIME, \
+ 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..f0fd0ac
--- /dev/null
+++ b/sbin/mount/mount.8
@@ -0,0 +1,310 @@
+.\" 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.8 (Berkeley) 6/16/94
+.\" $Id: mount.8,v 1.14 1997/02/22 14:32:43 peter Exp $
+.\"
+.Dd June 16, 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
+All the filesystems described in
+.Xr fstab 5
+are mounted.
+Exceptions are those marked as ``noauto'' or are excluded by the
+.Fl t
+flag (see below).
+.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 noauto
+This filesystem should be skipped when mount is run with the
+.Fl a
+flag.
+.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..b0d0105
--- /dev/null
+++ b/sbin/mount/mount.c
@@ -0,0 +1,557 @@
+/*
+ * 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.25 (Berkeley) 5/8/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+int debug, fstab_style, verbose;
+
+int checkvfsname __P((const char *, const char **));
+char *catopt __P((char *, const char *));
+struct statfs
+ *getmntpt __P((const char *));
+int hasopt __P((const char *, 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((struct statfs *));
+void putfsent __P((const struct statfs *));
+void usage __P((void));
+
+/* From mount_ufs.c. */
+int mount_ufs __P((int, char * const *));
+
+/* Map from mount otions to printable formats. */
+static struct opt {
+ int o_opt;
+ const char *o_name;
+} optnames[] = {
+ { MNT_ASYNC, "asynchronous" },
+ { MNT_EXPORTED, "NFS exported" },
+ { MNT_LOCAL, "local" },
+ { MNT_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" },
+ { NULL }
+};
+
+int
+main(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ const char *mntfromname, **vfslist, *vfstype;
+ struct fstab *fs;
+ struct statfs *mntbuf;
+ FILE *mountdfp;
+ pid_t pid;
+ int all, ch, i, init_flags, mntsize, rval;
+ char *options;
+
+ all = init_flags = 0;
+ options = NULL;
+ vfslist = NULL;
+ vfstype = "ufs";
+ while ((ch = getopt(argc, argv, "adfo:prwt:uv")) != -1)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ init_flags |= MNT_FORCE;
+ break;
+ case 'o':
+ if (*optarg)
+ options = catopt(options, optarg);
+ break;
+ case 'p':
+ fstab_style = 1;
+ verbose = 1;
+ 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 (checkvfsname(fs->fs_vfstype, vfslist))
+ continue;
+ if (hasopt(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 (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+ continue;
+ putfsent(&mntbuf[i]);
+ }
+ } else {
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (checkvfsname(mntbuf[i].f_fstypename,
+ vfslist))
+ continue;
+ prmount(&mntbuf[i]);
+ }
+ }
+ 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)
+ mntfromname = fs->fs_spec;
+ else
+ mntfromname = mntbuf->f_mntfromname;
+ rval = mountfs(mntbuf->f_fstypename, mntfromname,
+ mntbuf->f_mntonname, init_flags, options, 0);
+ break;
+ }
+ 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);
+ rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
+ 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
+hasopt(mntopts, option)
+ const char *mntopts, *option;
+{
+ int negative, found;
+ char *opt, *optbuf;
+
+ if (option[0] == 'n' && option[1] == 'o') {
+ negative = 1;
+ option += 2;
+ } else
+ negative = 0;
+ optbuf = strdup(mntopts);
+ found = 0;
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ if (!strcasecmp(opt + 2, option))
+ found = negative;
+ } else if (!strcasecmp(opt, option))
+ found = !negative;
+ }
+ free(optbuf);
+ return (found);
+}
+
+int
+mountfs(vfstype, spec, name, flags, options, mntopts)
+ const char *vfstype, *spec, *name, *options, *mntopts;
+ int flags;
+{
+ /* List of directories containing mount_xxx subcommands. */
+ static const char *edirs[] = {
+ _PATH_SBIN,
+ _PATH_USRSBIN,
+ NULL
+ };
+ const char *argv[100], **edir;
+ struct stat sb;
+ 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);
+ }
+
+ name = mntpath;
+
+ if (mntopts == NULL)
+ mntopts = "";
+ 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 = fork()) {
+ case -1: /* Error. */
+ warn("fork");
+ 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("statfs %s", name);
+ return (1);
+ }
+ if (fstab_style)
+ putfsent(&sf);
+ else
+ prmount(&sf);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+void
+prmount(sfp)
+ struct statfs *sfp;
+{
+ int flags;
+ struct opt *o;
+ struct passwd *pw;
+ int f;
+
+ (void)printf("%s on %s", sfp->f_mntfromname, sfp->f_mntonname);
+
+ flags = sfp->f_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;
+ }
+ if (sfp->f_owner) {
+ (void)printf("%smounted by ", !f++ ? " (" : ", ");
+ if ((pw = getpwuid(sfp->f_owner)) != NULL)
+ (void)printf("%s", pw->pw_name);
+ else
+ (void)printf("%d", sfp->f_owner);
+ }
+ (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);
+}
+
+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,
+ ent->f_fstypename, (ent->f_flags & MNT_RDONLY) ? "ro" : "rw");
+
+ /* XXX should use optnames[] - put shorter names in it. */
+ 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 (ent->f_flags & MNT_NOATIME)
+ printf(",noatime");
+
+ 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..b6344d1
--- /dev/null
+++ b/sbin/mount/mount_ufs.c
@@ -0,0 +1,151 @@
+/*-
+ * 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.4 (Berkeley) 4/26/95";
+#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 <ufs/ufs/ufsmount.h>
+
+#include "mntopts.h"
+
+void ufs_usage __P((void));
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ MOPT_SYNC,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ MOPT_FORCE,
+ { 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;
+ int error = 0;
+
+ mntflags = 0;
+ optind = optreset = 1; /* Reset for parse of new argv. */
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ 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;
+
+ error = getvfsbyname("ufs", &vfc);
+ if (error && vfsisloadable("ufs")) {
+ if (vfsload("ufs")) {
+ warn("vfsload(ufs)");
+ return (1);
+ }
+ endvfsent(); /* flush old table */
+ error = getvfsbyname("ufs", &vfc);
+ }
+ if (error) {
+ warnx("ufs filesystem is not available");
+ return (1);
+ }
+
+ if (mount(vfc.vfc_name, 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/vfslist.c b/sbin/mount/vfslist.c
new file mode 100644
index 0000000..1a00a23
--- /dev/null
+++ b/sbin/mount/vfslist.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int checkvfsname __P((const char *, const char **));
+const char **makevfslist __P((char *));
+static int skipvfs;
+
+int
+checkvfsname(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);
+}
+
+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);
+}
diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile
new file mode 100644
index 0000000..61129f3
--- /dev/null
+++ b/sbin/mount_cd9660/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_cd9660
+SRCS= mount_cd9660.c getmntopts.c
+MAN8= mount_cd9660.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..2e0e661
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.8
@@ -0,0 +1,118 @@
+.\" 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 egrv
+.Op Fl o Ar options
+.Op Fl s startsector
+.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.
+.It Fl s Ar startsector
+Start the filesystem at
+.Ar startsector .
+Normally, if the underlying device is a CD-ROM drive,
+.Nm
+will try to figure out the last track from the CD-ROM containing
+data, and start the filesystem there. If the device is not a CD-ROM,
+or the table of contents cannot be examined, the filesystem will be
+started at sector 0. This option can be used to override the behaviour.
+Note that
+.Ar startsector
+is measured in CD-ROM blocks, with 2048 bytes each. This is the same
+as for example the
+.Cm info
+command of
+.Xr cdcontrol 8
+is printing.
+.It Fl v
+Be verbose about the starting sector decisions made.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr cdcontrol 8 ,
+.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..a5b0cc0
--- /dev/null
+++ b/sbin/mount_cd9660/mount_cd9660.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software 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.7 (Berkeley) 5/1/95
+ */
+
+#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.7 (Berkeley) 5/1/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_cd9660.c,v 1.11 1997/03/29 03:32:35 imp Exp $";
+#endif /* not lint */
+
+#include <sys/cdio.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/../isofs/cd9660/cd9660_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 }
+};
+
+int get_ssector(const char *dev);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct iso_args args;
+ int ch, mntflags, opts;
+ char *dev, *dir;
+ struct vfsconf vfc;
+ int error, verbose;
+
+ mntflags = opts = verbose = 0;
+ memset(&args, 0, sizeof args);
+ args.ssector = -1;
+ while ((ch = getopt(argc, argv, "ego:rs:v")) != -1)
+ 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 's':
+ args.ssector = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ dev = argv[0];
+ dir = argv[1];
+
+#define DEFAULT_ROOTUID -2
+ /*
+ * ISO 9660 filesystems are not writeable.
+ */
+ mntflags |= MNT_RDONLY;
+ args.export.ex_flags = MNT_EXRDONLY;
+ args.fspec = dev;
+ args.export.ex_root = DEFAULT_ROOTUID;
+ args.flags = opts;
+
+ if (args.ssector == -1) {
+ /*
+ * The start of the session has not been specified on
+ * the command line. If we can successfully read the
+ * TOC of a CD-ROM, use the last data track we find.
+ * Otherwise, just use 0, in order to mount the very
+ * first session. This is compatible with the
+ * historic behaviour of mount_cd9660(8). If the user
+ * has specified -s <ssector> above, we don't get here
+ * and leave the user's will.
+ */
+ if ((args.ssector = get_ssector(dev)) == -1) {
+ if (verbose)
+ printf("could not determine starting sector, "
+ "using very first session\n");
+ args.ssector = 0;
+ } else if (verbose)
+ printf("using starting sector %d\n", args.ssector);
+ }
+
+ error = getvfsbyname("cd9660", &vfc);
+ if (error && vfsisloadable("cd9660")) {
+ if (vfsload("cd9660"))
+ err(EX_OSERR, "vfsload(cd9660)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("cd9660", &vfc);
+ }
+ if (error)
+ errx(1, "cd9660 filesystem is not available");
+
+ if (mount(vfc.vfc_name, dir, mntflags, &args) < 0)
+ err(1, NULL);
+ exit(0);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: mount_cd9660 [-egrv] [-o options] [-s startsector] special node\n");
+ exit(EX_USAGE);
+}
+
+int
+get_ssector(const char *dev)
+{
+ struct ioc_toc_header h;
+ struct ioc_read_toc_entry t;
+ struct cd_toc_entry toc_buffer[100];
+ int fd, ntocentries, i;
+
+ if ((fd = open(dev, O_RDONLY)) == -1)
+ return -1;
+ if (ioctl(fd, CDIOREADTOCHEADER, &h) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ ntocentries = h.ending_track - h.starting_track + 1;
+ if (ntocentries > 100) {
+ /* unreasonable, only 100 allowed */
+ close(fd);
+ return -1;
+ }
+ t.address_format = CD_LBA_FORMAT;
+ t.starting_track = 0;
+ t.data_len = ntocentries * sizeof(struct cd_toc_entry);
+ t.data = toc_buffer;
+
+ if (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t) == -1) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ for (i = ntocentries - 1; i >= 0; i--)
+ if ((toc_buffer[i].control & 4) != 0)
+ /* found a data track */
+ break;
+ if (i < 0)
+ return -1;
+
+ return ntohl(toc_buffer[i].addr.lba);
+}
diff --git a/sbin/mount_ext2fs/Makefile b/sbin/mount_ext2fs/Makefile
new file mode 100644
index 0000000..9a463a4
--- /dev/null
+++ b/sbin/mount_ext2fs/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_ext2fs
+SRCS= mount_ext2fs.c getmntopts.c
+MAN8= mount_ext2fs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..4eb41a3
--- /dev/null
+++ b/sbin/mount_ext2fs/mount_ext2fs.c
@@ -0,0 +1,130 @@
+/*-
+ * 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.7 1997/03/11 12:29:51 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 <ufs/ufs/ufsmount.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;
+ int error;
+
+ options = NULL;
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ 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;
+
+ error = getvfsbyname("ext2fs", &vfc);
+ if (error && vfsisloadable("ext2fs")) {
+ if (vfsload("ext2fs")) {
+ err(EX_OSERR, "vfsload(ext2fs)");
+ }
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("ext2fs", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "ext2fs filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..a06b102
--- /dev/null
+++ b/sbin/mount_ifs/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.6 (Berkeley) 5/8/95
+
+PROG= mount
+SRCS= mount.c mount_ufs.c getmntopts.c vfslist.c
+MAN8= mount.8
+# We do NOT install the getmntopts.3 man page.
+CFLAGS+= -D_NEW_VFSCONF
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_ifs/getmntopts.3 b/sbin/mount_ifs/getmntopts.3
new file mode 100644
index 0000000..8424104
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.3
@@ -0,0 +1,179 @@
+.\" 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.3 (Berkeley) 3/30/95
+.\"
+.Dd March 30, 1995
+.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 words are 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 = mntaltflags = 0;
+ ...
+ getmntopts(options, mopts, &mntflags, &mntaltflags);
+ ...
+.Ed
+.Sh DIAGNOSTICS
+If the external integer variable
+.Dv getmnt_silent
+is non-zero then the
+.Nm getmntopts
+function displays an error message and exits if an
+unrecognized option is encountered.
+By default
+.Dv getmnt_silent
+is zero.
+.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..f0cd27c
--- /dev/null
+++ b/sbin/mount_ifs/getmntopts.c
@@ -0,0 +1,106 @@
+/*-
+ * 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.3 (Berkeley) 3/29/95";
+#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, *p;
+ 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;
+
+ /*
+ * for options with assignments in them (ie. quotas)
+ * ignore the assignment as it's handled elsewhere
+ */
+ p = strchr(opt, '=');
+ if (p)
+ *++p = '\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..8ca1260
--- /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.7 (Berkeley) 3/29/95
+ */
+
+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; /* 1 => set bit in altflags */
+};
+
+/* User-visible MNT_ flags. */
+#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
+#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 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 }
+#define MOPT_USERQUOTA { "userquota", 0, 0, 0 }
+#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
+
+/* Control flags. */
+#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
+#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
+#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
+#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
+
+/* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */
+#define MOPT_AUTO { "auto", 0, 0, 0 }
+
+#define MOPT_FSTAB_COMPAT \
+ MOPT_RO, \
+ MOPT_RW, \
+ MOPT_AUTO
+
+/* Standard options which all mounts can understand. */
+#define MOPT_STDOPTS \
+ MOPT_USERQUOTA, \
+ MOPT_GROUPQUOTA, \
+ MOPT_FSTAB_COMPAT, \
+ MOPT_NOATIME, \
+ 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..f0fd0ac
--- /dev/null
+++ b/sbin/mount_ifs/mount.8
@@ -0,0 +1,310 @@
+.\" 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.8 (Berkeley) 6/16/94
+.\" $Id: mount.8,v 1.14 1997/02/22 14:32:43 peter Exp $
+.\"
+.Dd June 16, 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
+All the filesystems described in
+.Xr fstab 5
+are mounted.
+Exceptions are those marked as ``noauto'' or are excluded by the
+.Fl t
+flag (see below).
+.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 noauto
+This filesystem should be skipped when mount is run with the
+.Fl a
+flag.
+.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..b0d0105
--- /dev/null
+++ b/sbin/mount_ifs/mount.c
@@ -0,0 +1,557 @@
+/*
+ * 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.25 (Berkeley) 5/8/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+int debug, fstab_style, verbose;
+
+int checkvfsname __P((const char *, const char **));
+char *catopt __P((char *, const char *));
+struct statfs
+ *getmntpt __P((const char *));
+int hasopt __P((const char *, 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((struct statfs *));
+void putfsent __P((const struct statfs *));
+void usage __P((void));
+
+/* From mount_ufs.c. */
+int mount_ufs __P((int, char * const *));
+
+/* Map from mount otions to printable formats. */
+static struct opt {
+ int o_opt;
+ const char *o_name;
+} optnames[] = {
+ { MNT_ASYNC, "asynchronous" },
+ { MNT_EXPORTED, "NFS exported" },
+ { MNT_LOCAL, "local" },
+ { MNT_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" },
+ { NULL }
+};
+
+int
+main(argc, argv)
+ int argc;
+ char * const argv[];
+{
+ const char *mntfromname, **vfslist, *vfstype;
+ struct fstab *fs;
+ struct statfs *mntbuf;
+ FILE *mountdfp;
+ pid_t pid;
+ int all, ch, i, init_flags, mntsize, rval;
+ char *options;
+
+ all = init_flags = 0;
+ options = NULL;
+ vfslist = NULL;
+ vfstype = "ufs";
+ while ((ch = getopt(argc, argv, "adfo:prwt:uv")) != -1)
+ switch (ch) {
+ case 'a':
+ all = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ init_flags |= MNT_FORCE;
+ break;
+ case 'o':
+ if (*optarg)
+ options = catopt(options, optarg);
+ break;
+ case 'p':
+ fstab_style = 1;
+ verbose = 1;
+ 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 (checkvfsname(fs->fs_vfstype, vfslist))
+ continue;
+ if (hasopt(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 (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+ continue;
+ putfsent(&mntbuf[i]);
+ }
+ } else {
+ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+ err(1, "getmntinfo");
+ for (i = 0; i < mntsize; i++) {
+ if (checkvfsname(mntbuf[i].f_fstypename,
+ vfslist))
+ continue;
+ prmount(&mntbuf[i]);
+ }
+ }
+ 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)
+ mntfromname = fs->fs_spec;
+ else
+ mntfromname = mntbuf->f_mntfromname;
+ rval = mountfs(mntbuf->f_fstypename, mntfromname,
+ mntbuf->f_mntonname, init_flags, options, 0);
+ break;
+ }
+ 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);
+ rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
+ 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
+hasopt(mntopts, option)
+ const char *mntopts, *option;
+{
+ int negative, found;
+ char *opt, *optbuf;
+
+ if (option[0] == 'n' && option[1] == 'o') {
+ negative = 1;
+ option += 2;
+ } else
+ negative = 0;
+ optbuf = strdup(mntopts);
+ found = 0;
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ if (!strcasecmp(opt + 2, option))
+ found = negative;
+ } else if (!strcasecmp(opt, option))
+ found = !negative;
+ }
+ free(optbuf);
+ return (found);
+}
+
+int
+mountfs(vfstype, spec, name, flags, options, mntopts)
+ const char *vfstype, *spec, *name, *options, *mntopts;
+ int flags;
+{
+ /* List of directories containing mount_xxx subcommands. */
+ static const char *edirs[] = {
+ _PATH_SBIN,
+ _PATH_USRSBIN,
+ NULL
+ };
+ const char *argv[100], **edir;
+ struct stat sb;
+ 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);
+ }
+
+ name = mntpath;
+
+ if (mntopts == NULL)
+ mntopts = "";
+ 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 = fork()) {
+ case -1: /* Error. */
+ warn("fork");
+ 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("statfs %s", name);
+ return (1);
+ }
+ if (fstab_style)
+ putfsent(&sf);
+ else
+ prmount(&sf);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+void
+prmount(sfp)
+ struct statfs *sfp;
+{
+ int flags;
+ struct opt *o;
+ struct passwd *pw;
+ int f;
+
+ (void)printf("%s on %s", sfp->f_mntfromname, sfp->f_mntonname);
+
+ flags = sfp->f_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;
+ }
+ if (sfp->f_owner) {
+ (void)printf("%smounted by ", !f++ ? " (" : ", ");
+ if ((pw = getpwuid(sfp->f_owner)) != NULL)
+ (void)printf("%s", pw->pw_name);
+ else
+ (void)printf("%d", sfp->f_owner);
+ }
+ (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);
+}
+
+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,
+ ent->f_fstypename, (ent->f_flags & MNT_RDONLY) ? "ro" : "rw");
+
+ /* XXX should use optnames[] - put shorter names in it. */
+ 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 (ent->f_flags & MNT_NOATIME)
+ printf(",noatime");
+
+ 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..b6344d1
--- /dev/null
+++ b/sbin/mount_ifs/mount_ufs.c
@@ -0,0 +1,151 @@
+/*-
+ * 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.4 (Berkeley) 4/26/95";
+#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 <ufs/ufs/ufsmount.h>
+
+#include "mntopts.h"
+
+void ufs_usage __P((void));
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ MOPT_SYNC,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ MOPT_FORCE,
+ { 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;
+ int error = 0;
+
+ mntflags = 0;
+ optind = optreset = 1; /* Reset for parse of new argv. */
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ 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;
+
+ error = getvfsbyname("ufs", &vfc);
+ if (error && vfsisloadable("ufs")) {
+ if (vfsload("ufs")) {
+ warn("vfsload(ufs)");
+ return (1);
+ }
+ endvfsent(); /* flush old table */
+ error = getvfsbyname("ufs", &vfc);
+ }
+ if (error) {
+ warnx("ufs filesystem is not available");
+ return (1);
+ }
+
+ if (mount(vfc.vfc_name, 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_ifs/vfslist.c b/sbin/mount_ifs/vfslist.c
new file mode 100644
index 0000000..1a00a23
--- /dev/null
+++ b/sbin/mount_ifs/vfslist.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int checkvfsname __P((const char *, const char **));
+const char **makevfslist __P((char *));
+static int skipvfs;
+
+int
+checkvfsname(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);
+}
+
+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);
+}
diff --git a/sbin/mount_lfs/Makefile b/sbin/mount_lfs/Makefile
new file mode 100644
index 0000000..bcd1f51
--- /dev/null
+++ b/sbin/mount_lfs/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_lfs
+SRCS= mount_lfs.c getmntopts.c
+MAN8= mount_lfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..607d7a4
--- /dev/null
+++ b/sbin/mount_lfs/mount_lfs.c
@@ -0,0 +1,167 @@
+/*-
+ * 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.4 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_lfs.c,v 1.8 1997/03/11 12:30:33 peter Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.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 error;
+ int short_rds, cleaner_debug;
+
+
+ options = NULL;
+ mntflags = noclean = short_rds = cleaner_debug = 0;
+ while ((ch = getopt(argc, argv, "dno:s")) != -1)
+ 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;
+
+ error = getvfsbyname("lfs", &vfc);
+ if (error && vfsisloadable("lfs")) {
+ if(vfsload("lfs"))
+ err(EX_OSERR, "vfsload(lfs)");
+ endvfsent(); /* clear cache */
+ error = getvfsbyname("lfs", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "lfs filesystem is not available");
+
+ if (mount(vfc.vfc_name, fs_name, mntflags, &args))
+ err(1, NULL);
+
+ 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..f6c88ab
--- /dev/null
+++ b/sbin/mount_msdos/Makefile
@@ -0,0 +1,17 @@
+#
+# $Id: Makefile,v 1.4 1997/02/22 14:32:29 peter Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..4f6fcd1
--- /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$
+.\"
+.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..53ae83b
--- /dev/null
+++ b/sbin/mount_msdos/mount_msdos.c
@@ -0,0 +1,213 @@
+/*
+ * 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.8 1997/03/03 13:23:54 bde Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <msdosfs/msdosfsmount.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, error, 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:")) != -1) {
+ 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);
+ }
+
+ error = getvfsbyname("msdos", &vfc);
+ if (error && vfsisloadable("msdos")) {
+ if (vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ error = getvfsbyname("msdos", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..f6c88ab
--- /dev/null
+++ b/sbin/mount_msdosfs/Makefile
@@ -0,0 +1,17 @@
+#
+# $Id: Makefile,v 1.4 1997/02/22 14:32:29 peter Exp $
+#
+
+PROG= mount_msdos
+SRCS= mount_msdos.c getmntopts.c
+MAN8= mount_msdos.8
+
+BINOWN= root
+BINMODE= 4555
+
+MOUNT= ${.CURDIR}/../../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..4f6fcd1
--- /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$
+.\"
+.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..53ae83b
--- /dev/null
+++ b/sbin/mount_msdosfs/mount_msdosfs.c
@@ -0,0 +1,213 @@
+/*
+ * 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.8 1997/03/03 13:23:54 bde Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <msdosfs/msdosfsmount.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, error, 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:")) != -1) {
+ 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);
+ }
+
+ error = getvfsbyname("msdos", &vfc);
+ if (error && vfsisloadable("msdos")) {
+ if (vfsload("msdos"))
+ err(EX_OSERR, "vfsload(msdos)");
+ endvfsent(); /* clear cache */
+ error = getvfsbyname("msdos", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "msdos filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..090d37c
--- /dev/null
+++ b/sbin/mount_nfs/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_nfs
+SRCS= mount_nfs.c getmntopts.c
+MAN8= mount_nfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..e81b017
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.8
@@ -0,0 +1,301 @@
+.\" Copyright (c) 1992, 1993, 1994, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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.3 (Berkeley) 3/29/95
+.\"
+.\" $Id: mount_nfs.8,v 1.8 1997/03/11 12:31:54 peter Exp $
+.\""
+.Dd March 29, 1995
+.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 23KPTUbcdilqs
+.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 2
+Use the NFS Version 2 protocol (the default is to try version 3 first
+then version 2).
+.It Fl 3
+Use the NFS Version 3 protocol.
+.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 nfsv2
+Same as
+.Fl 2 .
+.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..dda54c5
--- /dev/null
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -0,0 +1,895 @@
+/*
+ * 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.11 (Berkeley) 5/4/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_nfs.c,v 1.22 1997/05/01 12:30:02 dfr 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 <kerberosIV/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"
+
+#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
+#define ALTF_NFSV2 0x4000
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_FORCE,
+ MOPT_UPDATE,
+ MOPT_ASYNC,
+ { "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 },
+ { "nfsv2", 0, ALTF_NFSV2, 1 },
+ { NULL }
+};
+
+struct nfs_args nfsdefargs = {
+ NFS_ARGSVERSION,
+ (struct sockaddr *)0,
+ sizeof (struct sockaddr_in),
+ SOCK_DGRAM,
+ 0,
+ (u_char *)0,
+ 0,
+ NFSMNT_RESVPORT,
+ 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;
+enum {
+ ANY,
+ V2,
+ V3
+} mountmode = ANY;
+
+#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 *));
+
+/*
+ * Used to set mount flags with getmntopts. Call with dir=TRUE to
+ * initialise altflags from the current mount flags. Call with
+ * dir=FALSE to update mount flags with the new value of altflags after
+ * the call to getmntopts.
+ */
+static void
+setflags(int* altflags, int* nfsflags, int dir)
+{
+#define F2(af, nf) \
+ if (dir) { \
+ if (*nfsflags & NFSMNT_##nf) \
+ *altflags |= ALTF_##af; \
+ else \
+ *altflags &= ~ALTF_##af; \
+ } else { \
+ if (*altflags & ALTF_##af) \
+ *nfsflags |= NFSMNT_##nf; \
+ else \
+ *nfsflags &= ~NFSMNT_##nf; \
+ }
+#define F(f) F2(f,f)
+
+ F(NOCONN);
+ F(DUMBTIMR);
+ F2(INTR, INT);
+#ifdef NFSKERB
+ F(KERB);
+#endif
+ F(RDIRPLUS);
+ F(RESVPORT);
+ F(NQNFS);
+ F(SOFT);
+
+#undef F
+#undef F2
+}
+
+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;
+ int error = 0;
+#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,
+ "23a:bcdD:g:I:iKL:lm:o:PpqR:r:sTt:w:x:U")) != -1)
+ switch (c) {
+ case '2':
+ mountmode = V2;
+ break;
+ case '3':
+ mountmode = V3;
+ 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':
+ altflags = 0;
+ setflags(&altflags, &nfsargsp->flags, TRUE);
+ if (mountmode == V2)
+ altflags |= ALTF_NFSV2;
+ else if (mountmode == V3)
+ altflags |= ALTF_NFSV3;
+ getmntopts(optarg, mopts, &mntflags, &altflags);
+ setflags(&altflags, &nfsargsp->flags, FALSE);
+ /*
+ * Handle altflags which don't map directly to
+ * mount flags.
+ */
+ if(altflags & ALTF_BG)
+ opflags |= BGRND;
+ if(altflags & ALTF_MNTUDP)
+ mnttcp_ok = 0;
+#ifdef ISO
+ if(altflags & ALTF_SEQPACKET)
+ nfsargsp->sotype = SOCK_SEQPACKET;
+#endif
+ if(altflags & ALTF_TCP) {
+ nfsargsp->sotype = SOCK_STREAM;
+ nfsproto = IPPROTO_TCP;
+ }
+ if(altflags & ALTF_PORT)
+ port_no = atoi(strstr(optarg, "port=") + 5);
+ mountmode = ANY;
+ if(altflags & ALTF_NFSV2)
+ mountmode = V2;
+ if(altflags & ALTF_NFSV3)
+ mountmode = V3;
+ break;
+ case 'P':
+ nfsargsp->flags |= NFSMNT_RESVPORT;
+ break;
+#ifdef ISO
+ case 'p':
+ nfsargsp->sotype = SOCK_SEQPACKET;
+ break;
+#endif
+ case 'q':
+ mountmode = V3;
+ nfsargsp->flags |= NFSMNT_NQNFS;
+ break;
+ case 'R':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -R value -- %s", optarg);
+ retrycnt = num;
+ break;
+ case 'r':
+ num = strtol(optarg, &p, 10);
+ if (*p || num <= 0)
+ errx(1, "illegal -r value -- %s", optarg);
+ nfsargsp->rsize = num;
+ nfsargsp->flags |= NFSMNT_RSIZE;
+ break;
+ case 's':
+ nfsargsp->flags |= NFSMNT_SOFT;
+ break;
+ case 'T':
+ nfsargsp->sotype = SOCK_STREAM;
+ 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();
+ /* NOTREACHED */
+ }
+
+ spec = *argv++;
+ name = *argv;
+
+ if (!getnfsargs(spec, nfsargsp))
+ exit(1);
+
+#ifdef __FreeBSD__
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(EX_OSERR, "vfsload(nfs)");
+ endvfsent(); /* clear cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "nfs filesystem is not available");
+
+ if (mount(vfc.vfc_name, name, mntflags, nfsargsp))
+ err(1, "%s", name);
+#else
+ if (mount("nfs", name, mntflags, nfsargsp))
+ err(1, "%s", name);
+#endif
+ 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);
+ memmove(ncd.ncd_key, kcr.session,
+ 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);
+}
+
+/*
+ * Return RPC_SUCCESS if server responds.
+ */
+enum clnt_stat
+pingnfsserver(addr, version, sotype)
+ struct sockaddr_in *addr;
+ int version;
+ int sotype;
+{
+ struct sockaddr_in sin;
+ int tport;
+ CLIENT *clp;
+ int so = RPC_ANYSOCK;
+ enum clnt_stat stat;
+ struct timeval pertry, try;
+
+ sin = *addr;
+
+ if ((tport = port_no ? port_no :
+ pmap_getport(&sin, RPCPROG_NFS, version, nfsproto)) == 0) {
+ return rpc_createerr.cf_stat;
+ }
+
+ sin.sin_port = htons(tport);
+
+ pertry.tv_sec = 10;
+ pertry.tv_usec = 0;
+ if (sotype == SOCK_STREAM)
+ clp = clnttcp_create(&sin, RPCPROG_NFS, version,
+ &so, 0, 0);
+ else
+ clp = clntudp_create(&sin, RPCPROG_NFS, version,
+ pertry, &so);
+ if (clp == NULL)
+ return rpc_createerr.cf_stat;
+
+ try.tv_sec = 10;
+ try.tv_usec = 0;
+ stat = clnt_call(clp, NFSPROC_NULL,
+ xdr_void, NULL, xdr_void, NULL, try);
+
+ clnt_destroy(clp);
+
+ return stat;
+}
+
+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, orgcnt;
+ 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);
+ }
+ memset(&isoaddr, 0, sizeof (isoaddr));
+ memmove(&isoaddr.siso_addr, isop, sizeof (struct iso_addr));
+ isoaddr.siso_len = sizeof (isoaddr);
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ isoport = htons(NFS_PORT);
+ memmove(TSEL(&isoaddr), &isoport, 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)
+ memmove(&saddr.sin_addr, hp->h_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);
+ }
+ memmove(&saddr.sin_addr, hp->h_addr, hp->h_length);
+ strncpy(inst, hp->h_name, INST_SZ);
+ inst[INST_SZ - 1] = '\0';
+ if (cp = strchr(inst, '.'))
+ *cp = '\0';
+ }
+#endif /* NFSKERB */
+
+ orgcnt = retrycnt;
+tryagain:
+ if (mountmode == ANY || mountmode == V3) {
+ nfsvers = 3;
+ mntvers = 3;
+ nfsargsp->flags |= NFSMNT_NFSV3;
+ } else {
+ nfsvers = 2;
+ mntvers = 1;
+ nfsargsp->flags &= ~NFSMNT_NFSV3;
+ }
+ 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 {
+ /*
+ * First ping the nfs server to see if it supports
+ * the version of the protocol we want to use.
+ */
+ clnt_stat = pingnfsserver(&saddr, nfsvers,
+ nfsargsp->sotype);
+ if (clnt_stat == RPC_PROGVERSMISMATCH) {
+ if (mountmode == ANY) {
+ mountmode = V2;
+ goto tryagain;
+ } else {
+ errx(1, "Can't contact NFS server");
+ }
+ }
+ 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 (clnt_stat == RPC_PROGVERSMISMATCH) {
+ if (mountmode == ANY) {
+ mountmode = V2;
+ goto tryagain;
+ } else {
+ errx(1, "%s",
+ clnt_sperror(clp, "MNT RPC"));
+ }
+ }
+ 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);
+ warnx("can't access %s: %s", spec, strerror(nfhret.stat));
+ 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 [-23KPTUbcdilqs] [-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..97f3c62
--- /dev/null
+++ b/sbin/mount_null/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_null
+SRCS= mount_null.c getmntopts.c
+MAN8= mount_null.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..38473fa
--- /dev/null
+++ b/sbin/mount_null/mount_null.8
@@ -0,0 +1,233 @@
+.\"
+.\" 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.6 (Berkeley) 5/1/95
+.\" $Id: mount_null.8,v 1.7 1997/02/22 14:32:50 peter Exp $
+.\"
+.Dd May 1, 1995
+.Dt MOUNT_NULL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_null
+.Nd mount a loopback filesystem sub-tree;
+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.
+This allows existing files and directories to be accessed
+using a different pathname.
+.Pp
+The primary differences between a virtual copy of the filesystem
+and a symbolic link are that
+.Xr getcwd 3
+functions correctly in the virtual copy, and that other filesystems
+may be mounted on the virtual copy without affecting the original.
+A different device number for the virtual copy is returned by
+.Xr stat 2 ,
+but in other respects it is indistinguishable from the original.
+.Pp
+The
+.Nm mount_null
+filesystem differs from a traditional
+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..4e7f4c3
--- /dev/null
+++ b/sbin/mount_null/mount_null.c
@@ -0,0 +1,150 @@
+/*
+ * 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.6 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_null.c,v 1.9 1997/03/29 03:32:42 imp 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 source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ struct vfsconf vfc;
+ int error;
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ 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 (realpath(argv[1], source) == 0)
+ err(EX_OSERR, "%s", source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ error = getvfsbyname("null", &vfc);
+ if (error && vfsisloadable("null")) {
+ if(vfsload("null"))
+ err(EX_OSERR, "vfsload(null)");
+ endvfsent();
+ error = getvfsbyname("null", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "null/loopback filesystem is not available");
+
+ if (mount(vfc.vfc_name, source, mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+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..97f3c62
--- /dev/null
+++ b/sbin/mount_nullfs/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_null
+SRCS= mount_null.c getmntopts.c
+MAN8= mount_null.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..38473fa
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -0,0 +1,233 @@
+.\"
+.\" 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.6 (Berkeley) 5/1/95
+.\" $Id: mount_null.8,v 1.7 1997/02/22 14:32:50 peter Exp $
+.\"
+.Dd May 1, 1995
+.Dt MOUNT_NULL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_null
+.Nd mount a loopback filesystem sub-tree;
+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.
+This allows existing files and directories to be accessed
+using a different pathname.
+.Pp
+The primary differences between a virtual copy of the filesystem
+and a symbolic link are that
+.Xr getcwd 3
+functions correctly in the virtual copy, and that other filesystems
+may be mounted on the virtual copy without affecting the original.
+A different device number for the virtual copy is returned by
+.Xr stat 2 ,
+but in other respects it is indistinguishable from the original.
+.Pp
+The
+.Nm mount_null
+filesystem differs from a traditional
+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..4e7f4c3
--- /dev/null
+++ b/sbin/mount_nullfs/mount_nullfs.c
@@ -0,0 +1,150 @@
+/*
+ * 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.6 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_null.c,v 1.9 1997/03/29 03:32:42 imp 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 source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ struct vfsconf vfc;
+ int error;
+
+ mntflags = 0;
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ 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 (realpath(argv[1], source) == 0)
+ err(EX_OSERR, "%s", source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s are not distinct paths",
+ argv[0], target, argv[1]);
+
+ args.target = target;
+
+ error = getvfsbyname("null", &vfc);
+ if (error && vfsisloadable("null")) {
+ if(vfsload("null"))
+ err(EX_OSERR, "vfsload(null)");
+ endvfsent();
+ error = getvfsbyname("null", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "null/loopback filesystem is not available");
+
+ if (mount(vfc.vfc_name, source, mntflags, &args))
+ err(1, NULL);
+ exit(0);
+}
+
+int
+subdir(p, dir)
+ const char *p;
+ const char *dir;
+{
+ int l;
+
+ l = strlen(dir);
+ if (l <= 1)
+ return (1);
+
+ if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
+ return (1);
+
+ return (0);
+}
+
+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..fd0e416
--- /dev/null
+++ b/sbin/mount_portal/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id: Makefile,v 1.6 1997/02/22 14:32:52 peter Exp $
+
+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+= -D_NEW_VFSCONF
+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..f14a4a3
--- /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.3 (Berkeley) 4/28/95
+ *
+ * $Id: activate.c,v 1.3 1997/02/22 14:32:53 peter Exp $
+ */
+
+#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;
+
+ memset(&msg, 0, 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
+ */
+ memset(&msg, 0, 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..b7f3e57
--- /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$
+ */
+
+#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..4f489ed
--- /dev/null
+++ b/sbin/mount_portal/mount_portal.c
@@ -0,0 +1,292 @@
+/*
+ * 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.6 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.11 1997/03/24 05:53:12 imp 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 && errno != ECHILD)
+ 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;
+ mode_t um;
+
+ qelem q;
+ int rc;
+ int so;
+ int error = 0;
+
+ /*
+ * Crack command line args
+ */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ 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");
+ }
+ um = umask(077);
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+
+ (void) unlink(un.sun_path);
+ (void) umask(um);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ error = getvfsbyname("portal", &vfc);
+ if (error && vfsisloadable("portal")) {
+ if (vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent();
+ error = getvfsbyname("portal", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc.vfc_name, mountpt, mntflags, &args);
+ if (rc < 0)
+ err(1, NULL);
+
+ /*
+ * Everything is ready to go - now is a good time to fork
+ */
+ daemon(0, 0);
+
+ /*
+ * 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);
+ 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..3203b00
--- /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$
+ */
+
+#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..3a4ddf0a
--- /dev/null
+++ b/sbin/mount_portal/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id$
+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..23fce24
--- /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$
+ */
+
+#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..ae588ad
--- /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$
+ */
+
+#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..6ee2bf5
--- /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$
+ */
+
+#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..9d71bc7
--- /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.3 (Berkeley) 7/3/94
+ *
+ * $Id: pt_file.c,v 1.5 1997/02/22 14:32:56 peter 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..ea7ac31
--- /dev/null
+++ b/sbin/mount_portal/pt_tcp.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ * @(#)pt_tcp.c 8.5 (Berkeley) 4/28/95
+ *
+ * $Id: pt_tcp.c,v 1.4 1997/02/22 14:32:56 peter 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 = strtoul(port, &p, 0);
+ if (s_port == 0 || *p != '\0')
+ return (EINVAL);
+ s_port = htons(s_port);
+ }
+#ifdef DEBUG
+ printf ("port number for %s is %d\n", port, s_port);
+#endif
+
+ memset(&sain, 0, 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..fd0e416
--- /dev/null
+++ b/sbin/mount_portalfs/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id: Makefile,v 1.6 1997/02/22 14:32:52 peter Exp $
+
+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+= -D_NEW_VFSCONF
+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..f14a4a3
--- /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.3 (Berkeley) 4/28/95
+ *
+ * $Id: activate.c,v 1.3 1997/02/22 14:32:53 peter Exp $
+ */
+
+#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;
+
+ memset(&msg, 0, 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
+ */
+ memset(&msg, 0, 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..b7f3e57
--- /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$
+ */
+
+#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..4f489ed
--- /dev/null
+++ b/sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,292 @@
+/*
+ * 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.6 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.11 1997/03/24 05:53:12 imp 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 && errno != ECHILD)
+ 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;
+ mode_t um;
+
+ qelem q;
+ int rc;
+ int so;
+ int error = 0;
+
+ /*
+ * Crack command line args
+ */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ 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");
+ }
+ um = umask(077);
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+
+ (void) unlink(un.sun_path);
+ (void) umask(um);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ error = getvfsbyname("portal", &vfc);
+ if (error && vfsisloadable("portal")) {
+ if (vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent();
+ error = getvfsbyname("portal", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc.vfc_name, mountpt, mntflags, &args);
+ if (rc < 0)
+ err(1, NULL);
+
+ /*
+ * Everything is ready to go - now is a good time to fork
+ */
+ daemon(0, 0);
+
+ /*
+ * 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);
+ 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..3203b00
--- /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$
+ */
+
+#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..3a4ddf0a
--- /dev/null
+++ b/sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id$
+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..23fce24
--- /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$
+ */
+
+#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..ae588ad
--- /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$
+ */
+
+#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..6ee2bf5
--- /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$
+ */
+
+#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..9d71bc7
--- /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.3 (Berkeley) 7/3/94
+ *
+ * $Id: pt_file.c,v 1.5 1997/02/22 14:32:56 peter 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..ea7ac31
--- /dev/null
+++ b/sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ * @(#)pt_tcp.c 8.5 (Berkeley) 4/28/95
+ *
+ * $Id: pt_tcp.c,v 1.4 1997/02/22 14:32:56 peter 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 = strtoul(port, &p, 0);
+ if (s_port == 0 || *p != '\0')
+ return (EINVAL);
+ s_port = htons(s_port);
+ }
+#ifdef DEBUG
+ printf ("port number for %s is %d\n", port, s_port);
+#endif
+
+ memset(&sain, 0, 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..91befcf
--- /dev/null
+++ b/sbin/mount_std/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.2 (Berkeley) 3/27/94
+
+PROG= mount_std
+SRCS= mount_std.c getmntopts.c
+MAN8= mount_std.8
+MLINKS= mount_std.8 mount_devfs.8 mount_std.8 mount_fdesc.8 \
+ mount_std.8 mount_kernfs.8 mount_std.8 mount_procfs.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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_std.8 b/sbin/mount_std/mount_std.8
new file mode 100644
index 0000000..3c5e081
--- /dev/null
+++ b/sbin/mount_std/mount_std.8
@@ -0,0 +1,170 @@
+.\"
+.\" 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$
+.\"
+.Dd May 13, 1996
+.Dt MOUNT_STD 8
+.Os FreeBSD 2.2
+.Sh NAME
+.Nm mount_std ,
+.Nm mount_devfs ,
+.Nm mount_fdesc ,
+.Nm mount_kernfs ,
+.Nm mount_procfs
+.Nd mount ``standard'' filesystems
+.Sh SYNOPSIS
+.Nm mount_ Ns Ar fsname
+.Op Fl o Ar options
+.Ar "fs"
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+command is a generic mechanism for attaching ``standard'' filesystems to
+the filesystem. The
+.Nm
+command currently supports the following filesystems:
+.Nm devfs ,
+.Nm fdesc ,
+.Nm kernfs
+and
+.Nm procfs .
+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 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
+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.
+.Pp
+Refer to the following manual pages for detailed information
+on these file system:
+.Xr devfs 5 ,
+.Xr fdesc 5 ,
+.Xr kernfs 5
+and
+.Xr procfs 5 .
+.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 devfs 5 ,
+.Xr fdesc 5 ,
+.Xr fstab 5 ,
+.Xr kernfs 5 ,
+.Xr procfs 5 ,
+.Xr mount 8
+.Sh CAVEATS
+None of the ``standard'' filesystems may be NFS-exported.
+.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..e85f4b3
--- /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.6 1997/03/11 12:38:16 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"
+
+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;
+ int error;
+
+ /*
+ * 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:")) != -1)
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ error = getvfsbyname(fsname, &vfc);
+ if (error && vfsisloadable(fsname)) {
+ if(vfsload(fsname))
+ err(EX_OSERR, "vfsload(%s)", fsname);
+ endvfsent();
+ error = getvfsbyname(fsname, &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "%s filesystem not available", fsname);
+
+ if (mount(vfc.vfc_name, 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..fce19db
--- /dev/null
+++ b/sbin/mount_umap/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_umap
+SRCS= mount_umap.c getmntopts.c
+MAN8= mount_umap.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..d0ecb29
--- /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.4 (Berkeley) 5/1/95
+.\"
+.Dd "May 1, 1995"
+.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..263c8d3
--- /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.5 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_umap.c,v 1.10 1997/03/11 12:39:32 peter 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;
+ int error;
+
+ mntflags = 0;
+ mapfile = gmapfile = NULL;
+ while ((ch = getopt(argc, argv, "g:o:u:")) != -1)
+ 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;
+
+ error = getvfsbyname("umap", &vfc);
+ if (error && vfsisloadable("umap")) {
+ if(vfsload("umap"))
+ err(1, "vfsload(umap)");
+ endvfsent();
+ error = getvfsbyname("umap", &vfc);
+ }
+ if (error)
+ errx(1, "umap filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..fce19db
--- /dev/null
+++ b/sbin/mount_umapfs/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_umap
+SRCS= mount_umap.c getmntopts.c
+MAN8= mount_umap.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..d0ecb29
--- /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.4 (Berkeley) 5/1/95
+.\"
+.Dd "May 1, 1995"
+.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..263c8d3
--- /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.5 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_umap.c,v 1.10 1997/03/11 12:39:32 peter 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;
+ int error;
+
+ mntflags = 0;
+ mapfile = gmapfile = NULL;
+ while ((ch = getopt(argc, argv, "g:o:u:")) != -1)
+ 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;
+
+ error = getvfsbyname("umap", &vfc);
+ if (error && vfsisloadable("umap")) {
+ if(vfsload("umap"))
+ err(1, "vfsload(umap)");
+ endvfsent();
+ error = getvfsbyname("umap", &vfc);
+ }
+ if (error)
+ errx(1, "umap filesystem is not available");
+
+ if (mount(vfc.vfc_name, 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..6ac36c9
--- /dev/null
+++ b/sbin/mount_union/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_union
+SRCS= mount_union.c getmntopts.c
+MAN8= mount_union.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..699a01c
--- /dev/null
+++ b/sbin/mount_union/mount_union.c
@@ -0,0 +1,157 @@
+/*
+ * 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 source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ struct vfsconf vfc;
+ int error;
+
+ mntflags = 0;
+ args.mntflags = UNMNT_ABOVE;
+ while ((ch = getopt(argc, argv, "bo:r")) != -1)
+ 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 (realpath(argv[1], source) == 0)
+ err(EX_OSERR, "%s", source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
+ argv[0], target, argv[1], source);
+
+ args.target = target;
+
+ error = getvfsbyname("union", &vfc);
+ if (error && vfsisloadable("union")) {
+ if (vfsload("union"))
+ err(EX_OSERR, "vfsload(union)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("union", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "union filesystem is not available");
+
+ if (mount(vfc.vfc_name, source, 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..6ac36c9
--- /dev/null
+++ b/sbin/mount_unionfs/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 3/27/94
+
+PROG= mount_union
+SRCS= mount_union.c getmntopts.c
+MAN8= mount_union.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+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..699a01c
--- /dev/null
+++ b/sbin/mount_unionfs/mount_unionfs.c
@@ -0,0 +1,157 @@
+/*
+ * 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 source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ struct vfsconf vfc;
+ int error;
+
+ mntflags = 0;
+ args.mntflags = UNMNT_ABOVE;
+ while ((ch = getopt(argc, argv, "bo:r")) != -1)
+ 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 (realpath(argv[1], source) == 0)
+ err(EX_OSERR, "%s", source);
+
+ if (subdir(target, source) || subdir(source, target))
+ errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
+ argv[0], target, argv[1], source);
+
+ args.target = target;
+
+ error = getvfsbyname("union", &vfc);
+ if (error && vfsisloadable("union")) {
+ if (vfsload("union"))
+ err(EX_OSERR, "vfsload(union)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("union", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "union filesystem is not available");
+
+ if (mount(vfc.vfc_name, source, 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..c05730d
--- /dev/null
+++ b/sbin/mountd/Makefile
@@ -0,0 +1,10 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $Id: Makefile,v 1.6 1997/02/22 14:33:01 peter Exp $
+
+PROG= mountd
+CFLAGS+= -D_NEW_VFSCONF
+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..3601668
--- /dev/null
+++ b/sbin/mountd/exports.5
@@ -0,0 +1,255 @@
+.\" 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.3 (Berkeley) 3/29/95
+.\" $Id$
+.\"
+.Dd March 29, 1995
+.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
+.Fl 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 option 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..487dacb
--- /dev/null
+++ b/sbin/mountd/mountd.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $Id: mountd.8,v 1.9 1997/04/09 20:25:43 guido Exp $
+.\"
+.Dd April 28, 1995
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm /sbin/mountd
+.Op Fl 2nr
+.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 2
+The
+.Fl 2
+option allows the administrator to force clients to use only the
+version 2 NFS protocol to mount filesystems from this server.
+.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 will automatically clear the vfs.nfs.nfs_privport sysctl flag, which
+controls if the kernel will accept nfs requests form reserved ports only.
+.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
+.Pa /etc/exports ,
+the entire file system in which the swapfiles resides
+will have to be exported with the
+.Fl 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 \-s 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..b670303
--- /dev/null
+++ b/sbin/mountd/mountd.c
@@ -0,0 +1,2132 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /*not lint*/
+
+#ifndef lint
+/*static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; */
+static const char rcsid[] =
+ "$Id: mountd.c,v 1.20 1997/04/23 11:03:10 msmith 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 <sys/sysctl.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>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */
+
+#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 force_v2 = 0;
+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;
+ int mib[3];
+#ifdef __FreeBSD__
+ struct vfsconf vfc;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS support is not available in the running kernel");
+#endif /* __FreeBSD__ */
+
+ while ((c = getopt(argc, argv, "2dnr")) != -1)
+ switch (c) {
+ case '2':
+ force_v2 = 1;
+ break;
+ case 'n':
+ resvport_only = 0;
+ break;
+ case 'r':
+ dir_only = 0;
+ break;
+ case 'd':
+ debug = debug ? 0 : 1;
+ break;
+ default:
+ fprintf(stderr, "Usage: mountd [-d] [-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 (!resvport_only) {
+ mib[0] = CTL_VFS;
+ mib[1] = MOUNT_NFS;
+ mib[2] = NFS_NFSPRIVPORT;
+ if (sysctl(mib, 3, NULL, NULL, &resvport_only,
+ sizeof(resvport_only)) != 0 && errno != ENOENT) {
+ syslog(LOG_ERR, "sysctl: %m");
+ exit(1);
+ }
+ }
+
+ 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 (!force_v2)
+ if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "Can't register mount");
+ exit(1);
+ }
+ if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 1, 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 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 */
+ memset(&fhr.fhr_fh, 0, 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 "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;
+ struct msdosfs_args da;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, "mfs") ||
+ !strcmp(fsp->f_fstypename, "ufs") ||
+ !strcmp(fsp->f_fstypename, "msdos") ||
+ !strcmp(fsp->f_fstypename, "cd9660")) {
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_fstypename, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE,
+ (caddr_t)&targs) < 0)
+ syslog(LOG_ERR, "Can't delete exports for %s",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", exname);
+ exit(2);
+ }
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ fprintf(stderr,"Got line %s\n",line);
+ cp = line;
+ nextfield(&cp, &endcp);
+ if (*cp == '#')
+ goto nextline;
+
+ /*
+ * Set defaults.
+ */
+ has_host = FALSE;
+ anon = def_anon;
+ exflags = MNT_EXPORTED;
+ got_nondir = 0;
+ opt_flags = 0;
+ ep = (struct exportlist *)NULL;
+
+ /*
+ * Create new exports list entry
+ */
+ len = endcp-cp;
+ tgrp = grp = get_grp();
+ while (len > 0) {
+ if (len > RPCMNT_NAMELEN) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (*cp == '-') {
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (debug)
+ fprintf(stderr, "doing opt %s\n", cp);
+ got_nondir = 1;
+ if (do_opt(&cp, &endcp, ep, grp, &has_host,
+ &exflags, &anon)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (*cp == '/') {
+ savedc = *endcp;
+ *endcp = '\0';
+ if (check_dirpath(cp) &&
+ statfs(cp, &fsb) >= 0) {
+ if (got_nondir) {
+ syslog(LOG_ERR, "Dirs must be first");
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (ep) {
+ if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+ ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else {
+ /*
+ * See if this directory is already
+ * in the list.
+ */
+ ep = ex_search(&fsb.f_fsid);
+ if (ep == (struct exportlist *)NULL) {
+ ep = get_exp();
+ ep->ex_fs = fsb.f_fsid;
+ ep->ex_fsdir = (char *)
+ malloc(strlen(fsb.f_mntonname) + 1);
+ if (ep->ex_fsdir)
+ strcpy(ep->ex_fsdir,
+ fsb.f_mntonname);
+ else
+ out_of_mem();
+ if (debug)
+ fprintf(stderr,
+ "Making new ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ fprintf(stderr,
+ "Found ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ }
+
+ /*
+ * Add dirpath to export mount point.
+ */
+ dirp = add_expdir(&dirhead, cp, len);
+ dirplen = len;
+ } else {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ *endcp = savedc;
+ } else {
+ savedc = *endcp;
+ *endcp = '\0';
+ got_nondir = 1;
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Get the host or netgroup.
+ */
+ setnetgrent(cp);
+ netgrp = getnetgrent(&hst, &usr, &dom);
+ do {
+ if (has_host) {
+ grp->gr_next = get_grp();
+ grp = grp->gr_next;
+ }
+ if (netgrp) {
+ if (get_host(hst, grp, 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();
+ memset(ep, 0, 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();
+ memset(gp, 0, 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 = strchr(cpopt, ',')) {
+ *cpoptend++ = '\0';
+ if (cpoptarg = strchr(cpopt, '='))
+ *cpoptarg++ = '\0';
+ } else {
+ if (cpoptarg = strchr(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();
+ memmove(nhp, hp, sizeof(struct hostent));
+ i = strlen(hp->h_name)+1;
+ nhp->h_name = (char *)malloc(i);
+ if (nhp->h_name == (char *)NULL)
+ out_of_mem();
+ memmove(nhp->h_name, hp->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();
+ memmove(*naddrp, *addrp, 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();
+ memset(isoaddr, 0, sizeof(struct sockaddr_iso));
+ memmove(&isoaddr->siso_addr, isop, 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;
+ memset(&sin, 0, sizeof(sin));
+ memset(&imask, 0, 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 "ufs".
+ */
+ while (mount(fsb->f_fstypename, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (errno == EPERM) {
+ syslog(LOG_ERR,
+ "Can't change attributes for %s.\n", dirp);
+ return (1);
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ syslog(LOG_ERR, "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 (isdigit(*cp) && ((netaddr = inet_network(cp)) != -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 if ((np = getnetbyname(cp)) != NULL) {
+ inetaddr = inet_makeaddr(np->n_net, 0);
+ } 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 *host, *dirp, *cp;
+ 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) {
+ cp = str;
+ host = strsep(&cp, " \t\n");
+ dirp = strsep(&cp, " \t\n");
+ if (host == NULL || dirp == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ strncpy(mlp->ml_host, host, 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;
+ 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..d44fdba
--- /dev/null
+++ b/sbin/newfs/Makefile
@@ -0,0 +1,15 @@
+# @(#)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+= -D_NEW_VFSCONF
+CFLAGS+=-DMFS -DFSIRAND -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..f36bcfd
--- /dev/null
+++ b/sbin/newfs/mkfs.c
@@ -0,0 +1,1401 @@
+/*
+ * 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.11 (Berkeley) 5/3/95";
+#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>
+#include <stdlib.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 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 */
+#ifdef STANDALONE
+extern caddr_t malloc(), calloc();
+#endif
+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;
+#ifdef FSIRAND
+int randinit;
+#endif
+daddr_t alloc();
+static int charsperline();
+long calcipg();
+
+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;
+ off_t usedb;
+ 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
+#ifdef FSIRAND
+ if (!randinit) {
+ randinit = 1;
+ srandomdev();
+ }
+#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 */
+ }
+#ifdef STANDALONE
+ (void)malloc(0);
+#else
+ raise_data_limit();
+#endif
+ 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(membase == MAP_FAILED) {
+ perror("mmap");
+ exit(12);
+ }
+ close(fd);
+ } else {
+#ifndef STANDALONE
+ get_memleft();
+#endif
+ if (fssize * sectorsize > (memleft - 16384))
+ fssize = (memleft - 16384) / sectorsize;
+ if ((membase = malloc(fssize * sectorsize)) == 0) {
+ perror("malloc");
+ exit(13);
+ }
+ }
+ }
+ 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;
+ inospercg = calcipg(mincpg, bpcg, &usedb);
+ 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",
+ (int)((mincpg * (off_t)bpcg - usedb)
+ / 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 = calcipg(mincpg, bpcg, &usedb);
+ sblock.fs_ipg = inospercg;
+ }
+ if (inodecramped) {
+ if (inospercg > MAXIPG(&sblock)) {
+ printf("Minimum bytes per inode is %d\n",
+ (int)((mincpg * (off_t)bpcg - usedb)
+ / 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 = calcipg(sblock.fs_cpg, bpcg, &usedb);
+ while (sblock.fs_ipg > MAXIPG(&sblock)) {
+ inodecramped = 1;
+ sblock.fs_cpg -= mincpc;
+ sblock.fs_ipg = calcipg(sblock.fs_cpg, bpcg, &usedb);
+ }
+ /*
+ * 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 = calcipg(sblock.fs_cpg, bpcg, &usedb);
+ }
+ 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_firstfield);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_firstfield);
+ } else {
+ /* use dynamic table space */
+ sblock.fs_postbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_firstfield);
+ 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);
+ if (fscs == NULL) {
+ perror("calloc");
+ exit(31);
+ }
+ sblock.fs_magic = FS_MAGIC;
+ sblock.fs_rotdelay = rotdelay;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxcontig = maxcontig;
+ 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;
+#ifdef FSIRAND
+ sblock.fs_id[0] = (long)utime;
+ sblock.fs_id[1] = random();
+#endif
+
+ /*
+ * 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;
+ memset(&acg, 0, 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_firstfield);
+ 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_firstfield) > 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) {
+#ifdef FSIRAND
+ for (j = 0; j < sblock.fs_bsize / sizeof(struct dinode); j++)
+ zino[j].di_gen = random();
+#endif
+ 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) {
+ int32_t *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 = utime;
+ node.di_mtime = utime;
+ node.di_ctime = 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)
+ memmove(&buf[i], &olost_found_dir[2],
+ DIRSIZ(0, &olost_found_dir[2]));
+ } else {
+ (void)makedir(lost_found_dir, 2);
+ for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
+ memmove(&buf[i], &lost_found_dir[2],
+ 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]);
+ memmove(cp, &protodir[i], protodir[i].d_reclen);
+ cp += protodir[i].d_reclen;
+ spcleft -= protodir[i].d_reclen;
+ }
+ protodir[i].d_reclen = spcleft;
+ memmove(cp, &protodir[i], 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);
+ if (sblock.fs_contigsumsize > 0)
+ 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);
+}
+
+/*
+ * Calculate number of inodes per group.
+ */
+long
+calcipg(cpg, bpcg, usedbp)
+ long cpg;
+ long bpcg;
+ off_t *usedbp;
+{
+ int i;
+ long ipg, new_ipg, ncg, ncyl;
+ off_t usedb;
+
+ /*
+ * Prepare to scale by fssize / (number of sectors in cylinder groups).
+ * Note that fssize is still in sectors, not filesystem blocks.
+ */
+ ncyl = howmany(fssize, secpercyl);
+ ncg = howmany(ncyl, cpg);
+ /*
+ * Iterate a few times to allow for ipg depending on itself.
+ */
+ ipg = 0;
+ for (i = 0; i < 10; i++) {
+ usedb = (sblock.fs_iblkno + ipg / INOPF(&sblock))
+ * NSPF(&sblock) * (off_t)sectorsize;
+ new_ipg = (cpg * (quad_t)bpcg - usedb) / density * fssize
+ / ncg / secpercyl / cpg;
+ new_ipg = roundup(new_ipg, INOPB(&sblock));
+ if (new_ipg == ipg)
+ break;
+ ipg = new_ipg;
+ }
+ *usedbp = usedb;
+ return (ipg);
+}
+
+/*
+ * 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;
+
+#ifdef FSIRAND
+ ip->di_gen = random();
+#endif
+ 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);
+}
+
+#ifdef STANDALONE
+/*
+ * 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);
+ memmove(p, ptr, 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);
+ memset(base, 0, size);
+ return (base);
+}
+
+/*
+ * Replace libc function with one suited to our needs.
+ */
+free(ptr)
+ char *ptr;
+{
+
+ /* do not worry about it for now */
+}
+
+#else /* !STANDALONE */
+
+raise_data_limit()
+{
+ struct rlimit rlp;
+
+ if (getrlimit(RLIMIT_DATA, &rlp) < 0)
+ perror("getrlimit");
+ rlp.rlim_cur = rlp.rlim_max;
+ if (setrlimit(RLIMIT_DATA, &rlp) < 0)
+ perror("setrlimit");
+}
+
+get_memleft()
+{
+ char *base;
+ static u_long pgsz, i;
+ struct rlimit rlp;
+
+ base = sbrk(0);
+ pgsz = getpagesize() - 1;
+ i = ((u_long)(base + pgsz) &~ pgsz);
+ if (getrlimit(RLIMIT_DATA, &rlp) < 0)
+ perror("getrlimit");
+ memleft = rlp.rlim_cur - (u_long)base - i;
+}
+#endif /* STANDALONE */
+
+/*
+ * read a block from the file system
+ */
+rdfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+
+ if (mfs) {
+ memmove(bf, membase + bno * sectorsize, 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) {
+ memmove(membase + bno * sectorsize, bf, 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..e266d08
--- /dev/null
+++ b/sbin/newfs/newfs.8
@@ -0,0 +1,324 @@
+.\" 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.6 (Berkeley) 5/3/95
+.\"
+.Dd May 3, 1995
+.Dt NEWFS 8
+.Os BSD 4.2
+.Sh NAME
+.Nm newfs ,
+.Nm mount_mfs
+.Nd construct a new file system
+.Sh SYNOPSIS
+.Nm newfs
+.Op Fl NO
+.Op Fl S Ar sector-size
+.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 k Ar skew
+.Op Fl l Ar interleave
+.Op Fl m Ar free space
+.Op Fl n Ar rotational positions
+.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 n Ar rotational positions
+.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 .
+If the
+.Fl T
+flag is specified (see below), the special file is unused.
+Otherwise, it 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 T
+Uses information for the specified disk from
+.Pa /etc/disktab
+instead of trying to get the information from a disklabel.
+.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.
+.ne 1i
+.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.
+The default is 1024 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 every (4 * frag-size) 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
+defined by
+.Dv MINFREE
+from
+.Ao Pa ufs/ffs/fs.h Ac ,
+currently 8%.
+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 8%,
+the default is to optimize for space;
+if the value of minfree is greater than or equal to 8%,
+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.
+.ne 1i
+.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 EXAMPLES
+.Pp
+.Dl mount_mfs -s=20480 -o nosuid,nodev /dev/sd0b /tmp
+.Pp
+Mount a 10240 KB large memory file system on /tmp, with
+.Xr mount 8
+options nosuid and nodev.
+.Sh SEE ALSO
+.Xr fdformat 1 ,
+.Xr disktab 5 ,
+.Xr fs 5 ,
+.Xr disklabel 8 ,
+.Xr diskpart 8 ,
+.Xr dumpfs 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..71a3dfd
--- /dev/null
+++ b/sbin/newfs/newfs.c
@@ -0,0 +1,695 @@
+/*
+ * 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.13 (Berkeley) 5/1/95";
+#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/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/ufsmount.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;
+ int error;
+#endif
+
+ if (progname = strrchr(*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)) != -1)
+ 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 = strrchr(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 = strchr(argv[0], '\0') - 1;
+ if (cp == (char *)-1 ||
+ (*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;
+
+ error = getvfsbyname("mfs", &vfc);
+ if (error && vfsisloadable("mfs")) {
+ if (vfsload("mfs"))
+ fatal("vfsload(mfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("mfs", &vfc);
+ }
+ if (error)
+ fatal("mfs filesystem not available");
+
+ if (mount(vfc.vfc_name, 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..2a0889a
--- /dev/null
+++ b/sbin/newlfs/config.h
@@ -0,0 +1,140 @@
+/*-
+ * 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.3 (Berkeley) 5/24/95
+ */
+
+/*
+ * 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 4096
+#define DFL_LFSBLOCK_SHIFT 12
+#define DFL_LFSBLOCK_MASK 0xFFF
+
+#define DFL_LFSFRAG 4096
+#define DFL_LFS_FFMASK DFL_LFSBLOCK_MASK
+#define DFL_LFS_FFSHIFT DFL_LFSBLOCK_SHIFT
+#define DFL_LFS_FBMASK 0
+#define DFL_LFS_FBSHIFT 0
diff --git a/sbin/newlfs/extern.h b/sbin/newlfs/extern.h
new file mode 100644
index 0000000..27da835
--- /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.2 (Berkeley) 5/24/95
+ */
+
+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));
+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..59413fc
--- /dev/null
+++ b/sbin/newlfs/lfs.c
@@ -0,0 +1,684 @@
+/*-
+ * 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.5 (Berkeley) 5/24/95";
+#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_LFSFRAG,
+ /* 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 */ DFL_LFS_FFMASK,
+ /* lfs_ffshift */ DFL_LFS_FFSHIFT,
+ /* lfs_fbmask */ DFL_LFS_FBMASK,
+ /* lfs_fbshift */ DFL_LFS_FBSHIFT,
+ /* lfs_fsbtodb */ 0,
+ /* lfs_sushift */ 0,
+ /* lfs_sboffs */ { 0 },
+ /* lfs_sp */ NULL,
+ /* lfs_ivnode */ NULL,
+ /* lfs_seglock */ 0,
+ /* lfs_lockpid */ 0,
+ /* lfs_iocount */ 0,
+ /* lfs_writer */ 0,
+ /* lfs_dirops */ 0,
+ /* lfs_doifile */ 0,
+ /* lfs_nactive */ 0,
+ /* lfs_fmod */ 0,
+ /* lfs_clean */ 0,
+ /* lfs_ronly */ 0,
+ /* lfs_flags */ 0,
+ /* lfs_fsmnt */ { 0 },
+ /* lfs_pad */ { 0 },
+ /* lfs_cksum */ 0,
+ /* lfs_maxsymlinklen */ MAXSYMLINKLEN
+};
+
+
+struct direct lfs_root_dir[] = {
+ { ROOTINO, sizeof(struct direct), DT_DIR, 1, "."},
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".."},
+ { LFS_IFILE_INUM, sizeof(struct direct), DT_REG, 5, "ifile"},
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found"},
+};
+
+struct direct lfs_lf_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
+};
+
+static daddr_t make_dinode
+ __P((ino_t, struct dinode *, int, daddr_t, struct lfs *));
+static void make_dir __P(( void *, struct direct *, int));
+static void put __P((int, off_t, void *, size_t));
+
+int
+make_lfs(fd, lp, partp, minfree, block_size, frag_size, seg_size)
+ int fd;
+ struct disklabel *lp;
+ struct partition *partp;
+ int minfree;
+ int block_size;
+ int frag_size;
+ int seg_size;
+{
+ struct dinode *dip; /* Pointer to a disk inode */
+ struct dinode *dpagep; /* Pointer to page of disk inodes */
+ CLEANERINFO *cleaninfo; /* Segment cleaner information table */
+ FINFO file_info; /* File info structure in summary blocks */
+ IFILE *ifile; /* Pointer to array of ifile structures */
+ IFILE *ip; /* Pointer to array of ifile structures */
+ struct lfs *lfsp; /* Superblock */
+ SEGUSE *segp; /* Segment usage table */
+ SEGUSE *segtable; /* Segment usage table */
+ SEGSUM summary; /* Segment summary structure */
+ SEGSUM *sp; /* Segment summary pointer */
+ daddr_t last_sb_addr; /* Address of superblocks */
+ daddr_t last_addr; /* Previous segment address */
+ daddr_t sb_addr; /* Address of superblocks */
+ daddr_t seg_addr; /* Address of current segment */
+ void *ipagep; /* Pointer to the page we use to write stuff */
+ void *sump; /* Used to copy stuff into segment buffer */
+ u_long *block_array; /* Array of logical block nos to put in sum */
+ u_long blocks_used; /* Number of blocks in first segment */
+ u_long *dp; /* Used to computed checksum on data */
+ u_long *datasump; /* Used to computed checksum on data */
+ int block_array_size; /* How many entries in block array */
+ int bsize; /* Block size */
+ int fsize; /* Fragment size */
+ int db_per_fb; /* Disk blocks per file block */
+ int i, j;
+ int off; /* Offset at which to write */
+ int sb_interval; /* number of segs between super blocks */
+ int seg_seek; /* Seek offset for a segment */
+ int ssize; /* Segment size */
+ int sum_size; /* Size of the summary block */
+
+ lfsp = &lfs_default;
+
+ if (!(bsize = block_size))
+ bsize = DFL_LFSBLOCK;
+ if (!(fsize = frag_size))
+ fsize = DFL_LFSFRAG;
+ if (!(ssize = seg_size))
+ ssize = DFL_LFSSEG;
+
+ /* Modify parts of superblock overridden by command line arguments */
+ if (bsize != DFL_LFSBLOCK || fsize != DFL_LFSFRAG) {
+ 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 = fsize;
+ lfsp->lfs_bmask = bsize - 1;
+ lfsp->lfs_inopb = bsize / sizeof(struct dinode);
+ lfsp->lfs_ffmask = fsize - 1;
+ lfsp->lfs_ffshift = log2(fsize);
+ if (1 << lfsp->lfs_ffshift != fsize)
+ fatal("%d: frag size not a power of 2", fsize);
+ lfsp->lfs_frag = numfrags(lfsp, bsize);
+ lfsp->lfs_fbmask = lfsp->lfs_frag - 1;
+ lfsp->lfs_fbshift = log2(lfsp->lfs_frag);
+/* 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;
+ memset(dip, 0, 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 */
+ memset(ipagep, 0, 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;
+
+ memset(ipagep, 0, 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_magic = SS_MAGIC;
+ 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;
+ memmove(sump, &summary, sizeof(SEGSUM));
+ sump += sizeof(SEGSUM);
+
+ /* Now, add the ifile */
+ file_info.fi_nblocks = block_array_size;
+ file_info.fi_version = 1;
+ file_info.fi_lastlength = lfsp->lfs_bsize;
+ file_info.fi_ino = LFS_IFILE_INUM;
+
+ memmove(sump, &file_info, sizeof(FINFO) - sizeof(u_long));
+ sump += sizeof(FINFO) - sizeof(u_long);
+ memmove(sump, block_array, 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_lastlength = lfsp->lfs_bsize;
+ file_info.fi_ino = ROOTINO;
+ file_info.fi_blocks[0] = 0;
+ memmove(sump, &file_info, sizeof(FINFO));
+ sump += sizeof(FINFO);
+
+ /* Now, add the lost and found */
+ file_info.fi_ino = LOSTFOUNDINO;
+ memmove(sump, &file_info, 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;
+ sp->ss_magic = SS_MAGIC;
+
+ /* 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.
+ */
+
+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 = dip->di_mtime = dip->di_ctime = lfsp->lfs_tstamp;
+ dip->di_atimensec = dip->di_mtimensec = dip->di_ctimensec = 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]);
+ memmove(cp, &protodir[i], 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;
+ memmove(cp, &protodir[i], 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..8d7b62b
--- /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.5 (Berkeley) 5/24/95";
+#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 = strrchr(*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)) != -1)
+ 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 (strchr(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 = strchr(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, fsize, 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));
+ memset(blk, 0, 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..63abcbe
--- /dev/null
+++ b/sbin/newlfs/newlfs.8
@@ -0,0 +1,99 @@
+.\" 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
+.\" $Id$
+.\"
+.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 disklabel 8 ,
+.Xr diskpart 8 ,
+.Xr dumplfs 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..4d7da18
--- /dev/null
+++ b/sbin/nextboot/nextboot.8
@@ -0,0 +1,88 @@
+.\" $Id: nextboot.8,v 1.6 1997/02/22 14:32:31 peter 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-existent, 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..5461884
--- /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")) != -1) {
+ 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..6a144fa
--- /dev/null
+++ b/sbin/nfsd/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+= -D_NEW_VFSCONF
+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..8e13e72
--- /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.4 (Berkeley) 3/29/95
+.\"
+.Dd March 29, 1995
+.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..399b12e
--- /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.9 (Berkeley) 3/29/95";
+#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 <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#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;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if (vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ 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)) != -1)
+ 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..9928806
--- /dev/null
+++ b/sbin/nfsiod/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsiod
+CFLAGS+= -D_NEW_VFSCONF
+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..1b74554
--- /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$
+.\"
+.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..e2479fe
--- /dev/null
+++ b/sbin/nfsiod/nfsiod.c
@@ -0,0 +1,184 @@
+/*
+ * 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.4 (Berkeley) 5/3/95";
+#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;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if (vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if(error)
+ 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:")) != -1)
+ 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..e30ea08
--- /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}${BINDIR}/nologin
+
+.include <bsd.prog.mk>
diff --git a/sbin/nologin/nologin.5 b/sbin/nologin/nologin.5
new file mode 100644
index 0000000..70b7392
--- /dev/null
+++ b/sbin/nologin/nologin.5
@@ -0,0 +1,64 @@
+.\" 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
+.Pa /etc/nologin
+exists.
+Programs display the contents of
+.Pa /etc/nologin
+to the user and exit.
+.Sh SECURITY
+Ignored by
+.Xr 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..a396e9c
--- /dev/null
+++ b/sbin/nologin/nologin.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -p
+#
+# 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..a4a3685
--- /dev/null
+++ b/sbin/ping/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= ping
+MAN8= ping.8
+BINOWN= root
+BINMODE=4555
+COPTS+= -Wall -Wmissing-prototypes
+
+.include <bsd.prog.mk>
diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8
new file mode 100644
index 0000000..6539352
--- /dev/null
+++ b/sbin/ping/ping.8
@@ -0,0 +1,412 @@
+.\" 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: ping.8,v 1.10 1997/03/02 20:01:07 imp Exp $
+.\"
+.Dd March 1, 1997
+.Dt PING 8
+.Os BSD 4.3
+.Sh NAME
+.Nm ping
+.Nd send
+.Tn ICMP ECHO_REQUEST
+packets to network hosts
+.Sh SYNOPSIS
+.Nm
+.Op Fl QRadfnqrv
+.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
+.Bo
+.Ar host |
+.Op Fl L
+.Op Fl I Ar interface
+.Op Fl T Ar ttl
+.Ar mcast-group
+.Bc
+.Sh DESCRIPTION
+.Nm Ping
+uses the
+.Tn ICMP
+.No protocol Ap s mandatory
+.Tn ECHO_REQUEST
+datagram to elicit an
+.Tn ICMP ECHO_RESPONSE
+from a host or gateway.
+.Tn ECHO_REQUEST
+datagrams
+.Pq Dq pings
+have an IP and
+.Tn ICMP
+header, followed by a
+.Dq struct timeval
+and then an arbitrary number of
+.Dq pad
+bytes used to fill out the packet. The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Audible. Include a bell
+.Pq ASCII 0x07
+character in the output when any packet is received. This option is ignored
+if other format options are present.
+.It Fl c Ar count
+Stop after sending
+.Pq 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
+.Dq \&.
+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 I Ar interface
+Source multicast packets with the given interface address.
+This flag only applies if the ping destination is a multicast address.
+.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.
+Only the super-user may use this option.
+.It Fl L
+Suppress loopback of multicast packets.
+This flag only applies if the ping destination is a multicast address.
+.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
+.Dq 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.
+.No Don Ap 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;
+the
+.Xr traceroute 8
+command is usually better at determining the route packets take to a
+particular destination.
+Many hosts ignore or discard the
+.Tn RECORD_ROUTE
+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
+.Po
+e.g., after the interface was dropped by
+.Xr routed 8
+.Pc .
+.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 T Ar ttl
+Set the IP Time To Live for multicasted packets.
+This flag only applies if the ping destination is a multicast address.
+.It Fl v
+Verbose output.
+.Tn ICMP
+packets other than
+.Tn ECHO_RESPONSE
+that are received are listed.
+.El
+.Pp
+When using
+.Nm
+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
+.Dq 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
+.Pq 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
+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
+.Pq 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
+.Pq the Tn ICMP header .
+.Pp
+If the data space is at least eight bytes large,
+.Nm
+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 when pinging a unicast address,
+and seem to be caused by
+inappropriate link-level retransmissions.
+Duplicates may occur in many situations and are rarely
+.Pq if ever
+a good sign, although the presence of low levels of duplicates may not
+always be cause for alarm.
+Duplicates are expected when pinging a broadcast or multicast address,
+since they are not really duplicates but replies from different hosts
+to the same request.
+.Pp
+Damaged packets are obviously serious cause for alarm and often
+indicate broken hardware somewhere in the
+.Nm ping
+.No packet Ap s path
+.Pq in the network or in the hosts .
+.Sh TRYING DIFFERENT DATA PATTERNS
+The
+.Po
+inter
+.Pc Ns 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
+.No that doesn Ap t have sufficient
+.Dq transitions ,
+such as all ones or all zeros, or a pattern right at the edge, such as
+almost all zeros.
+.No It isn Ap t
+necessarily enough to specify a data pattern of all zeros
+.Pq 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
+.No can Ap 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 Ns .
+.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
+.Pq Bx 4.3 \ uses 30, Bx 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
+.Dq 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
+.Em from
+the remote system
+.Em to
+the
+.Nm 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 RETURN VALUES
+The
+.Nm
+command returns an exit status of zero if at least one response was
+heard from the specified
+.Ar host ;
+a status of two if the transmission was successful but no responses
+were received; or another value
+.Pq from Aq Pa sysexits.h
+if an error occurred.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr routed 8 ,
+.Xr traceroute 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
+.Sh AUTHORS
+The original
+.Nm
+command was written by Mike Muuss while at the US Army Ballistics
+Research Laboratory.
+.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.
+.No There Ap 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.
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
new file mode 100644
index 0000000..59136b1
--- /dev/null
+++ b/sbin/ping/ping.c
@@ -0,0 +1,1182 @@
+/*
+ * 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 const 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";
+*/
+static const char rcsid[] =
+ "$Id: ping.c,v 1.20 1997/03/03 09:50:21 imp Exp $";
+#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> /* NB: we rely on this for <sys/types.h> */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <arpa/inet.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 0x0001
+#define F_INTERVAL 0x0002
+#define F_NUMERIC 0x0004
+#define F_PINGFILLED 0x0008
+#define F_QUIET 0x0010
+#define F_RROUTE 0x0020
+#define F_SO_DEBUG 0x0040
+#define F_SO_DONTROUTE 0x0080
+#define F_VERBOSE 0x0100
+#define F_QUIET2 0x0200
+#define F_NOLOOP 0x0400
+#define F_MTTL 0x0800
+#define F_MIF 0x1000
+#define F_AUDIBLE 0x2000
+
+/*
+ * 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 */
+int uid; /* cached uid for micro-optimization */
+
+/* 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;
+
+static void fill(char *, char *);
+static u_short in_cksum(u_short *, int);
+static void catcher(int sig);
+static void check_status(void);
+static void finish(int) __dead2;
+static void pinger(void);
+static char *pr_addr(struct in_addr);
+static void pr_icmph(struct icmp *);
+static void pr_iph(struct ip *);
+static void pr_pack(char *, int, struct sockaddr_in *);
+static void pr_retip(struct ip *);
+static void status(int);
+static void tvsub(struct timeval *, struct timeval *);
+static void usage(const char *) __dead2;
+
+int
+main(argc, argv)
+ int argc;
+ char *const *argv;
+{
+ struct timeval timeout;
+ struct hostent *hp;
+ struct sockaddr_in *to;
+ struct termios ts;
+ register int i;
+ int ch, fdmask, hold, packlen, preload, sockerrno;
+ struct in_addr ifaddr;
+ unsigned char ttl, loop;
+ u_char *datap, *packet;
+ char *target, hnamebuf[MAXHOSTNAMELEN];
+ char *ep;
+ u_long ultmp;
+#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.
+ */
+ s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ sockerrno = errno;
+
+ setuid(getuid());
+ uid = getuid();
+
+ preload = 0;
+
+ datap = &outpack[8 + sizeof(struct timeval)];
+ while ((ch = getopt(argc, argv, "I:LQRT:c:adfi:l:np:qrs:v")) != -1) {
+ switch(ch) {
+ case 'a':
+ options |= F_AUDIBLE;
+ break;
+ case 'c':
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp)
+ errx(EX_USAGE,
+ "invalid count of packets to transmit: `%s'",
+ optarg);
+ npackets = ultmp;
+ break;
+ case 'd':
+ options |= F_SO_DEBUG;
+ break;
+ case 'f':
+ if (getuid()) {
+ errno = EPERM;
+ err(EX_NOPERM, "-f flag");
+ }
+ options |= F_FLOOD;
+ setbuf(stdout, (char *)NULL);
+ break;
+ case 'i': /* wait between sending packets */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > INT_MAX)
+ errx(EX_USAGE,
+ "invalid timing interval: `%s'", optarg);
+ options |= F_INTERVAL;
+ interval = ultmp;
+ break;
+ case 'I': /* multicast interface */
+ if (inet_aton(optarg, &ifaddr) == 0)
+ errx(EX_USAGE,
+ "invalid multicast interface: `%s'",
+ optarg);
+ options |= F_MIF;
+ break;
+ case 'l':
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > INT_MAX)
+ errx(EX_USAGE,
+ "invalid preload value: `%s'", optarg);
+ if (getuid()) {
+ errno = EPERM;
+ err(EX_NOPERM, "-l flag");
+ }
+ options |= F_FLOOD;
+ preload = ultmp;
+ break;
+ case 'L':
+ options |= F_NOLOOP;
+ loop = 0;
+ 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 */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (ultmp > MAXPACKET)
+ errx(EX_USAGE, "packet size too large: %lu",
+ ultmp);
+ if (*ep || ep == optarg || !ultmp)
+ errx(EX_USAGE, "invalid packet size: `%s'",
+ optarg);
+ datalen = ultmp;
+ break;
+ case 'T': /* multicast TTL */
+ ultmp = strtoul(optarg, &ep, 0);
+ if (*ep || ep == optarg || ultmp > 255)
+ errx(EX_USAGE, "invalid multicast TTL: `%s'",
+ optarg);
+ ttl = ultmp;
+ options |= F_MTTL;
+ break;
+ case 'v':
+ options |= F_VERBOSE;
+ break;
+ default:
+
+ usage(argv[0]);
+ }
+ }
+
+ if (argc - optind != 1)
+ usage(argv[0]);
+ target = argv[optind];
+
+ bzero((char *)&whereto, sizeof(struct sockaddr));
+ to = (struct sockaddr_in *)&whereto;
+ to->sin_family = AF_INET;
+ if (inet_aton(target, &to->sin_addr) != 0) {
+ hostname = target;
+ } else {
+ hp = gethostbyname2(target, AF_INET);
+ if (!hp)
+ errx(EX_NOHOST, "cannot resolve %s: %s",
+ target, hstrerror(h_errno));
+
+ to->sin_len = sizeof *to;
+ if (hp->h_length > sizeof(to->sin_addr))
+ errx(1,"gethostbyname2 returned an illegal address");
+ memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr);
+ (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
+ hnamebuf[(sizeof hnamebuf) - 1] = '\0';
+ hostname = hnamebuf;
+ }
+
+ if (options & F_FLOOD && options & F_INTERVAL)
+ errx(EX_USAGE, "-f and -i: incompatible options");
+
+ if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr)))
+ errx(EX_USAGE,
+ "-f flag cannot be used with multicast destination");
+ if (options & (F_MIF | F_NOLOOP | F_MTTL)
+ && !IN_MULTICAST(ntohl(to->sin_addr.s_addr)))
+ errx(EX_USAGE,
+ "-I, -L, -T flags cannot be used with unicast destination");
+
+ if (datalen >= sizeof(struct timeval)) /* can we time transfer */
+ timing = 1;
+ packlen = datalen + MAXIPLEN + MAXICMPLEN;
+ if (!(packet = (u_char *)malloc((size_t)packlen)))
+ err(EX_UNAVAILABLE, "malloc");
+
+ if (!(options & F_PINGFILLED))
+ for (i = 8; i < datalen; ++i)
+ *datap++ = i;
+
+ ident = getpid() & 0xFFFF;
+
+ if (s < 0) {
+ errno = sockerrno;
+ err(EX_OSERR, "socket");
+ }
+ 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)
+ err(EX_OSERR, "setsockopt IP_OPTIONS");
+#else
+ errx(EX_UNAVAILABLE,
+ "record route not available in this implementation");
+#endif /* IP_OPTIONS */
+ }
+
+ if (options & F_NOLOOP) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
+ sizeof(loop)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP");
+ }
+ }
+ if (options & F_MTTL) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_TTL");
+ }
+ }
+ if (options & F_MIF) {
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr,
+ sizeof(ifaddr)) < 0) {
+ err(EX_OSERR, "setsockopt IP_MULTICAST_IF");
+ }
+ }
+
+ /*
+ * 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. But beware: RFC 1122 allows hosts to ignore broadcast
+ * or multicast pings if they wish.
+ */
+ 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(to->sin_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) {
+ err(EX_OSERR, "sigaction");
+ }
+
+ 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(0); /* 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(0);
+ /* NOTREACHED */
+ exit(0); /* Make the compiler happy */
+}
+
+/*
+ * 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.
+ */
+static void
+catcher(int sig)
+{
+ 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.
+ */
+static void
+pinger(void)
+{
+ 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) {
+ warn("sendto");
+ } else {
+ warn("%s: partial write: %d of %d bytes",
+ 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!).
+ */
+static void
+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)
+ warn("packet too short (%d bytes) from %s", cc,
+ inet_ntoa(from->sin_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!)");
+ if (options & F_AUDIBLE)
+ (void)printf("\a");
+ /* 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.
+ *
+ * Only print all the error messages if we are running
+ * as root to avoid leaking information not normally
+ * available to those not running as root.
+ */
+#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) && uid == 0) ||
+ (!(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));
+ 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) {
+ printf("\t0.0.0.0");
+ } else {
+ struct in_addr ina;
+ ina.s_addr = ntohl(l);
+ printf("\t%s", pr_addr(ina));
+ }
+ 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) {
+ printf("\t0.0.0.0");
+ } else {
+ struct in_addr ina;
+ ina.s_addr = ntohl(l);
+ printf("\t%s", pr_addr(ina));
+ }
+ 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)
+ */
+u_short
+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.
+ */
+static void
+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.
+ */
+
+static void
+status(sig)
+ int sig;
+{
+ siginfo_p = 1;
+}
+
+static 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.
+ */
+static void
+finish(int sig)
+{
+ 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.
+ */
+static void
+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.
+ */
+static void
+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(" %1lx %04lx", (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.
+ */
+static char *
+pr_addr(ina)
+ struct in_addr ina;
+{
+ struct hostent *hp;
+ static char buf[16 + 3 + MAXHOSTNAMELEN];
+
+ if ((options & F_NUMERIC) ||
+ !(hp = gethostbyaddr((char *)&ina, 4, AF_INET)))
+ return inet_ntoa(ina);
+ else
+ (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
+ inet_ntoa(ina));
+ return(buf);
+}
+
+/*
+ * pr_retip --
+ * Dump some info on a returned (via ICMP) IP packet.
+ */
+static void
+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)));
+}
+
+static void
+fill(bp, patp)
+ char *bp, *patp;
+{
+ register int ii, jj, kk;
+ int pat[16];
+ char *cp;
+
+ for (cp = patp; *cp; cp++) {
+ if (!isxdigit(*cp))
+ errx(EX_USAGE,
+ "patterns must be specified as hex digits");
+
+ }
+ 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");
+ }
+}
+
+static void
+usage(argv0)
+ const char *argv0;
+{
+ if (strrchr(argv0,'/'))
+ argv0 = strrchr(argv0,'/') + 1;
+ fprintf(stderr,
+ "usage: %s [-QRadfnqrv] [-c count] [-i wait] [-l preload] "
+ "[-p pattern]\n [-s packetsize] "
+ "[host | [-L] [-I iface] [-T ttl] mcast-group]\n",
+ argv0);
+ exit(EX_USAGE);
+}
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..383467b
--- /dev/null
+++ b/sbin/quotacheck/preen.c
@@ -0,0 +1,384 @@
+/*
+ * 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.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ufs/ufs/dinode.h>
+
+#include <ctype.h>
+#include <fstab.h>
+#include <string.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;
+
+int nrun, ndisks;
+char hotroot;
+
+static void addpart __P((char *name, char *fsname, long auxdata));
+static struct disk *finddisk __P((char *name));
+static char *rawname __P((char *name));
+static int startdisk __P((struct disk *dk,
+ int (*checkit)(char *, char *, long, int)));
+static char *unrawname __P((char *name));
+
+int
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen;
+ int maxrun;
+ int (*docheck)(struct fstab *);
+ int (*chkit)(char *, char *, long, int);
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 ||
+ (passno == 1 && fsp->fs_passno == 1)) {
+ if ((name = blockcheck(fsp->fs_spec)) != 0) {
+ if ((sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0)) != 0)
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while ((ret = startdisk(nextdisk, chkit)) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while ((ret = startdisk(dk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while ((ret = startdisk(nextdisk, chkit)) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+static struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (len = strlen(name), p = name + len - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+
+ 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);
+}
+
+static 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;
+}
+
+static int
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)(char *, char *, long, int);
+{
+ 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(origname)
+ char *origname;
+{
+ struct stat stslash, stblock, stchar;
+ char *newname, *raw;
+ struct fstab *fsinfo;
+ int retried = 0, l;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (origname);
+ }
+ newname = origname;
+retry:
+ if (stat(newname, &stblock) < 0) {
+ perror(newname);
+ printf("Can't stat %s\n", newname);
+ return (origname);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(newname);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (origname);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (origname);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ newname = unrawname(origname);
+ retried++;
+ goto retry;
+ } else if ((stblock.st_mode & S_IFMT) == S_IFDIR && !retried) {
+ l = strlen(origname) - 1;
+ if (l > 0 && origname[l] == '/')
+ /* remove trailing slash */
+ origname[l] = '\0';
+ if(!(fsinfo=getfsfile(origname))) {
+ printf("Can't resolve %s to character special device",
+ origname);
+ return (0);
+ }
+ newname = fsinfo->fs_spec;
+ retried++;
+ goto retry;
+ }
+ /*
+ * Not a block or character device, just return name and
+ * let the user decide whether to use it.
+ */
+ return (origname);
+}
+
+static char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = strrchr(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);
+}
+
+static char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = strrchr(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..f70b6d1
--- /dev/null
+++ b/sbin/quotacheck/quotacheck.c
@@ -0,0 +1,648 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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>
+#include <err.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 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:")) != -1) {
+ 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)
+ errx(1, "%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, "%s\n%s\n",
+ "usage: 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(1, NULL);
+ 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) {
+ warnx("creating quota file %s", quotafile);
+#define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
+ (void) fchown(fileno(qfo), getuid(), getquotagid());
+ (void) fchmod(fileno(qfo), MODE);
+ } else {
+ warn("%s", quotafile);
+ return (1);
+ }
+ }
+ if ((qfi = fopen(quotafile, "r")) == NULL) {
+ warn("%s", quotafile);
+ (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) {
+ warn("%s: seek failed", quotafile);
+ 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(1, NULL);
+ 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)
+ errx(1, "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(1, NULL);
+ 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)
+ errx(1, "block %ld", bno);
+}
+
+#ifdef 0
+#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 */
+}
+#endif
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..90dcdf4
--- /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$
+.\"
+.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 disklabel 8 ,
+.Xr halt 8 ,
+.Xr reboot 8 ,
+.Xr shutdown 8
+.Sh BUGS
+The disklabel format used by this version of
+.Bx
+is quite
+different from that of other architectures.
+.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..9ed2642
--- /dev/null
+++ b/sbin/reboot/reboot.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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>
+#include <err.h>
+
+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")) != -1)
+ 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()) {
+ errno = EPERM;
+ err(1, NULL);
+ }
+
+ if (qflag) {
+ reboot(howto);
+ err(1, NULL);
+ }
+
+ /* 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(1, "SIGTSTP init");
+
+ /* 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(1, "SIGTERM processes");
+
+ /*
+ * 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;
+ errx(1, "%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);
+}
+
+#ifdef 0
+#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 */
+}
+#endif
diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile
new file mode 100644
index 0000000..65f0d55
--- /dev/null
+++ b/sbin/restore/Makefile
@@ -0,0 +1,22 @@
+# @(#)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=2555
+MAN8= restore.8
+MLINKS+=restore.8 rrestore.8
+.PATH: ${.CURDIR}/../dump
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && defined(MAKE_EBONES)
+.PATH: ${.CURDIR}/../../usr.bin/rlogin
+SRCS+= krcmd.c kcmd.c
+LDADD+= -lkrb -ldes
+CFLAGS+=-DKERBEROS
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c
new file mode 100644
index 0000000..f698e93
--- /dev/null
+++ b/sbin/restore/dirs.c
@@ -0,0 +1,770 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <protocols/dumprestore.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.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];
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int flags;
+};
+
+/*
+ * Definitions for library routines operating on directories.
+ */
+#undef DIRBLKSIZ
+#define DIRBLKSIZ 1024
+struct rstdirdesc {
+ int dd_fd;
+ long dd_loc;
+ long dd_size;
+ char dd_buf[DIRBLKSIZ];
+};
+
+/*
+ * Global variables for this file.
+ */
+static long seekpt;
+static FILE *df, *mf;
+static RST_DIR *dirp;
+static char dirfile[MAXPATHLEN] = "#"; /* No file */
+static char modefile[MAXPATHLEN] = "#"; /* 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;
+ int fd;
+
+ vprintf(stdout, "Extract directories from tape\n");
+ (void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate);
+ if (command != 'r' && command != 'R') {
+ (void *) strcat(dirfile, "-XXXXXX");
+ fd = mkstemp(dirfile);
+ } else
+ fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
+ if (fd != -1)
+ close(fd);
+ warn("%s - cannot create directory temporary\nfopen", dirfile);
+ done(1);
+ }
+ if (genmode != 0) {
+ (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
+ if (command != 'r' && command != 'R') {
+ (void *) strcat(modefile, "-XXXXXX");
+ fd = mkstemp(modefile);
+ } else
+ fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
+ if (fd != -1)
+ close(fd);
+ warn("%s - cannot create modefile\nfopen", modefile);
+ 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, sizeof(locname) - 1);
+ locname[sizeof(locname) - 1] = '\0';
+ (void) strncat(locname, "/", sizeof(locname) - strlen(locname));
+ namelen = strlen(locname);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = rst_readdir(dirp); /* "." */
+ if (dp != NULL && strcmp(dp->d_name, ".") == 0)
+ dp = rst_readdir(dirp); /* ".." */
+ else
+ fprintf(stderr, "Warning: `.' missing from directory %s\n",
+ pname);
+ if (dp != NULL && strcmp(dp->d_name, "..") == 0)
+ dp = rst_readdir(dirp); /* first real entry */
+ else
+ fprintf(stderr, "Warning: `..' missing from directory %s\n",
+ pname);
+ bpt = rst_telldir(dirp);
+ /*
+ * a zero inode signals end of directory
+ */
+ while (dp != NULL) {
+ locname[namelen] = '\0';
+ if (namelen + dp->d_namlen >= sizeof(locname)) {
+ fprintf(stderr, "%s%s: name exceeds %d char\n",
+ locname, dp->d_name, sizeof(locname) - 1);
+ } else {
+ (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
+ treescan(locname, dp->d_ino, todo);
+ rst_seekdir(dirp, bpt, itp->t_seekpt);
+ }
+ dp = rst_readdir(dirp);
+ bpt = rst_telldir(dirp);
+ }
+}
+
+/*
+ * Lookup a pathname which is always assumed to start from the ROOTINO.
+ */
+struct direct *
+pathsearch(pathname)
+ const char *pathname;
+{
+ ino_t ino;
+ struct direct *dp;
+ char *path, *name, buffer[MAXPATHLEN];
+
+ strcpy(buffer, pathname);
+ path = buffer;
+ ino = ROOTINO;
+ while (*path == '/')
+ path++;
+ dp = NULL;
+ while ((name = strsep(&path, "/")) != NULL && *name != NULL) {
+ if ((dp = searchdir(ino, name)) == NULL)
+ return (NULL);
+ ino = dp->d_ino;
+ }
+ return (dp);
+}
+
+/*
+ * Lookup the requested name in directory inum.
+ * Return its inode number if found, zero if it does not exist.
+ */
+static struct direct *
+searchdir(inum, name)
+ ino_t inum;
+ char *name;
+{
+ register struct direct *dp;
+ register struct inotab *itp;
+ int len;
+
+ itp = inotablookup(inum);
+ if (itp == NULL)
+ return (NULL);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ len = strlen(name);
+ do {
+ dp = rst_readdir(dirp);
+ if (dp == NULL)
+ return (NULL);
+ } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
+ return (dp);
+}
+
+/*
+ * Put the directory entries in the directory file
+ */
+static void
+putdir(buf, size)
+ char *buf;
+ long size;
+{
+ struct direct cvtbuf;
+ register struct odirect *odp;
+ struct odirect *eodp;
+ register struct direct *dp;
+ long loc, i;
+
+ if (cvtflag) {
+ eodp = (struct odirect *)&buf[size];
+ for (odp = (struct odirect *)buf; odp < eodp; odp++)
+ if (odp->d_ino != 0) {
+ dcvt(odp, &cvtbuf);
+ putent(&cvtbuf);
+ }
+ } else {
+ for (loc = 0; loc < size; ) {
+ dp = (struct direct *)(buf + loc);
+ if (Bcvt)
+ swabst((u_char *)"ls", (u_char *) dp);
+ if (oldinofmt && dp->d_ino != 0) {
+# if BYTE_ORDER == BIG_ENDIAN
+ if (Bcvt)
+ dp->d_namlen = dp->d_type;
+# else
+ if (!Bcvt)
+ dp->d_namlen = dp->d_type;
+# endif
+ 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;
+ }
+ memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
+ prev = dirloc;
+ dirloc += dp->d_reclen;
+}
+
+/*
+ * flush out a directory that is finished.
+ */
+static void
+flushent()
+{
+ ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
+ (void) fwrite(dirbuf, (int)dirloc, 1, df);
+ seekpt = ftell(df);
+ dirloc = 0;
+}
+
+static void
+dcvt(odp, ndp)
+ register struct odirect *odp;
+ register struct direct *ndp;
+{
+
+ memset(ndp, 0, (long)(sizeof *ndp));
+ ndp->d_ino = odp->d_ino;
+ 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)
+ return (NULL);
+ if (dp->d_ino >= maxino) {
+ dprintf(stderr, "corrupted directory: bad inum %d\n",
+ dp->d_ino);
+ continue;
+ }
+ return (dp);
+ }
+}
+
+/*
+ * Simulate the opening of a directory
+ */
+RST_DIR *
+rst_opendir(name)
+ const char *name;
+{
+ struct inotab *itp;
+ RST_DIR *dirp;
+ ino_t ino;
+
+ if ((ino = dirlookup(name)) > 0 &&
+ (itp = inotablookup(ino)) != NULL) {
+ dirp = opendirfile(dirfile);
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ return (dirp);
+ }
+ return (NULL);
+}
+
+/*
+ * In our case, there is nothing to do when closing a directory.
+ */
+void
+rst_closedir(dirp)
+ RST_DIR *dirp;
+{
+
+ (void)close(dirp->dd_fd);
+ free(dirp);
+ return;
+}
+
+/*
+ * Simulate finding the current offset in the directory.
+ */
+static long
+rst_telldir(dirp)
+ RST_DIR *dirp;
+{
+ return ((long)lseek(dirp->dd_fd,
+ (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
+}
+
+/*
+ * Open a directory file.
+ */
+static RST_DIR *
+opendirfile(name)
+ const char *name;
+{
+ register RST_DIR *dirp;
+ register int fd;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ return (NULL);
+ if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
+ (void)close(fd);
+ return (NULL);
+ }
+ dirp->dd_fd = fd;
+ dirp->dd_loc = 0;
+ return (dirp);
+}
+
+/*
+ * Set the mode, owner, and times for all new or changed directories
+ */
+void
+setdirmodes(flags)
+ int flags;
+{
+ FILE *mf;
+ struct modeinfo node;
+ struct entry *ep;
+ char *cp;
+
+ vprintf(stdout, "Set directory mode, owner, and times.\n");
+ if (command == 'r' || command == 'R')
+ (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
+ if (modefile[0] == '#') {
+ panic("modefile not defined\n");
+ fprintf(stderr, "directory mode, owner, and times not set\n");
+ return;
+ }
+ 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);
+ (void) chflags(cp, node.flags);
+ utimes(cp, node.timep);
+ ep->e_flags &= ~NEW;
+ }
+ }
+ if (ferror(mf))
+ panic("error setting directory modes\n");
+ (void) fclose(mf);
+}
+
+/*
+ * Generate a literal copy of a directory.
+ */
+int
+genliteraldir(name, ino)
+ char *name;
+ ino_t ino;
+{
+ register struct inotab *itp;
+ int ofile, dp, i, size;
+ char buf[BUFSIZ];
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ panic("Cannot find directory inode %d named %s\n", ino, name);
+ if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+ fprintf(stderr, "%s: ", name);
+ (void) fflush(stderr);
+ fprintf(stderr, "cannot create file: %s\n", strerror(errno));
+ return (FAIL);
+ }
+ rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
+ dp = dup(dirp->dd_fd);
+ for (i = itp->t_size; i > 0; i -= BUFSIZ) {
+ size = i < BUFSIZ ? i : BUFSIZ;
+ if (read(dp, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ done(1);
+ }
+ if (!Nflag && write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr,
+ "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ fprintf(stderr, "write: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ (void) close(dp);
+ (void) close(ofile);
+ return (GOOD);
+}
+
+/*
+ * Determine the type of an inode
+ */
+int
+inodetype(ino)
+ ino_t ino;
+{
+ struct inotab *itp;
+
+ itp = inotablookup(ino);
+ if (itp == NULL)
+ return (LEAF);
+ return (NODE);
+}
+
+/*
+ * Allocate and initialize a directory inode entry.
+ * If requested, save its pertinent mode, owner, and time info.
+ */
+static struct inotab *
+allocinotab(ino, dip, seekpt)
+ ino_t ino;
+ 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;
+ node.timep[0].tv_usec = dip->di_atimensec / 1000;
+ node.timep[1].tv_sec = dip->di_mtime;
+ node.timep[1].tv_usec = dip->di_mtimensec / 1000;
+ node.mode = dip->di_mode;
+ node.flags = dip->di_flags;
+ node.uid = dip->di_uid;
+ node.gid = dip->di_gid;
+ (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
+ return (itp);
+}
+
+/*
+ * Look up an inode in the table of directories
+ */
+static struct inotab *
+inotablookup(ino)
+ ino_t ino;
+{
+ register struct inotab *itp;
+
+ for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
+ if (itp->t_ino == ino)
+ return (itp);
+ return (NULL);
+}
+
+/*
+ * Clean up and exit
+ */
+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..c82f997
--- /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 *, int));
+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..600cc90
--- /dev/null
+++ b/sbin/restore/interactive.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)interactive.c 8.5 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.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 *, int, struct arglist *));
+struct dirent *glob_readdir __P((RST_DIR *dirp));
+static int glob_stat __P((const char *, struct stat *));
+static void mkentry __P((char *, struct direct *, struct afile *));
+static void printlist __P((char *, char *));
+
+/*
+ * Read and execute commands from the terminal.
+ */
+void
+runcmdshell()
+{
+ register struct entry *np;
+ ino_t ino;
+ struct arglist arglist;
+ char curdir[MAXPATHLEN];
+ char name[MAXPATHLEN];
+ char cmd[BUFSIZ];
+
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
+ arglist.glob.gl_opendir = (void *)rst_opendir;
+ arglist.glob.gl_readdir = (void *)glob_readdir;
+ arglist.glob.gl_closedir = (void *)rst_closedir;
+ arglist.glob.gl_lstat = glob_stat;
+ arglist.glob.gl_stat = glob_stat;
+ canon("/", curdir, sizeof(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, sizeof(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, size, ap)
+ char *curdir, *cmd, *name;
+ struct arglist *ap;
+ int size;
+{
+ 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, size);
+ } 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, size);
+ }
+ 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, len)
+ char *rawname, *canonname;
+ int len;
+{
+ 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, "./");
+ if (strlen(canonname) + strlen(rawname) >= len) {
+ fprintf(stderr, "canonname: not enough bufferspace\n");
+ done(1);
+ }
+
+ (void) strcat(canonname, rawname);
+ /*
+ * Eliminate multiple and trailing '/'s
+ */
+ for (cp = np = canonname; *np != '\0'; cp++) {
+ *cp = *np++;
+ while (*cp == '/' && *np == '/')
+ np++;
+ }
+ *cp = '\0';
+ if (*--cp == '/')
+ *cp = '\0';
+ /*
+ * Eliminate extraneous "." and ".." from pathnames.
+ */
+ for (np = canonname; *np != '\0'; ) {
+ np++;
+ cp = np;
+ while (*np != '/' && *np != '\0')
+ np++;
+ if (np - cp == 1 && *cp == '.') {
+ cp--;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
+ cp--;
+ while (cp > &canonname[1] && *--cp != '/')
+ /* find beginning of name */;
+ (void) strcpy(cp, np);
+ np = cp;
+ }
+ }
+}
+
+/*
+ * Do an "ls" style listing of a directory
+ */
+static void
+printlist(name, basename)
+ char *name;
+ char *basename;
+{
+ register struct afile *fp, *list, *listp;
+ register struct direct *dp;
+ struct afile single;
+ RST_DIR *dirp;
+ int entries, len, namelen;
+ char locname[MAXPATHLEN + 1];
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+ (!vflag && dp->d_ino == WINO))
+ return;
+ if ((dirp = rst_opendir(name)) == NULL) {
+ entries = 1;
+ list = &single;
+ mkentry(name, dp, list);
+ len = strlen(basename) + 1;
+ if (strlen(name) - len > single.len) {
+ freename(single.fname);
+ single.fname = savename(&name[len]);
+ single.len = strlen(single.fname);
+ }
+ } else {
+ entries = 0;
+ while (dp = rst_readdir(dirp))
+ entries++;
+ rst_closedir(dirp);
+ list = (struct afile *)malloc(entries * sizeof(struct afile));
+ if (list == NULL) {
+ fprintf(stderr, "ls: out of memory\n");
+ return;
+ }
+ if ((dirp = rst_opendir(name)) == NULL)
+ panic("directory reopen failed\n");
+ fprintf(stderr, "%s:\n", name);
+ entries = 0;
+ listp = list;
+ (void) strncpy(locname, name, MAXPATHLEN);
+ (void) strncat(locname, "/", MAXPATHLEN);
+ namelen = strlen(locname);
+ while (dp = rst_readdir(dirp)) {
+ if (dp == NULL)
+ break;
+ if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
+ continue;
+ if (!vflag && (dp->d_ino == WINO ||
+ strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0))
+ continue;
+ locname[namelen] = '\0';
+ if (namelen + dp->d_namlen >= MAXPATHLEN) {
+ fprintf(stderr, "%s%s: name exceeds %d char\n",
+ locname, dp->d_name, MAXPATHLEN);
+ } else {
+ (void) strncat(locname, dp->d_name,
+ (int)dp->d_namlen);
+ mkentry(locname, dp, listp++);
+ entries++;
+ }
+ }
+ rst_closedir(dirp);
+ if (entries == 0) {
+ fprintf(stderr, "\n");
+ free(list);
+ return;
+ }
+ qsort((char *)list, entries, sizeof(struct afile), fcmp);
+ }
+ formatf(list, entries);
+ if (dirp != NULL) {
+ for (fp = listp - 1; fp >= list; fp--)
+ freename(fp->fname);
+ fprintf(stderr, "\n");
+ free(list);
+ }
+}
+
+/*
+ * Read the contents of a directory.
+ */
+static void
+mkentry(name, dp, fp)
+ char *name;
+ struct direct *dp;
+ register struct afile *fp;
+{
+ char *cp;
+ struct entry *np;
+
+ fp->fnum = dp->d_ino;
+ fp->fname = savename(dp->d_name);
+ for (cp = fp->fname; *cp; cp++)
+ if (!vflag && (*cp < ' ' || *cp >= 0177))
+ *cp = '?';
+ fp->len = cp - fp->fname;
+ if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
+ fp->prefix = '^';
+ else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
+ fp->prefix = '*';
+ else
+ fp->prefix = ' ';
+ switch(dp->d_type) {
+
+ default:
+ fprintf(stderr, "Warning: undefined file type %d\n",
+ dp->d_type);
+ /* fall through */
+ case DT_REG:
+ fp->postfix = ' ';
+ break;
+
+ case DT_LNK:
+ fp->postfix = '@';
+ break;
+
+ case DT_FIFO:
+ case DT_SOCK:
+ fp->postfix = '=';
+ break;
+
+ case DT_CHR:
+ case DT_BLK:
+ fp->postfix = '#';
+ break;
+
+ case DT_WHT:
+ fp->postfix = '%';
+ break;
+
+ case DT_UNKNOWN:
+ case DT_DIR:
+ if (inodetype(dp->d_ino) == NODE)
+ fp->postfix = '/';
+ else
+ fp->postfix = ' ';
+ break;
+ }
+ 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 (!vflag && dp->d_ino == WINO)
+ continue;
+ if (dflag || TSTINO(dp->d_ino, dumpmap))
+ break;
+ }
+ if (dp == NULL)
+ return (NULL);
+ adirent.d_fileno = dp->d_ino;
+ adirent.d_namlen = dp->d_namlen;
+ memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
+ return (&adirent);
+}
+
+/*
+ * Return st_mode information in response to stat or lstat calls
+ */
+static int
+glob_stat(name, stp)
+ const char *name;
+ struct stat *stp;
+{
+ register struct direct *dp;
+
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
+ (!vflag && dp->d_ino == WINO))
+ return (-1);
+ if (inodetype(dp->d_ino) == NODE)
+ stp->st_mode = IFDIR;
+ else
+ stp->st_mode = IFREG;
+ return (0);
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+static int
+fcmp(f1, f2)
+ register const void *f1, *f2;
+{
+ return (strcmp(((struct afile *)f1)->fname,
+ ((struct afile *)f2)->fname));
+}
+
+/*
+ * respond to interrupts
+ */
+void
+onintr(signo)
+ int signo;
+{
+ if (command == 'i' && runshell)
+ longjmp(reset, 1);
+ if (reply("restore interrupted, continue") == FAIL)
+ done(1);
+}
diff --git a/sbin/restore/main.c b/sbin/restore/main.c
new file mode 100644
index 0000000..6cb87d8
--- /dev/null
+++ b/sbin/restore/main.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <protocols/dumprestore.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
+int dokerberos = 0;
+char command = '\0';
+long dumpnum = 1;
+long volno = 0;
+long ntrec;
+char *dumpmap;
+char *usedinomap;
+ino_t maxino;
+time_t dumptime;
+time_t dumpdate;
+FILE *terminal;
+
+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;
+ char *symtbl = "./restoresymtable";
+ char *p, name[MAXPATHLEN];
+
+ /* Temp files should *not* be readable. We set permissions later. */
+ (void) umask(077);
+
+ if (argc < 2)
+ usage();
+
+ if ((inputdev = getenv("TAPE")) == NULL)
+ inputdev = _PATH_DEFTAPE;
+ obsolete(&argc, &argv);
+#ifdef KERBEROS
+#define optlist "b:cdf:hikmNRrs:tvxy"
+#else
+#define optlist "b:cdf:himNRrs:tvxy"
+#endif
+ while ((ch = getopt(argc, argv, optlist)) != -1)
+ 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;
+#ifdef KERBEROS
+ case 'k':
+ dokerberos = 1;
+ break;
+#endif
+ 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, sizeof(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, sizeof(name));
+ ino = dirlookup(name);
+ if (ino == 0)
+ continue;
+ if (mflag)
+ pathcheck(name);
+ treescan(name, ino, addfile);
+ }
+ createfiles();
+ createlinks();
+ setdirmodes(0);
+ if (dflag)
+ checkrestore();
+ break;
+ }
+ done(0);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
+ "restore -i [-chkmvy] [-b blocksize] [-f file] [-s fileno]",
+ "restore -r [-ckvy] [-b blocksize] [-f file] [-s fileno]",
+ "restore -R [-ckvy] [-b blocksize] [-f file] [-s fileno]",
+ "restore -x [-chkmvy] [-b blocksize] [-f file] [-s fileno] [file ...]",
+ "restore -t [-chkvy] [-b blocksize] [-f file] [-s fileno] [file ...]");
+ done(1);
+}
+
+/*
+ * obsolete --
+ * Change set of key letters and ordered arguments into something
+ * getopt(3) will like.
+ */
+static void
+obsolete(argcp, argvp)
+ int *argcp;
+ char **argvp[];
+{
+ int argc, flags;
+ char *ap, **argv, *flagsp, **nargv, *p;
+
+ /* Setup. */
+ argv = *argvp;
+ argc = *argcp;
+
+ /* Return if no arguments or first argument has leading dash. */
+ ap = argv[1];
+ if (argc == 1 || *ap == '-')
+ return;
+
+ /* Allocate space for new arguments. */
+ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL ||
+ (p = flagsp = malloc(strlen(ap) + 2)) == NULL)
+ err(1, NULL);
+
+ *nargv++ = *argv;
+ argv += 2, argc -= 2;
+
+ for (flags = 0; *ap; ++ap) {
+ switch (*ap) {
+ case 'b':
+ case 'f':
+ case 's':
+ if (*argv == NULL) {
+ warnx("option requires an argument -- %c", *ap);
+ usage();
+ }
+ if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL)
+ err(1, NULL);
+ nargv[0][0] = '-';
+ nargv[0][1] = *ap;
+ (void)strcpy(&nargv[0][2], *argv);
+ ++argv;
+ ++nargv;
+ break;
+ default:
+ if (!flags) {
+ *p++ = '-';
+ flags = 1;
+ }
+ *p++ = *ap;
+ break;
+ }
+ }
+
+ /* Terminate flags. */
+ if (flags) {
+ *p = '\0';
+ *nargv++ = flagsp;
+ }
+
+ /* Copy remaining arguments. */
+ while (*nargv++ = *argv++);
+
+ /* Update argument count. */
+ *argcp = nargv - *argvp - 1;
+}
diff --git a/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..97cae95
--- /dev/null
+++ b/sbin/restore/restore.8
@@ -0,0 +1,438 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)restore.8 8.4 (Berkeley) 5/1/95
+.\" $Id: restore.8,v 1.10 1997/03/11 12:55:16 peter Exp $
+.\"
+.Dd May 1, 1995
+.Dt RESTORE 8
+.Os BSD 4
+.Sh NAME
+.Nm restore
+.Nd "restore files or file systems from backups made with dump"
+.Sh SYNOPSIS
+.Nm restore
+.Fl i
+.Op Fl chkmvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Nm restore
+.Fl R
+.Op Fl ckvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Nm restore
+.Fl r
+.Op Fl ckvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Nm restore
+.Fl t
+.Op Fl chkvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op file ...
+.Nm restore
+.Fl x
+.Op Fl chkmvy
+.Op Fl b Ar blocksize
+.Op Fl f Ar file
+.Op Fl s Ar fileno
+.Op file ...
+.Pp
+.in -\\n(iSu
+(The
+.Bx 4.3
+option syntax is implemented for backward compatibility, but
+is not documented here.)
+.Sh DESCRIPTION
+The
+.Nm restore
+command performs the inverse function of
+.Xr dump 8 .
+A full backup of a file system may be restored and
+subsequent incremental backups layered on top of it.
+Single files and
+directory subtrees may be restored from full or partial
+backups.
+.Nm Restore
+works across a network;
+to do this see the
+.Fl f
+flag described below.
+Other arguments to the command are file or directory
+names specifying the files that are to be restored.
+Unless the
+.Fl h
+flag is specified (see below),
+the appearance of a directory name refers to
+the files and (recursively) subdirectories of that directory.
+.Pp
+Exactly one of the following flags is required:
+.Bl -tag -width Ds
+.It Fl i
+This mode allows interactive restoration of files from a dump.
+After reading in the directory information from the dump,
+.Nm restore
+provides a shell like interface that allows the user to move
+around the directory tree selecting files to be extracted.
+The available commands are given below;
+for those commands that require an argument,
+the default is the current directory.
+.Bl -tag -width Fl
+.It Ic add Op Ar arg
+The current directory or specified argument is added to the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+added to the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+Files that are on the extraction list are prepended with a ``*''
+when they are listed by
+.Ic ls .
+.It Ic \&cd Ar arg
+Change the current working directory to the specified argument.
+.It Ic delete Op Ar arg
+The current directory or specified argument is deleted from the list of
+files to be extracted.
+If a directory is specified, then it and all its descendents are
+deleted from the extraction list
+(unless the
+.Fl h
+flag is specified on the command line).
+The most expedient way to extract most of the files from a directory
+is to add the directory to the extraction list and then delete
+those files that are not needed.
+.It Ic extract
+All the files that are on the extraction list are extracted
+from the dump.
+.Nm Restore
+will ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.It Ic help
+List a summary of the available commands.
+.It Ic \&ls Op Ar arg
+List the current or specified directory.
+Entries that are directories are appended with a ``/''.
+Entries that have been marked for extraction are prepended with a ``*''.
+If the verbose
+flag is set the inode number of each entry is also listed.
+.It Ic pwd
+Print the full pathname of the current working directory.
+.It Ic quit
+Restore immediately exits,
+even if the extraction list is not empty.
+.It Ic setmodes
+All the directories that have been added to the extraction list
+have their owner, modes, and times set;
+nothing is extracted from the dump.
+This is useful for cleaning up after a restore has been prematurely aborted.
+.It Ic verbose
+The sense of the
+.Fl v
+flag is toggled.
+When set, the verbose flag causes the
+.Ic ls
+command to list the inode numbers of all entries.
+It also causes
+.Nm restore
+to print out information about each file as it is extracted.
+.El
+.It Fl R
+.Nm Restore
+requests a particular tape of a multi volume set on which to restart
+a full restore
+(see the
+.Fl r
+flag below).
+This is useful if the restore has been interrupted.
+.It Fl r
+Restore (rebuild a file system).
+The target file system should be made pristine with
+.Xr newfs 8 ,
+mounted and the user
+.Xr cd Ns 'd
+into the pristine file system
+before starting the restoration of the initial level 0 backup. If the
+level 0 restores successfully, the
+.Fl r
+flag may be used to restore
+any necessary incremental backups on top of the level 0.
+The
+.Fl r
+flag precludes an interactive file extraction and can be
+detrimental to one's health if not used carefully (not to mention
+the disk). An example:
+.Bd -literal -offset indent
+newfs /dev/rrp0g eagle
+mount /dev/rp0g /mnt
+cd /mnt
+
+restore rf /dev/rst8
+.Ed
+.Pp
+Note that
+.Nm restore
+leaves a file
+.Pa restoresymtable
+in the root directory to pass information between incremental
+restore passes.
+This file should be removed when the last incremental has been
+restored.
+.Pp
+.Nm Restore ,
+in conjunction with
+.Xr newfs 8
+and
+.Xr dump 8 ,
+may be used to modify file system parameters
+such as size or block size.
+.It Fl t
+The names of the specified files are listed if they occur
+on the backup.
+If no file argument is given,
+then the root directory is listed,
+which results in the entire content of the
+backup being listed,
+unless the
+.Fl h
+flag has been specified.
+Note that the
+.Fl t
+flag replaces the function of the old
+.Xr dumpdir 8
+program.
+.ne 1i
+.It Fl x
+The named files are read from the given media.
+If a named file matches a directory whose contents
+are on the backup
+and the
+.Fl h
+flag is not specified,
+the directory is recursively extracted.
+The owner, modification time,
+and mode are restored (if possible).
+If no file argument is given,
+then the root directory is extracted,
+which results in the entire content of the
+backup being extracted,
+unless the
+.Fl h
+flag has been specified.
+.El
+.Pp
+The following additional options may be specified:
+.Bl -tag -width Ds
+.It Fl b Ar blocksize
+The number of kilobytes per dump record.
+If the
+.Fl b
+option is not specified,
+.Nm restore
+tries to determine the media block size dynamically.
+.It Fl c
+Normally,
+.Nm restore
+will try to determine dynamically whether the dump was made from an
+old (pre-4.4) or new format file sytem. The
+.Fl c
+flag disables this check, and only allows reading a dump in the old
+format.
+.It Fl f Ar file
+Read the backup from
+.Ar file ;
+.Ar file
+may be a special device file
+like
+.Pa /dev/rmt12
+(a tape drive),
+.Pa /dev/rsd1c
+(a disk drive),
+an ordinary file,
+or
+.Ql Fl
+(the standard input).
+If the name of the file is of the form
+.Dq host:file ,
+or
+.Dq user@host:file ,
+.Nm restore
+reads from the named file on the remote host using
+.Xr rmt 8 .
+.Pp
+.It Fl k
+Use Kerberos authentication when contacting the remote tape server.
+(Only available if this options was enabled when
+.Nm restore
+was compiled.)
+.Pp
+.It Fl h
+Extract the actual directory,
+rather than the files that it references.
+This prevents hierarchical restoration of complete subtrees
+from the dump.
+.It Fl m
+Extract by inode numbers rather than by file name.
+This is useful if only a few files are being extracted,
+and one wants to avoid regenerating the complete pathname
+to the file.
+.It Fl s Ar fileno
+Read from the specified
+.Ar fileno
+on a multi-file tape.
+File numbering starts at 1.
+.It Fl v
+Normally
+.Nm restore
+does its work silently.
+The
+.Fl v
+(verbose)
+flag causes it to type the name of each file it treats
+preceded by its file type.
+.It Fl y
+Do not ask the user whether to abort the restore in the event of an error.
+Always try to skip over the bad block(s) and continue.
+.El
+.Sh DIAGNOSTICS
+Complaints if it gets a read error.
+If
+.Fl y
+has been specified, or the user responds
+.Ql y ,
+.Nm restore
+will attempt to continue the restore.
+.Pp
+If a backup was made using more than one tape volume,
+.Nm restore
+will notify the user when it is time to mount the next volume.
+If the
+.Fl x
+or
+.Fl i
+flag has been specified,
+.Nm restore
+will also ask which volume the user wishes to mount.
+The fastest way to extract a few files is to
+start with the last volume, and work towards the first volume.
+.Pp
+There are numerous consistency checks that can be listed by
+.Nm restore .
+Most checks are self-explanatory or can ``never happen''.
+Common errors are given below.
+.Pp
+.Bl -tag -width Ds -compact
+.It Converting to new file system format.
+A dump tape created from the old file system has been loaded.
+It is automatically converted to the new file system format.
+.Pp
+.It <filename>: not found on tape
+The specified file name was listed in the tape directory,
+but was not found on the tape.
+This is caused by tape read errors while looking for the file,
+and from using a dump tape created on an active file system.
+.Pp
+.It expected next file <inumber>, got <inumber>
+A file that was not listed in the directory showed up.
+This can occur when using a dump created on an active file system.
+.Pp
+.It Incremental dump too low
+When doing incremental restore,
+a dump that was written before the previous incremental dump,
+or that has too low an incremental level has been loaded.
+.Pp
+.It Incremental dump too high
+When doing incremental restore,
+a dump that does not begin its coverage where the previous incremental
+dump left off,
+or that has too high an incremental level has been loaded.
+.Pp
+.It Tape read error while restoring <filename>
+.It Tape read error while skipping over inode <inumber>
+.It Tape read error while trying to resynchronize
+A tape (or other media) read error has occurred.
+If a file name is specified,
+then its contents are probably partially wrong.
+If an inode is being skipped or the tape is trying to resynchronize,
+then no extracted files have been corrupted,
+though files may not be found on the tape.
+.Pp
+.It resync restore, skipped <num> blocks
+After a dump read error,
+.Nm restore
+may have to resynchronize itself.
+This message lists the number of blocks that were skipped over.
+.El
+.Sh FILES
+.Bl -tag -width "./restoresymtable" -compact
+.It Pa /dev/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 mount 8 ,
+.Xr newfs 8 ,
+.Xr rmt 8
+.Sh BUGS
+.Nm Restore
+can get confused when doing incremental restores from
+dumps that were made on active file systems.
+.Pp
+A level zero dump must be done after a full restore.
+Because restore runs in user code,
+it has no control over inode allocation;
+thus a full dump must be done to get a new set of directories
+reflecting the new inode numbering,
+even though the contents of the files is unchanged.
+.Pp
+To do a network restore, you have to run restore as root. This is due
+to the previous security history of dump and restore. (restore is
+written to be setuid root, but we are not certain all bugs are gone
+from the restore code - run setuid at your own risk.)
+.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..936d857
--- /dev/null
+++ b/sbin/restore/restore.c
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)restore.c 8.3 (Berkeley) 9/13/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 (ino == WINO && command == 'i' && !vflag)
+ return (descend);
+ if (!mflag) {
+ (void) sprintf(buf, "./%u", ino);
+ name = buf;
+ if (type == NODE) {
+ (void) genliteraldir(name, ino);
+ return (descend);
+ }
+ }
+ ep = lookupino(ino);
+ if (ep != NULL) {
+ if (strcmp(name, myname(ep)) == 0) {
+ ep->e_flags |= NEW;
+ return (descend);
+ }
+ type |= LINK;
+ }
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW;
+ return (descend);
+}
+
+/*
+ * This is used by the 'i' option to undo previous requests made by addfile.
+ * Delete entries from the request queue.
+ */
+/* ARGSUSED */
+long
+deletefile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ long descend = hflag ? GOOD : FAIL;
+ struct entry *ep;
+
+ if (TSTINO(ino, dumpmap) == 0)
+ return (descend);
+ ep = lookupname(name);
+ if (ep != NULL) {
+ ep->e_flags &= ~NEW;
+ ep->e_flags |= REMOVED;
+ if (ep->e_type != NODE)
+ freeentry(ep);
+ }
+ return (descend);
+}
+
+/*
+ * The following four routines implement the incremental
+ * restore algorithm. The first removes old entries, the second
+ * does renames and calculates the extraction list, the third
+ * cleans up link names missed by the first two, and the final
+ * one deletes old directories.
+ *
+ * Directories cannot be immediately deleted, as they may have
+ * other files in them which need to be moved out first. As
+ * directories to be deleted are found, they are put on the
+ * following deletion list. After all deletions and renames
+ * are done, this list is actually deleted.
+ */
+static struct entry *removelist;
+
+/*
+ * Remove invalid whiteouts from the old tree.
+ * Remove unneeded leaves from the old tree.
+ * Remove directories from the lookup chains.
+ */
+void
+removeoldleaves()
+{
+ register struct entry *ep, *nextep;
+ register ino_t i, mydirino;
+
+ vprintf(stdout, "Mark entries to be removed.\n");
+ if (ep = lookupino(WINO)) {
+ vprintf(stdout, "Delete whiteouts\n");
+ for ( ; ep != NULL; ep = nextep) {
+ nextep = ep->e_links;
+ mydirino = ep->e_parent->e_ino;
+ /*
+ * We remove all whiteouts that are in directories
+ * that have been removed or that have been dumped.
+ */
+ if (TSTINO(mydirino, usedinomap) &&
+ !TSTINO(mydirino, dumpmap))
+ continue;
+ delwhiteout(ep);
+ freeentry(ep);
+ }
+ }
+ for (i = ROOTINO + 1; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ if (TSTINO(i, usedinomap))
+ continue;
+ for ( ; ep != NULL; ep = ep->e_links) {
+ dprintf(stdout, "%s: REMOVE\n", myname(ep));
+ if (ep->e_type == LEAF) {
+ removeleaf(ep);
+ freeentry(ep);
+ } else {
+ mktempname(ep);
+ deleteino(ep->e_ino);
+ ep->e_next = removelist;
+ removelist = ep;
+ }
+ }
+ }
+}
+
+/*
+ * For each directory entry on the incremental tape, determine which
+ * category it falls into as follows:
+ * KEEP - entries that are to be left alone.
+ * NEW - new entries to be added.
+ * EXTRACT - files that must be updated with new contents.
+ * LINK - new links to be added.
+ * Renames are done at the same time.
+ */
+long
+nodeupdates(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ register struct entry *ep, *np, *ip;
+ long descend = GOOD;
+ int lookuptype = 0;
+ int key = 0;
+ /* key values */
+# define ONTAPE 0x1 /* inode is on the tape */
+# define INOFND 0x2 /* inode already exists */
+# define NAMEFND 0x4 /* name already exists */
+# define MODECHG 0x8 /* mode of inode changed */
+
+ /*
+ * This routine is called once for each element in the
+ * directory hierarchy, with a full path name.
+ * The "type" value is incorrectly specified as LEAF for
+ * directories that are not on the dump tape.
+ *
+ * Check to see if the file is on the tape.
+ */
+ if (TSTINO(ino, dumpmap))
+ key |= ONTAPE;
+ /*
+ * Check to see if the name exists, and if the name is a link.
+ */
+ np = lookupname(name);
+ if (np != NULL) {
+ key |= NAMEFND;
+ ip = lookupino(np->e_ino);
+ if (ip == NULL)
+ panic("corrupted symbol table\n");
+ if (ip != np)
+ lookuptype = LINK;
+ }
+ /*
+ * Check to see if the inode exists, and if one of its links
+ * corresponds to the name (if one was found).
+ */
+ ip = lookupino(ino);
+ if (ip != NULL) {
+ key |= INOFND;
+ for (ep = ip->e_links; ep != NULL; ep = ep->e_links) {
+ if (ep == np) {
+ ip = ep;
+ break;
+ }
+ }
+ }
+ /*
+ * If both a name and an inode are found, but they do not
+ * correspond to the same file, then both the inode that has
+ * been found and the inode corresponding to the name that
+ * has been found need to be renamed. The current pathname
+ * is the new name for the inode that has been found. Since
+ * all files to be deleted have already been removed, the
+ * named file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number.
+ */
+ if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ dprintf(stdout, "name/inode conflict, mktempname %s\n",
+ myname(np));
+ mktempname(np);
+ }
+ np = NULL;
+ key &= ~NAMEFND;
+ }
+ if ((key & ONTAPE) &&
+ (((key & INOFND) && ip->e_type != type) ||
+ ((key & NAMEFND) && np->e_type != type)))
+ key |= MODECHG;
+
+ /*
+ * Decide on the disposition of the file based on its flags.
+ * Note that we have already handled the case in which
+ * a name and inode are found that correspond to different files.
+ * Thus if both NAMEFND and INOFND are set then ip == np.
+ */
+ switch (key) {
+
+ /*
+ * A previously existing file has been found.
+ * Mark it as KEEP so that other links to the inode can be
+ * detected, and so that it will not be reclaimed by the search
+ * for unreferenced names.
+ */
+ case INOFND|NAMEFND:
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A file on the tape has a name which is the same as a name
+ * corresponding to a different file in the previous dump.
+ * Since all files to be deleted have already been removed,
+ * this file is either a now unneeded link, or it must live
+ * under a new name in this dump level. If it is a link, it
+ * can simply be removed. If it is not a link, it is given a
+ * temporary name in anticipation that it will be renamed
+ * when it is later found by inode number (see INOFND case
+ * below). The entry is then treated as a new file.
+ */
+ case ONTAPE|NAMEFND:
+ case ONTAPE|NAMEFND|MODECHG:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ } else {
+ mktempname(np);
+ }
+ /* fall through */
+
+ /*
+ * A previously non-existent file.
+ * Add it to the file system, and request its extraction.
+ * If it is a directory, create it immediately.
+ * (Since the name is unused there can be no conflict)
+ */
+ case ONTAPE:
+ ep = addentry(name, ino, type);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A file with the same inode number, but a different
+ * name has been found. If the other name has not already
+ * been found (indicated by the KEEP flag, see above) then
+ * this must be a new name for the file, and it is renamed.
+ * If the other name has been found then this must be a
+ * link to the file. Hard links to directories are not
+ * permitted, and are either deleted or converted to
+ * symbolic links. Finally, if the file is on the tape,
+ * a request is made to extract it.
+ */
+ case ONTAPE|INOFND:
+ if (type == LEAF && (ip->e_flags & KEEP) == 0)
+ ip->e_flags |= EXTRACT;
+ /* fall through */
+ case INOFND:
+ if ((ip->e_flags & KEEP) == 0) {
+ renameit(myname(ip), name);
+ moveentry(ip, name);
+ ip->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+ }
+ if (ip->e_type == NODE) {
+ descend = FAIL;
+ fprintf(stderr,
+ "deleted hard link %s to directory %s\n",
+ name, myname(ip));
+ break;
+ }
+ ep = addentry(name, ino, type|LINK);
+ ep->e_flags |= NEW;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+
+ /*
+ * A previously known file which is to be updated. If it is a link,
+ * then all names referring to the previous file must be removed
+ * so that the subset of them that remain can be recreated.
+ */
+ case ONTAPE|INOFND|NAMEFND:
+ if (lookuptype == LINK) {
+ removeleaf(np);
+ freeentry(np);
+ ep = addentry(name, ino, type|LINK);
+ if (type == NODE)
+ newnode(ep);
+ ep->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
+ flagvalues(ep));
+ break;
+ }
+ if (type == LEAF && lookuptype != LINK)
+ np->e_flags |= EXTRACT;
+ np->e_flags |= KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(np));
+ break;
+
+ /*
+ * An inode is being reused in a completely different way.
+ * Normally an extract can simply do an "unlink" followed
+ * by a "creat". Here we must do effectively the same
+ * thing. The complications arise because we cannot really
+ * delete a directory since it may still contain files
+ * that we need to rename, so we delete it from the symbol
+ * table, and put it on the list to be deleted eventually.
+ * Conversely if a directory is to be created, it must be
+ * done immediately, rather than waiting until the
+ * extraction phase.
+ */
+ case ONTAPE|INOFND|MODECHG:
+ case ONTAPE|INOFND|NAMEFND|MODECHG:
+ if (ip->e_flags & KEEP) {
+ badentry(ip, "cannot KEEP and change modes");
+ break;
+ }
+ if (ip->e_type == LEAF) {
+ /* changing from leaf to node */
+ removeleaf(ip);
+ freeentry(ip);
+ ip = addentry(name, ino, type);
+ newnode(ip);
+ } else {
+ /* changing from node to leaf */
+ if ((ip->e_flags & TMPNAME) == 0)
+ mktempname(ip);
+ deleteino(ip->e_ino);
+ ip->e_next = removelist;
+ removelist = ip;
+ ip = addentry(name, ino, type);
+ }
+ ip->e_flags |= NEW|KEEP;
+ dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
+ flagvalues(ip));
+ break;
+
+ /*
+ * A hard link to a diirectory that has been removed.
+ * Ignore it.
+ */
+ case NAMEFND:
+ dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key),
+ name);
+ descend = FAIL;
+ break;
+
+ /*
+ * If we find a directory entry for a file that is not on
+ * the tape, then we must have found a file that was created
+ * while the dump was in progress. Since we have no contents
+ * for it, we discard the name knowing that it will be on the
+ * next incremental tape.
+ */
+ case NULL:
+ 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];
+
+ if (ep = lookupino(WINO)) {
+ vprintf(stdout, "Add whiteouts\n");
+ for ( ; ep != NULL; ep = ep->e_links) {
+ if ((ep->e_flags & NEW) == 0)
+ continue;
+ (void) addwhiteout(myname(ep));
+ ep->e_flags &= ~NEW;
+ }
+ }
+ vprintf(stdout, "Add links\n");
+ for (i = ROOTINO; i < maxino; i++) {
+ ep = lookupino(i);
+ if (ep == NULL)
+ continue;
+ for (np = ep->e_links; np != NULL; np = np->e_links) {
+ if ((np->e_flags & NEW) == 0)
+ continue;
+ (void) strcpy(name, myname(ep));
+ if (ep->e_type == NODE) {
+ (void) linkit(name, myname(np), SYMLINK);
+ } else {
+ (void) linkit(name, myname(np), HARDLINK);
+ }
+ np->e_flags &= ~NEW;
+ }
+ }
+}
+
+/*
+ * Check the symbol table.
+ * We do this to insure that all the requested work was done, and
+ * that no temporary names remain.
+ */
+void
+checkrestore()
+{
+ register struct entry *ep;
+ register ino_t i;
+
+ vprintf(stdout, "Check the symbol table.\n");
+ for (i = WINO; i < maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_flags &= ~KEEP;
+ if (ep->e_type == NODE)
+ ep->e_flags &= ~(NEW|EXISTED);
+ if (ep->e_flags != NULL)
+ badentry(ep, "incomplete operations");
+ }
+ }
+}
+
+/*
+ * Compare with the directory structure on the tape
+ * A paranoid check that things are as they should be.
+ */
+long
+verifyfile(name, ino, type)
+ char *name;
+ ino_t ino;
+ int type;
+{
+ struct entry *np, *ep;
+ long descend = GOOD;
+
+ ep = lookupname(name);
+ if (ep == NULL) {
+ fprintf(stderr, "Warning: missing name %s\n", name);
+ return (FAIL);
+ }
+ np = lookupino(ino);
+ if (np != ep)
+ descend = FAIL;
+ for ( ; np != NULL; np = np->e_links)
+ if (np == ep)
+ break;
+ if (np == NULL)
+ panic("missing inumber %d\n", ino);
+ if (ep->e_type == LEAF && type != LEAF)
+ badentry(ep, "type should be LEAF");
+ return (descend);
+}
diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h
new file mode 100644
index 0000000..8dbcee8
--- /dev/null
+++ b/sbin/restore/restore.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)restore.h 8.3 (Berkeley) 9/13/94
+ */
+
+/*
+ * Flags
+ */
+extern int cvtflag; /* convert from old to new tape format */
+extern int bflag; /* set input block size */
+extern int dflag; /* print out debugging info */
+extern int hflag; /* restore heirarchies */
+extern int mflag; /* restore by name instead of inode number */
+extern int Nflag; /* do not write the disk */
+extern int vflag; /* print out actions taken */
+extern int yflag; /* always try to recover from tape errors */
+/*
+ * Global variables
+ */
+extern char *dumpmap; /* map of inodes on this dump tape */
+extern char *usedinomap; /* map of inodes that are in use on this fs */
+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 SETINO(ino, map) \
+ map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY)
+
+#define dprintf if (dflag) fprintf
+#define vprintf if (vflag) fprintf
+
+#define GOOD 1
+#define FAIL 0
diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c
new file mode 100644
index 0000000..6895053
--- /dev/null
+++ b/sbin/restore/symtab.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+/*
+ * These routines maintain the symbol table which tracks the state
+ * of the file system being restored. They provide lookup by either
+ * name or inode number. They also provide for creation, deletion,
+ * and renaming of entries. Because of the dynamic nature of pathnames,
+ * names should not be saved, but always constructed just before they
+ * are needed, by calling "myname".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#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 < WINO || inum >= maxino)
+ return (NULL);
+ for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
+ if (ep->e_ino == inum)
+ return (ep);
+ return (NULL);
+}
+
+/*
+ * Add an entry into the entry table
+ */
+static void
+addino(inum, np)
+ ino_t inum;
+ struct entry *np;
+{
+ struct entry **epp;
+
+ if (inum < WINO || inum >= maxino)
+ panic("addino: out of range %d\n", inum);
+ epp = &entry[inum % entrytblsize];
+ np->e_ino = inum;
+ np->e_next = *epp;
+ *epp = np;
+ if (dflag)
+ for (np = np->e_next; np != NULL; np = np->e_next)
+ if (np->e_ino == inum)
+ badentry(np, "duplicate inum");
+}
+
+/*
+ * Delete an entry from the entry table
+ */
+void
+deleteino(inum)
+ ino_t inum;
+{
+ register struct entry *next;
+ struct entry **prev;
+
+ if (inum < WINO || inum >= maxino)
+ panic("deleteino: out of range %d\n", inum);
+ prev = &entry[inum % entrytblsize];
+ for (next = *prev; next != NULL; next = next->e_next) {
+ if (next->e_ino == inum) {
+ next->e_ino = 0;
+ *prev = next->e_next;
+ return;
+ }
+ prev = &next->e_next;
+ }
+ panic("deleteino: %d not found\n", inum);
+}
+
+/*
+ * Look up an entry by name
+ */
+struct entry *
+lookupname(name)
+ char *name;
+{
+ register struct entry *ep;
+ register char *np, *cp;
+ char buf[MAXPATHLEN];
+
+ cp = name;
+ for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
+ for (np = buf; *cp != '/' && *cp != '\0' &&
+ np < &buf[sizeof(buf)]; )
+ *np++ = *cp++;
+ if (np == &buf[sizeof(buf)])
+ break;
+ *np = '\0';
+ for ( ; ep != NULL; ep = ep->e_sibling)
+ if (strcmp(ep->e_name, buf) == 0)
+ break;
+ if (ep == NULL)
+ break;
+ if (*cp++ == '\0')
+ return (ep);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up the parent of a pathname
+ */
+static struct entry *
+lookupparent(name)
+ char *name;
+{
+ struct entry *ep;
+ char *tailindex;
+
+ tailindex = strrchr(name, '/');
+ if (tailindex == NULL)
+ return (NULL);
+ *tailindex = '\0';
+ ep = lookupname(name);
+ *tailindex = '/';
+ if (ep == NULL)
+ return (NULL);
+ if (ep->e_type != NODE)
+ panic("%s is not a directory\n", name);
+ return (ep);
+}
+
+/*
+ * Determine the current pathname of a node or leaf
+ */
+char *
+myname(ep)
+ register struct entry *ep;
+{
+ register char *cp;
+ static char namebuf[MAXPATHLEN];
+
+ for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
+ cp -= ep->e_namlen;
+ memmove(cp, ep->e_name, (long)ep->e_namlen);
+ if (ep == lookupino(ROOTINO))
+ return (cp);
+ *(--cp) = '/';
+ ep = ep->e_parent;
+ }
+ panic("%s: pathname too long\n", cp);
+ return(cp);
+}
+
+/*
+ * Unused symbol table entries are linked together on a freelist
+ * headed by the following pointer.
+ */
+static struct entry *freelist = NULL;
+
+/*
+ * add an entry to the symbol table
+ */
+struct entry *
+addentry(name, inum, type)
+ char *name;
+ ino_t inum;
+ int type;
+{
+ register struct entry *np, *ep;
+
+ if (freelist != NULL) {
+ np = freelist;
+ freelist = np->e_next;
+ memset(np, 0, (long)sizeof(struct entry));
+ } else {
+ np = (struct entry *)calloc(1, sizeof(struct entry));
+ if (np == NULL)
+ panic("no memory to extend symbol table\n");
+ }
+ np->e_type = type & ~LINK;
+ ep = lookupparent(name);
+ if (ep == NULL) {
+ if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
+ panic("bad name to addentry %s\n", name);
+ np->e_name = savename(name);
+ np->e_namlen = strlen(name);
+ np->e_parent = np;
+ addino(ROOTINO, np);
+ return (np);
+ }
+ np->e_name = savename(strrchr(name, '/') + 1);
+ np->e_namlen = strlen(np->e_name);
+ np->e_parent = ep;
+ np->e_sibling = ep->e_entries;
+ ep->e_entries = np;
+ if (type & LINK) {
+ ep = lookupino(inum);
+ if (ep == NULL)
+ panic("link to non-existant name\n");
+ np->e_ino = inum;
+ np->e_links = ep->e_links;
+ ep->e_links = np;
+ } else if (inum != 0) {
+ if (lookupino(inum) != NULL)
+ panic("duplicate entry\n");
+ addino(inum, np);
+ }
+ return (np);
+}
+
+/*
+ * delete an entry from the symbol table
+ */
+void
+freeentry(ep)
+ register struct entry *ep;
+{
+ register struct entry *np;
+ ino_t inum;
+
+ if (ep->e_flags != REMOVED)
+ badentry(ep, "not marked REMOVED");
+ if (ep->e_type == NODE) {
+ if (ep->e_links != NULL)
+ badentry(ep, "freeing referenced directory");
+ if (ep->e_entries != NULL)
+ badentry(ep, "freeing non-empty directory");
+ }
+ if (ep->e_ino != 0) {
+ np = lookupino(ep->e_ino);
+ if (np == NULL)
+ badentry(ep, "lookupino failed");
+ if (np == ep) {
+ inum = ep->e_ino;
+ deleteino(inum);
+ if (ep->e_links != NULL)
+ addino(inum, ep->e_links);
+ } else {
+ for (; np != NULL; np = np->e_links) {
+ if (np->e_links == ep) {
+ np->e_links = ep->e_links;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "link not found");
+ }
+ }
+ removeentry(ep);
+ freename(ep->e_name);
+ ep->e_next = freelist;
+ freelist = ep;
+}
+
+/*
+ * Relocate an entry in the tree structure
+ */
+void
+moveentry(ep, newname)
+ register struct entry *ep;
+ char *newname;
+{
+ struct entry *np;
+ char *cp;
+
+ np = lookupparent(newname);
+ if (np == NULL)
+ badentry(ep, "cannot move ROOT");
+ if (np != ep->e_parent) {
+ removeentry(ep);
+ ep->e_parent = np;
+ ep->e_sibling = np->e_entries;
+ np->e_entries = ep;
+ }
+ cp = strrchr(newname, '/') + 1;
+ freename(ep->e_name);
+ ep->e_name = savename(cp);
+ ep->e_namlen = strlen(cp);
+ if (strcmp(gentempname(ep), ep->e_name) == 0)
+ ep->e_flags |= TMPNAME;
+ else
+ ep->e_flags &= ~TMPNAME;
+}
+
+/*
+ * Remove an entry in the tree structure
+ */
+static void
+removeentry(ep)
+ register struct entry *ep;
+{
+ register struct entry *np;
+
+ np = ep->e_parent;
+ if (np->e_entries == ep) {
+ np->e_entries = ep->e_sibling;
+ } else {
+ for (np = np->e_entries; np != NULL; np = np->e_sibling) {
+ if (np->e_sibling == ep) {
+ np->e_sibling = ep->e_sibling;
+ break;
+ }
+ }
+ if (np == NULL)
+ badentry(ep, "cannot find entry in parent list");
+ }
+}
+
+/*
+ * Table of unused string entries, sorted by length.
+ *
+ * Entries are allocated in STRTBLINCR sized pieces so that names
+ * of similar lengths can use the same entry. The value of STRTBLINCR
+ * is chosen so that every entry has at least enough space to hold
+ * a "struct strtbl" header. Thus every entry can be linked onto an
+ * apprpriate free list.
+ *
+ * NB. The macro "allocsize" below assumes that "struct strhdr"
+ * has a size that is a power of two.
+ */
+struct strhdr {
+ struct strhdr *next;
+};
+
+#define STRTBLINCR (sizeof(struct strhdr))
+#define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
+
+static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
+
+/*
+ * Allocate space for a name. It first looks to see if it already
+ * has an appropriate sized entry, and if not allocates a new one.
+ */
+char *
+savename(name)
+ char *name;
+{
+ struct strhdr *np;
+ long len;
+ char *cp;
+
+ if (name == NULL)
+ panic("bad name\n");
+ len = strlen(name);
+ np = strtblhdr[len / STRTBLINCR].next;
+ if (np != NULL) {
+ strtblhdr[len / STRTBLINCR].next = np->next;
+ cp = (char *)np;
+ } else {
+ cp = malloc((unsigned)allocsize(len));
+ if (cp == NULL)
+ panic("no space for string table\n");
+ }
+ (void) strcpy(cp, name);
+ return (cp);
+}
+
+/*
+ * Free space for a name. The resulting entry is linked onto the
+ * appropriate free list.
+ */
+void
+freename(name)
+ char *name;
+{
+ struct strhdr *tp, *np;
+
+ tp = &strtblhdr[strlen(name) / STRTBLINCR];
+ np = (struct strhdr *)name;
+ np->next = tp->next;
+ tp->next = np;
+}
+
+/*
+ * Useful quantities placed at the end of a dumped symbol table.
+ */
+struct symtableheader {
+ long volno;
+ long stringsize;
+ long entrytblsize;
+ time_t dumptime;
+ time_t dumpdate;
+ ino_t maxino;
+ long ntrec;
+};
+
+/*
+ * dump a snapshot of the symbol table
+ */
+void
+dumpsymtable(filename, checkpt)
+ char *filename;
+ long checkpt;
+{
+ register struct entry *ep, *tep;
+ register ino_t i;
+ struct entry temp, *tentry;
+ long mynum = 1, stroff = 0;
+ FILE *fd;
+ struct symtableheader hdr;
+
+ vprintf(stdout, "Check pointing the restore\n");
+ if (Nflag)
+ return;
+ if ((fd = fopen(filename, "w")) == NULL) {
+ fprintf(stderr, "fopen: %s\n", strerror(errno));
+ panic("cannot create save file %s for symbol table\n",
+ filename);
+ }
+ clearerr(fd);
+ /*
+ * Assign indicies to each entry
+ * Write out the string entries
+ */
+ for (i = WINO; i <= maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ ep->e_index = mynum++;
+ (void) fwrite(ep->e_name, sizeof(char),
+ (int)allocsize(ep->e_namlen), fd);
+ }
+ }
+ /*
+ * Convert pointers to indexes, and output
+ */
+ tep = &temp;
+ stroff = 0;
+ for (i = WINO; i <= maxino; i++) {
+ for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
+ memmove(tep, ep, (long)sizeof(struct entry));
+ tep->e_name = (char *)stroff;
+ stroff += allocsize(ep->e_namlen);
+ tep->e_parent = (struct entry *)ep->e_parent->e_index;
+ if (ep->e_links != NULL)
+ tep->e_links =
+ (struct entry *)ep->e_links->e_index;
+ if (ep->e_sibling != NULL)
+ tep->e_sibling =
+ (struct entry *)ep->e_sibling->e_index;
+ if (ep->e_entries != NULL)
+ tep->e_entries =
+ (struct entry *)ep->e_entries->e_index;
+ if (ep->e_next != NULL)
+ tep->e_next =
+ (struct entry *)ep->e_next->e_index;
+ (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
+ }
+ }
+ /*
+ * Convert entry pointers to indexes, and output
+ */
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ tentry = NULL;
+ else
+ tentry = (struct entry *)entry[i]->e_index;
+ (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
+ }
+ hdr.volno = checkpt;
+ hdr.maxino = maxino;
+ hdr.entrytblsize = entrytblsize;
+ hdr.stringsize = stroff;
+ hdr.dumptime = dumptime;
+ hdr.dumpdate = dumpdate;
+ hdr.ntrec = ntrec;
+ (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
+ if (ferror(fd)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ panic("output error to file %s writing symbol table\n",
+ filename);
+ }
+ (void) fclose(fd);
+}
+
+/*
+ * Initialize a symbol table from a file
+ */
+void
+initsymtable(filename)
+ char *filename;
+{
+ char *base;
+ long tblsize;
+ register struct entry *ep;
+ struct entry *baseep, *lep;
+ struct symtableheader hdr;
+ struct stat stbuf;
+ register long i;
+ int fd;
+
+ vprintf(stdout, "Initialize symbol table.\n");
+ if (filename == NULL) {
+ entrytblsize = maxino / HASHFACTOR;
+ entry = (struct entry **)
+ calloc((unsigned)entrytblsize, sizeof(struct entry *));
+ if (entry == (struct entry **)NULL)
+ panic("no memory for entry table\n");
+ ep = addentry(".", ROOTINO, NODE);
+ ep->e_flags |= NEW;
+ return;
+ }
+ if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ panic("cannot open symbol table file %s\n", filename);
+ }
+ if (fstat(fd, &stbuf) < 0) {
+ fprintf(stderr, "stat: %s\n", strerror(errno));
+ panic("cannot stat symbol table file %s\n", filename);
+ }
+ tblsize = stbuf.st_size - sizeof(struct symtableheader);
+ base = calloc(sizeof(char), (unsigned)tblsize);
+ if (base == NULL)
+ panic("cannot allocate space for symbol table\n");
+ if (read(fd, base, (int)tblsize) < 0 ||
+ read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ panic("cannot read symbol table file %s\n", filename);
+ }
+ switch (command) {
+ case 'r':
+ /*
+ * For normal continuation, insure that we are using
+ * the next incremental tape
+ */
+ if (hdr.dumpdate != dumptime) {
+ if (hdr.dumpdate < dumptime)
+ fprintf(stderr, "Incremental tape too low\n");
+ else
+ fprintf(stderr, "Incremental tape too high\n");
+ done(1);
+ }
+ break;
+ case 'R':
+ /*
+ * For restart, insure that we are using the same tape
+ */
+ curfile.action = SKIP;
+ dumptime = hdr.dumptime;
+ dumpdate = hdr.dumpdate;
+ if (!bflag)
+ newtapebuf(hdr.ntrec);
+ getvol(hdr.volno);
+ break;
+ default:
+ panic("initsymtable called from command %c\n", command);
+ break;
+ }
+ maxino = hdr.maxino;
+ entrytblsize = hdr.entrytblsize;
+ entry = (struct entry **)
+ (base + tblsize - (entrytblsize * sizeof(struct entry *)));
+ baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
+ lep = (struct entry *)entry;
+ for (i = 0; i < entrytblsize; i++) {
+ if (entry[i] == NULL)
+ continue;
+ entry[i] = &baseep[(long)entry[i]];
+ }
+ for (ep = &baseep[1]; ep < lep; ep++) {
+ ep->e_name = base + (long)ep->e_name;
+ ep->e_parent = &baseep[(long)ep->e_parent];
+ if (ep->e_sibling != NULL)
+ ep->e_sibling = &baseep[(long)ep->e_sibling];
+ if (ep->e_links != NULL)
+ ep->e_links = &baseep[(long)ep->e_links];
+ if (ep->e_entries != NULL)
+ ep->e_entries = &baseep[(long)ep->e_entries];
+ if (ep->e_next != NULL)
+ ep->e_next = &baseep[(long)ep->e_next];
+ }
+}
diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c
new file mode 100644
index 0000000..9ba2683
--- /dev/null
+++ b/sbin/restore/tape.c
@@ -0,0 +1,1385 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/stat.h>
+
+#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;
+static int blkcnt;
+static int numtrec;
+static char *tapebuf;
+static union u_spcl endoftapemark;
+static long blksread; /* blocks read since last header */
+static long tpblksread = 0; /* TP_BSIZE blocks read */
+static long tapesread;
+static jmp_buf restart;
+static int gettingfile = 0; /* restart has a valid frame */
+static char *host = NULL;
+
+static int ofile;
+static char *map;
+static char lnkbuf[MAXPATHLEN + 1];
+static int pathlen;
+
+int oldinofmt; /* old inode format conversion required */
+int Bcvt; /* Swap Bytes (for CCI or sun) */
+static int Qcvt; /* Swap quads (for sun) */
+
+#define FLUSHTAPEBUF() blkcnt = ntrec + 1
+
+static void accthdr __P((struct s_spcl *));
+static int checksum __P((int *));
+static void findinode __P((struct s_spcl *));
+static void findtapeblksize __P((void));
+static int gethead __P((struct s_spcl *));
+static void readtape __P((char *));
+static void setdumpnum __P((void));
+static u_long swabl __P((u_long));
+static u_char *swablong __P((u_char *, int));
+static u_char *swabshort __P((u_char *, int));
+static void terminateinput __P((void));
+static void xtrfile __P((char *, long));
+static void xtrlnkfile __P((char *, long));
+static void xtrlnkskip __P((char *, long));
+static void xtrmap __P((char *, long));
+static void xtrmapskip __P((char *, long));
+static void xtrskip __P((char *, long));
+
+/*
+ * Set up an input source
+ */
+void
+setinput(source)
+ char *source;
+{
+ FLUSHTAPEBUF();
+ if (bflag)
+ newtapebuf(ntrec);
+ else
+ newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+ terminal = stdin;
+
+#ifdef RRESTORE
+ if (strchr(source, ':')) {
+ host = source;
+ source = strchr(host, ':');
+ *source++ = '\0';
+ if (rmthost(host) == 0)
+ done(1);
+ } else
+#endif
+ if (strcmp(source, "-") == 0) {
+ /*
+ * Since input is coming from a pipe we must establish
+ * our own connection to the terminal.
+ */
+ terminal = fopen(_PATH_TTY, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_TTY, strerror(errno));
+ terminal = fopen(_PATH_DEVNULL, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "cannot open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ done(1);
+ }
+ }
+ pipein++;
+ }
+ setuid(getuid()); /* no longer need or want root privileges */
+ magtape = strdup(source);
+ if (magtape == NULL) {
+ fprintf(stderr, "Cannot allocate space for magtape buffer\n");
+ done(1);
+ }
+}
+
+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 active inode map\n");
+ usedinomap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ if (spcl.c_type != TS_BITS) {
+ fprintf(stderr, "Cannot find file dump list\n");
+ done(1);
+ }
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == (char *)NULL)
+ panic("no memory for file dump list\n");
+ dumpmap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ /*
+ * If there may be whiteout entries on the tape, pretend that the
+ * whiteout inode exists, so that the whiteout entries can be
+ * extracted.
+ */
+ if (oldinofmt == 0)
+ SETINO(WINO, dumpmap);
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overrridden by
+ * the user when only extracting a subset of the files.
+ */
+void
+getvol(nextvol)
+ long nextvol;
+{
+ long newvol, savecnt, wantnext, i;
+ union u_spcl tmpspcl;
+# define tmpbuf tmpspcl.s_spcl
+ char buf[TP_BSIZE];
+
+ if (nextvol == 1) {
+ tapesread = 0;
+ gettingfile = 0;
+ }
+ if (pipein) {
+ if (nextvol != 1)
+ panic("Changing volumes on pipe input?\n");
+ if (volno == 1)
+ return;
+ goto gethdr;
+ }
+ savecnt = blksread;
+again:
+ if (pipein)
+ done(1); /* pipes do not get a second chance */
+ if (command == 'R' || command == 'r' || curfile.action != SKIP) {
+ newvol = nextvol;
+ wantnext = 1;
+ } else {
+ newvol = 0;
+ wantnext = 0;
+ }
+ while (newvol <= 0) {
+ if (tapesread == 0) {
+ fprintf(stderr, "%s%s%s%s%s",
+ "You have not read any tapes yet.\n",
+ "Unless you know which volume your",
+ " file(s) are on you should start\n",
+ "with the last volume and work",
+ " towards 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 flags;
+ mode_t mode;
+ struct timeval timep[2];
+ struct entry *ep;
+
+ curfile.name = name;
+ curfile.action = USING;
+ timep[0].tv_sec = curfile.dip->di_atime;
+ timep[0].tv_usec = curfile.dip->di_atimensec / 1000;
+ timep[1].tv_sec = curfile.dip->di_mtime;
+ timep[1].tv_usec = curfile.dip->di_mtimensec / 1000;
+ mode = curfile.dip->di_mode;
+ flags = curfile.dip->di_flags;
+ switch (mode & IFMT) {
+
+ default:
+ fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+ skipfile();
+ return (FAIL);
+
+ case IFSOCK:
+ vprintf(stdout, "skipped socket %s\n", name);
+ skipfile();
+ return (GOOD);
+
+ case IFDIR:
+ if (mflag) {
+ ep = lookupname(name);
+ if (ep == NULL || ep->e_flags & EXTRACT)
+ panic("unextracted directory %s\n", name);
+ skipfile();
+ return (GOOD);
+ }
+ vprintf(stdout, "extract file %s\n", name);
+ return (genliteraldir(name, curfile.ino));
+
+ case IFLNK:
+ lnkbuf[0] = '\0';
+ pathlen = 0;
+ 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:
+ vprintf(stdout, "extract fifo %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (mkfifo(name, mode) < 0) {
+ fprintf(stderr, "%s: cannot create fifo: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) chmod(name, mode);
+ (void) chflags(name, flags);
+ 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);
+ (void) chflags(name, flags);
+ skipfile();
+ utimes(name, timep);
+ return (GOOD);
+
+ case IFREG:
+ vprintf(stdout, "extract file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
+ 0666)) < 0) {
+ fprintf(stderr, "%s: cannot create file: %s\n",
+ name, strerror(errno));
+ skipfile();
+ return (FAIL);
+ }
+ (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) fchmod(ofile, mode);
+ (void) fchflags(ofile, flags);
+ getfile(xtrfile, xtrskip);
+ (void) close(ofile);
+ utimes(name, timep);
+ return (GOOD);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+void
+skipmaps()
+{
+
+ while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
+ skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+void
+skipfile()
+{
+
+ curfile.action = SKIP;
+ getfile(xtrnull, xtrnull);
+}
+
+/*
+ * Extract a file from the tape.
+ * When an allocated block is found it is passed to the fill function;
+ * when an unallocated block (hole) is found, a zeroed buffer is passed
+ * to the skip function.
+ */
+void
+getfile(fill, skip)
+ void (*fill) __P((char *, long));
+ void (*skip) __P((char *, long));
+{
+ register int i;
+ int curblk = 0;
+ quad_t size = spcl.c_dinode.di_size;
+ static char clearedbuf[MAXBSIZE];
+ char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+ char junk[TP_BSIZE];
+
+ if (spcl.c_type == TS_END)
+ panic("ran off end of tape\n");
+ if (spcl.c_magic != NFS_MAGIC)
+ panic("not at beginning of a file\n");
+ if (!gettingfile && setjmp(restart) != 0)
+ return;
+ gettingfile++;
+loop:
+ for (i = 0; i < spcl.c_count; i++) {
+ if (spcl.c_addr[i]) {
+ readtape(&buf[curblk++][0]);
+ if (curblk == fssize / TP_BSIZE) {
+ (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+ fssize : (curblk - 1) * TP_BSIZE + size));
+ curblk = 0;
+ }
+ } else {
+ if (curblk > 0) {
+ (*fill)((char *)buf, (long)(size > TP_BSIZE ?
+ curblk * TP_BSIZE :
+ (curblk - 1) * TP_BSIZE + size));
+ curblk = 0;
+ }
+ (*skip)(clearedbuf, (long)(size > TP_BSIZE ?
+ TP_BSIZE : size));
+ }
+ 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, (long)((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;
+{
+
+ memmove(map, buf, size);
+ map += size;
+}
+
+/*
+ * Skip over a hole in a bit map (should never happen).
+ */
+/* ARGSUSED */
+static void
+xtrmapskip(buf, size)
+ char *buf;
+ long size;
+{
+
+ panic("hole in map\n");
+ map += size;
+}
+
+/*
+ * Noop, when an extraction function is not needed.
+ */
+/* ARGSUSED */
+void
+xtrnull(buf, size)
+ char *buf;
+ long size;
+{
+
+ return;
+}
+
+/*
+ * Read TP_BSIZE blocks from the input.
+ * Handle read errors, and end of media.
+ */
+static void
+readtape(buf)
+ char *buf;
+{
+ long rd, newvol, i;
+ int cnt, seek_failed;
+
+ if (blkcnt < numtrec) {
+ memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+ blksread++;
+ tpblksread++;
+ return;
+ }
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ if (numtrec == 0)
+ numtrec = ntrec;
+ cnt = ntrec * TP_BSIZE;
+ rd = 0;
+getmore:
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(&tapebuf[rd], cnt);
+ else
+#endif
+ i = read(mt, &tapebuf[rd], cnt);
+ /*
+ * Check for mid-tape short read error.
+ * If found, skip rest of buffer and start with the next.
+ */
+ if (!pipein && numtrec < ntrec && i > 0) {
+ dprintf(stdout, "mid-media short read error.\n");
+ numtrec = ntrec;
+ }
+ /*
+ * Handle partial block read.
+ */
+ if (pipein && i == 0 && rd > 0)
+ i = rd;
+ else if (i > 0 && i != ntrec * TP_BSIZE) {
+ if (pipein) {
+ rd += i;
+ cnt -= i;
+ if (cnt > 0)
+ goto getmore;
+ i = rd;
+ } else {
+ /*
+ * Short read. Process the blocks read.
+ */
+ if (i % TP_BSIZE != 0)
+ vprintf(stdout,
+ "partial block read: %d should be %d\n",
+ i, ntrec * TP_BSIZE);
+ numtrec = i / TP_BSIZE;
+ }
+ }
+ /*
+ * Handle read error.
+ */
+ if (i < 0) {
+ fprintf(stderr, "Tape read error while ");
+ switch (curfile.action) {
+ default:
+ fprintf(stderr, "trying to set up tape\n");
+ break;
+ case UNKNOWN:
+ fprintf(stderr, "trying to resynchronize\n");
+ break;
+ case USING:
+ fprintf(stderr, "restoring %s\n", curfile.name);
+ break;
+ case SKIP:
+ fprintf(stderr, "skipping over inode %d\n",
+ curfile.ino);
+ break;
+ }
+ if (!yflag && !reply("continue"))
+ done(1);
+ i = ntrec * TP_BSIZE;
+ memset(tapebuf, 0, i);
+#ifdef RRESTORE
+ if (host)
+ seek_failed = (rmtseek(i, 1) < 0);
+ else
+#endif
+ seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
+
+ if (seek_failed) {
+ fprintf(stderr,
+ "continuation failed: %s\n", strerror(errno));
+ done(1);
+ }
+ }
+ /*
+ * Handle end of tape.
+ */
+ if (i == 0) {
+ vprintf(stdout, "End-of-tape encountered\n");
+ if (!pipein) {
+ newvol = volno + 1;
+ volno = 0;
+ numtrec = 0;
+ getvol(newvol);
+ readtape(buf);
+ return;
+ }
+ if (rd % TP_BSIZE != 0)
+ panic("partial block read: %d should be %d\n",
+ rd, ntrec * TP_BSIZE);
+ terminateinput();
+ memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
+ }
+ blkcnt = 0;
+ memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
+ blksread++;
+ tpblksread++;
+}
+
+static void
+findtapeblksize()
+{
+ register long i;
+
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
+ blkcnt = 0;
+#ifdef RRESTORE
+ if (host)
+ i = rmtread(tapebuf, ntrec * TP_BSIZE);
+ else
+#endif
+ i = read(mt, tapebuf, ntrec * TP_BSIZE);
+
+ if (i <= 0) {
+ fprintf(stderr, "tape read error: %s\n", strerror(errno));
+ done(1);
+ }
+ if (i % TP_BSIZE != 0) {
+ fprintf(stderr, "Tape block size (%d) %s (%d)\n",
+ i, "is not a multiple of dump block size", TP_BSIZE);
+ done(1);
+ }
+ ntrec = i / TP_BSIZE;
+ numtrec = ntrec;
+ vprintf(stdout, "Tape block size is %d\n", ntrec);
+}
+
+void
+closemt()
+{
+
+ if (mt < 0)
+ return;
+#ifdef RRESTORE
+ if (host)
+ rmtclose();
+ else
+#endif
+ (void) close(mt);
+}
+
+/*
+ * Read the next block from the tape.
+ * Check to see if it is one of several vintage headers.
+ * If it is an old style header, convert it to a new style header.
+ * If it is not any valid header, return an error.
+ */
+static int
+gethead(buf)
+ struct s_spcl *buf;
+{
+ long i;
+ union {
+ quad_t qval;
+ long val[2];
+ } qcvt;
+ union u_ospcl {
+ char dummy[TP_BSIZE];
+ struct s_ospcl {
+ long c_type;
+ long c_date;
+ long c_ddate;
+ long c_volume;
+ long c_tapea;
+ u_short c_inumber;
+ long c_magic;
+ long c_checksum;
+ struct odinode {
+ unsigned short odi_mode;
+ u_short odi_nlink;
+ u_short odi_uid;
+ u_short odi_gid;
+ long odi_size;
+ long odi_rdev;
+ char odi_addr[36];
+ long odi_atime;
+ long odi_mtime;
+ long odi_ctime;
+ } c_dinode;
+ long c_count;
+ char c_addr[256];
+ } s_ospcl;
+ } u_ospcl;
+
+ if (!cvtflag) {
+ readtape((char *)buf);
+ if (buf->c_magic != NFS_MAGIC) {
+ if (swabl(buf->c_magic) != NFS_MAGIC)
+ return (FAIL);
+ if (!Bcvt) {
+ vprintf(stdout, "Note: Doing Byte swapping\n");
+ Bcvt = 1;
+ }
+ }
+ if (checksum((int *)buf) == FAIL)
+ return (FAIL);
+ if (Bcvt) {
+ swabst((u_char *)"8l4s31l", (u_char *)buf);
+ swabst((u_char *)"l",(u_char *) &buf->c_level);
+ swabst((u_char *)"2l",(u_char *) &buf->c_flags);
+ }
+ goto good;
+ }
+ readtape((char *)(&u_ospcl.s_ospcl));
+ memset(buf, 0, (long)TP_BSIZE);
+ buf->c_type = u_ospcl.s_ospcl.c_type;
+ buf->c_date = u_ospcl.s_ospcl.c_date;
+ buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
+ buf->c_volume = u_ospcl.s_ospcl.c_volume;
+ buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
+ buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
+ buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
+ buf->c_magic = u_ospcl.s_ospcl.c_magic;
+ buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
+ buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
+ buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
+ buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
+ buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
+ buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
+ buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
+ buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+ buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+ buf->c_count = u_ospcl.s_ospcl.c_count;
+ memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256);
+ if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
+ checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
+ return(FAIL);
+ buf->c_magic = NFS_MAGIC;
+
+good:
+ if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
+ (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
+ qcvt.qval = buf->c_dinode.di_size;
+ if (qcvt.val[0] || qcvt.val[1]) {
+ printf("Note: Doing Quad swapping\n");
+ Qcvt = 1;
+ }
+ }
+ if (Qcvt) {
+ qcvt.qval = buf->c_dinode.di_size;
+ i = qcvt.val[1];
+ qcvt.val[1] = qcvt.val[0];
+ qcvt.val[0] = i;
+ buf->c_dinode.di_size = qcvt.qval;
+ }
+
+ switch (buf->c_type) {
+
+ case TS_CLRI:
+ case TS_BITS:
+ /*
+ * Have to patch up missing information in bit map headers
+ */
+ buf->c_inumber = 0;
+ buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
+ for (i = 0; i < buf->c_count; i++)
+ buf->c_addr[i]++;
+ break;
+
+ case TS_TAPE:
+ if ((buf->c_flags & DR_NEWINODEFMT) == 0)
+ oldinofmt = 1;
+ /* fall through */
+ case TS_END:
+ buf->c_inumber = 0;
+ break;
+
+ case TS_INODE:
+ case TS_ADDR:
+ break;
+
+ default:
+ panic("gethead: unknown inode type %d\n", buf->c_type);
+ break;
+ }
+ /*
+ * If we are restoring a filesystem with old format inodes,
+ * copy the uid/gid to the new location.
+ */
+ if (oldinofmt) {
+ buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
+ buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
+ }
+ if (dflag)
+ accthdr(buf);
+ return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+static void
+accthdr(header)
+ struct s_spcl *header;
+{
+ static ino_t previno = 0x7fffffff;
+ static int prevtype;
+ static long predict;
+ long blks, i;
+
+ if (header->c_type == TS_TAPE) {
+ fprintf(stderr, "Volume header (%s inode format) ",
+ oldinofmt ? "old" : "new");
+ if (header->c_firstrec)
+ fprintf(stderr, "begins with record %d",
+ header->c_firstrec);
+ fprintf(stderr, "\n");
+ previno = 0x7fffffff;
+ return;
+ }
+ if (previno == 0x7fffffff)
+ goto newcalc;
+ switch (prevtype) {
+ case TS_BITS:
+ fprintf(stderr, "Dumped inodes map header");
+ break;
+ case TS_CLRI:
+ fprintf(stderr, "Used inodes map header");
+ break;
+ case TS_INODE:
+ fprintf(stderr, "File header, ino %d", previno);
+ break;
+ case TS_ADDR:
+ fprintf(stderr, "File continuation header, ino %d", previno);
+ break;
+ case TS_END:
+ fprintf(stderr, "End of tape header");
+ break;
+ }
+ if (predict != blksread - 1)
+ fprintf(stderr, "; predicted %d blocks, got %d blocks",
+ predict, blksread - 1);
+ fprintf(stderr, "\n");
+newcalc:
+ blks = 0;
+ if (header->c_type != TS_END)
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i] != 0)
+ blks++;
+ predict = blks;
+ blksread = 0;
+ prevtype = header->c_type;
+ previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip, and complain is set.
+ */
+static void
+findinode(header)
+ struct s_spcl *header;
+{
+ static long skipcnt = 0;
+ long i;
+ char buf[TP_BSIZE];
+
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.dip = NULL;
+ curfile.ino = 0;
+ do {
+ if (header->c_magic != NFS_MAGIC) {
+ skipcnt++;
+ while (gethead(header) == FAIL ||
+ header->c_date != dumpdate)
+ skipcnt++;
+ }
+ switch (header->c_type) {
+
+ case TS_ADDR:
+ /*
+ * Skip up to the beginning of the next record
+ */
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i])
+ readtape(buf);
+ while (gethead(header) == FAIL ||
+ header->c_date != dumpdate)
+ skipcnt++;
+ break;
+
+ case TS_INODE:
+ curfile.dip = &header->c_dinode;
+ curfile.ino = header->c_inumber;
+ break;
+
+ case TS_END:
+ curfile.ino = maxino;
+ break;
+
+ case TS_CLRI:
+ curfile.name = "<file removal list>";
+ break;
+
+ case TS_BITS:
+ curfile.name = "<file dump list>";
+ break;
+
+ case TS_TAPE:
+ panic("unexpected tape header\n");
+ /* NOTREACHED */
+
+ default:
+ panic("unknown tape header type %d\n", spcl.c_type);
+ /* NOTREACHED */
+
+ }
+ } while (header->c_type == TS_ADDR);
+ if (skipcnt > 0)
+ fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
+ skipcnt = 0;
+}
+
+static int
+checksum(buf)
+ register int *buf;
+{
+ register int i, j;
+
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ if(!Bcvt) {
+ do
+ i += *buf++;
+ while (--j);
+ } else {
+ /* What happens if we want to read restore tapes
+ for a 16bit int machine??? */
+ do
+ i += swabl(*buf++);
+ while (--j);
+ }
+
+ if (i != CHECKSUM) {
+ fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+ curfile.ino, curfile.name);
+ return(FAIL);
+ }
+ return(GOOD);
+}
+
+#ifdef RRESTORE
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+msg(const char *fmt, ...)
+#else
+msg(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* RRESTORE */
+
+static u_char *
+swabshort(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+ sp += 2;
+ }
+ return (sp);
+}
+
+static u_char *
+swablong(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+ c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+ sp += 4;
+ }
+ return (sp);
+}
+
+void
+swabst(cp, sp)
+ register u_char *cp, *sp;
+{
+ int n = 0;
+
+ while (*cp) {
+ switch (*cp) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = (n * 10) + (*cp++ - '0');
+ continue;
+
+ case 's': case 'w': case 'h':
+ if (n == 0)
+ n = 1;
+ sp = swabshort(sp, n);
+ break;
+
+ case 'l':
+ if (n == 0)
+ n = 1;
+ sp = swablong(sp, n);
+ break;
+
+ default: /* Any other character, like 'b' counts as byte. */
+ if (n == 0)
+ n = 1;
+ sp += n;
+ break;
+ }
+ cp++;
+ n = 0;
+ }
+}
+
+static u_long
+swabl(x)
+ u_long x;
+{
+ swabst((u_char *)"l", (u_char *)&x);
+ return (x);
+}
diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c
new file mode 100644
index 0000000..6008f7a
--- /dev/null
+++ b/sbin/restore/utilities.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#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 = strchr(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);
+}
+
+/*
+ * Create a whiteout.
+ */
+int
+addwhiteout(name)
+ char *name;
+{
+
+ if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
+ fprintf(stderr, "warning: cannot create whiteout %s: %s\n",
+ name, strerror(errno));
+ return (FAIL);
+ }
+ vprintf(stdout, "Create whiteout %s\n", name);
+ return (GOOD);
+}
+
+/*
+ * Delete a whiteout.
+ */
+void
+delwhiteout(ep)
+ register struct entry *ep;
+{
+ char *name;
+
+ if (ep->e_type != LEAF)
+ badentry(ep, "delwhiteout: not a leaf");
+ ep->e_flags |= REMOVED;
+ ep->e_flags &= ~TMPNAME;
+ name = myname(ep);
+ if (!Nflag && undelete(name) < 0) {
+ fprintf(stderr, "warning: cannot delete whiteout %s: %s\n",
+ name, strerror(errno));
+ return;
+ }
+ vprintf(stdout, "Delete whiteout %s\n", name);
+}
+
+/*
+ * 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..84fd623
--- /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
+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/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..aea6425
--- /dev/null
+++ b/sbin/route/route.8
@@ -0,0 +1,333 @@
+.\" 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
+.|' $Id: route.8,v 1.10 1997/02/22 14:33:09 peter Exp $
+.\"
+.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
+modifier 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 IPXrouted 8 ,
+.Xr routed 8
+.\" .Xr XNSrouted 8
+.\" Xr esis 4 ,
+.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..7ffd132
--- /dev/null
+++ b/sbin/route/route.c
@@ -0,0 +1,1483 @@
+/*
+ *
+ * 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.24 1997/04/02 17:05:30 phk 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
+#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
+ struct sockaddr_dl sdl;
+} 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();
+
+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 ] command [[ modifiers ] 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;
+ 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));
+
+ 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));
+
+
+ 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;
+ case K_INET:
+ af = AF_INET;
+ aflen = sizeof(struct sockaddr_in);
+ break;
+ case K_ATALK:
+ af = AF_APPLETALK;
+ aflen = sizeof(struct sockaddr_at);
+ break;
+ 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 {
+ (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, bits)
+ u_long net, bits;
+ 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 (bits) {
+ addr = net;
+ mask = 0xffffffff << (32 - bits);
+ } 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
+ struct hostent *hp;
+ struct netent *np;
+ u_long val;
+ char *q,qs;
+
+ 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
+
+
+ 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);
+
+
+ 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;
+
+ q = strchr(s,'/');
+ if (q && which == RTA_DST) {
+ qs = *q;
+ *q = '\0';
+ if (((val = inet_addr(s)) != INADDR_NONE)) {
+ inet_makenetandmask(
+ htonl(val), &su->sin, strtoul(q+1, 0, 0));
+ return (0);
+ }
+ *q =qs;
+ }
+ 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, 0);
+ 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 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+MAXHOSTNAMELEN], 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) snprintf(mybuf, sizeof(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:
+ case 0:
+ return;
+ }
+ 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;
+}
+
+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",
+ "RTM_NEWMADDR: new multicast group membership on iface",
+ "RTM_DELMADDR: multicast group membership removed from iface",
+ 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;
+#ifdef RTM_NEWMADDR
+ struct ifma_msghdr *ifmam;
+#endif
+
+ 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;
+#ifdef RTM_NEWMADDR
+ case RTM_NEWMADDR:
+ case RTM_DELMADDR:
+ ifmam = (struct ifma_msghdr *)rtm;
+ pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs);
+ break;
+#endif
+ 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;
+ 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..c2187c6
--- /dev/null
+++ b/sbin/routed/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/19/93
+# $Id$
+
+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
new file mode 100644
index 0000000..bba230a
--- /dev/null
+++ b/sbin/routed/defs.h
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 1983, 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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ * $Id$
+ */
+
+/* Definitions for RIPv2 routing process.
+ *
+ * This code is based on the 4.4BSD `routed` daemon, with extensions to
+ * support:
+ * RIPv2, including variable length subnet masks.
+ * Router Discovery
+ * aggregate routes in the kernel tables.
+ * aggregate advertised routes.
+ * maintain spare routes for faster selection of another gateway
+ * when the current gateway dies.
+ * timers on routes with second granularity so that selection
+ * of a new route does not wait 30-60 seconds.
+ * tolerance of static routes.
+ * tell the kernel hop counts
+ * do not advertise if ipforwarding=0
+ *
+ * The vestigual support for other protocols has been removed. There
+ * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with
+ * other protocols. The result is far smaller, faster, cleaner, and
+ * perhaps understandable.
+ *
+ * The accumulation of special flags and kludges added over the many
+ * years have been simplified and integrated.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#ifdef sgi
+#include <strings.h>
+#include <bstring.h>
+#endif
+#include <stdarg.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#ifdef sgi
+#include <net/radix.h>
+#else
+#include "radix.h"
+#endif
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+
+
+/* Type of an IP address.
+ * Some systems do not like to pass structures, so do not use in_addr.
+ * Some systems think a long has 64 bits, which would be a gross waste.
+ * So define it here so it can be changed for the target system.
+ * It should be defined somewhere netinet/in.h, but it is not.
+ */
+#ifdef sgi
+#define naddr __uint32_t
+#else
+#ifdef __NetBSD__
+#define naddr u_int32_t
+#else
+#define naddr u_long
+#endif
+#define _HAVE_SA_LEN
+#define _HAVE_SIN_LEN
+#endif
+
+/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at
+ * the dstaddr of point-to-point interfaces.
+ */
+/* #define MCAST_PPP_BUG */
+
+#define DAY (24*60*60)
+#define NEVER DAY /* 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
+ * turned off. These must be less than STALE_TIME.
+ */
+#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */
+#define CHECK_ACT_INTERVAL 30 /* when advertising */
+#define CHECK_QUIET_INTERVAL 300 /* when not */
+
+#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l)))
+
+/* Metric used for fake default routes. It ought to be 15, but when
+ * processing advertised routes, previous versions of `routed` added
+ * to the received metric and discarded the route if the total was 16
+ * or larger.
+ */
+#define FAKE_METRIC (HOPCNT_INFINITY-2)
+
+
+/* Router Discovery parameters */
+#ifndef sgi
+#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */
+#endif
+#define MaxMaxAdvertiseInterval 1800
+#define MinMaxAdvertiseInterval 4
+#define DefMaxAdvertiseInterval 600
+#define DEF_PreferenceLevel 0
+#define MIN_PreferenceLevel 0x80000000
+
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTS 3
+#define MAX_RESPONSE_DELAY 2
+
+#define MAX_SOLICITATION_DELAY 1
+#define SOLICITATION_INTERVAL 3
+#define MAX_SOLICITATIONS 3
+
+
+/* Bloated packet size for systems that simply add authentication to
+ * full-sized packets
+ */
+#define OVER_MAXPACKETSIZE (MAXPACKETSIZE+sizeof(struct netinfo)*2)
+/* typical packet buffers */
+union pkt_buf {
+ char packet[OVER_MAXPACKETSIZE*2];
+ struct rip rip;
+};
+
+
+/* No more routes than this, to protect ourself in case something goes
+ * whacko and starts broadcasting zillions of bogus routes.
+ */
+#define MAX_ROUTES (128*1024)
+extern int total_routes;
+
+/* Main, daemon routing table structure
+ */
+struct rt_entry {
+ struct radix_node rt_nodes[2]; /* radix tree glue */
+ u_int rt_state;
+# define RS_IF 0x001 /* for network interface */
+# define RS_NET_INT 0x002 /* authority route */
+# define RS_NET_SYN 0x004 /* fake net route for subnet */
+# define RS_NO_NET_SYN (RS_LOCAL | RS_LOCAL | RS_IF)
+# define RS_SUBNET 0x008 /* subnet route from any source */
+# define RS_LOCAL 0x010 /* loopback for pt-to-pt */
+# define RS_MHOME 0x020 /* from -m */
+# define RS_STATIC 0x040 /* from the kernel */
+# define RS_RDISC 0x080 /* from router discovery */
+ struct sockaddr_in rt_dst_sock;
+ naddr rt_mask;
+ struct rt_spare {
+ struct interface *rts_ifp;
+ naddr rts_gate; /* forward packets here */
+ naddr rts_router; /* on the authority of this router */
+ char rts_metric;
+ u_short rts_tag;
+ time_t rts_time; /* timer to junk stale routes */
+#define NUM_SPARES 4
+ } rt_spares[NUM_SPARES];
+ u_int rt_seqno; /* when last changed */
+ char rt_poison_metric; /* to notice maximum recently */
+ time_t rt_poison_time; /* advertised metric */
+};
+#define rt_dst rt_dst_sock.sin_addr.s_addr
+#define rt_ifp rt_spares[0].rts_ifp
+#define rt_gate rt_spares[0].rts_gate
+#define rt_router rt_spares[0].rts_router
+#define rt_metric rt_spares[0].rts_metric
+#define rt_tag rt_spares[0].rts_tag
+#define rt_time rt_spares[0].rts_time
+
+#define HOST_MASK 0xffffffff
+#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK)
+
+/* age all routes that
+ * are not from -g, -m, or static routes from the kernel
+ * not unbroken interface routes
+ * but not broken interfaces
+ * nor non-passive, remote interfaces that are not aliases
+ * (i.e. remote & metric=0)
+ */
+#define AGE_RT(rt_state,ifp) (0 == ((rt_state) & (RS_MHOME | RS_STATIC \
+ | RS_NET_SYN | RS_RDISC)) \
+ && (!((rt_state) & RS_IF) \
+ || (ifp) == 0 \
+ || (((ifp)->int_state & IS_REMOTE) \
+ && !((ifp)->int_state & IS_PASSIVE))))
+
+/* true if A is better than B
+ * Better if
+ * - A is not a poisoned route
+ * - and A is not stale
+ * - and A has a shorter path
+ * - or is the router speaking for itself
+ * - or the current route is equal but stale
+ * - or it is a host route advertised by a system for itself
+ */
+#define BETTER_LINK(rt,A,B) ((A)->rts_metric < HOPCNT_INFINITY \
+ && now_stale <= (A)->rts_time \
+ && ((A)->rts_metric < (B)->rts_metric \
+ || ((A)->rts_gate == (A)->rts_router \
+ && (B)->rts_gate != (B)->rts_router) \
+ || ((A)->rts_metric == (B)->rts_metric \
+ && now_stale > (B)->rts_time) \
+ || (RT_ISHOST(rt) \
+ && (rt)->rt_dst == (A)->rts_router \
+ && (A)->rts_metric == (B)->rts_metric)))
+
+
+/* An "interface" is similar to a kernel ifnet structure, except it also
+ * handles "logical" or "IS_REMOTE" interfaces (remote gateways).
+ */
+struct interface {
+ struct interface *int_next, **int_prev;
+ struct interface *int_ahash, **int_ahash_prev;
+ struct interface *int_bhash, **int_bhash_prev;
+ struct interface *int_rlink, **int_rlink_prev;
+ struct interface *int_nhash, **int_nhash_prev;
+ char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */
+ u_short int_index;
+ naddr int_addr; /* address on this host (net order) */
+ naddr int_brdaddr; /* broadcast address (n) */
+ naddr int_dstaddr; /* other end of pt-to-pt link (n) */
+ naddr int_net; /* working network # (host order)*/
+ naddr int_mask; /* working net mask (host order) */
+ naddr int_ripv1_mask; /* for inferring a mask (n) */
+ naddr int_std_addr; /* class A/B/C address (n) */
+ naddr int_std_net; /* class A/B/C network (h) */
+ naddr int_std_mask; /* class A/B/C netmask (h) */
+ int int_rip_sock; /* for queries */
+ int int_if_flags; /* some bits copied from kernel */
+ u_int int_state;
+ time_t int_act_time; /* last thought healthy */
+ time_t int_query_time;
+ u_short int_transitions; /* times gone up-down */
+ char int_metric;
+ char int_d_metric; /* for faked default route */
+ struct int_data {
+ u_int ipackets; /* previous network stats */
+ u_int ierrors;
+ u_int opackets;
+ u_int oerrors;
+#ifdef sgi
+ u_int odrops;
+#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];
+ int int_rdisc_pref; /* advertised rdisc preference */
+ int int_rdisc_int; /* MaxAdvertiseInterval */
+ int int_rdisc_cnt;
+ struct timeval int_rdisc_timer;
+};
+
+/* bits in int_state */
+#define IS_ALIAS 0x0000001 /* interface alias */
+#define IS_SUBNET 0x0000002 /* interface on subnetted network */
+#define IS_REMOTE 0x0000004 /* interface is not on this machine */
+#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */
+#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */
+#define IS_CHECKED 0x0000020 /* still exists */
+#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_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 */
+#define IS_NO_RIPV1_IN 0x0010000 /* no RIPv1 input at all */
+#define IS_NO_RIPV2_IN 0x0020000 /* no RIPv2 input at all */
+#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN)
+#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN)
+#define IS_NO_RIPV1_OUT 0x0040000 /* no RIPv1 output at all */
+#define IS_NO_RIPV2_OUT 0x0080000 /* no RIPv2 output at all */
+#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT)
+#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN)
+#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT)
+#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP)
+#define IS_NO_ADV_IN 0x0100000
+#define IS_NO_SOL_OUT 0x0200000 /* no solicitations */
+#define IS_SOL_OUT 0x0400000 /* send solicitations */
+#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT)
+#define IS_NO_ADV_OUT 0x0800000 /* do not advertise rdisc */
+#define IS_ADV_OUT 0x1000000 /* advertise rdisc */
+#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT)
+#define IS_BCAST_RDISC 0x2000000 /* broadcast instead of multicast */
+#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT)
+#define IS_PM_RDISC 0x4000000 /* poor-man's router discovery */
+
+#ifdef sgi
+#define IFF_UP_RUNNING (IFF_RUNNING|IFF_UP)
+#else
+#define IFF_UP_RUNNING IFF_UP
+#endif
+#define iff_alive(f) (((f) & IFF_UP_RUNNING) == IFF_UP_RUNNING)
+
+
+/* Information for aggregating routes */
+#define NUM_AG_SLOTS 32
+struct ag_info {
+ struct ag_info *ag_fine; /* slot with finer netmask */
+ struct ag_info *ag_cors; /* more coarse netmask */
+ naddr ag_dst_h; /* destination in host byte order */
+ naddr ag_mask;
+ naddr ag_gate;
+ naddr ag_nhop;
+ char ag_metric; /* metric to be advertised */
+ char ag_pref; /* aggregate based on this */
+ u_int ag_seqno;
+ u_short ag_tag;
+ u_short ag_state;
+#define AGS_SUPPRESS 0x001 /* combine with coaser mask */
+#define AGS_PROMOTE 0x002 /* synthesize combined routes */
+#define AGS_REDUN0 0x004 /* redundant, finer routes output */
+#define AGS_REDUN1 0x008
+#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \
+ == (AGS_REDUN0 | AGS_REDUN1))
+#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */
+#define AGS_IF 0x020 /* for an interface */
+#define AGS_RIPV2 0x040 /* send only as RIPv2 */
+#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when this
+ * has the finer netmask */
+#define AGS_CORS_GATE 0x100 /* ignore differing gate when this
+ * has the coarser netmaks */
+#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */
+
+ /* some bits are set if they are set on either route */
+#define AGS_PROMOTE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \
+ AGS_SUPPRESS | AGS_CORS_GATE)
+};
+
+
+/* parameters for interfaces */
+extern struct parm {
+ struct parm *parm_next;
+ char parm_name[IFNAMSIZ+1];
+ naddr parm_net;
+ naddr parm_mask;
+
+ char parm_d_metric;
+ u_int parm_int_state;
+ int parm_rdisc_pref;
+ int parm_rdisc_int;
+ struct auth parm_auth[MAX_AUTH_KEYS];
+} *parms;
+
+/* authority for internal networks */
+extern struct intnet {
+ struct intnet *intnet_next;
+ naddr intnet_addr;
+ naddr intnet_mask;
+ char intnet_metric;
+} *intnets;
+
+/* trusted routers */
+extern struct tgate {
+ struct tgate *tgate_next;
+ naddr tgate_addr;
+} *tgates;
+
+enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST,
+ NO_OUT_MULTICAST, NO_OUT_RIPV2};
+
+/* common output buffers */
+extern struct ws_buf {
+ struct rip *buf;
+ struct netinfo *n;
+ struct netinfo *base;
+ struct netinfo *lim;
+ enum output_type type;
+} v12buf, v2buf;
+
+extern pid_t mypid;
+extern naddr myaddr; /* main address of this system */
+
+extern int stopint; /* !=0 to stop */
+
+extern int sock_max;
+extern int rip_sock; /* RIP socket */
+extern struct interface *rip_sock_mcast; /* current multicast interface */
+extern int rt_sock; /* routing socket */
+extern int rt_sock_seqno;
+extern int rdisc_sock; /* router-discovery raw socket */
+
+extern int seqno; /* sequence number for messages */
+extern int supplier; /* process should supply updates */
+extern int lookforinterfaces; /* 1=probe for new up interfaces */
+extern int supplier_set; /* -s or -q requested */
+extern int ridhosts; /* 1=reduce host routes */
+extern int mhome; /* 1=want multi-homed host route */
+extern int advertise_mhome; /* 1=must continue adverising it */
+extern int auth_ok; /* 1=ignore auth if we do not care */
+
+extern struct timeval clk; /* system clock's idea of time */
+extern struct timeval epoch; /* system clock when started */
+extern struct timeval now; /* current idea of time */
+extern time_t now_stale;
+extern time_t now_expire;
+extern time_t now_garbage;
+
+extern struct timeval next_bcast; /* next general broadcast */
+extern struct timeval age_timer; /* next check of old routes */
+extern struct timeval no_flash; /* inhibit flash update until then */
+extern struct timeval rdisc_timer; /* next advert. or solicitation */
+extern int rdisc_ok; /* using solicited route */
+
+extern struct timeval ifinit_timer; /* time to check interfaces */
+
+extern naddr loopaddr; /* our address on loopback */
+extern int tot_interfaces; /* # of remote and local interfaces */
+extern int rip_interfaces; /* # of interfaces doing RIP */
+extern struct interface *ifnet; /* all interfaces */
+extern struct interface *remote_if; /* remote interfaces */
+extern int have_ripv1_out; /* have a RIPv1 interface */
+extern int have_ripv1_in;
+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;
+#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;
+
+
+#ifdef sgi
+/* Fix conflicts */
+#define dup2(x,y) BSDdup2(x,y)
+#endif /* sgi */
+
+extern void fix_sock(int, char *);
+extern void fix_select(void);
+extern void rip_off(void);
+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 rip_query(void);
+extern void rip_bcast(int);
+extern void supply(struct sockaddr_in *, struct interface *,
+ enum output_type, int, int, int);
+
+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))
+extern void logbad(int, char *, ...);
+#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno))
+#ifdef DEBUG
+#define DBGERR(dump,msg) BADERR(dump,msg)
+#else
+#define DBGERR(dump,msg) LOGERR(msg)
+#endif
+extern char *naddr_ntoa(naddr);
+extern char *saddr_ntoa(struct sockaddr *);
+
+extern void *rtmalloc(size_t, char *);
+extern void timevaladd(struct timeval *, struct timeval *);
+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 *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_off(char*, ...);
+extern void set_tracelevel(void);
+extern void trace_flush(void);
+extern void trace_kernel(char *, ...);
+extern void trace_act(char *, ...);
+extern void trace_pkt(char *, ...);
+extern void trace_add_del(char *, struct rt_entry *);
+extern void trace_change(struct rt_entry *, u_int, naddr, naddr, int,
+ u_short, struct interface *, time_t, char *);
+extern void trace_if(char *, struct interface *);
+extern void trace_upslot(struct rt_entry *, struct rt_spare *,
+ naddr, naddr,
+ struct interface *, int, u_short, time_t);
+extern void trace_rip(char*, char*, struct sockaddr_in *,
+ struct interface *, struct rip *, int);
+extern char *addrname(naddr, naddr, int);
+
+extern void rdisc_age(naddr);
+extern void set_rdisc_mg(struct interface *, int);
+extern void set_supplier(void);
+extern void if_bad_rdisc(struct interface *);
+extern void if_ok_rdisc(struct interface *);
+extern void read_rip(int, struct interface *);
+extern void read_rt(void);
+extern void read_d(void);
+extern void rdisc_adv(void);
+extern void rdisc_sol(void);
+
+extern void sigalrm(int);
+extern void sigterm(int);
+
+extern void sigtrace_on(int);
+extern void sigtrace_off(int);
+
+extern void flush_kern(void);
+extern void age(naddr);
+
+extern void ag_flush(naddr, naddr, void (*)(struct ag_info *));
+extern void ag_check(naddr, naddr, naddr, naddr, char, char, u_int,
+ u_short, u_short, void (*)(struct ag_info *));
+extern void del_static(naddr, naddr, int);
+extern void del_redirects(naddr, time_t);
+extern struct rt_entry *rtget(naddr, naddr);
+extern struct rt_entry *rtfind(naddr);
+extern void rtinit(void);
+extern void rtadd(naddr, naddr, naddr, naddr,
+ int, u_short, u_int, struct interface *);
+extern void rtchange(struct rt_entry *, u_int, naddr,naddr, int, u_short,
+ struct interface *ifp, time_t, char *);
+extern void rtdelete(struct rt_entry *);
+extern void rtbad_sub(struct rt_entry *);
+extern void rtswitch(struct rt_entry *, struct rt_spare *);
+extern void rtbad(struct rt_entry *);
+
+
+#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr)
+#define INFO_DST(I) ((I)->rti_info[RTAX_DST])
+#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY])
+#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK])
+#define INFO_IFA(I) ((I)->rti_info[RTAX_IFA])
+#define INFO_IFP(I) ((I)->rti_info[RTAX_IFP])
+#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR])
+#define INFO_BRD(I) ((I)->rti_info[RTAX_BRD])
+void rt_xaddrs(struct rt_addrinfo *, struct sockaddr *, struct sockaddr *,
+ int);
+
+extern naddr std_mask(naddr);
+extern naddr ripv1_mask_net(naddr, struct interface *);
+extern naddr ripv1_mask_host(naddr,struct interface *);
+#define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0)
+extern int check_dst(naddr);
+extern struct interface *check_dup(naddr, naddr, naddr, int);
+extern int check_remote(struct interface *);
+extern int addrouteforif(register struct interface *);
+extern void ifinit(void);
+extern int walk_bad(struct radix_node *, struct walkarg *);
+extern int if_ok(struct interface *, char *);
+extern void if_sick(struct interface *);
+extern void if_bad(struct interface *);
+extern void if_link(struct interface *);
+extern struct interface *ifwithaddr(naddr, int, int);
+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 *);
+
+#include <md5.h>
diff --git a/sbin/routed/if.c b/sbin/routed/if.c
new file mode 100644
index 0000000..b68ff52
--- /dev/null
+++ b/sbin/routed/if.c
@@ -0,0 +1,1341 @@
+/*
+ * 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.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.23 $"
+
+#include "defs.h"
+#include "pathnames.h"
+
+struct interface *ifnet; /* all interfaces */
+
+/* hash table for all interfaces, big enough to tolerate ridiculous
+ * numbers of IP aliases. Crazy numbers of aliases such as 7000
+ * still will not do well, but not just in looking up interfaces
+ * by name or address.
+ */
+#define AHASH_LEN 211 /* must be prime */
+#define AHASH(a) &ahash_tbl[(a)%AHASH_LEN]
+struct interface *ahash_tbl[AHASH_LEN];
+
+#define BHASH_LEN 211 /* must be prime */
+#define BHASH(a) &bhash_tbl[(a)%BHASH_LEN]
+struct interface *bhash_tbl[BHASH_LEN];
+
+struct interface *remote_if; /* remote interfaces */
+
+/* hash for physical interface names.
+ * Assume there are never more 100 or 200 real interfaces, and that
+ * aliases are put on the end of the hash chains.
+ */
+#define NHASH_LEN 97
+struct interface *nhash_tbl[NHASH_LEN];
+
+int tot_interfaces; /* # of remote and local interfaces */
+int rip_interfaces; /* # of interfaces doing RIP */
+int foundloopback; /* valid flag for loopaddr */
+naddr loopaddr; /* our address on loopback */
+
+struct timeval ifinit_timer;
+static struct timeval last_ifinit;
+
+int have_ripv1_out; /* have a RIPv1 interface */
+int have_ripv1_in;
+
+
+static struct interface**
+nhash(register char *p)
+{
+ register u_int i;
+
+ for (i = 0; *p != '\0'; p++) {
+ i = ((i<<1) & 0x7fffffff) | ((i>>31) & 1);
+ i ^= *p;
+ }
+ return &nhash_tbl[i % NHASH_LEN];
+}
+
+
+/* Link a new interface into the lists and hash tables.
+ */
+void
+if_link(struct interface *ifp)
+{
+ struct interface **hifp;
+
+ ifp->int_prev = &ifnet;
+ ifp->int_next = ifnet;
+ if (ifnet != 0)
+ ifnet->int_prev = &ifp->int_next;
+ ifnet = ifp;
+
+ hifp = AHASH(ifp->int_addr);
+ ifp->int_ahash_prev = hifp;
+ if ((ifp->int_ahash = *hifp) != 0)
+ (*hifp)->int_ahash_prev = &ifp->int_ahash;
+ *hifp = ifp;
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ hifp = BHASH(ifp->int_brdaddr);
+ ifp->int_bhash_prev = hifp;
+ if ((ifp->int_bhash = *hifp) != 0)
+ (*hifp)->int_bhash_prev = &ifp->int_bhash;
+ *hifp = ifp;
+ }
+
+ if (ifp->int_state & IS_REMOTE) {
+ ifp->int_rlink_prev = &remote_if;
+ ifp->int_rlink = remote_if;
+ if (remote_if != 0)
+ remote_if->int_rlink_prev = &ifp->int_rlink;
+ remote_if = ifp;
+ }
+
+ hifp = nhash(ifp->int_name);
+ if (ifp->int_state & IS_ALIAS) {
+ /* put aliases on the end of the hash chain */
+ while (*hifp != 0)
+ hifp = &(*hifp)->int_nhash;
+ }
+ ifp->int_nhash_prev = hifp;
+ if ((ifp->int_nhash = *hifp) != 0)
+ (*hifp)->int_nhash_prev = &ifp->int_nhash;
+ *hifp = ifp;
+}
+
+
+/* Find the interface with an address
+ */
+struct interface *
+ifwithaddr(naddr addr,
+ int bcast, /* notice IFF_BROADCAST address */
+ int remote) /* include IS_REMOTE interfaces */
+{
+ struct interface *ifp, *possible = 0;
+
+ remote = (remote == 0) ? IS_REMOTE : 0;
+
+ for (ifp = *AHASH(addr); ifp; ifp = ifp->int_ahash) {
+ if (ifp->int_addr != addr)
+ continue;
+ if ((ifp->int_state & remote) != 0)
+ continue;
+ if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
+ return ifp;
+ possible = ifp;
+ }
+
+ if (possible || !bcast)
+ return possible;
+
+ for (ifp = *BHASH(addr); ifp; ifp = ifp->int_bhash) {
+ if (ifp->int_brdaddr != addr)
+ continue;
+ if ((ifp->int_state & remote) != 0)
+ continue;
+ if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
+ return ifp;
+ possible = ifp;
+ }
+
+ return possible;
+}
+
+
+/* find the interface with a name
+ */
+struct interface *
+ifwithname(char *name, /* "ec0" or whatever */
+ naddr addr) /* 0 or network address */
+{
+ struct interface *ifp;
+
+ for (;;) {
+ for (ifp = *nhash(name); ifp != 0; ifp = ifp->int_nhash) {
+ /* If the network address is not specified,
+ * ignore any alias interfaces. Otherwise, look
+ * for the interface with the target name and address.
+ */
+ if (!strcmp(ifp->int_name, name)
+ && ((addr == 0 && !(ifp->int_state & IS_ALIAS))
+ || (ifp->int_addr == addr)))
+ return ifp;
+ }
+
+ /* If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (last_ifinit.tv_sec == now.tv_sec
+ && last_ifinit.tv_usec == now.tv_usec)
+ return 0;
+ ifinit();
+ }
+}
+
+
+struct interface *
+ifwithindex(u_short index)
+{
+ struct interface *ifp;
+
+
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_index == index)
+ return ifp;
+ }
+ return 0;
+}
+
+
+/* Find an interface from which the specified address
+ * should have come from. Used for figuring out which
+ * interface a packet came in on.
+ */
+struct interface *
+iflookup(naddr addr)
+{
+ struct interface *ifp, *maybe;
+
+ maybe = 0;
+ for (;;) {
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* finished with a match */
+ if (ifp->int_dstaddr == addr)
+ return ifp;
+
+ } else {
+ /* finished with an exact match */
+ if (ifp->int_addr == addr)
+ return ifp;
+
+ /* Look for the longest approximate match.
+ */
+ if (on_net(addr, ifp->int_net, ifp->int_mask)
+ && (maybe == 0
+ || ifp->int_mask > maybe->int_mask))
+ maybe = ifp;
+ }
+ }
+
+ if (maybe != 0
+ || (last_ifinit.tv_sec == now.tv_sec
+ && last_ifinit.tv_usec == now.tv_usec))
+ return maybe;
+
+ /* If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ ifinit();
+ }
+}
+
+
+/* Return the classical netmask for an IP address.
+ */
+naddr /* host byte order */
+std_mask(naddr addr) /* network byte order */
+{
+ NTOHL(addr); /* was a host, not a network */
+
+ if (addr == 0) /* default route has mask 0 */
+ return 0;
+ if (IN_CLASSA(addr))
+ return IN_CLASSA_NET;
+ if (IN_CLASSB(addr))
+ return IN_CLASSB_NET;
+ return IN_CLASSC_NET;
+}
+
+
+/* Find the netmask that would be inferred by RIPv1 listeners
+ * on the given interface for a given network.
+ * If no interface is specified, look for the best fitting interface.
+ */
+naddr
+ripv1_mask_net(naddr addr, /* in network byte order */
+ struct interface *ifp) /* as seen on this interface */
+{
+ naddr mask = 0;
+
+ if (addr == 0) /* default always has 0 mask */
+ return mask;
+
+ if (ifp != 0) {
+ /* If the target network is that of the associated interface
+ * on which it arrived, then use the netmask of the interface.
+ */
+ if (on_net(addr, ifp->int_net, ifp->int_std_mask))
+ mask = ifp->int_ripv1_mask;
+
+ } else {
+ /* Examine all interfaces, and if it the target seems
+ * to have the same network number of an interface, use the
+ * netmask of that interface. If there is more than one
+ * such interface, prefer the interface with the longest
+ * match.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (on_net(addr, ifp->int_std_net, ifp->int_std_mask)
+ && ifp->int_ripv1_mask > mask)
+ mask = ifp->int_ripv1_mask;
+ }
+ }
+
+ /* Otherwise, make the classic A/B/C guess.
+ */
+ if (mask == 0)
+ mask = std_mask(addr);
+
+ return mask;
+}
+
+
+naddr
+ripv1_mask_host(naddr addr, /* in network byte order */
+ struct interface *ifp) /* as seen on this interface */
+{
+ naddr mask = ripv1_mask_net(addr, ifp);
+
+
+ /* If the computed netmask does not mask the address,
+ * then assume it is a host address
+ */
+ if ((ntohl(addr) & ~mask) != 0)
+ mask = HOST_MASK;
+ return mask;
+}
+
+
+/* See if a IP address looks reasonable as a destination
+ */
+int /* 0=bad */
+check_dst(naddr addr)
+{
+ NTOHL(addr);
+
+ if (IN_CLASSA(addr)) {
+ if (addr == 0)
+ return 1; /* default */
+
+ addr >>= IN_CLASSA_NSHIFT;
+ return (addr != 0 && addr != IN_LOOPBACKNET);
+ }
+
+ return (IN_CLASSB(addr) || IN_CLASSC(addr));
+}
+
+
+/* See a new interface duplicates an existing interface.
+ */
+struct interface *
+check_dup(naddr addr, /* IP address, so network byte order */
+ naddr dstaddr, /* ditto */
+ naddr mask, /* mask, so host byte order */
+ int if_flags)
+{
+ struct interface *ifp;
+
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_mask != mask)
+ continue;
+
+ if (!iff_alive(ifp->int_if_flags))
+ continue;
+
+ /* The local address can only be shared with a point-to-
+ * point link.
+ */
+ if (ifp->int_addr == addr
+ && (((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0))
+ return ifp;
+
+ if (on_net(ifp->int_dstaddr, ntohl(dstaddr),mask))
+ return ifp;
+ }
+ return 0;
+}
+
+
+/* See that a remote gateway is reachable.
+ * Note that the answer can change as real interfaces come and go.
+ */
+int /* 0=bad */
+check_remote(struct interface *ifp)
+{
+ struct rt_entry *rt;
+
+ /* do not worry about other kinds */
+ if (!(ifp->int_state & IS_REMOTE))
+ return 1;
+
+ rt = rtfind(ifp->int_addr);
+ if (rt != 0
+ && rt->rt_ifp != 0
+ &&on_net(ifp->int_addr,
+ rt->rt_ifp->int_net, rt->rt_ifp->int_mask))
+ return 1;
+
+ /* the gateway cannot be reached directly from one of our
+ * interfaces
+ */
+ if (!(ifp->int_state & IS_BROKE)) {
+ msglog("unreachable gateway %s in "_PATH_GATEWAYS,
+ naddr_ntoa(ifp->int_addr));
+ if_bad(ifp);
+ }
+ return 0;
+}
+
+
+/* Delete an interface.
+ */
+static void
+ifdel(struct interface *ifp)
+{
+ struct ip_mreq m;
+ struct interface *ifp1;
+
+
+ trace_if("Del", ifp);
+
+ ifp->int_state |= IS_BROKE;
+
+ /* unlink the interface
+ */
+ *ifp->int_prev = ifp->int_next;
+ if (ifp->int_next != 0)
+ ifp->int_next->int_prev = ifp->int_prev;
+ *ifp->int_ahash_prev = ifp->int_ahash;
+ if (ifp->int_ahash != 0)
+ ifp->int_ahash->int_ahash_prev = ifp->int_ahash_prev;
+ *ifp->int_nhash_prev = ifp->int_nhash;
+ if (ifp->int_nhash != 0)
+ ifp->int_nhash->int_nhash_prev = ifp->int_nhash_prev;
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ *ifp->int_bhash_prev = ifp->int_bhash;
+ if (ifp->int_bhash != 0)
+ ifp->int_bhash->int_bhash_prev = ifp->int_bhash_prev;
+ }
+ if (ifp->int_state & IS_REMOTE) {
+ *ifp->int_rlink_prev = ifp->int_rlink;
+ if (ifp->int_rlink != 0)
+ ifp->int_rlink->int_rlink_prev = ifp->int_rlink_prev;
+ }
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ /* delete aliases when the main interface dies
+ */
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ ifdel(ifp1);
+ }
+
+ if ((ifp->int_if_flags & IFF_MULTICAST)
+#ifdef MCAST_PPP_BUG
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+#endif
+ && rip_sock >= 0) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+ m.imr_interface.s_addr = ((ifp->int_if_flags
+ & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+ if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0
+ && errno != EADDRNOTAVAIL
+ && !TRACEACTIONS)
+ LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)");
+ if (rip_sock_mcast == ifp)
+ rip_sock_mcast = 0;
+ }
+ if (ifp->int_rip_sock >= 0) {
+ (void)close(ifp->int_rip_sock);
+ ifp->int_rip_sock = -1;
+ fix_select();
+ }
+
+ tot_interfaces--;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces--;
+
+ /* Zap all routes associated with this interface.
+ * Assume routes just using gateways beyond this interface will
+ * timeout naturally, and have probably already died.
+ */
+ (void)rn_walktree(rhead, walk_bad, 0);
+
+ set_rdisc_mg(ifp, 0);
+ if_bad_rdisc(ifp);
+ }
+
+ free(ifp);
+}
+
+
+/* Mark an interface ill.
+ */
+void
+if_sick(struct interface *ifp)
+{
+ if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) {
+ ifp->int_state |= IS_SICK;
+ ifp->int_act_time = NEVER;
+ trace_if("Chg", ifp);
+
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+ }
+}
+
+
+/* Mark an interface dead.
+ */
+void
+if_bad(struct interface *ifp)
+{
+ struct interface *ifp1;
+
+
+ if (ifp->int_state & IS_BROKE)
+ return;
+
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ ifp->int_state |= (IS_BROKE | IS_SICK);
+ ifp->int_act_time = NEVER;
+ ifp->int_query_time = NEVER;
+ ifp->int_data.ts = 0;
+
+ trace_if("Chg", ifp);
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ if_bad(ifp1);
+ }
+ (void)rn_walktree(rhead, walk_bad, 0);
+ if_bad_rdisc(ifp);
+ }
+}
+
+
+/* Mark an interface alive
+ */
+int /* 1=it was dead */
+if_ok(struct interface *ifp,
+ char *type)
+{
+ struct interface *ifp1;
+
+
+ if (!(ifp->int_state & IS_BROKE)) {
+ if (ifp->int_state & IS_SICK) {
+ trace_act("%sinterface %s to %s working better",
+ type,
+ ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~IS_SICK;
+ }
+ return 0;
+ }
+
+ msglog("%sinterface %s to %s restored",
+ type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~(IS_BROKE | IS_SICK);
+ ifp->int_data.ts = 0;
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1 != ifp
+ && !strcmp(ifp->int_name, ifp1->int_name))
+ if_ok(ifp1, type);
+ }
+ if_ok_rdisc(ifp);
+ }
+
+ if (ifp->int_state & IS_REMOTE) {
+ if (!addrouteforif(ifp))
+ return 0;
+ }
+ return 1;
+}
+
+
+/* disassemble routing message
+ */
+void
+rt_xaddrs(struct rt_addrinfo *info,
+ struct sockaddr *sa,
+ struct sockaddr *lim,
+ int addrs)
+{
+ int i;
+#ifdef _HAVE_SA_LEN
+ static struct sockaddr sa_zero;
+#endif
+#ifdef sgi
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \
+ : sizeof(__uint64_t))
+#else
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \
+ : sizeof(long))
+#endif
+
+
+ bzero(info, sizeof(*info));
+ info->rti_addrs = addrs;
+ for (i = 0; i < RTAX_MAX && sa < lim; i++) {
+ if ((addrs & (1 << i)) == 0)
+ continue;
+#ifdef _HAVE_SA_LEN
+ info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero;
+ sa = (struct sockaddr *)((char*)(sa)
+ + ROUNDUP(sa->sa_len));
+#else
+ info->rti_info[i] = sa;
+ sa = (struct sockaddr *)((char*)(sa)
+ + ROUNDUP(_FAKE_SA_LEN_DST(sa)));
+#endif
+ }
+}
+
+
+/* Find the network interfaces which have configured themselves.
+ * This must be done regularly, if only for extra addresses
+ * that come and go on interfaces.
+ */
+void
+ifinit(void)
+{
+ static char *sysctl_buf;
+ static size_t sysctl_buf_size = 0;
+ uint complaints = 0;
+ static u_int prev_complaints = 0;
+# define COMP_NOT_INET 0x001
+# define COMP_NOADDR 0x002
+# define COMP_BADADDR 0x004
+# define COMP_NODST 0x008
+# define COMP_NOBADR 0x010
+# define COMP_NOMASK 0x020
+# define COMP_DUP 0x040
+# define COMP_BAD_METRIC 0x080
+# define COMP_NETMASK 0x100
+
+ struct interface ifs, ifs0, *ifp, *ifp1;
+ struct rt_entry *rt;
+ size_t needed;
+ int mib[6];
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam, *ifam_lim, *ifam2;
+ int in, ierr, out, oerr;
+ struct intnet *intnetp;
+ struct rt_addrinfo info;
+#ifdef SIOCGIFMETRIC
+ struct ifreq ifr;
+#endif
+
+
+ last_ifinit = now;
+ ifinit_timer.tv_sec = now.tv_sec + (supplier
+ ? CHECK_ACT_INTERVAL
+ : CHECK_QUIET_INTERVAL);
+
+ /* mark all interfaces so we can get rid of thost that disappear */
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next)
+ ifp->int_state &= ~(IS_CHECKED | IS_DUP);
+
+ /* Fetch the interface list, without too many system calls
+ * since we do it repeatedly.
+ */
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ for (;;) {
+ if ((needed = sysctl_buf_size) != 0) {
+ if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0)
+ break;
+ if (errno != ENOMEM && errno != EFAULT)
+ BADERR(1, "ifinit: get interface table");
+ free(sysctl_buf);
+ needed = 0;
+ }
+ if (sysctl(mib, 6, 0, &needed, 0, 0) < 0)
+ BADERR(1,"ifinit: route-sysctl-estimate");
+ sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit");
+ }
+
+ ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed);
+ for (ifam = (struct ifa_msghdr *)sysctl_buf;
+ ifam < ifam_lim;
+ ifam = ifam2) {
+
+ ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen);
+
+ if (ifam->ifam_type == RTM_IFINFO) {
+ struct sockaddr_dl *sdl;
+
+ ifm = (struct if_msghdr *)ifam;
+ /* make prototype structure for the IP aliases
+ */
+ bzero(&ifs0, sizeof(ifs0));
+ ifs0.int_rip_sock = -1;
+ ifs0.int_index = ifm->ifm_index;
+ ifs0.int_if_flags = ifm->ifm_flags;
+ ifs0.int_state = IS_CHECKED;
+ ifs0.int_query_time = NEVER;
+ ifs0.int_act_time = now.tv_sec;
+ ifs0.int_data.ts = now.tv_sec;
+ ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets;
+ ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors;
+ ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets;
+ ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors;
+#ifdef sgi
+ ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops;
+#endif
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ sdl->sdl_data[sdl->sdl_nlen] = 0;
+ strncpy(ifs0.int_name, sdl->sdl_data,
+ MIN(sizeof(ifs0.int_name), sdl->sdl_nlen));
+ continue;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR) {
+ logbad(1,"ifinit: out of sync");
+ continue;
+ }
+ rt_xaddrs(&info, (struct sockaddr *)(ifam+1),
+ (struct sockaddr *)ifam2,
+ ifam->ifam_addrs);
+
+ /* Prepare for the next address of this interface, which
+ * will be an alias.
+ * Do not output RIP or Router-Discovery packets via aliases.
+ */
+ bcopy(&ifs0, &ifs, sizeof(ifs));
+ ifs0.int_state |= (IS_ALIAS | IS_NO_RIP | IS_NO_RDISC);
+
+ if (INFO_IFA(&info) == 0) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOADDR))
+ msglog("%s has no address",
+ ifs.int_name);
+ complaints |= COMP_NOADDR;
+ }
+ continue;
+ }
+ if (INFO_IFA(&info)->sa_family != AF_INET) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOT_INET))
+ trace_act("%s: not AF_INET",
+ ifs.int_name);
+ complaints |= COMP_NOT_INET;
+ }
+ continue;
+ }
+
+ ifs.int_addr = S_ADDR(INFO_IFA(&info));
+
+ if (ntohl(ifs.int_addr)>>24 == 0
+ || ntohl(ifs.int_addr)>>24 == 0xff) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_BADADDR))
+ msglog("%s has a bad address",
+ ifs.int_name);
+ complaints |= COMP_BADADDR;
+ }
+ continue;
+ }
+
+ if (ifs.int_if_flags & IFF_LOOPBACK) {
+ ifs.int_state |= IS_PASSIVE | IS_NO_RIP | IS_NO_RDISC;
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_mask = HOST_MASK;
+ ifs.int_ripv1_mask = HOST_MASK;
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+ ifs.int_net = ntohl(ifs.int_dstaddr);
+ if (!foundloopback) {
+ foundloopback = 1;
+ loopaddr = ifs.int_addr;
+ }
+
+ } else if (ifs.int_if_flags & IFF_POINTOPOINT) {
+ if (INFO_BRD(&info) == 0
+ || INFO_BRD(&info)->sa_family != AF_INET) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ msglog("%s has a bad"
+ " destination address",
+ ifs.int_name);
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_dstaddr = S_ADDR(INFO_BRD(&info));
+ if (ntohl(ifs.int_dstaddr)>>24 == 0
+ || ntohl(ifs.int_dstaddr)>>24 == 0xff) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ msglog("%s has a bad"
+ " destination address",
+ ifs.int_name);
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_mask = HOST_MASK;
+ ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info)));
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+ ifs.int_net = ntohl(ifs.int_dstaddr);
+
+ } else {
+ if (INFO_MASK(&info) == 0) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOMASK))
+ msglog("%s has no netmask",
+ ifs.int_name);
+ complaints |= COMP_NOMASK;
+ }
+ continue;
+ }
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info)));
+ ifs.int_ripv1_mask = ifs.int_mask;
+ ifs.int_std_mask = std_mask(ifs.int_addr);
+ ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask;
+ if (ifs.int_mask != ifs.int_std_mask)
+ ifs.int_state |= IS_SUBNET;
+
+ if (ifs.int_if_flags & IFF_BROADCAST) {
+ if (INFO_BRD(&info) == 0) {
+ if (iff_alive(ifs.int_if_flags)) {
+ if (!(prev_complaints
+ & COMP_NOBADR))
+ msglog("%s has"
+ "no broadcast address",
+ ifs.int_name);
+ complaints |= COMP_NOBADR;
+ }
+ continue;
+ }
+ ifs.int_brdaddr = S_ADDR(INFO_BRD(&info));
+ }
+ }
+ ifs.int_std_net = ifs.int_net & ifs.int_std_mask;
+ ifs.int_std_addr = htonl(ifs.int_std_net);
+
+ /* Use a minimum metric of one. Treat the interface metric
+ * (default 0) as an increment to the hop count of one.
+ *
+ * The metric obtained from the routing socket dump of
+ * interface addresses is wrong. It is not set by the
+ * SIOCSIFMETRIC ioctl.
+ */
+#ifdef SIOCGIFMETRIC
+ strncpy(ifr.ifr_name, ifs.int_name, sizeof(ifr.ifr_name));
+ if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) {
+ DBGERR(1, "ioctl(SIOCGIFMETRIC)");
+ ifs.int_metric = 0;
+ } else {
+ ifs.int_metric = ifr.ifr_metric;
+ }
+#else
+ ifs.int_metric = ifam->ifam_metric;
+#endif
+ if (ifs.int_metric > HOPCNT_INFINITY) {
+ ifs.int_metric = 0;
+ if (!(prev_complaints & COMP_BAD_METRIC)
+ && iff_alive(ifs.int_if_flags)) {
+ complaints |= COMP_BAD_METRIC;
+ msglog("%s has a metric of %d",
+ ifs.int_name, ifs.int_metric);
+ }
+ }
+
+ /* See if this is a familiar interface.
+ * If so, stop worrying about it if it is the same.
+ * Start it over if it now is to somewhere else, as happens
+ * frequently with PPP and SLIP.
+ */
+ ifp = ifwithname(ifs.int_name, ((ifs.int_state & IS_ALIAS)
+ ? ifs.int_addr
+ : 0));
+ if (ifp != 0) {
+ ifp->int_state |= IS_CHECKED;
+
+ if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags)
+ & (IFF_BROADCAST
+ | IFF_LOOPBACK
+ | IFF_POINTOPOINT
+ | IFF_MULTICAST))
+ || 0 != ((ifp->int_state ^ ifs.int_state)
+ & IS_ALIAS)
+ || ifp->int_addr != ifs.int_addr
+ || ifp->int_brdaddr != ifs.int_brdaddr
+ || ifp->int_dstaddr != ifs.int_dstaddr
+ || ifp->int_mask != ifs.int_mask
+ || ifp->int_metric != ifs.int_metric) {
+ /* Forget old information about
+ * a changed interface.
+ */
+ trace_act("interface %s has changed",
+ ifp->int_name);
+ ifdel(ifp);
+ ifp = 0;
+ }
+ }
+
+ if (ifp != 0) {
+ /* The primary representative of an alias worries
+ * about how things are working.
+ */
+ if (ifp->int_state & IS_ALIAS)
+ continue;
+
+ /* note interfaces that have been turned off
+ */
+ if (!iff_alive(ifs.int_if_flags)) {
+ if (iff_alive(ifp->int_if_flags)) {
+ msglog("interface %s to %s turned off",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr));
+ if_bad(ifp);
+ ifp->int_if_flags &= ~IFF_UP_RUNNING;
+ }
+ continue;
+ }
+ /* or that were off and are now ok */
+ if (!iff_alive(ifp->int_if_flags)) {
+ ifp->int_if_flags |= IFF_UP_RUNNING;
+ (void)if_ok(ifp, "");
+ }
+
+ /* If it has been long enough,
+ * see if the interface is broken.
+ */
+ if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL)
+ continue;
+
+ in = ifs.int_data.ipackets - ifp->int_data.ipackets;
+ ierr = ifs.int_data.ierrors - ifp->int_data.ierrors;
+ out = ifs.int_data.opackets - ifp->int_data.opackets;
+ oerr = ifs.int_data.oerrors - ifp->int_data.oerrors;
+#ifdef sgi
+ /* Through at least IRIX 6.2, PPP and SLIP
+ * count packets dropped by the filters.
+ * But FDDI rings stuck non-operational count
+ * dropped packets as they wait for improvement.
+ */
+ if (!(ifp->int_if_flags & IFF_POINTOPOINT))
+ oerr += (ifs.int_data.odrops
+ - ifp->int_data.odrops);
+#endif
+ /* If the interface just awoke, restart the counters.
+ */
+ if (ifp->int_data.ts == 0) {
+ ifp->int_data = ifs.int_data;
+ continue;
+ }
+ ifp->int_data = ifs.int_data;
+
+ /* Withhold judgement when the short error
+ * counters wrap or the interface is reset.
+ */
+ if (ierr < 0 || in < 0 || oerr < 0 || out < 0) {
+ LIM_SEC(ifinit_timer,
+ now.tv_sec+CHECK_BAD_INTERVAL);
+ continue;
+ }
+
+ /* Withhold judgement when there is no traffic
+ */
+ if (in == 0 && out == 0 && ierr == 0 && oerr == 0)
+ continue;
+
+ /* It is bad if input or output is not working.
+ * Require presistent problems before marking it dead.
+ */
+ if ((in <= ierr && ierr > 0)
+ || (out <= oerr && oerr > 0)) {
+ if (!(ifp->int_state & IS_SICK)) {
+ trace_act("interface %s to %s"
+ " sick: in=%d ierr=%d"
+ " out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_sick(ifp);
+ continue;
+ }
+ if (!(ifp->int_state & IS_BROKE)) {
+ msglog("interface %s to %s broken:"
+ " in=%d ierr=%d out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_bad(ifp);
+ }
+ continue;
+ }
+
+ /* otherwise, it is active and healthy
+ */
+ ifp->int_act_time = now.tv_sec;
+ (void)if_ok(ifp, "");
+ continue;
+ }
+
+ /* This is a new interface.
+ * If it is dead, forget it.
+ */
+ if (!iff_alive(ifs.int_if_flags))
+ continue;
+
+ /* If it duplicates an existing interface,
+ * complain about it, mark the other one
+ * duplicated, and forget this one.
+ */
+ ifp = check_dup(ifs.int_addr,ifs.int_dstaddr,ifs.int_mask,
+ ifs.int_if_flags);
+ if (ifp != 0) {
+ if (!(prev_complaints & COMP_DUP)) {
+ complaints |= COMP_DUP;
+ msglog("%s (%s%s%s) is duplicated by"
+ " %s (%s%s%s)",
+ ifs.int_name,
+ addrname(ifs.int_addr,ifs.int_mask,1),
+ ((ifs.int_if_flags & IFF_POINTOPOINT)
+ ? "-->" : ""),
+ ((ifs.int_if_flags & IFF_POINTOPOINT)
+ ? naddr_ntoa(ifs.int_dstaddr) : ""),
+ ifp->int_name,
+ addrname(ifp->int_addr,ifp->int_mask,1),
+ ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? "-->" : ""),
+ ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? naddr_ntoa(ifp->int_dstaddr) : ""));
+ }
+ ifp->int_state |= IS_DUP;
+ continue;
+ }
+
+ if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | IFF_BROADCAST))
+ && !(ifs.int_state & IS_PASSIVE)) {
+ trace_act("%s is neither broadcast, point-to-point,"
+ " nor loopback",
+ ifs.int_name);
+ if (!(ifs.int_state & IFF_MULTICAST))
+ ifs.int_state |= IS_NO_RDISC;
+ }
+
+
+ /* It is new and ok. Add it to the list of interfaces
+ */
+ ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit");
+ bcopy(&ifs, ifp, sizeof(*ifp));
+ get_parms(ifp);
+ if_link(ifp);
+ trace_if("Add", ifp);
+
+ /* Notice likely bad netmask.
+ */
+ if (!(prev_complaints & COMP_NETMASK)
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+ && ifp->int_addr != RIP_DEFAULT) {
+ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) {
+ if (ifp1->int_mask == ifp->int_mask)
+ continue;
+ if (ifp1->int_if_flags & IFF_POINTOPOINT)
+ continue;
+ if (ifp1->int_dstaddr == RIP_DEFAULT)
+ continue;
+ if (on_net(ifp->int_dstaddr,
+ ifp1->int_net, ifp1->int_mask)
+ || on_net(ifp1->int_dstaddr,
+ ifp->int_net, ifp->int_mask)) {
+ msglog("possible netmask problem"
+ " between %s:%s and %s:%s",
+ ifp->int_name,
+ addrname(htonl(ifp->int_net),
+ ifp->int_mask, 1),
+ ifp1->int_name,
+ addrname(htonl(ifp1->int_net),
+ ifp1->int_mask, 1));
+ complaints |= COMP_NETMASK;
+ }
+ }
+ }
+
+ if (!(ifp->int_state & IS_ALIAS)) {
+ /* Count the # of directly connected networks.
+ */
+ if (!(ifp->int_if_flags & IFF_LOOPBACK))
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+
+ /* turn on router discovery and RIP If needed */
+ if_ok_rdisc(ifp);
+ rip_on(ifp);
+ }
+ }
+
+ /* If we are multi-homed and have at least two interfaces
+ * listening to RIP, then output by default.
+ */
+ if (!supplier_set && rip_interfaces > 1)
+ set_supplier();
+
+ /* If we are multi-homed, optionally advertise a route to
+ * our main address.
+ */
+ if (advertise_mhome
+ || (tot_interfaces > 1
+ && mhome
+ && (ifp = ifwithaddr(myaddr, 0, 0)) != 0
+ && foundloopback)) {
+ advertise_mhome = 1;
+ rt = rtget(myaddr, HOST_MASK);
+ if (rt != 0) {
+ if (rt->rt_ifp != ifp
+ || rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = 0;
+ } else {
+ rtchange(rt, rt->rt_state | RS_MHOME,
+ loopaddr, loopaddr,
+ 0, 0, ifp, rt->rt_time, 0);
+ }
+ }
+ if (rt == 0)
+ rtadd(myaddr, HOST_MASK, loopaddr, loopaddr,
+ 0, 0, RS_MHOME, ifp);
+ }
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp1) {
+ ifp1 = ifp->int_next; /* because we may delete it */
+
+ /* Forget any interfaces that have disappeared.
+ */
+ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) {
+ trace_act("interface %s has disappeared",
+ ifp->int_name);
+ ifdel(ifp);
+ continue;
+ }
+
+ if ((ifp->int_state & IS_BROKE)
+ && !(ifp->int_state & IS_PASSIVE))
+ LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ /* If we ever have a RIPv1 interface, assume we always will.
+ * It might come back if it ever goes away.
+ */
+ if (!(ifp->int_state & IS_NO_RIPV1_OUT) && supplier)
+ have_ripv1_out = 1;
+ if (!(ifp->int_state & IS_NO_RIPV1_IN))
+ have_ripv1_in = 1;
+ }
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Ensure there is always a network route for interfaces,
+ * after any dead interfaces have been deleted, which
+ * might affect routes for point-to-point links.
+ */
+ if (!addrouteforif(ifp))
+ continue;
+
+ /* Add routes to the local end of point-to-point interfaces
+ * using loopback.
+ */
+ if ((ifp->int_if_flags & IFF_POINTOPOINT)
+ && !(ifp->int_state & IS_REMOTE)
+ && foundloopback) {
+ /* Delete any routes to the network address through
+ * foreign routers. Remove even static routes.
+ */
+ del_static(ifp->int_addr, HOST_MASK, 0);
+ rt = rtget(ifp->int_addr, HOST_MASK);
+ if (rt != 0 && rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt != 0) {
+ if (!(rt->rt_state & RS_LOCAL)
+ || rt->rt_metric > ifp->int_metric) {
+ ifp1 = ifp;
+ } else {
+ ifp1 = rt->rt_ifp;
+ }
+ rtchange(rt,((rt->rt_state & ~RS_NET_SYN)
+ | (RS_IF|RS_LOCAL)),
+ loopaddr, loopaddr,
+ 0, 0, ifp1, rt->rt_time, 0);
+ } else {
+ rtadd(ifp->int_addr, HOST_MASK,
+ loopaddr, loopaddr,
+ 0, 0, (RS_IF | RS_LOCAL), ifp);
+ }
+ }
+ }
+
+ /* add the authority routes */
+ for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) {
+ rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask);
+ if (rt != 0
+ && !(rt->rt_state & RS_NO_NET_SYN)
+ && !(rt->rt_state & RS_NET_INT)) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt == 0)
+ rtadd(intnetp->intnet_addr, intnetp->intnet_mask,
+ loopaddr, loopaddr, intnetp->intnet_metric-1,
+ 0, RS_NET_SYN | RS_NET_INT, 0);
+ }
+
+ prev_complaints = complaints;
+}
+
+
+static void
+check_net_syn(struct interface *ifp)
+{
+ struct rt_entry *rt;
+
+
+ /* Turn on the need to automatically synthesize a network route
+ * for this interface only if we are running RIPv1 on some other
+ * interface that is on a different class-A,B,or C network.
+ */
+ if (have_ripv1_out || have_ripv1_in) {
+ ifp->int_state |= IS_NEED_NET_SYN;
+ rt = rtget(ifp->int_std_addr, ifp->int_std_mask);
+ if (rt != 0
+ && 0 == (rt->rt_state & RS_NO_NET_SYN)
+ && (!(rt->rt_state & RS_NET_SYN)
+ || rt->rt_metric > ifp->int_metric)) {
+ rtdelete(rt);
+ rt = 0;
+ }
+ if (rt == 0)
+ rtadd(ifp->int_std_addr, ifp->int_std_mask,
+ ifp->int_addr, ifp->int_addr,
+ ifp->int_metric, 0, RS_NET_SYN, ifp);
+
+ } else {
+ ifp->int_state &= ~IS_NEED_NET_SYN;
+
+ rt = rtget(ifp->int_std_addr,
+ ifp->int_std_mask);
+ if (rt != 0
+ && (rt->rt_state & RS_NET_SYN)
+ && rt->rt_ifp == ifp)
+ rtbad_sub(rt);
+ }
+}
+
+
+/* Add route for interface if not currently installed.
+ * Create route to other end if a point-to-point link,
+ * otherwise a route to this (sub)network.
+ */
+int /* 0=bad interface */
+addrouteforif(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ naddr dst, gate;
+
+
+ /* skip sick interfaces
+ */
+ if (ifp->int_state & IS_BROKE)
+ return 0;
+
+ /* If the interface on a subnet, then install a RIPv1 route to
+ * the network as well (unless it is sick).
+ */
+ if (ifp->int_state & IS_SUBNET)
+ check_net_syn(ifp);
+
+ gate = ifp->int_addr;
+ dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))
+ ? ifp->int_dstaddr
+ : htonl(ifp->int_net));
+
+ /* If we are going to send packets to the gateway,
+ * it must be reachable using our physical interfaces
+ */
+ if ((ifp->int_state & IS_REMOTE)
+ && !(ifp->int_state & IS_EXTERNAL)
+ && !check_remote(ifp))
+ return 0;
+
+ /* We are finished if the correct main interface route exists.
+ * The right route must be for the right interface, not synthesized
+ * from a subnet, be a "gateway" or not as appropriate, and so forth.
+ */
+ del_static(dst, ifp->int_mask, 0);
+ rt = rtget(dst, ifp->int_mask);
+ if (rt != 0) {
+ if ((rt->rt_ifp != ifp
+ || rt->rt_router != ifp->int_addr)
+ && (!(ifp->int_state & IS_DUP)
+ || rt->rt_ifp == 0
+ || (rt->rt_ifp->int_state & IS_BROKE))) {
+ rtdelete(rt);
+ rt = 0;
+ } else {
+ rtchange(rt, ((rt->rt_state | RS_IF)
+ & ~(RS_NET_SYN | RS_LOCAL)),
+ ifp->int_addr, ifp->int_addr,
+ ifp->int_metric, 0, ifp, now.tv_sec, 0);
+ }
+ }
+ if (rt == 0) {
+ if (ifp->int_transitions++ > 0)
+ trace_act("re-install interface %s",
+ ifp->int_name);
+
+ rtadd(dst, ifp->int_mask, gate, gate,
+ ifp->int_metric, 0, RS_IF, ifp);
+ }
+
+ return 1;
+}
diff --git a/sbin/routed/input.c b/sbin/routed/input.c
new file mode 100644
index 0000000..f177226
--- /dev/null
+++ b/sbin/routed/input.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.1.1.5 $"
+
+#include "defs.h"
+
+static void input(struct sockaddr_in *, struct interface *, struct interface *,
+ struct rip *, int);
+static void input_route(struct interface *, naddr,
+ naddr, naddr, naddr, struct netinfo *);
+static int ck_passwd(struct interface *, struct rip *, void *,
+ naddr, struct msg_limit *);
+
+
+/* process RIP input
+ */
+void
+read_rip(int sock,
+ struct interface *sifp)
+{
+ struct sockaddr_in from;
+ struct interface *aifp;
+ int fromlen, cc;
+#ifdef USE_PASSIFNAME
+ static struct msg_limit bad_name;
+ struct {
+ char ifname[IFNAMSIZ];
+ union pkt_buf pbuf;
+ } inbuf;
+#else
+ struct {
+ union pkt_buf pbuf;
+ } inbuf;
+#endif
+
+
+ for (;;) {
+ fromlen = sizeof(from);
+ cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0,
+ (struct sockaddr*)&from, &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("recvfrom(rip)");
+ break;
+ }
+ if (fromlen != sizeof(struct sockaddr_in))
+ logbad(1,"impossible recvfrom(rip) fromlen=%d",
+ fromlen);
+
+ /* aifp is the "authenticated" interface via which the packet
+ * arrived. In fact, it is only the interface on which
+ * the packet should have arrived based on is source
+ * address.
+ * sifp is interface associated with the socket through which
+ * the packet was received.
+ */
+#ifdef USE_PASSIFNAME
+ if ((cc -= sizeof(inbuf.ifname)) < 0)
+ logbad(0,"missing USE_PASSIFNAME; only %d bytes",
+ cc+sizeof(inbuf.ifname));
+
+ /* check the remote interfaces first */
+ for (aifp = remote_if; aifp; aifp = aifp->int_rlink) {
+ if (aifp->int_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (aifp == 0) {
+ aifp = ifwithname(inbuf.ifname, 0);
+ if (aifp == 0) {
+ msglim(&bad_name, from.sin_addr.s_addr,
+ "impossible interface name %.*s",
+ IFNAMSIZ, inbuf.ifname);
+ } else if (((aifp->int_if_flags & IFF_POINTOPOINT)
+ && aifp->int_dstaddr!=from.sin_addr.s_addr)
+ || (!(aifp->int_if_flags & IFF_POINTOPOINT)
+ && !on_net(from.sin_addr.s_addr,
+ aifp->int_net,
+ aifp->int_mask))) {
+ /* If it came via the wrong interface, do not
+ * trust it.
+ */
+ aifp = 0;
+ }
+ }
+#else
+ aifp = iflookup(from.sin_addr.s_addr);
+#endif
+ if (sifp == 0)
+ sifp = aifp;
+
+ input(&from, sifp, aifp, &inbuf.pbuf.rip, cc);
+ }
+}
+
+
+/* Process a RIP packet
+ */
+static void
+input(struct sockaddr_in *from, /* received from this IP address */
+ struct interface *sifp, /* interface of incoming socket */
+ struct interface *aifp, /* "authenticated" interface */
+ struct rip *rip,
+ int cc)
+{
+# define FROM_NADDR from->sin_addr.s_addr
+ static struct msg_limit use_auth, bad_len, bad_mask;
+ static struct msg_limit unk_router, bad_router, bad_nhop;
+
+ struct rt_entry *rt;
+ struct netinfo *n, *lim;
+ struct interface *ifp1;
+ naddr gate, mask, v1_mask, dst, ddst_h;
+ struct auth *ap;
+ int i;
+
+ /* Notice when we hear from a remote gateway
+ */
+ if (aifp != 0
+ && (aifp->int_state & IS_REMOTE))
+ aifp->int_act_time = now.tv_sec;
+
+ trace_rip("Recv", "from", from, sifp, rip, cc);
+
+ if (rip->rip_vers == 0) {
+ msglim(&bad_router, FROM_NADDR,
+ "RIP version 0, cmd %d, packet received from %s",
+ rip->rip_cmd, naddr_ntoa(FROM_NADDR));
+ return;
+ } else if (rip->rip_vers > RIPv2) {
+ rip->rip_vers = RIPv2;
+ }
+ if (cc > OVER_MAXPACKETSIZE) {
+ msglim(&bad_router, FROM_NADDR,
+ "packet at least %d bytes too long received from %s",
+ cc-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ n = rip->rip_nets;
+ lim = (struct netinfo *)((char*)rip + cc);
+
+ /* Notice authentication.
+ * As required by section 4.2 in RFC 1723, discard authenticated
+ * RIPv2 messages, but only if configured for that silliness.
+ *
+ * RIPv2 authentication is lame. Why authenticate queries?
+ * Why should a RIPv2 implementation with authentication disabled
+ * not be able to listen to RIPv2 packets with authenication, while
+ * RIPv1 systems will listen? Crazy!
+ */
+ if (!auth_ok
+ && rip->rip_vers == RIPv2
+ && n < lim && n->n_family == RIP_AF_AUTH) {
+ msglim(&use_auth, FROM_NADDR,
+ "RIPv2 message with authentication from %s discarded",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ /* For mere requests, be a little sloppy about the source
+ */
+ if (aifp == 0)
+ aifp = sifp;
+
+ /* Are we talking to ourself or a remote gateway?
+ */
+ ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
+ if (ifp1) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ aifp = ifp1;
+ if (check_remote(aifp)) {
+ aifp->int_act_time = now.tv_sec;
+ (void)if_ok(aifp, "remote ");
+ }
+ } else if (from->sin_port == htons(RIP_PORT)) {
+ trace_pkt(" discard our own RIP request");
+ return;
+ }
+ }
+
+ /* did the request come from a router?
+ */
+ if (from->sin_port == htons(RIP_PORT)) {
+ /* yes, ignore the request if RIP is off so that
+ * the router does not depend on us.
+ */
+ if (rip_sock < 0
+ || (aifp != 0
+ && IS_RIP_OUT_OFF(aifp->int_state))) {
+ trace_pkt(" discard request while RIP off");
+ return;
+ }
+ }
+
+ /* According to RFC 1723, we should ignore unathenticated
+ * queries. That is too silly to bother with. Sheesh!
+ * Are forwarding tables supposed to be secret, when
+ * a bad guy can infer them with test traffic? When RIP
+ * is still the most common router-discovery protocol
+ * and so hosts need to send queries that will be answered?
+ * What about `rtquery`?
+ * Maybe on firewalls you'd care, but not enough to
+ * give up the diagnostic facilities of remote probing.
+ */
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty request from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "request of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ if (rip->rip_vers == RIPv2
+ && (aifp == 0 || (aifp->int_state & IS_NO_RIPV1_OUT))) {
+ v12buf.buf->rip_vers = RIPv2;
+ /* If we have a secret but it is a cleartext secret,
+ * do not disclose our secret unless the other guy
+ * already knows it.
+ */
+ ap = find_auth(aifp);
+ if (ap != 0 && ap->type == RIP_AUTH_PW
+ && n->n_family == RIP_AF_AUTH
+ && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth))
+ ap = 0;
+ } else {
+ v12buf.buf->rip_vers = RIPv1;
+ ap = 0;
+ }
+ clr_ws_buf(&v12buf, ap);
+
+ do {
+ NTOHL(n->n_metric);
+
+ /* A single entry with family RIP_AF_UNSPEC and
+ * metric HOPCNT_INFINITY means "all routes".
+ * We respond to routers only if we are acting
+ * as a supplier, or to anyone other than a router
+ * (i.e. a query).
+ */
+ if (n->n_family == RIP_AF_UNSPEC
+ && n->n_metric == HOPCNT_INFINITY) {
+ if (from->sin_port != htons(RIP_PORT)) {
+ /* Answer a query from a utility
+ * program with all we know.
+ */
+ supply(from, aifp, OUT_QUERY, 0,
+ rip->rip_vers, ap != 0);
+ return;
+ }
+
+ /* A router trying to prime its tables.
+ * Filter the answer in the about same way
+ * broadcasts are filtered.
+ *
+ * Only answer a router if we are a supplier
+ * to keep an unwary host that is just starting
+ * from picking us as a router. Respond with
+ * RIPv1 instead of RIPv2 if that is what we
+ * are broadcasting on the interface to keep
+ * the remote router from getting the wrong
+ * initial idea of the routes we send.
+ */
+ if (aifp == 0) {
+ trace_pkt("ignore distant router");
+ return;
+ }
+ if (!supplier
+ || IS_RIP_OFF(aifp->int_state)) {
+ trace_pkt("ignore; not supplying");
+ return;
+ }
+
+ supply(from, aifp, OUT_UNICAST, 0,
+ (aifp->int_state&IS_NO_RIPV1_OUT)
+ ? RIPv2 : RIPv1,
+ ap != 0);
+ return;
+ }
+
+ /* Ignore authentication */
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ if (n->n_family != RIP_AF_INET) {
+ msglim(&bad_router, FROM_NADDR,
+ "request from %s for unsupported (af"
+ " %d) %s",
+ naddr_ntoa(FROM_NADDR),
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst));
+ return;
+ }
+
+ /* We are being asked about a specific destination.
+ */
+ dst = n->n_dst;
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad queried destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ /* decide what mask was intended */
+ if (rip->rip_vers == RIPv1
+ || 0 == (mask = ntohl(n->n_mask))
+ || 0 != (ntohl(dst) & ~mask))
+ mask = ripv1_mask_host(dst, aifp);
+
+ /* try to find the answer */
+ rt = rtget(dst, mask);
+ if (!rt && dst != RIP_DEFAULT)
+ rt = rtfind(n->n_dst);
+
+ if (v12buf.buf->rip_vers != RIPv1)
+ v12buf.n->n_mask = mask;
+ if (rt == 0) {
+ /* we do not have the answer */
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ } else {
+ /* we have the answer, so compute the
+ * right metric and next hop.
+ */
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = dst;
+ v12buf.n->n_metric = (rt->rt_metric+1
+ + ((aifp!=0)
+ ? aifp->int_metric
+ : 1));
+ if (v12buf.n->n_metric > HOPCNT_INFINITY)
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ if (v12buf.buf->rip_vers != RIPv1) {
+ v12buf.n->n_tag = rt->rt_tag;
+ v12buf.n->n_mask = mask;
+ if (aifp != 0
+ && on_net(rt->rt_gate,
+ aifp->int_net,
+ aifp->int_mask)
+ && rt->rt_gate != aifp->int_addr)
+ v12buf.n->n_nhop = rt->rt_gate;
+ }
+ }
+ HTONL(v12buf.n->n_metric);
+
+ /* Stop paying attention if we fill the output buffer.
+ */
+ if (++v12buf.n >= v12buf.lim)
+ break;
+ } while (++n < lim);
+
+ /* Send the answer about specific routes.
+ */
+ if (ap != 0 && ap->type == RIP_AUTH_MD5)
+ end_md5_auth(&v12buf, ap);
+
+ if (from->sin_port != htons(RIP_PORT)) {
+ /* query */
+ (void)output(OUT_QUERY, from, aifp,
+ v12buf.buf,
+ ((char *)v12buf.n - (char*)v12buf.buf));
+ } else if (supplier) {
+ (void)output(OUT_UNICAST, from, aifp,
+ v12buf.buf,
+ ((char *)v12buf.n - (char*)v12buf.buf));
+ } else {
+ /* Only answer a router if we are a supplier
+ * to keep an unwary host that is just starting
+ * from picking us an a router.
+ */
+ ;
+ }
+ return;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ /* verify message came from a privileged port */
+ if (ntohs(from->sin_port) > IPPORT_RESERVED) {
+ msglog("trace command from untrusted port on %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (aifp == 0) {
+ msglog("trace command from unknown router %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (rip->rip_cmd == RIPCMD_TRACEON) {
+ rip->rip_tracefile[cc-4] = '\0';
+ set_tracefile((char*)rip->rip_tracefile,
+ "trace command: %s\n", 0);
+ } else {
+ trace_off("tracing turned off by %s\n",
+ naddr_ntoa(FROM_NADDR));
+ }
+ return;
+
+ case RIPCMD_RESPONSE:
+ if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "response of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ /* verify message came from a router */
+ if (from->sin_port != ntohs(RIP_PORT)) {
+ msglim(&bad_router, FROM_NADDR,
+ " discard RIP response from unknown port"
+ " %d", from->sin_port);
+ return;
+ }
+
+ if (rip_sock < 0) {
+ trace_pkt(" discard response while RIP off");
+ return;
+ }
+
+ /* Are we talking to ourself or a remote gateway?
+ */
+ ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
+ if (ifp1) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ aifp = ifp1;
+ if (check_remote(aifp)) {
+ aifp->int_act_time = now.tv_sec;
+ (void)if_ok(aifp, "remote ");
+ }
+ } else {
+ trace_pkt(" discard our own RIP response");
+ return;
+ }
+ }
+
+ /* Accept routing packets from routers directly connected
+ * via broadcast or point-to-point networks, and from
+ * those listed in /etc/gateways.
+ */
+ if (aifp == 0) {
+ msglim(&unk_router, FROM_NADDR,
+ " discard response from %s"
+ " via unexpected interface",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (IS_RIP_IN_OFF(aifp->int_state)) {
+ trace_pkt(" discard RIPv%d response"
+ " via disabled interface %s",
+ rip->rip_vers, aifp->int_name);
+ return;
+ }
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty response from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (((aifp->int_state & IS_NO_RIPV1_IN)
+ && rip->rip_vers == RIPv1)
+ || ((aifp->int_state & IS_NO_RIPV2_IN)
+ && rip->rip_vers != RIPv1)) {
+ trace_pkt(" discard RIPv%d response",
+ rip->rip_vers);
+ return;
+ }
+
+ /* Ignore routes via dead interface.
+ */
+ if (aifp->int_state & IS_BROKE) {
+ trace_pkt("%sdiscard response via broken interface %s",
+ aifp->int_name);
+ return;
+ }
+
+ /* If the interface cares, ignore bad routers.
+ * Trace but do not log this problem, because where it
+ * happens, it happens frequently.
+ */
+ if (aifp->int_state & IS_DISTRUST) {
+ struct tgate *tg = tgates;
+ while (tg->tgate_addr != FROM_NADDR) {
+ tg = tg->tgate_next;
+ if (tg == 0) {
+ trace_pkt(" discard RIP response"
+ " from untrusted router %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ }
+ }
+
+ /* Authenticate the packet if we have a secret.
+ * If we do not have any secrets, ignore the error in
+ * RFC 1723 and accept it regardless.
+ */
+ if (aifp->int_auth[0].type != RIP_AUTH_NONE
+ && rip->rip_vers != RIPv1
+ && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth))
+ return;
+
+ do {
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ NTOHL(n->n_metric);
+ dst = n->n_dst;
+ if (n->n_family != RIP_AF_INET
+ && (n->n_family != RIP_AF_UNSPEC
+ || dst != RIP_DEFAULT)) {
+ msglim(&bad_router, FROM_NADDR,
+ "route from %s to unsupported"
+ " address family=%d destination=%s",
+ naddr_ntoa(FROM_NADDR),
+ n->n_family,
+ naddr_ntoa(dst));
+ continue;
+ }
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (n->n_metric == 0
+ || n->n_metric > HOPCNT_INFINITY) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad metric %d from %s"
+ " for destination %s",
+ n->n_metric,
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst));
+ return;
+ }
+
+ /* Notice the next-hop.
+ */
+ gate = FROM_NADDR;
+ if (n->n_nhop != 0) {
+ if (rip->rip_vers == RIPv2) {
+ n->n_nhop = 0;
+ } else {
+ /* Use it only if it is valid. */
+ if (on_net(n->n_nhop,
+ aifp->int_net, aifp->int_mask)
+ && check_dst(n->n_nhop)) {
+ gate = n->n_nhop;
+ } else {
+ msglim(&bad_nhop, FROM_NADDR,
+ "router %s to %s"
+ " has bad next hop %s",
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst),
+ naddr_ntoa(n->n_nhop));
+ n->n_nhop = 0;
+ }
+ }
+ }
+
+ if (rip->rip_vers == RIPv1
+ || 0 == (mask = ntohl(n->n_mask))) {
+ mask = ripv1_mask_host(dst,aifp);
+ } else if ((ntohl(dst) & ~mask) != 0) {
+ msglim(&bad_mask, FROM_NADDR,
+ "router %s sent bad netmask"
+ " %#x with %s",
+ naddr_ntoa(FROM_NADDR),
+ mask,
+ naddr_ntoa(dst));
+ continue;
+ }
+ if (rip->rip_vers == RIPv1)
+ n->n_tag = 0;
+
+ /* Adjust metric according to incoming interface..
+ */
+ n->n_metric += aifp->int_metric;
+ if (n->n_metric > HOPCNT_INFINITY)
+ n->n_metric = HOPCNT_INFINITY;
+
+ /* Recognize and ignore a default route we faked
+ * which is being sent back to us by a machine with
+ * broken split-horizon.
+ * Be a little more paranoid than that, and reject
+ * default routes with the same metric we advertised.
+ */
+ if (aifp->int_d_metric != 0
+ && dst == RIP_DEFAULT
+ && n->n_metric >= aifp->int_d_metric)
+ continue;
+
+ /* We can receive aggregated RIPv2 routes that must
+ * be broken down before they are transmitted by
+ * RIPv1 via an interface on a subnet.
+ * We might also receive the same routes aggregated
+ * via other RIPv2 interfaces.
+ * This could cause duplicate routes to be sent on
+ * the RIPv1 interfaces. "Longest matching variable
+ * length netmasks" lets RIPv2 listeners understand,
+ * but breaking down the aggregated routes for RIPv1
+ * listeners can produce duplicate routes.
+ *
+ * Breaking down aggregated routes here bloats
+ * the daemon table, but does not hurt the kernel
+ * table, since routes are always aggregated for
+ * the kernel.
+ *
+ * Notice that this does not break down network
+ * routes corresponding to subnets. This is part
+ * of the defense against RS_NET_SYN.
+ */
+ if (have_ripv1_out
+ && (((rt = rtget(dst,mask)) == 0
+ || !(rt->rt_state & RS_NET_SYN)))
+ && (v1_mask = ripv1_mask_net(dst,0)) > mask) {
+ ddst_h = v1_mask & -v1_mask;
+ i = (v1_mask & ~mask)/ddst_h;
+ if (i >= 511) {
+ /* Punt if we would have to generate
+ * an unreasonable number of routes.
+ */
+#ifdef DEBUG
+ msglog("accept %s from %s as 1"
+ " instead of %d routes",
+ addrname(dst,mask,0),
+ naddr_ntoa(FROM_NADDR),
+ i+1);
+#endif
+ i = 0;
+ } else {
+ mask = v1_mask;
+ }
+ } else {
+ i = 0;
+ }
+
+ for (;;) {
+ input_route(aifp, FROM_NADDR,
+ dst, mask, gate, n);
+ if (i-- == 0)
+ break;
+ dst = htonl(ntohl(dst) + ddst_h);
+ }
+ } while (++n < lim);
+ break;
+ }
+#undef FROM_NADDR
+}
+
+
+/* Process a single input route.
+ */
+static void
+input_route(struct interface *ifp,
+ naddr from,
+ naddr dst,
+ naddr mask,
+ naddr gate,
+ struct netinfo *n)
+{
+ int i;
+ struct rt_entry *rt;
+ struct rt_spare *rts, *rts0;
+ struct interface *ifp1;
+ time_t new_time;
+
+
+ /* See if the other guy is telling us to send our packets to him.
+ * Sometimes network routes arrive over a point-to-point link for
+ * the network containing the address(es) of the link.
+ *
+ * If our interface is broken, switch to using the other guy.
+ */
+ ifp1 = ifwithaddr(dst, 1, 1);
+ if (ifp1 != 0
+ && (!(ifp1->int_state & IS_BROKE)
+ || (ifp1->int_state & IS_PASSIVE)))
+ return;
+
+ /* Look for the route in our table.
+ */
+ rt = rtget(dst, mask);
+
+ /* Consider adding the route if we do not already have it.
+ */
+ if (rt == 0) {
+ /* Ignore unknown routes being poisoned.
+ */
+ if (n->n_metric == HOPCNT_INFINITY)
+ return;
+
+ /* Ignore the route if it points to us */
+ if (n->n_nhop != 0
+ && 0 != ifwithaddr(n->n_nhop, 1, 0))
+ return;
+
+ /* If something has not gone crazy and tried to fill
+ * our memory, accept the new route.
+ */
+ if (total_routes < MAX_ROUTES)
+ rtadd(dst, mask, gate, from, n->n_metric,
+ n->n_tag, 0, ifp);
+ return;
+ }
+
+ /* We already know about the route. Consider this update.
+ *
+ * If (rt->rt_state & RS_NET_SYN), then this route
+ * is the same as a network route we have inferred
+ * for subnets we know, in order to tell RIPv1 routers
+ * about the subnets.
+ *
+ * It is impossible to tell if the route is coming
+ * from a distant RIPv2 router with the standard
+ * netmask because that router knows about the entire
+ * network, or if it is a round-about echo of a
+ * synthetic, RIPv1 network route of our own.
+ * The worst is that both kinds of routes might be
+ * received, and the bad one might have the smaller
+ * metric. Partly solve this problem by never
+ * aggregating into such a route. Also keep it
+ * around as long as the interface exists.
+ */
+
+ rts0 = rt->rt_spares;
+ for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) {
+ if (rts->rts_router == from)
+ break;
+ /* Note the worst slot to reuse,
+ * other than the current slot.
+ */
+ if (rts0 == rt->rt_spares
+ || BETTER_LINK(rt, rts0, rts))
+ rts0 = rts;
+ }
+ if (i != 0) {
+ /* Found the router
+ */
+ int old_metric = rts->rts_metric;
+
+ /* Keep poisoned routes around only long enough to pass
+ * the poison on. Get a new timestamp for good routes.
+ */
+ new_time =((old_metric == HOPCNT_INFINITY)
+ ? rts->rts_time
+ : now.tv_sec);
+
+ /* If this is an update for the router we currently prefer,
+ * then note it.
+ */
+ if (i == NUM_SPARES) {
+ rtchange(rt,rt->rt_state, gate,rt->rt_router,
+ n->n_metric, n->n_tag, ifp, new_time, 0);
+ /* If the route got worse, check for something better.
+ */
+ if (n->n_metric > old_metric)
+ rtswitch(rt, 0);
+ return;
+ }
+
+ /* This is an update for a spare route.
+ * Finished if the route is unchanged.
+ */
+ if (rts->rts_gate == gate
+ && old_metric == n->n_metric
+ && rts->rts_tag == n->n_tag) {
+ rts->rts_time = new_time;
+ return;
+ }
+
+ } else {
+ /* The update is for a route we know about,
+ * but not from a familiar router.
+ *
+ * Ignore the route if it points to us.
+ */
+ if (n->n_nhop != 0
+ && 0 != ifwithaddr(n->n_nhop, 1, 0))
+ return;
+
+ rts = rts0;
+
+ /* Save the route as a spare only if it has
+ * a better metric than our worst spare.
+ * This also ignores poisoned routes (those
+ * received with metric HOPCNT_INFINITY).
+ */
+ if (n->n_metric >= rts->rts_metric)
+ return;
+
+ new_time = now.tv_sec;
+ }
+
+ trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time);
+
+ rts->rts_gate = gate;
+ rts->rts_router = from;
+ rts->rts_metric = n->n_metric;
+ rts->rts_tag = n->n_tag;
+ rts->rts_time = new_time;
+ rts->rts_ifp = ifp;
+
+ /* try to switch to a better route */
+ rtswitch(rt, rts);
+}
+
+
+static int /* 0 if bad */
+ck_passwd(struct interface *aifp,
+ struct rip *rip,
+ void *lim,
+ naddr from,
+ struct msg_limit *use_authp)
+{
+# define NA (rip->rip_auths)
+ struct netauth *na2;
+ struct auth *ap;
+ MD5_CTX md5_ctx;
+ u_char hash[RIP_AUTH_PW_LEN];
+ int i;
+
+
+ if ((void *)NA >= lim || NA->a_family != RIP_AF_AUTH) {
+ msglim(use_authp, from, "missing password from %s",
+ naddr_ntoa(from));
+ return 0;
+ }
+
+ /* accept any current (+/- 24 hours) password
+ */
+ for (ap = aifp->int_auth, i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ if (ap->type != NA->a_type
+ || (u_long)ap->start > (u_long)clk.tv_sec+DAY
+ || (u_long)ap->end+DAY < (u_long)clk.tv_sec)
+ continue;
+
+ if (NA->a_type == RIP_AUTH_PW) {
+ if (!bcmp(NA->au.au_pw, ap->key, RIP_AUTH_PW_LEN))
+ return 1;
+
+ } else {
+ /* accept MD5 secret with the right key ID
+ */
+ if (NA->au.a_md5.md5_keyid != ap->keyid)
+ continue;
+
+ na2 = (struct netauth *)((char *)(NA+1)
+ + NA->au.a_md5.md5_pkt_len);
+ if (NA->au.a_md5.md5_pkt_len % sizeof(*NA) != 0
+ || lim < (void *)(na2+1)) {
+ msglim(use_authp, from,
+ "bad MD5 RIP-II pkt length %d from %s",
+ NA->au.a_md5.md5_pkt_len,
+ naddr_ntoa(from));
+ return 0;
+ }
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)NA,
+ (char *)na2->au.au_pw - (char *)NA);
+ MD5Update(&md5_ctx,
+ (u_char *)ap->key, sizeof(ap->key));
+ MD5Final(hash, &md5_ctx);
+ if (na2->a_family != RIP_AF_AUTH
+ || na2->a_type != 1
+ || NA->au.a_md5.md5_auth_len != RIP_AUTH_PW_LEN
+ || bcmp(hash, na2->au.au_pw, sizeof(hash)))
+ return 0;
+ return 1;
+ }
+ }
+
+ msglim(use_authp, from, "bad password from %s",
+ naddr_ntoa(from));
+ return 0;
+#undef NA
+}
diff --git a/sbin/routed/main.c b/sbin/routed/main.c
new file mode 100644
index 0000000..509c350
--- /dev/null
+++ b/sbin/routed/main.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (c) 1983, 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.
+ */
+
+char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+
+#include "defs.h"
+#include "pathnames.h"
+#ifdef sgi
+#include "math.h"
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+pid_t mypid;
+
+naddr myaddr; /* system address */
+char myname[MAXHOSTNAMELEN+1];
+
+int supplier; /* supply or broadcast updates */
+int supplier_set;
+int ipforwarding = 1; /* kernel forwarding on */
+
+int default_gateway; /* 1=advertise default */
+int background = 1;
+int ridhosts; /* 1=reduce host routes */
+int mhome; /* 1=want multi-homed host route */
+int advertise_mhome; /* 1=must continue adverising it */
+int auth_ok = 1; /* 1=ignore auth if we do not care */
+
+struct timeval epoch; /* when started */
+struct timeval clk, prev_clk;
+struct timeval now; /* current idea of time */
+time_t now_stale;
+time_t now_expire;
+time_t now_garbage;
+
+struct timeval next_bcast; /* next general broadcast */
+struct timeval no_flash = {EPOCH+SUPPLY_INTERVAL}; /* inhibit flash update */
+
+fd_set fdbits;
+int sock_max;
+int rip_sock = -1; /* RIP socket */
+struct interface *rip_sock_mcast; /* current multicast interface */
+int rt_sock; /* routing socket */
+int rt_sock_seqno;
+
+
+static int get_rip_sock(naddr, int);
+static void timevalsub(struct timeval *, struct timeval *, struct timeval *);
+
+int
+main(int argc,
+ char *argv[])
+{
+ int n, mib[4], off;
+ size_t len;
+ char *p, *q;
+ struct timeval wtime, t2;
+ time_t dt;
+ fd_set ibits;
+ naddr p_net, p_mask;
+ struct interface *ifp;
+ struct parm parm;
+ char *tracename = 0;
+
+
+ /* Some shells are badly broken and send SIGHUP to backgrounded
+ * processes.
+ */
+ signal(SIGHUP, SIG_IGN);
+
+ openlog("routed", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ ftrace = stdout;
+
+ gettimeofday(&clk, 0);
+ prev_clk = clk;
+ epoch = clk;
+ epoch.tv_sec -= EPOCH;
+ now.tv_sec = EPOCH;
+ now_stale = EPOCH - STALE_TIME;
+ now_expire = EPOCH - EXPIRE_TIME;
+ now_garbage = EPOCH - GARBAGE_TIME;
+ wtime.tv_sec = 0;
+
+ (void)gethostname(myname, sizeof(myname)-1);
+ (void)gethost(myname, &myaddr);
+
+ while ((n = getopt(argc, argv, "sqdghmAtT:F:P:")) != -1) {
+ switch (n) {
+ case 's':
+ supplier = 1;
+ supplier_set = 1;
+ break;
+
+ case 'q':
+ supplier = 0;
+ supplier_set = 1;
+ break;
+
+ case 'd':
+ background = 0;
+ break;
+
+ case 'g':
+ bzero(&parm, sizeof(parm));
+ parm.parm_d_metric = 1;
+ p = check_parms(&parm);
+ if (p != 0)
+ msglog("bad -g: %s", p);
+ else
+ default_gateway = 1;
+ break;
+
+ case 'h': /* suppress extra host routes */
+ ridhosts = 1;
+ break;
+
+ case 'm': /* advertise host route */
+ mhome = 1; /* on multi-homed hosts */
+ break;
+
+ case 'A':
+ /* Ignore authentication if we do not care.
+ * Crazy as it is, that is what RFC 1723 requires.
+ */
+ auth_ok = 0;
+ break;
+
+ case 't':
+ new_tracelevel++;
+ break;
+
+ case 'T':
+ tracename = optarg;
+ break;
+
+ case 'F': /* minimal routes for SLIP */
+ n = FAKE_METRIC;
+ p = strchr(optarg,',');
+ if (p && *p != '\0') {
+ n = (int)strtoul(p+1, &q, 0);
+ if (*q == '\0'
+ && n <= HOPCNT_INFINITY-1
+ && n >= 1)
+ *p = '\0';
+ }
+ if (!getnet(optarg, &p_net, &p_mask)) {
+ msglog("bad network; \"-F %s\"",
+ optarg);
+ break;
+ }
+ bzero(&parm, sizeof(parm));
+ parm.parm_net = p_net;
+ parm.parm_mask = p_mask;
+ parm.parm_d_metric = n;
+ p = check_parms(&parm);
+ if (p != 0)
+ msglog("bad -F: %s", p);
+ break;
+
+ case 'P':
+ /* 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);
+ }
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (tracename == 0 && argc >= 1) {
+ tracename = *argv++;
+ argc--;
+ }
+ if (tracename != 0 && tracename[0] == '\0')
+ goto usage;
+ if (argc != 0) {
+usage:
+ logbad(0, "usage: routed [-sqdghmpAt] [-T tracefile]"
+ " [-F net[/mask[,metric]]] [-P parms]");
+ }
+ if (geteuid() != 0)
+ logbad(0, "requires UID 0");
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_FORWARDING;
+ len = sizeof(ipforwarding);
+ if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0)
+ LOGERR("sysctl(IPCTL_FORWARDING)");
+
+ if (!ipforwarding) {
+ if (supplier)
+ msglog("-s incompatible with ipforwarding=0");
+ if (default_gateway) {
+ msglog("-g incompatible with ipforwarding=0");
+ default_gateway = 0;
+ }
+ supplier = 0;
+ supplier_set = 1;
+ }
+ if (default_gateway) {
+ if (supplier_set && !supplier) {
+ msglog("-g and -q incompatible");
+ } else {
+ supplier = 1;
+ supplier_set = 1;
+ }
+ }
+
+
+ signal(SIGALRM, sigalrm);
+ if (!background)
+ signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */
+ signal(SIGTERM, sigterm);
+ signal(SIGINT, sigterm);
+ signal(SIGUSR1, sigtrace_on);
+ signal(SIGUSR2, sigtrace_off);
+
+ /* get into the 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()");
+#else
+ if (background && daemon(0, new_tracelevel) < 0)
+ BADERR(0,"daemon()");
+#endif
+
+ mypid = getpid();
+ srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid));
+
+ /* prepare socket connected to the kernel.
+ */
+ rt_sock = socket(AF_ROUTE, SOCK_RAW, 0);
+ if (rt_sock < 0)
+ BADERR(1,"rt_sock = socket()");
+ if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno));
+ off = 0;
+ if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK,
+ &off,sizeof(off)) < 0)
+ LOGERR("setsockopt(SO_USELOOPBACK,0)");
+
+ fix_select();
+
+
+ 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 */
+ }
+
+ bufinit();
+
+ /* initialize radix tree */
+ rtinit();
+
+ /* Pick a random part of the second for our output to minimize
+ * collisions.
+ *
+ * Start broadcasting after hearing from other routers, and
+ * at a random time so a bunch of systems do not get synchronized
+ * after a power failure.
+ */
+ intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL);
+ age_timer.tv_usec = next_bcast.tv_usec;
+ age_timer.tv_sec = EPOCH+MIN_WAITTIME;
+ rdisc_timer = next_bcast;
+ ifinit_timer.tv_usec = next_bcast.tv_usec;
+
+ /* Collect an initial view of the world by checking the interface
+ * configuration and the kludge file.
+ */
+ gwkludge();
+ ifinit();
+ flush_kern();
+
+ /* Ask for routes */
+ rip_query();
+ rdisc_sol();
+
+ /* Loop forever, listening and broadcasting.
+ */
+ for (;;) {
+ prev_clk = clk;
+ gettimeofday(&clk, 0);
+ timevalsub(&t2, &clk, &prev_clk);
+ if (t2.tv_sec < 0
+ || t2.tv_sec > wtime.tv_sec + 5) {
+ /* Deal with time changes before other housekeeping to
+ * keep everything straight.
+ */
+ dt = t2.tv_sec;
+ if (dt > 0)
+ dt -= wtime.tv_sec;
+ trace_act("time changed by %d sec", dt);
+ epoch.tv_sec += dt;
+ }
+ timevalsub(&now, &clk, &epoch);
+ now_stale = now.tv_sec - STALE_TIME;
+ now_expire = now.tv_sec - EXPIRE_TIME;
+ now_garbage = now.tv_sec - GARBAGE_TIME;
+
+ /* deal with signals that should affect tracing */
+ set_tracelevel();
+
+ if (stopint != 0) {
+ rip_bcast(0);
+ rdisc_adv();
+ trace_off("exiting with signal %d\n", stopint);
+ exit(stopint | 128);
+ }
+
+ /* look for new or dead interfaces */
+ timevalsub(&wtime, &ifinit_timer, &now);
+ if (wtime.tv_sec <= 0) {
+ wtime.tv_sec = 0;
+ ifinit();
+ rip_query();
+ continue;
+ }
+
+ /* If it is time, then broadcast our routes.
+ */
+ if (supplier || advertise_mhome) {
+ timevalsub(&t2, &next_bcast, &now);
+ if (t2.tv_sec <= 0) {
+ /* Synchronize the aging and broadcast
+ * timers to minimize awakenings
+ */
+ age(0);
+
+ rip_bcast(0);
+
+ /* It is desirable to send routing updates
+ * regularly. So schedule the next update
+ * 30 seconds after the previous one was
+ * secheduled, instead of 30 seconds after
+ * the previous update was finished.
+ * Even if we just started after discovering
+ * a 2nd interface or were otherwise delayed,
+ * pick a 30-second aniversary of the
+ * original broadcast time.
+ */
+ n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL;
+ next_bcast.tv_sec += n*SUPPLY_INTERVAL;
+
+ continue;
+ }
+
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+ }
+
+ /* If we need a flash update, either do it now or
+ * set the delay to end when it is time.
+ *
+ * If we are within MIN_WAITTIME seconds of a full update,
+ * do not bother.
+ */
+ if (need_flash
+ && supplier
+ && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) {
+ /* accurate to the millisecond */
+ if (!timercmp(&no_flash, &now, >))
+ rip_bcast(1);
+ timevalsub(&t2, &no_flash, &now);
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+ }
+
+ /* trigger the main aging timer.
+ */
+ timevalsub(&t2, &age_timer, &now);
+ if (t2.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+ /* update the kernel routing table
+ */
+ timevalsub(&t2, &need_kern, &now);
+ if (t2.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+ /* take care of router discovery,
+ * but do it to the millisecond
+ */
+ if (!timercmp(&rdisc_timer, &now, >)) {
+ rdisc_age(0);
+ continue;
+ }
+ timevalsub(&t2, &rdisc_timer, &now);
+ if (timercmp(&t2, &wtime, <))
+ wtime = t2;
+
+
+ /* wait for input or a timer to expire.
+ */
+ trace_flush();
+ ibits = fdbits;
+ n = select(sock_max, &ibits, 0, 0, &wtime);
+ if (n <= 0) {
+ if (n < 0 && errno != EINTR && errno != EAGAIN)
+ BADERR(1,"select");
+ continue;
+ }
+
+ if (FD_ISSET(rt_sock, &ibits)) {
+ read_rt();
+ n--;
+ }
+ if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) {
+ read_d();
+ n--;
+ }
+ if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) {
+ read_rip(rip_sock, 0);
+ n--;
+ }
+
+ for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0
+ && FD_ISSET(ifp->int_rip_sock, &ibits)) {
+ read_rip(ifp->int_rip_sock, ifp);
+ n--;
+ }
+ }
+ }
+}
+
+
+/* ARGSUSED */
+void
+sigalrm(int s)
+{
+ /* Historically, SIGALRM would cause the daemon to check for
+ * new and broken interfaces.
+ */
+ ifinit_timer.tv_sec = now.tv_sec;
+ trace_act("SIGALRM");
+}
+
+
+/* watch for fatal signals */
+void
+sigterm(int sig)
+{
+ stopint = sig;
+ (void)signal(sig, SIG_DFL); /* catch it only once */
+}
+
+
+void
+fix_select(void)
+{
+ struct interface *ifp;
+
+
+ FD_ZERO(&fdbits);
+ sock_max = 0;
+
+ FD_SET(rt_sock, &fdbits);
+ if (sock_max <= rt_sock)
+ sock_max = rt_sock+1;
+ if (rip_sock >= 0) {
+ FD_SET(rip_sock, &fdbits);
+ if (sock_max <= rip_sock)
+ sock_max = rip_sock+1;
+ }
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0) {
+ FD_SET(ifp->int_rip_sock, &fdbits);
+ if (sock_max <= ifp->int_rip_sock)
+ sock_max = ifp->int_rip_sock+1;
+ }
+ }
+ if (rdisc_sock >= 0) {
+ FD_SET(rdisc_sock, &fdbits);
+ if (sock_max <= rdisc_sock)
+ sock_max = rdisc_sock+1;
+ }
+}
+
+
+void
+fix_sock(int sock,
+ char *name)
+{
+ int on;
+#define MIN_SOCKBUF (4*1024)
+ static int rbuf;
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(1, "fcntl(%s) O_NONBLOCK: %s",
+ name, strerror(errno));
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, &on,sizeof(on)) < 0)
+ msglog("setsockopt(%s,SO_BROADCAST): %s",
+ name, strerror(errno));
+#ifdef USE_PASSIFNAME
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSIFNAME, &on,sizeof(on)) < 0)
+ msglog("setsockopt(%s,SO_PASSIFNAME): %s",
+ name, strerror(errno));
+#endif
+
+ if (rbuf >= MIN_SOCKBUF) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof(rbuf)) < 0)
+ msglog("setsockopt(%s,SO_RCVBUF=%d): %s",
+ name, rbuf, strerror(errno));
+ } else {
+ for (rbuf = 60*1024; ; rbuf -= 4096) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof(rbuf)) == 0) {
+ trace_act("RCVBUF=%d", rbuf);
+ break;
+ }
+ if (rbuf < MIN_SOCKBUF) {
+ msglog("setsockopt(%s,SO_RCVBUF = %d): %s",
+ name, rbuf, strerror(errno));
+ break;
+ }
+ }
+ }
+}
+
+
+/* get a rip socket
+ */
+static int /* <0 or file descriptor */
+get_rip_sock(naddr addr,
+ int serious) /* 1=failure to bind is serious */
+{
+ struct sockaddr_in sin;
+ unsigned char ttl;
+ int s;
+
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ BADERR(1,"rip_sock = socket()");
+
+ bzero(&sin,sizeof(sin));
+#ifdef _HAVE_SIN_LEN
+ sin.sin_len = sizeof(sin);
+#endif
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(RIP_PORT);
+ sin.sin_addr.s_addr = addr;
+ if (bind(s, (struct sockaddr *)&sin,sizeof(sin)) < 0) {
+ if (serious)
+ BADERR(errno != EADDRINUSE, "bind(rip_sock)");
+ return -1;
+ }
+ fix_sock(s,"rip_sock");
+
+ ttl = 1;
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)) < 0)
+ DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)");
+
+ return s;
+}
+
+
+/* turn off main RIP socket */
+void
+rip_off(void)
+{
+ struct interface *ifp;
+ register naddr addr;
+
+
+ if (rip_sock >= 0 && !mhome) {
+ trace_act("turn off RIP");
+
+ (void)close(rip_sock);
+ rip_sock = -1;
+
+ /* get non-broadcast sockets to listen to queries.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (ifp->int_state & IS_REMOTE)
+ continue;
+ if (ifp->int_rip_sock < 0) {
+ addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+ ifp->int_rip_sock = get_rip_sock(addr, 0);
+ }
+ }
+
+ fix_select();
+
+ age(0);
+ }
+}
+
+
+/* turn on RIP multicast input via an interface
+ */
+static void
+rip_mcast_on(struct interface *ifp)
+{
+ struct ip_mreq m;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state)
+ && (ifp->int_if_flags & IFF_MULTICAST)
+#ifdef MCAST_PPP_BUG
+ && !(ifp->int_if_flags & IFF_POINTOPOINT)
+#endif
+ && !(ifp->int_state & IS_ALIAS)) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+ m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+ if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)");
+ }
+}
+
+
+/* Prepare socket used for RIP.
+ */
+void
+rip_on(struct interface *ifp)
+{
+ /* If the main RIP socket is already alive, only start receiving
+ * multicasts for this interface.
+ */
+ if (rip_sock >= 0) {
+ if (ifp != 0)
+ rip_mcast_on(ifp);
+ return;
+ }
+
+ /* If the main RIP socket is off and it makes sense to turn it on,
+ * then turn it on for all of the interfaces.
+ */
+ if (rip_interfaces > 0 && !rdisc_ok) {
+ trace_act("turn on RIP");
+
+ /* Close all of the query sockets so that we can open
+ * the main socket. SO_REUSEPORT is not a solution,
+ * since that would let two daemons bind to the broadcast
+ * socket.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if (ifp->int_rip_sock >= 0) {
+ (void)close(ifp->int_rip_sock);
+ ifp->int_rip_sock = -1;
+ }
+ }
+
+ rip_sock = get_rip_sock(INADDR_ANY, 1);
+ rip_sock_mcast = 0;
+
+ /* Do not advertise anything until we have heard something
+ */
+ if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME)
+ next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME;
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ ifp->int_query_time = NEVER;
+ rip_mcast_on(ifp);
+ }
+ ifinit_timer.tv_sec = now.tv_sec;
+
+ } else if (ifp != 0
+ && !(ifp->int_state & IS_REMOTE)
+ && ifp->int_rip_sock < 0) {
+ /* RIP is off, so ensure there are sockets on which
+ * to listen for queries.
+ */
+ ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0);
+ }
+
+ fix_select();
+}
+
+
+/* die if malloc(3) fails
+ */
+void *
+rtmalloc(size_t size,
+ char *msg)
+{
+ void *p = malloc(size);
+ if (p == 0)
+ logbad(1,"malloc() failed in %s", msg);
+ return p;
+}
+
+
+/* get a random instant in an interval
+ */
+void
+intvl_random(struct timeval *tp, /* put value here */
+ u_long lo, /* value is after this second */
+ u_long hi) /* and before this */
+{
+ tp->tv_sec = (time_t)(hi == lo
+ ? lo
+ : (lo + random() % ((hi - lo))));
+ tp->tv_usec = random() % 1000000;
+}
+
+
+void
+timevaladd(struct timeval *t1,
+ struct timeval *t2)
+{
+
+ t1->tv_sec += t2->tv_sec;
+ if ((t1->tv_usec += t2->tv_usec) > 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
+
+
+/* t1 = t2 - t3
+ */
+static void
+timevalsub(struct timeval *t1,
+ struct timeval *t2,
+ struct timeval *t3)
+{
+ t1->tv_sec = t2->tv_sec - t3->tv_sec;
+ if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+}
+
+
+/* put a message into the system log
+ */
+void
+msglog(char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ if (ftrace != 0) {
+ if (ftrace == stdout)
+ (void)fputs("routed: ", ftrace);
+ (void)vfprintf(ftrace, p, args);
+ (void)fputc('\n', ftrace);
+ }
+}
+
+
+/* Put a message about a bad system 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 */
+
+ trace_flush();
+ for (p1 = p; *p1 == ' '; p1++)
+ continue;
+ vsyslog(LOG_ERR, p1, args);
+ }
+
+ /* always display the message if tracing */
+ if (ftrace != 0) {
+ (void)vfprintf(ftrace, p, args);
+ (void)fputc('\n', ftrace);
+ }
+}
+
+
+void
+logbad(int dump, char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ (void)fputs("routed: ", stderr);
+ (void)vfprintf(stderr, p, args);
+ (void)fputs("; giving up\n",stderr);
+ (void)fflush(stderr);
+
+ if (dump)
+ abort();
+ exit(1);
+}
diff --git a/sbin/routed/output.c b/sbin/routed/output.c
new file mode 100644
index 0000000..de8f97d
--- /dev/null
+++ b/sbin/routed/output.c
@@ -0,0 +1,952 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.21 $"
+
+#include "defs.h"
+
+
+int update_seqno;
+
+
+/* walk the tree of routes with this for output
+ */
+struct {
+ struct sockaddr_in to;
+ naddr to_mask;
+ naddr to_net;
+ naddr to_std_mask;
+ naddr to_std_net;
+ struct interface *ifp; /* usually output interface */
+ struct auth *a;
+ char metric; /* adjust metrics by interface */
+ int npackets;
+ int gen_limit;
+ u_int state;
+#define WS_ST_FLASH 0x001 /* send only changed routes */
+#define WS_ST_RIP2_ALL 0x002 /* send full featured RIPv2 */
+#define WS_ST_AG 0x004 /* ok to aggregate subnets */
+#define WS_ST_SUPER_AG 0x008 /* ok to aggregate networks */
+#define WS_ST_SUB_AG 0x010 /* aggregate subnets in odd case */
+#define WS_ST_QUERY 0x020 /* responding to a query */
+#define WS_ST_TO_ON_NET 0x040 /* sending onto one of our nets */
+#define WS_ST_DEFAULT 0x080 /* faking a default */
+} ws;
+
+/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */
+struct ws_buf v12buf;
+union pkt_buf ripv12_buf;
+
+/* Another for only RIPv2 listeners */
+struct ws_buf v2buf;
+union pkt_buf rip_v2_buf;
+
+
+
+void
+bufinit(void)
+{
+ ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ v12buf.buf = &ripv12_buf.rip;
+ v12buf.base = &v12buf.buf->rip_nets[0];
+
+ rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ rip_v2_buf.rip.rip_vers = RIPv2;
+ v2buf.buf = &rip_v2_buf.rip;
+ v2buf.base = &v2buf.buf->rip_nets[0];
+}
+
+
+/* Send the contents of the global buffer via the non-multicast socket
+ */
+int /* <0 on failure */
+output(enum output_type type,
+ struct sockaddr_in *dst, /* send to here */
+ struct interface *ifp,
+ struct rip *buf,
+ int size) /* this many bytes */
+{
+ struct sockaddr_in sin;
+ int flags;
+ char *msg;
+ int res;
+ naddr tgt_mcast;
+ int soc;
+ int serrno;
+
+ sin = *dst;
+ if (sin.sin_port == 0)
+ sin.sin_port = htons(RIP_PORT);
+#ifdef _HAVE_SIN_LEN
+ if (sin.sin_len == 0)
+ sin.sin_len = sizeof(sin);
+#endif
+
+ soc = rip_sock;
+ flags = 0;
+
+ switch (type) {
+ case OUT_QUERY:
+ msg = "Answer Query";
+ if (soc < 0)
+ soc = ifp->int_rip_sock;
+ break;
+ case OUT_UNICAST:
+ msg = "Send";
+ if (soc < 0)
+ soc = ifp->int_rip_sock;
+ flags = MSG_DONTROUTE;
+ break;
+ case OUT_BROADCAST:
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send";
+ } else {
+ msg = "Send bcast";
+ }
+ flags = MSG_DONTROUTE;
+ break;
+ case OUT_MULTICAST:
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send pt-to-pt";
+ } else if (ifp->int_state & IS_DUP) {
+ trace_act("abort multicast output via %s"
+ " with duplicate address",
+ ifp->int_name);
+ return 0;
+ } else {
+ msg = "Send mcast";
+ if (rip_sock_mcast != ifp) {
+#ifdef MCAST_PPP_BUG
+ /* Do not specifiy the primary interface
+ * explicitly if we have the multicast
+ * point-to-point kernel bug, since the
+ * kernel will do the wrong thing if the
+ * local address of a point-to-point link
+ * is the same as the address of an ordinary
+ * interface.
+ */
+ if (ifp->int_addr == myaddr) {
+ tgt_mcast = 0;
+ } else
+#endif
+ tgt_mcast = ifp->int_addr;
+ if (0 > setsockopt(rip_sock,
+ IPPROTO_IP, IP_MULTICAST_IF,
+ &tgt_mcast,
+ sizeof(tgt_mcast))) {
+ serrno = errno;
+ LOGERR("setsockopt(rip_sock,"
+ "IP_MULTICAST_IF)");
+ errno = serrno;
+ ifp = 0;
+ return -1;
+ }
+ rip_sock_mcast = ifp;
+ }
+ sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
+ }
+ break;
+
+ case NO_OUT_MULTICAST:
+ case NO_OUT_RIPV2:
+ default:
+#ifdef DEBUG
+ abort();
+#endif
+ return -1;
+ }
+
+ trace_rip(msg, "to", &sin, ifp, buf, size);
+
+ res = sendto(soc, buf, size, flags,
+ (struct sockaddr *)&sin, sizeof(sin));
+ if (res < 0
+ && (ifp == 0 || !(ifp->int_state & IS_BROKE))) {
+ serrno = errno;
+ msglog("%s sendto(%s%s%s.%d): %s", msg,
+ ifp != 0 ? ifp->int_name : "",
+ ifp != 0 ? ", " : "",
+ inet_ntoa(sin.sin_addr),
+ ntohs(sin.sin_port),
+ strerror(errno));
+ errno = serrno;
+ }
+
+ return res;
+}
+
+
+/* Find the first key for a packet to send.
+ * Try for a key that is eligable and has not expired, but settle for
+ * the last key if they have all expired.
+ * If no key is ready yet, give up.
+ */
+struct auth *
+find_auth(struct interface *ifp)
+{
+ struct auth *ap, *res;
+ int i;
+
+
+ if (ifp == 0)
+ return 0;
+
+ res = 0;
+ ap = ifp->int_auth;
+ for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ /* stop looking after the last key */
+ if (ap->type == RIP_AUTH_NONE)
+ break;
+
+ /* ignore keys that are not ready yet */
+ if ((u_long)ap->start > (u_long)clk.tv_sec)
+ continue;
+
+ if ((u_long)ap->end < (u_long)clk.tv_sec) {
+ /* note best expired password as a fall-back */
+ if (res == 0 || (u_long)ap->end > (u_long)res->end)
+ res = ap;
+ continue;
+ }
+
+ /* note key with the best future */
+ if (res == 0 || (u_long)res->end < (u_long)ap->end)
+ res = ap;
+ }
+ return res;
+}
+
+
+void
+clr_ws_buf(struct ws_buf *wb,
+ struct auth *ap)
+{
+ struct netauth *na;
+
+ wb->lim = wb->base + NETS_LEN;
+ wb->n = wb->base;
+ bzero(wb->n, NETS_LEN*sizeof(*wb->n));
+
+ /* install authentication if appropriate
+ */
+ if (ap == 0)
+ return;
+ na = (struct netauth*)wb->n;
+ if (ap->type == RIP_AUTH_PW) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_PW;
+ bcopy(ap->key, na->au.au_pw, sizeof(na->au.au_pw));
+ wb->n++;
+
+ } else if (ap->type == RIP_AUTH_MD5) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_MD5;
+ na->au.a_md5.md5_keyid = ap->keyid;
+ na->au.a_md5.md5_auth_len = RIP_AUTH_PW_LEN;
+ na->au.a_md5.md5_seqno = clk.tv_sec;
+ wb->n++;
+ wb->lim--; /* make room for trailer */
+ }
+}
+
+
+void
+end_md5_auth(struct ws_buf *wb,
+ struct auth *ap)
+{
+ struct netauth *na, *na2;
+ MD5_CTX md5_ctx;
+
+
+ na = (struct netauth*)wb->base;
+ na2 = (struct netauth*)wb->n;
+ na2->a_family = RIP_AF_AUTH;
+ na2->a_type = 1;
+ bcopy(ap->key, na2->au.au_pw, sizeof(na2->au.au_pw));
+ na->au.a_md5.md5_pkt_len = (char *)na2-(char *)(na+1);
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)na,
+ (char *)(na2+1) - (char *)na);
+ MD5Final(na2->au.au_pw, &md5_ctx);
+ wb->n++;
+}
+
+
+/* Send the buffer
+ */
+static void
+supply_write(struct ws_buf *wb)
+{
+ /* Output multicast only if legal.
+ * If we would multcast and it would be illegal, then discard the
+ * packet.
+ */
+ switch (wb->type) {
+ case NO_OUT_MULTICAST:
+ trace_pkt("skip multicast to %s because impossible",
+ naddr_ntoa(ws.to.sin_addr.s_addr));
+ break;
+ case NO_OUT_RIPV2:
+ break;
+ default:
+ if (ws.a != 0 && ws.a->type == RIP_AUTH_MD5)
+ end_md5_auth(wb,ws.a);
+ if (output(wb->type, &ws.to, ws.ifp, wb->buf,
+ ((char *)wb->n - (char*)wb->buf)) < 0
+ && ws.ifp != 0)
+ if_sick(ws.ifp);
+ ws.npackets++;
+ break;
+ }
+
+ clr_ws_buf(wb,ws.a);
+}
+
+
+/* put an entry into the packet
+ */
+static void
+supply_out(struct ag_info *ag)
+{
+ int i;
+ naddr mask, v1_mask, dst_h, ddst_h = 0;
+ struct ws_buf *wb;
+
+
+ /* Skip this route if doing a flash update and it and the routes
+ * it aggregates have not changed recently.
+ */
+ if (ag->ag_seqno < update_seqno
+ && (ws.state & WS_ST_FLASH))
+ return;
+
+ /* Skip this route if required by split-horizon.
+ */
+ if (ag->ag_state & AGS_SPLIT_HZ)
+ return;
+
+ dst_h = ag->ag_dst_h;
+ mask = ag->ag_mask;
+ v1_mask = ripv1_mask_host(htonl(dst_h),
+ (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0);
+ i = 0;
+
+ /* If we are sending RIPv2 packets that cannot (or must not) be
+ * heard by RIPv1 listeners, do not worry about sub- or supernets.
+ * Subnets (from other networks) can only be sent via multicast.
+ * A pair of subnet routes might have been promoted so that they
+ * are legal to send by RIPv1.
+ * If RIPv1 is off, use the multicast buffer.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL)
+ || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) {
+ /* use the RIPv2-only buffer */
+ wb = &v2buf;
+
+ } else {
+ /* use the RIPv1-or-RIPv2 buffer */
+ wb = &v12buf;
+
+ /* Convert supernet route into corresponding set of network
+ * routes for RIPv1, but leave non-contiguous netmasks
+ * to ag_check().
+ */
+ if (v1_mask > mask
+ && mask + (mask & -mask) == 0) {
+ ddst_h = v1_mask & -v1_mask;
+ i = (v1_mask & ~mask)/ddst_h;
+
+ if (i > ws.gen_limit) {
+ /* Punt if we would have to generate an
+ * unreasonable number of routes.
+ */
+#ifdef DEBUG
+ msglog("sending %s to %s as 1 instead"
+ " of %d routes",
+ addrname(htonl(dst_h),mask,1),
+ naddr_ntoa(ws.to.sin_addr.s_addr),
+ i+1);
+#endif
+ i = 0;
+
+ } else {
+ mask = v1_mask;
+ ws.gen_limit -= i;
+ }
+ }
+ }
+
+ do {
+ wb->n->n_family = RIP_AF_INET;
+ wb->n->n_dst = htonl(dst_h);
+ /* If the route is from router-discovery or we are
+ * shutting down, admit only a bad metric.
+ */
+ wb->n->n_metric = ((stopint || ag->ag_metric < 1)
+ ? HOPCNT_INFINITY
+ : ag->ag_metric);
+ HTONL(wb->n->n_metric);
+ /* Any non-zero bits in the supposedly unused RIPv1 fields
+ * cause the old `routed` to ignore the route.
+ * That means the mask and so forth cannot be sent
+ * in the hybrid RIPv1/RIPv2 mode.
+ */
+ if (ws.state & WS_ST_RIP2_ALL) {
+ if (ag->ag_nhop != 0
+ && ((ws.state & WS_ST_QUERY)
+ || (ag->ag_nhop != ws.ifp->int_addr
+ && on_net(ag->ag_nhop,
+ ws.ifp->int_net,
+ ws.ifp->int_mask))))
+ wb->n->n_nhop = ag->ag_nhop;
+ wb->n->n_mask = htonl(mask);
+ wb->n->n_tag = ag->ag_tag;
+ }
+ dst_h += ddst_h;
+
+ if (++wb->n >= wb->lim)
+ supply_write(wb);
+ } while (i-- != 0);
+}
+
+
+/* supply one route from the table
+ */
+/* ARGSUSED */
+static int
+walk_supply(struct radix_node *rn,
+ struct walkarg *w)
+{
+#define RT ((struct rt_entry *)rn)
+ u_short ags;
+ char metric, pref;
+ naddr dst, nhop;
+
+
+ /* Do not advertise external remote interfaces or passive interfaces.
+ */
+ if ((RT->rt_state & RS_IF)
+ && RT->rt_ifp != 0
+ && (RT->rt_ifp->int_if_flags & IS_PASSIVE)
+ && !(RT->rt_state & RS_MHOME))
+ return 0;
+
+ /* If being quiet about our ability to forward, then
+ * do not say anything unless responding to a query,
+ * except about our main interface.
+ */
+ if (!supplier && !(ws.state & WS_ST_QUERY)
+ && !(RT->rt_state & RS_MHOME))
+ return 0;
+
+ dst = RT->rt_dst;
+
+ /* do not collide with the fake default route */
+ if (dst == RIP_DEFAULT
+ && (ws.state & WS_ST_DEFAULT))
+ return 0;
+
+ if (RT->rt_state & RS_NET_SYN) {
+ if (RT->rt_state & RS_NET_INT) {
+ /* Do not send manual synthetic network routes
+ * into the subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return 0;
+
+ } else {
+ /* Do not send automatic synthetic network routes
+ * if they are not needed becaus no RIPv1 listeners
+ * can hear them.
+ */
+ if (ws.state & WS_ST_RIP2_ALL)
+ return 0;
+
+ /* Do not send automatic synthetic network routes to
+ * the real subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return 0;
+ }
+ nhop = 0;
+
+ } else {
+ /* Advertise the next hop if this is not a route for one
+ * of our interfaces and the next hop is on the same
+ * network as the target.
+ */
+ if (!(RT->rt_state & RS_IF)
+ && RT->rt_gate != myaddr
+ && RT->rt_gate != loopaddr)
+ nhop = RT->rt_gate;
+ else
+ nhop = 0;
+ }
+
+ metric = RT->rt_metric;
+ ags = 0;
+
+ if (RT->rt_state & RS_MHOME) {
+ /* retain host route of multi-homed servers */
+ ;
+
+ } else if (RT_ISHOST(RT)) {
+ /* We should always aggregate the host routes
+ * for the local end of our point-to-point links.
+ * If we are suppressing host routes in general, then do so.
+ * Avoid advertising host routes onto their own network,
+ * where they should be handled by proxy-ARP.
+ */
+ if ((RT->rt_state & RS_LOCAL)
+ || ridhosts
+ || (ws.state & WS_ST_SUPER_AG)
+ || on_net(dst, ws.to_net, ws.to_mask))
+ ags |= AGS_SUPPRESS;
+
+ if (ws.state & WS_ST_SUPER_AG)
+ ags |= AGS_PROMOTE;
+
+ } else if (ws.state & WS_ST_AG) {
+ /* Aggregate network routes, if we are allowed.
+ */
+ ags |= AGS_SUPPRESS;
+
+ /* Generate supernets if allowed.
+ * If we can be heard by RIPv1 systems, we will
+ * later convert back to ordinary nets.
+ * This unifies dealing with received supernets.
+ */
+ if ((RT->rt_state & RS_SUBNET)
+ || (ws.state & WS_ST_SUPER_AG))
+ ags |= AGS_PROMOTE;
+
+ }
+
+ /* Do not send RIPv1 advertisements of subnets to other
+ * networks. If possible, multicast them by RIPv2.
+ */
+ if ((RT->rt_state & RS_SUBNET)
+ && !(ws.state & WS_ST_RIP2_ALL)
+ && !on_net(dst, ws.to_std_net, ws.to_std_mask)) {
+ ags |= AGS_RIPV2 | AGS_PROMOTE;
+ if (ws.state & WS_ST_SUB_AG)
+ ags |= AGS_SUPPRESS;
+ }
+
+ /* Do not send a route back to where it came from, except in
+ * response to a query. This is "split-horizon". That means not
+ * advertising back to the same network and so via the same interface.
+ *
+ * We want to suppress routes that might have been fragmented
+ * from this route by a RIPv1 router and sent back to us, and so we
+ * cannot forget this route here. Let the split-horizon route
+ * aggregate (suppress) the fragmented routes and then itself be
+ * forgotten.
+ *
+ * Include the routes for both ends of point-to-point interfaces
+ * among those suppressed by split-horizon, since the other side
+ * should knows them as well as we do.
+ */
+ if (RT->rt_ifp == ws.ifp && ws.ifp != 0
+ && !(ws.state & WS_ST_QUERY)
+ && (ws.state & WS_ST_TO_ON_NET)
+ && (!(RT->rt_state & RS_IF)
+ || ws.ifp->int_if_flags & IFF_POINTOPOINT)) {
+ /* If we do not mark the route with AGS_SPLIT_HZ here,
+ * it will be poisoned-reverse, or advertised back toward
+ * its source with an infinite metric. If we have recently
+ * advertised the route with a better metric than we now
+ * have, then we should poison-reverse the route before
+ * suppressing it for split-horizon.
+ *
+ * In almost all cases, if there is no spare for the route
+ * then it is either old and dead or a brand new route.
+ * If it is brand new, there is no need for poison-reverse.
+ * If it is old and dead, it is already poisoned.
+ */
+ if (RT->rt_poison_time < now_expire
+ || RT->rt_poison_metric >= metric
+ || RT->rt_spares[1].rts_gate == 0) {
+ ags |= AGS_SPLIT_HZ;
+ ags &= ~(AGS_PROMOTE | AGS_SUPPRESS);
+ }
+ metric = HOPCNT_INFINITY;
+ }
+
+ /* Adjust the outgoing metric by the cost of the link.
+ */
+ pref = metric + ws.metric;
+ if (pref < HOPCNT_INFINITY) {
+ /* Keep track of the best metric with which the
+ * route has been advertised recently.
+ */
+ if (RT->rt_poison_metric >= metric
+ || RT->rt_poison_time < now_expire) {
+ RT->rt_poison_time = now.tv_sec;
+ RT->rt_poison_metric = metric;
+ }
+ metric = pref;
+
+ } else {
+ /* Do not advertise stable routes that will be ignored,
+ * unless we are answering a query.
+ * If the route recently was advertised with a metric that
+ * would have been less than infinity through this interface,
+ * we need to continue to advertise it in order to poison it.
+ */
+ pref = RT->rt_poison_metric + ws.metric;
+ if (!(ws.state & WS_ST_QUERY)
+ && (pref >= HOPCNT_INFINITY
+ || RT->rt_poison_time < now_garbage))
+ return 0;
+
+ metric = HOPCNT_INFINITY;
+ }
+
+ ag_check(dst, RT->rt_mask, 0, nhop, metric, pref,
+ RT->rt_seqno, RT->rt_tag, ags, supply_out);
+ return 0;
+#undef RT
+}
+
+
+/* Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ */
+void
+supply(struct sockaddr_in *dst,
+ struct interface *ifp, /* output interface */
+ enum output_type type,
+ int flash, /* 1=flash update */
+ int vers, /* RIP version */
+ int passwd_ok) /* OK to include cleartext password */
+{
+ struct rt_entry *rt;
+ int def_metric;
+
+
+ ws.state = 0;
+ ws.gen_limit = 1024;
+
+ ws.to = *dst;
+ ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr);
+ ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask;
+
+ if (ifp != 0) {
+ ws.to_mask = ifp->int_mask;
+ ws.to_net = ifp->int_net;
+ if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask))
+ ws.state |= WS_ST_TO_ON_NET;
+
+ } else {
+ ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0);
+ ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask;
+ rt = rtfind(dst->sin_addr.s_addr);
+ if (rt)
+ ifp = rt->rt_ifp;
+ }
+
+ ws.npackets = 0;
+ if (flash)
+ ws.state |= WS_ST_FLASH;
+ if (type == OUT_QUERY)
+ ws.state |= WS_ST_QUERY;
+
+ if ((ws.ifp = ifp) == 0) {
+ ws.metric = 1;
+ } else {
+ /* Adjust the advertised metric by the outgoing interface
+ * metric.
+ */
+ ws.metric = ifp->int_metric+1;
+ }
+
+ ripv12_buf.rip.rip_vers = vers;
+
+ switch (type) {
+ case OUT_BROADCAST:
+ v2buf.type = ((ifp != 0 && (ifp->int_if_flags & IFF_MULTICAST))
+ ? OUT_MULTICAST
+ : NO_OUT_MULTICAST);
+ v12buf.type = OUT_BROADCAST;
+ break;
+ case OUT_MULTICAST:
+ v2buf.type = ((ifp != 0 && (ifp->int_if_flags & IFF_MULTICAST))
+ ? OUT_MULTICAST
+ : NO_OUT_MULTICAST);
+ v12buf.type = OUT_BROADCAST;
+ break;
+ case OUT_UNICAST:
+ case OUT_QUERY:
+ v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2;
+ v12buf.type = type;
+ break;
+ default:
+ v2buf.type = type;
+ v12buf.type = type;
+ break;
+ }
+
+ if (vers == RIPv2) {
+ /* full RIPv2 only if cannot be heard by RIPv1 listeners */
+ if (type != OUT_BROADCAST)
+ ws.state |= WS_ST_RIP2_ALL;
+ if (!(ws.state & WS_ST_TO_ON_NET)) {
+ ws.state |= (WS_ST_AG | WS_ST_SUPER_AG);
+ } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) {
+ ws.state |= WS_ST_AG;
+ if (type != OUT_BROADCAST
+ && (ifp == 0 || !(ifp->int_state&IS_NO_SUPER_AG)))
+ ws.state |= WS_ST_SUPER_AG;
+ }
+
+ } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) {
+ ws.state |= WS_ST_SUB_AG;
+ }
+
+ ws.a = (vers == RIPv2) ? find_auth(ifp) : 0;
+ if (!passwd_ok && ws.a != 0 && ws.a->type == RIP_AUTH_PW)
+ ws.a = 0;
+ clr_ws_buf(&v12buf,ws.a);
+ clr_ws_buf(&v2buf,ws.a);
+
+ /* Fake a default route if asked and if there is not already
+ * a better, real default route.
+ */
+ if (supplier && (def_metric = ifp->int_d_metric) != 0) {
+ if (0 == (rt = rtget(RIP_DEFAULT, 0))
+ || rt->rt_metric+ws.metric >= def_metric) {
+ ws.state |= WS_ST_DEFAULT;
+ ag_check(0, 0, 0, 0, def_metric, def_metric,
+ 0, 0, 0, supply_out);
+ } else {
+ def_metric = rt->rt_metric+ws.metric;
+ }
+
+ /* If both RIPv2 and the poor-man's router discovery
+ * kludge are on, arrange to advertise an extra
+ * default route via RIPv1.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL)
+ && (ifp->int_state & IS_PM_RDISC)) {
+ ripv12_buf.rip.rip_vers = RIPv1;
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = htonl(RIP_DEFAULT);
+ v12buf.n->n_metric = htonl(def_metric);
+ v12buf.n++;
+ }
+ }
+
+ (void)rn_walktree(rhead, walk_supply, 0);
+ ag_flush(0,0,supply_out);
+
+ /* Flush the packet buffers, provided they are not empty and
+ * do not contain only the password.
+ */
+ if (v12buf.n != v12buf.base
+ && (v12buf.n > v12buf.base+1
+ || v12buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v12buf);
+ if (v2buf.n != v2buf.base
+ && (v2buf.n > v2buf.base+1
+ || v2buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v2buf);
+
+ /* If we sent nothing and this is an answer to a query, send
+ * an empty buffer.
+ */
+ if (ws.npackets == 0
+ && (ws.state & WS_ST_QUERY))
+ supply_write(&v12buf);
+}
+
+
+/* send all of the routing table or just do a flash update
+ */
+void
+rip_bcast(int flash)
+{
+#ifdef _HAVE_SIN_LEN
+ static struct sockaddr_in dst = {sizeof(dst), AF_INET};
+#else
+ static struct sockaddr_in dst = {AF_INET};
+#endif
+ struct interface *ifp;
+ enum output_type type;
+ int vers;
+ struct timeval rtime;
+
+
+ need_flash = 0;
+ intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME);
+ no_flash = rtime;
+ timevaladd(&no_flash, &now);
+
+ if (rip_sock < 0)
+ return;
+
+ trace_act("send %s and inhibit dynamic updates for %.3f sec",
+ flash ? "dynamic update" : "all routes",
+ rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0);
+
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Skip interfaces not doing RIP.
+ * Do try broken interfaces to see if they have healed.
+ */
+ if (IS_RIP_OUT_OFF(ifp->int_state))
+ continue;
+
+ /* skip turned off interfaces */
+ if (!iff_alive(ifp->int_if_flags))
+ continue;
+
+ vers = (ifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1;
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ /* ordinary, hardware interface */
+ dst.sin_addr.s_addr = ifp->int_brdaddr;
+
+ /* If RIPv1 is not turned off, then broadcast so
+ * that RIPv1 listeners can hear.
+ */
+ if (vers == RIPv2
+ && (ifp->int_state & IS_NO_RIPV1_OUT)) {
+ type = OUT_MULTICAST;
+ } else {
+ type = OUT_BROADCAST;
+ }
+
+ } else if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* point-to-point hardware interface */
+ dst.sin_addr.s_addr = ifp->int_dstaddr;
+ type = OUT_UNICAST;
+
+ } else if (ifp->int_state & IS_REMOTE) {
+ /* remote interface */
+ dst.sin_addr.s_addr = ifp->int_addr;
+ type = OUT_UNICAST;
+
+ } else {
+ /* ATM, HIPPI, etc. */
+ continue;
+ }
+
+ supply(&dst, ifp, type, flash, vers, 1);
+ }
+
+ update_seqno++; /* all routes are up to date */
+}
+
+
+/* Ask for routes
+ * Do it only once to an interface, and not even after the interface
+ * was broken and recovered.
+ */
+void
+rip_query(void)
+{
+#ifdef _HAVE_SIN_LEN
+ static struct sockaddr_in dst = {sizeof(dst), AF_INET};
+#else
+ static struct sockaddr_in dst = {AF_INET};
+#endif
+ struct interface *ifp;
+ struct rip buf;
+ enum output_type type;
+
+
+ if (rip_sock < 0)
+ return;
+
+ bzero(&buf, sizeof(buf));
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ /* Skip interfaces those already queried.
+ * Do not ask via interfaces through which we don't
+ * accept input. Do not ask via interfaces that cannot
+ * send RIP packets.
+ * Do try broken interfaces to see if they have healed.
+ */
+ if (IS_RIP_IN_OFF(ifp->int_state)
+ || ifp->int_query_time != NEVER)
+ continue;
+
+ /* skip turned off interfaces */
+ if (!iff_alive(ifp->int_if_flags))
+ continue;
+
+ buf.rip_vers = (ifp->int_state&IS_NO_RIPV1_OUT) ? RIPv2:RIPv1;
+ buf.rip_cmd = RIPCMD_REQUEST;
+ buf.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ if (ifp->int_if_flags & IFF_BROADCAST) {
+ /* ordinary, hardware interface */
+ dst.sin_addr.s_addr = ifp->int_brdaddr;
+ /* if RIPv1 is not turned off, then broadcast so
+ * that RIPv1 listeners can hear.
+ */
+ if (buf.rip_vers == RIPv2
+ && (ifp->int_state & IS_NO_RIPV1_OUT)) {
+ type = OUT_MULTICAST;
+ } else {
+ type = OUT_BROADCAST;
+ }
+
+ } else if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* point-to-point hardware interface */
+ dst.sin_addr.s_addr = ifp->int_dstaddr;
+ type = OUT_UNICAST;
+
+ } else if (ifp->int_state & IS_REMOTE) {
+ /* remote interface */
+ dst.sin_addr.s_addr = ifp->int_addr;
+ type = OUT_UNICAST;
+
+ } else {
+ /* ATM, HIPPI, etc. */
+ continue;
+ }
+
+ ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL;
+ if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0)
+ if_sick(ifp);
+ }
+}
diff --git a/sbin/routed/parms.c b/sbin/routed/parms.c
new file mode 100644
index 0000000..87ca4a8
--- /dev/null
+++ b/sbin/routed/parms.c
@@ -0,0 +1,916 @@
+/*
+ * 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.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.12 $"
+
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/stat.h>
+
+
+struct parm *parms;
+struct intnet *intnets;
+struct tgate *tgates;
+
+
+/* use configured parameters
+ */
+void
+get_parms(struct interface *ifp)
+{
+ static warned_auth_in, warned_auth_out;
+ struct parm *parmp;
+ int i, num_passwds = 0;
+
+ /* get all relevant parameters
+ */
+ for (parmp = parms; parmp != 0; parmp = parmp->parm_next) {
+ if (parmp->parm_name[0] == '\0'
+ || !strcmp(ifp->int_name, parmp->parm_name)
+ || (parmp->parm_name[0] == '\n'
+ && on_net(ifp->int_addr,
+ parmp->parm_net, parmp->parm_mask))) {
+
+ /* This group of parameters is relevant,
+ * so get its settings
+ */
+ ifp->int_state |= parmp->parm_int_state;
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[0].type == RIP_AUTH_NONE
+ || num_passwds >= MAX_AUTH_KEYS)
+ break;
+ bcopy(&parmp->parm_auth[i],
+ &ifp->int_auth[num_passwds++],
+ sizeof(ifp->int_auth[0]));
+ }
+ if (parmp->parm_rdisc_pref != 0)
+ ifp->int_rdisc_pref = parmp->parm_rdisc_pref;
+ if (parmp->parm_rdisc_int != 0)
+ ifp->int_rdisc_int = parmp->parm_rdisc_int;
+ if (parmp->parm_d_metric != 0)
+ ifp->int_d_metric = parmp->parm_d_metric;
+ }
+ }
+
+ /* Set general defaults.
+ *
+ * Default poor-man's router discovery to a metric that will
+ * be heard by old versions of `routed`. They ignored received
+ * routes with metric 15.
+ */
+ if ((ifp->int_state & IS_PM_RDISC)
+ && ifp->int_d_metric == 0)
+ ifp->int_d_metric = FAKE_METRIC;
+
+ if (ifp->int_rdisc_int == 0)
+ ifp->int_rdisc_int = DefMaxAdvertiseInterval;
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST)
+ && !(ifp->int_state & IS_REMOTE))
+ ifp->int_state |= IS_BCAST_RDISC;
+
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ ifp->int_state |= IS_BCAST_RDISC;
+ /* By default, point-to-point links should be passive
+ * about router-discovery for the sake of demand-dialing.
+ */
+ if (0 == (ifp->int_state & GROUP_IS_SOL))
+ ifp->int_state |= IS_NO_SOL_OUT;
+ if (0 == (ifp->int_state & GROUP_IS_ADV))
+ ifp->int_state |= IS_NO_ADV_OUT;
+ }
+
+ if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE)))
+ ifp->int_state |= IS_NO_RDISC;
+ if (ifp->int_state & IS_PASSIVE)
+ ifp->int_state |= IS_NO_RIP;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state)
+ && ifp->int_auth[0].type != RIP_AUTH_NONE
+ && !(ifp->int_state & IS_NO_RIPV1_IN)
+ && !warned_auth_in) {
+ msglog("Warning: RIPv1 input via %s"
+ " will be accepted without authentication",
+ ifp->int_name);
+ warned_auth_in = 1;
+ }
+ if (!IS_RIP_OUT_OFF(ifp->int_state)
+ && ifp->int_auth[0].type != RIP_AUTH_NONE
+ && !(ifp->int_state & IS_NO_RIPV1_OUT)) {
+ if (!warned_auth_out) {
+ msglog("Warning: RIPv1 output via %s"
+ " will be sent without authentication",
+ ifp->int_name);
+ warned_auth_out = 1;
+ }
+ }
+}
+
+
+/* Read a list of gateways from /etc/gateways and add them to our tables.
+ *
+ * This file contains a list of "remote" gateways. That is usually
+ * a gateway which we cannot immediately determine if it is present or
+ * not as we can do for those provided by directly connected hardware.
+ *
+ * If a gateway is marked "passive" in the file, then we assume it
+ * does not understand RIP and assume it is always present. Those
+ * not marked passive are treated as if they were directly connected
+ * and assumed to be broken if they do not send us advertisements.
+ * All remote interfaces are added to our list, and those not marked
+ * passive are sent routing updates.
+ *
+ * A passive interface can also be local, hardware interface exempt
+ * from RIP.
+ */
+void
+gwkludge(void)
+{
+ FILE *fp;
+ char *p, *lptr;
+ char lbuf[200], net_host[5], dname[64+1+64+1], gname[64+1], qual[9];
+ struct interface *ifp;
+ naddr dst, netmask, gate;
+ int metric, n;
+ struct stat sb;
+ u_int state;
+ char *type;
+
+
+ fp = fopen(_PATH_GATEWAYS, "r");
+ if (fp == 0)
+ return;
+
+ if (0 > fstat(fileno(fp), &sb)) {
+ msglog("could not stat() "_PATH_GATEWAYS);
+ (void)fclose(fp);
+ return;
+ }
+
+ for (;;) {
+ if (0 == fgets(lbuf, sizeof(lbuf)-1, fp))
+ break;
+ lptr = lbuf;
+ while (*lptr == ' ')
+ lptr++;
+ if (*lptr == '\n' /* ignore null and comment lines */
+ || *lptr == '#')
+ continue;
+ p = lptr+strlen(lptr)-1;
+ while (*p == '\n' || *p == ' ')
+ *p-- = '\0';
+
+ /* notice newfangled parameter lines
+ */
+ if (strncasecmp("net", lptr, 3)
+ && strncasecmp("host", lptr, 4)) {
+ p = parse_parms(lptr,
+ (sb.st_uid == 0
+ && !(sb.st_mode&(S_IRWXG|S_IRWXO))));
+ if (p != 0) {
+ if (strcasecmp(p,lptr))
+ msglog("%s in "_PATH_GATEWAYS
+ " entry \"%s\"", p, lptr);
+ else
+ msglog("bad \"%s\" in "_PATH_GATEWAYS,
+ lptr);
+ }
+ continue;
+ }
+
+/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */
+ qual[0] = '\0';
+ n = sscanf(lptr, "%4s %129[^ \t] gateway"
+ " %64[^ / \t] metric %u %8s\n",
+ net_host, dname, gname, &metric, qual);
+ if (n != 4 && n != 5) {
+ msglog("bad "_PATH_GATEWAYS" entry \"%s\"; %d values",
+ lptr, n);
+ continue;
+ }
+ if (metric >= HOPCNT_INFINITY) {
+ msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"",
+ lptr);
+ continue;
+ }
+ if (!strcasecmp(net_host, "host")) {
+ if (!gethost(dname, &dst)) {
+ msglog("bad host \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ netmask = HOST_MASK;
+ } else if (!strcasecmp(net_host, "net")) {
+ if (!getnet(dname, &dst, &netmask)) {
+ msglog("bad net \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ HTONL(dst); /* make network # into IP address */
+ } else {
+ msglog("bad \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", lptr);
+ continue;
+ }
+
+ if (!gethost(gname, &gate)) {
+ msglog("bad gateway \"%s\" in "_PATH_GATEWAYS
+ " entry \"%s\"", gname, lptr);
+ continue;
+ }
+
+ if (!strcasecmp(qual, type = "passive")) {
+ /* Passive entries are not placed in our tables,
+ * only the kernel's, so we don't copy all of the
+ * external routing information within a net.
+ * Internal machines should use the default
+ * route to a suitable gateway (like us).
+ */
+ state = IS_REMOTE | IS_PASSIVE;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (!strcasecmp(qual, type = "external")) {
+ /* External entries are handled by other means
+ * such as EGP, and are placed only in the daemon
+ * tables to prevent overriding them with something
+ * else.
+ */
+ strcpy(qual,"external");
+ state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (!strcasecmp(qual, "active")
+ || qual[0] == '\0') {
+ if (metric != 0) {
+ /* Entries that are neither "passive" nor
+ * "external" are "remote" and must behave
+ * like physical interfaces. If they are not
+ * heard from regularly, they are deleted.
+ */
+ state = IS_REMOTE;
+ type = "remote";
+ } else {
+ /* "remote" entries with a metric of 0
+ * are aliases for our own interfaces
+ */
+ state = IS_REMOTE | IS_PASSIVE | IS_ALIAS;
+ type = "alias";
+ }
+
+ } else {
+ msglog("bad "_PATH_GATEWAYS" entry \"%s\";"
+ " unknown type %s", lptr, qual);
+ continue;
+ }
+
+ if (0 != (state & (IS_PASSIVE | IS_REMOTE)))
+ state |= IS_NO_RDISC;
+ if (state & IS_PASSIVE)
+ state |= IS_NO_RIP;
+
+ ifp = check_dup(gate,dst,netmask,0);
+ if (ifp != 0) {
+ msglog("duplicate "_PATH_GATEWAYS" entry \"%s\"",lptr);
+ continue;
+ }
+
+ ifp = (struct interface *)malloc(sizeof(*ifp));
+ bzero(ifp, sizeof(*ifp));
+
+ ifp->int_state = state;
+ if (netmask == HOST_MASK)
+ ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP_RUNNING;
+ else
+ ifp->int_if_flags = IFF_UP_RUNNING;
+ ifp->int_act_time = NEVER;
+ ifp->int_addr = gate;
+ ifp->int_dstaddr = dst;
+ ifp->int_mask = netmask;
+ ifp->int_ripv1_mask = netmask;
+ ifp->int_std_mask = std_mask(gate);
+ ifp->int_net = ntohl(dst);
+ ifp->int_std_net = ifp->int_net & ifp->int_std_mask;
+ ifp->int_std_addr = htonl(ifp->int_std_net);
+ ifp->int_metric = metric;
+ if (!(state & IS_EXTERNAL)
+ && ifp->int_mask != ifp->int_std_mask)
+ ifp->int_state |= IS_SUBNET;
+ (void)sprintf(ifp->int_name, "%s(%s)", type, gname);
+ ifp->int_index = -1;
+
+ if_link(ifp);
+ }
+
+ /* After all of the parameter lines have been read,
+ * apply them to any remote interfaces.
+ */
+ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) {
+ get_parms(ifp);
+
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+
+ trace_if("Add", ifp);
+ }
+
+ (void)fclose(fp);
+}
+
+
+/* strtok(), but honoring backslash
+ */
+static int /* 0=ok, -1=bad */
+parse_quote(char **linep,
+ char *delims,
+ char *delimp,
+ char *buf,
+ int lim)
+{
+ char c, *pc, *p;
+
+
+ pc = *linep;
+ if (*pc == '\0')
+ return -1;
+
+ while (lim != 0) {
+ c = *pc++;
+ if (c == '\0')
+ break;
+
+ if (c == '\\' && pc != '\0') {
+ if ((c = *pc++) == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c >= '0' && c <= '7') {
+ c -= '0';
+ if (*pc >= '0' && *pc <= '7') {
+ c = (c<<3)+(*pc++ - '0');
+ if (*pc >= '0' && *pc <= '7')
+ c = (c<<3)+(*pc++ - '0');
+ }
+ }
+
+ } else {
+ for (p = delims; *p != '\0'; ++p) {
+ if (*p == c)
+ goto exit;
+ }
+ }
+
+ *buf++ = c;
+ --lim;
+ }
+exit:
+ if (lim == 0)
+ return -1;
+
+ *buf = '\0';
+ if (delimp != 0)
+ *delimp = c;
+ *linep = pc-1;
+ return 0;
+}
+
+
+/* Parse password timestamp
+ */
+static char *
+parse_ts(time_t *tp,
+ char **valp,
+ char *val0,
+ char *delimp,
+ char *buf,
+ u_int bufsize)
+{
+ struct tm tm;
+
+ if (0 > parse_quote(valp, "| ,\n\r", delimp,
+ buf,bufsize)
+ || buf[bufsize-1] != '\0'
+ || buf[bufsize-2] != '\0') {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+ strcat(buf,"\n");
+ bzero(&tm, sizeof(tm));
+ if (5 != sscanf(buf, "%u/%u/%u@%u:%u\n",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min)) {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+ if (tm.tm_year <= 37)
+ tm.tm_year += 100;
+
+ if ((*tp = mktime(&tm)) == -1) {
+ sprintf(buf,"bad timestamp %.25s", val0);
+ return buf;
+ }
+
+ return 0;
+}
+
+
+/* Get a password, key ID, and expiration date in the format
+ * passwd|keyID|year/mon/day@hour:min|year/mon/day@hour:min
+ */
+static char * /* 0 or error message */
+get_passwd(char *tgt,
+ char *val,
+ struct parm *parmp,
+ u_char type,
+ int safe) /* 1=from secure file */
+{
+ static char buf[80];
+ char *val0, *p, delim;
+ struct auth k, *ap, *ap2;
+ int i;
+ u_long l;
+
+
+ if (!safe)
+ return "unsafe password";
+
+ for (ap = parmp->parm_auth, i = 0;
+ ap->type != RIP_AUTH_NONE; i++, ap++) {
+ if (i >= MAX_AUTH_KEYS)
+ return "too many passwords";
+ }
+
+ bzero(&k, sizeof(k));
+ k.type = type;
+ k.end = -1-DAY;
+
+ val0 = val;
+ if (0 > parse_quote(&val, "| ,\n\r", &delim,
+ (char *)k.key, sizeof(k.key)))
+ return tgt;
+
+ if (delim != '|') {
+ if (type == RIP_AUTH_MD5)
+ return "missing Keyid";
+ } else {
+ val0 = ++val;
+ buf[sizeof(buf)-1] = '\0';
+ if (0 > parse_quote(&val, "| ,\n\r", &delim, buf,sizeof(buf))
+ || buf[sizeof(buf)-1] != '\0'
+ || (l = strtoul(buf,&p,0)) > 255
+ || *p != '\0') {
+ sprintf(buf,"bad KeyID \"%.20s\"", val0);
+ return buf;
+ }
+ for (ap2 = parmp->parm_auth; ap2 < ap; ap2++) {
+ if (ap2->keyid == l) {
+ sprintf(buf,"duplicate KeyID \"%.20s\"", val0);
+ return buf;
+ }
+ }
+ k.keyid = (int)l;
+
+ if (delim == '|') {
+ val0 = ++val;
+ if (0 != (p = parse_ts(&k.start,&val,val0,&delim,
+ buf,sizeof(buf))))
+ return p;
+ if (delim != '|')
+ return "missing second timestamp";
+ val0 = ++val;
+ if (0 != (p = parse_ts(&k.end,&val,val0,&delim,
+ buf,sizeof(buf))))
+ return p;
+ if ((u_long)k.start > (u_long)k.end) {
+ sprintf(buf,"out of order timestamp %.30s",
+ val0);
+ return buf;
+ }
+ }
+ }
+ if (delim != '\0')
+ return tgt;
+
+ bcopy(&k, ap, sizeof(*ap));
+ return 0;
+}
+
+
+/* Parse a set of parameters for an interface.
+ */
+char * /* 0 or error message */
+parse_parms(char *line,
+ int safe) /* 1=from secure file */
+{
+#define PARS(str) (!strcasecmp(tgt, str))
+#define PARSEQ(str) (!strncasecmp(tgt, str"=", sizeof(str)))
+#define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \
+ parm.parm_int_state |= (b);}
+ struct parm parm;
+ struct intnet *intnetp;
+ struct tgate *tg;
+ naddr addr, mask;
+ char delim, *val0, *tgt, *val, *p;
+ char buf[64];
+
+
+ /* "subnet=x.y.z.u/mask,metric" must be alone on the line */
+ if (!strncasecmp(line, "subnet=", sizeof("subnet=")-1)
+ && *(val = &line[sizeof("subnet=")]) != '\0') {
+ intnetp = (struct intnet*)malloc(sizeof(*intnetp));
+ intnetp->intnet_metric = 1;
+ if ((p = strrchr(val,','))) {
+ *p++ = '\0';
+ intnetp->intnet_metric = (int)strtol(p,&p,0);
+ if (*p != '\0'
+ || intnetp->intnet_metric <= 0
+ || intnetp->intnet_metric >= HOPCNT_INFINITY)
+ return line;
+ }
+ if (!getnet(val, &intnetp->intnet_addr, &intnetp->intnet_mask)
+ || intnetp->intnet_mask == HOST_MASK
+ || intnetp->intnet_addr == RIP_DEFAULT) {
+ free(intnetp);
+ return line;
+ }
+ HTONL(intnetp->intnet_addr);
+ intnetp->intnet_next = intnets;
+ intnets = intnetp;
+ return 0;
+ }
+
+ bzero(&parm, sizeof(parm));
+
+ tgt = "null";
+ for (;;) {
+ tgt = line + strspn(line, " ,\n\r");
+ if (*tgt == '\0')
+ break;
+
+ line += strcspn(tgt, "= ,\n\r");
+ delim = *line;
+ if (delim == '=') {
+ val0 = ++line;
+ if (0 > parse_quote(&line," ,\n\r",&delim,
+ buf,sizeof(buf)))
+ return tgt;
+ }
+ if (delim != '\0')
+ *line++ = '\0';
+
+ if (PARSEQ("if")) {
+ if (parm.parm_name[0] != '\0'
+ || strlen(buf) > IFNAMSIZ)
+ return tgt;
+ strcpy(parm.parm_name, buf);
+
+ } else if (PARSEQ("addr")) {
+ /* This is a bad idea, because the address based
+ * sets of parameters cannot be checked for
+ * consistency with the interface name parameters.
+ * The parm_net stuff is needed to allow several
+ * -F settings.
+ */
+ if (!getnet(val0, &addr, &mask)
+ || parm.parm_name[0] != '\0')
+ return tgt;
+ parm.parm_net = addr;
+ parm.parm_mask = mask;
+ parm.parm_name[0] = '\n';
+
+ } else if (PARSEQ("passwd")) {
+ /* since cleartext passwords are so weak allow
+ * them anywhere
+ */
+ tgt = get_passwd(tgt,val0,&parm,RIP_AUTH_PW,1);
+ if (tgt) {
+ *val0 = '\0';
+ return tgt;
+ }
+
+ } else if (PARSEQ("md5_passwd")) {
+ tgt = get_passwd(tgt,val0,&parm,RIP_AUTH_MD5,safe);
+ if (tgt) {
+ *val0 = '\0';
+ return tgt;
+ }
+
+ } else if (PARS("no_ag")) {
+ parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG);
+
+ } else if (PARS("no_super_ag")) {
+ parm.parm_int_state |= IS_NO_SUPER_AG;
+
+ } else if (PARS("no_ripv1_in")) {
+ parm.parm_int_state |= IS_NO_RIPV1_IN;
+
+ } else if (PARS("no_ripv2_in")) {
+ parm.parm_int_state |= IS_NO_RIPV2_IN;
+
+ } else if (PARS("ripv2_out")) {
+ if (parm.parm_int_state & IS_NO_RIPV2_OUT)
+ return tgt;
+ parm.parm_int_state |= IS_NO_RIPV1_OUT;
+
+ } else if (PARS("ripv2")) {
+ if ((parm.parm_int_state & IS_NO_RIPV2_OUT)
+ || (parm.parm_int_state & IS_NO_RIPV2_IN))
+ return tgt;
+ parm.parm_int_state |= (IS_NO_RIPV1_IN
+ | IS_NO_RIPV1_OUT);
+
+ } else if (PARS("no_rip")) {
+ CKF(IS_PM_RDISC, IS_NO_RIP);
+
+ } else if (PARS("no_rdisc")) {
+ CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC);
+
+ } else if (PARS("no_solicit")) {
+ CKF(GROUP_IS_SOL, IS_NO_SOL_OUT);
+
+ } else if (PARS("send_solicit")) {
+ CKF(GROUP_IS_SOL, IS_SOL_OUT);
+
+ } else if (PARS("no_rdisc_adv")) {
+ CKF(GROUP_IS_ADV, IS_NO_ADV_OUT);
+
+ } else if (PARS("rdisc_adv")) {
+ CKF(GROUP_IS_ADV, IS_ADV_OUT);
+
+ } else if (PARS("bcast_rdisc")) {
+ parm.parm_int_state |= IS_BCAST_RDISC;
+
+ } else if (PARS("passive")) {
+ CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC);
+ parm.parm_int_state |= IS_NO_RIP;
+
+ } else if (PARSEQ("rdisc_pref")) {
+ if (parm.parm_rdisc_pref != 0
+ || (parm.parm_rdisc_pref = (int)strtoul(buf, &p,0),
+ *p != '\0'))
+ return tgt;
+
+ } else if (PARS("pm_rdisc")) {
+ if (IS_RIP_OUT_OFF(parm.parm_int_state))
+ return tgt;
+ parm.parm_int_state |= IS_PM_RDISC;
+
+ } else if (PARSEQ("rdisc_interval")) {
+ if (parm.parm_rdisc_int != 0
+ || (parm.parm_rdisc_int = (int)strtoul(buf,&p,0),
+ *p != '\0')
+ || parm.parm_rdisc_int < MinMaxAdvertiseInterval
+ || parm.parm_rdisc_int > MaxMaxAdvertiseInterval)
+ return tgt;
+
+ } else if (PARSEQ("fake_default")) {
+ if (parm.parm_d_metric != 0
+ || IS_RIP_OUT_OFF(parm.parm_int_state)
+ || (parm.parm_d_metric = (int)strtoul(buf,&p,0),
+ *p != '\0')
+ || parm.parm_d_metric > HOPCNT_INFINITY-1)
+ return tgt;
+
+ } else if (PARSEQ("trust_gateway")) {
+ if (!gethost(buf,&addr))
+ return tgt;
+ tg = (struct tgate *)malloc(sizeof(*tg));
+ tg->tgate_next = tgates;
+ tg->tgate_addr = addr;
+ tgates = tg;
+ parm.parm_int_state |= IS_DISTRUST;
+
+ } else if (PARS("redirect_ok")) {
+ parm.parm_int_state |= IS_REDIRECT_OK;
+
+ } else {
+ return tgt; /* error */
+ }
+ }
+
+ return check_parms(&parm);
+#undef PARS
+#undef PARSEQ
+}
+
+
+/* check for duplicate parameter specifications */
+char * /* 0 or error message */
+check_parms(struct parm *new)
+{
+ struct parm *parmp, **parmpp;
+ int i, num_passwds;
+
+ /* set implicit values
+ */
+ if (new->parm_int_state & IS_NO_ADV_IN)
+ new->parm_int_state |= IS_NO_SOL_OUT;
+
+ for (i = num_passwds = 0; i < MAX_AUTH_KEYS; i++) {
+ if (new->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+
+ /* compare with existing sets of parameters
+ */
+ for (parmpp = &parms;
+ (parmp = *parmpp) != 0;
+ parmpp = &parmp->parm_next) {
+ if (strcmp(new->parm_name, parmp->parm_name))
+ continue;
+ if (!on_net(htonl(parmp->parm_net),
+ new->parm_net, new->parm_mask)
+ && !on_net(htonl(new->parm_net),
+ parmp->parm_net, parmp->parm_mask))
+ continue;
+
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+ if (num_passwds > MAX_AUTH_KEYS)
+ return "too many conflicting passwords";
+
+ if ((0 != (new->parm_int_state & GROUP_IS_SOL)
+ && 0 != (parmp->parm_int_state & GROUP_IS_SOL)
+ && 0 != ((new->parm_int_state ^ parmp->parm_int_state)
+ && GROUP_IS_SOL))
+ || (0 != (new->parm_int_state & GROUP_IS_ADV)
+ && 0 != (parmp->parm_int_state & GROUP_IS_ADV)
+ && 0 != ((new->parm_int_state ^ parmp->parm_int_state)
+ && GROUP_IS_ADV))
+ || (new->parm_rdisc_pref != 0
+ && parmp->parm_rdisc_pref != 0
+ && new->parm_rdisc_pref != parmp->parm_rdisc_pref)
+ || (new->parm_rdisc_int != 0
+ && parmp->parm_rdisc_int != 0
+ && new->parm_rdisc_int != parmp->parm_rdisc_int)) {
+ return ("conflicting, duplicate router discovery"
+ " parameters");
+
+ }
+
+ if (new->parm_d_metric != 0
+ && parmp->parm_d_metric != 0
+ && new->parm_d_metric != parmp->parm_d_metric) {
+ return ("conflicting, duplicate poor man's router"
+ " discovery or fake default metric");
+ }
+ }
+
+ /* link new entry on the so that when the entries are scanned,
+ * they affect the result in the order the operator specified.
+ */
+ parmp = (struct parm*)malloc(sizeof(*parmp));
+ bcopy(new, parmp, sizeof(*parmp));
+ *parmpp = parmp;
+
+ return 0;
+}
+
+
+/* get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+int /* 0=bad */
+getnet(char *name,
+ naddr *netp, /* a network so host byte order */
+ naddr *maskp) /* masks are always in host order */
+{
+ int i;
+ struct netent *np;
+ naddr mask; /* in host byte order */
+ struct in_addr in; /* a network and so host byte order */
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /* Detect and separate "1.2.3.4/24"
+ */
+ if (0 != (mname = rindex(name,'/'))) {
+ i = (int)(mname - name);
+ if (i > sizeof(hname)-1) /* name too long */
+ return 0;
+ bcopy(name, hname, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ np = getnetbyname(name);
+ if (np != 0) {
+ in.s_addr = (naddr)np->n_net;
+ } else if (inet_aton(name, &in) == 1) {
+ NTOHL(in.s_addr);
+ } else if (!mname && !strcasecmp(name,"default")) {
+ in.s_addr = RIP_DEFAULT;
+ } else {
+ return 0;
+ }
+
+ if (!mname) {
+ /* we cannot use the interfaces here because we have not
+ * looked at them yet.
+ */
+ mask = std_mask(htonl(in.s_addr));
+ if ((~mask & in.s_addr) != 0)
+ mask = HOST_MASK;
+ } else {
+ mask = (naddr)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32)
+ return 0;
+ mask = HOST_MASK << (32-mask);
+ }
+
+ /* must have mask of 0 with default */
+ if (mask != 0 && in.s_addr == RIP_DEFAULT)
+ return 0;
+ /* no host bits allowed in a network number */
+ if ((~mask & in.s_addr) != 0)
+ return 0;
+ /* require non-zero network number */
+ if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT)
+ return 0;
+ if (in.s_addr>>24 == 0 && in.s_addr != RIP_DEFAULT)
+ return 0;
+ if (in.s_addr>>24 == 0xff)
+ return 0;
+
+ *netp = in.s_addr;
+ *maskp = mask;
+ return 1;
+}
+
+
+int /* 0=bad */
+gethost(char *name,
+ naddr *addrp)
+{
+ struct hostent *hp;
+ struct in_addr in;
+
+
+ /* Try for a number first, even in IRIX where gethostbyname()
+ * is smart. This avoids hitting the name server which
+ * might be sick because routing is.
+ */
+ if (inet_aton(name, &in) == 1) {
+ /* get a good number, but check that it it makes some
+ * sense.
+ */
+ if (ntohl(in.s_addr)>>24 == 0
+ || ntohl(in.s_addr)>>24 == 0xff)
+ return 0;
+ *addrp = in.s_addr;
+ return 1;
+ }
+
+ hp = gethostbyname(name);
+ if (hp) {
+ bcopy(hp->h_addr, addrp, sizeof(*addrp));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h
new file mode 100644
index 0000000..eb90433
--- /dev/null
+++ b/sbin/routed/pathnames.h
@@ -0,0 +1,52 @@
+/*
+ * 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
+ *
+ * $NetBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_GATEWAYS "/etc/gateways"
+
+/* All remotely requested trace files must either start with this prefix
+ * or be the same as the tracefile specified when the daemon was started.
+ * If this is a directory, routed will create log files in it. That
+ * might be a security problem.
+ *
+ * Leave this undefined, and only the trace file originally specified
+ * when routed was started, if any, will be appended to.
+ */
+#if 0
+#define _PATH_TRACE "/var/log/routed.trace"
+#endif
diff --git a/sbin/routed/radix.c b/sbin/routed/radix.c
new file mode 100644
index 0000000..7f7e1e4
--- /dev/null
+++ b/sbin/routed/radix.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (c) 1988, 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.
+ *
+ * @(#)radix.c 8.4 (Berkeley) 11/2/94
+ */
+
+/*
+ * Routines to build and maintain radix trees for routing lookups.
+ */
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.10 $"
+
+#include "defs.h"
+
+#define log(x, msg) syslog(x, msg)
+#define panic(s) {log(LOG_ERR,s); exit(1);}
+#define min(a,b) (((a)<(b))?(a):(b))
+
+int max_keylen;
+struct radix_mask *rn_mkfreelist;
+struct radix_node_head *mask_rnhead;
+static char *addmask_key;
+static char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1};
+static char *rn_zeros, *rn_ones;
+
+#define rn_masktop (mask_rnhead->rnh_treetop)
+#undef Bcmp
+#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l))
+
+static int rn_satsifies_leaf(char *, struct radix_node *, int);
+
+/*
+ * The data structure for the keys is a radix tree with one way
+ * branching removed. The index rn_b at an internal node n represents a bit
+ * position to be tested. The tree is arranged so that all descendants
+ * of a node n have keys whose bits all agree up to position rn_b - 1.
+ * (We say the index of n is rn_b.)
+ *
+ * There is at least one descendant which has a one bit at position rn_b,
+ * and at least one with a zero there.
+ *
+ * A route is determined by a pair of key and mask. We require that the
+ * bit-wise logical and of the key and mask to be the key.
+ * We define the index of a route to associated with the mask to be
+ * the first bit number in the mask where 0 occurs (with bit number 0
+ * representing the highest order bit).
+ *
+ * We say a mask is normal if every bit is 0, past the index of the mask.
+ * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b,
+ * and m is a normal mask, then the route applies to every descendant of n.
+ * If the index(m) < rn_b, this implies the trailing last few bits of k
+ * before bit b are all 0, (and hence consequently true of every descendant
+ * of n), so the route applies to all descendants of the node as well.
+ *
+ * Similar logic shows that a non-normal mask m such that
+ * index(m) <= index(n) could potentially apply to many children of n.
+ * Thus, for each non-host route, we attach its mask to a list at an internal
+ * node as high in the tree as we can go.
+ *
+ * The present version of the code makes use of normal routes in short-
+ * circuiting an explict mask and compare operation when testing whether
+ * a key satisfies a normal route, and also in remembering the unique leaf
+ * that governs a subtree.
+ */
+
+struct radix_node *
+rn_search(void *v_arg,
+ struct radix_node *head)
+{
+ register struct radix_node *x;
+ register caddr_t v;
+
+ for (x = head, v = v_arg; x->rn_b >= 0;) {
+ if (x->rn_bmask & v[x->rn_off])
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return (x);
+}
+
+struct radix_node *
+rn_search_m(void *v_arg,
+ struct radix_node *head,
+ void *m_arg)
+{
+ register struct radix_node *x;
+ register caddr_t v = v_arg, m = m_arg;
+
+ for (x = head; x->rn_b >= 0;) {
+ if ((x->rn_bmask & m[x->rn_off]) &&
+ (x->rn_bmask & v[x->rn_off]))
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return x;
+}
+
+int
+rn_refines(void* m_arg, void *n_arg)
+{
+ register caddr_t m = m_arg, n = n_arg;
+ register caddr_t lim, lim2 = lim = n + *(u_char *)n;
+ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
+ int masks_are_equal = 1;
+
+ if (longer > 0)
+ lim -= longer;
+ while (n < lim) {
+ if (*n & ~(*m))
+ return 0;
+ if (*n++ != *m++)
+ masks_are_equal = 0;
+ }
+ while (n < lim2)
+ if (*n++)
+ return 0;
+ if (masks_are_equal && (longer < 0))
+ for (lim2 = m - longer; m < lim2; )
+ if (*m++)
+ return 1;
+ return (!masks_are_equal);
+}
+
+struct radix_node *
+rn_lookup(v_arg, m_arg, head)
+ void *v_arg, *m_arg;
+ struct radix_node_head *head;
+{
+ register struct radix_node *x;
+ caddr_t netmask = 0;
+
+ if (m_arg) {
+ if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0)
+ return (0);
+ netmask = x->rn_key;
+ }
+ x = rn_match(v_arg, head);
+ if (x && netmask) {
+ while (x && x->rn_mask != netmask)
+ x = x->rn_dupedkey;
+ }
+ return x;
+}
+
+static int
+rn_satsifies_leaf(char *trial,
+ register struct radix_node *leaf,
+ int skip)
+{
+ register char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask;
+ char *cplim;
+ int length = min(*(u_char *)cp, *(u_char *)cp2);
+
+ if (cp3 == 0)
+ cp3 = rn_ones;
+ else
+ length = min(length, *(u_char *)cp3);
+ cplim = cp + length; cp3 += skip; cp2 += skip;
+ for (cp += skip; cp < cplim; cp++, cp2++, cp3++)
+ if ((*cp ^ *cp2) & *cp3)
+ return 0;
+ return 1;
+}
+
+struct radix_node *
+rn_match(void *v_arg,
+ struct radix_node_head *head)
+{
+ caddr_t v = v_arg;
+ register struct radix_node *t = head->rnh_treetop, *x;
+ register caddr_t cp = v, cp2;
+ caddr_t cplim;
+ struct radix_node *saved_t, *top = t;
+ int off = t->rn_off, vlen = *(u_char *)cp, matched_off;
+ register int test, b, rn_b;
+
+ /*
+ * Open code rn_search(v, top) to avoid overhead of extra
+ * subroutine call.
+ */
+ for (; t->rn_b >= 0; ) {
+ if (t->rn_bmask & cp[t->rn_off])
+ t = t->rn_r;
+ else
+ t = t->rn_l;
+ }
+ /*
+ * See if we match exactly as a host destination
+ * or at least learn how many bits match, for normal mask finesse.
+ *
+ * It doesn't hurt us to limit how many bytes to check
+ * to the length of the mask, since if it matches we had a genuine
+ * match and the leaf we have is the most specific one anyway;
+ * if it didn't match with a shorter length it would fail
+ * with a long one. This wins big for class B&C netmasks which
+ * are probably the most common case...
+ */
+ if (t->rn_mask)
+ vlen = *(u_char *)t->rn_mask;
+ cp += off; cp2 = t->rn_key + off; cplim = v + vlen;
+ for (; cp < cplim; cp++, cp2++)
+ if (*cp != *cp2)
+ goto on1;
+ /*
+ * This extra grot is in case we are explicitly asked
+ * to look up the default. Ugh!
+ * Or 255.255.255.255
+ *
+ * In this case, we have a complete match of the key. Unless
+ * the node is one of the roots, we are finished.
+ * If it is the zeros root, then take what we have, prefering
+ * any real data.
+ * If it is the ones root, then pretend the target key was followed
+ * by a byte of zeros.
+ */
+ if (!(t->rn_flags & RNF_ROOT))
+ return t; /* not a root */
+ if (t->rn_dupedkey) {
+ t = t->rn_dupedkey;
+ return t; /* have some real data */
+ }
+ if (*(cp-1) == 0)
+ return t; /* not the ones root */
+ b = 0; /* fake a zero after 255.255.255.255 */
+ goto on2;
+on1:
+ test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */
+ for (b = 7; (test >>= 1) > 0;)
+ b--;
+on2:
+ matched_off = cp - v;
+ b += matched_off << 3;
+ rn_b = -1 - b;
+ /*
+ * If there is a host route in a duped-key chain, it will be first.
+ */
+ if ((saved_t = t)->rn_mask == 0)
+ t = t->rn_dupedkey;
+ for (; t; t = t->rn_dupedkey)
+ /*
+ * Even if we don't match exactly as a host,
+ * we may match if the leaf we wound up at is
+ * a route to a net.
+ */
+ if (t->rn_flags & RNF_NORMAL) {
+ if (rn_b <= t->rn_b)
+ return t;
+ } else if (rn_satsifies_leaf(v, t, matched_off))
+ return t;
+ t = saved_t;
+ /* start searching up the tree */
+ do {
+ register struct radix_mask *m;
+ t = t->rn_p;
+ if ((m = t->rn_mklist)) {
+ /*
+ * If non-contiguous masks ever become important
+ * we can restore the masking and open coding of
+ * the search and satisfaction test and put the
+ * calculation of "off" back before the "do".
+ */
+ do {
+ if (m->rm_flags & RNF_NORMAL) {
+ if (rn_b <= m->rm_b)
+ return (m->rm_leaf);
+ } else {
+ off = min(t->rn_off, matched_off);
+ x = rn_search_m(v, t, m->rm_mask);
+ while (x && x->rn_mask != m->rm_mask)
+ x = x->rn_dupedkey;
+ if (x && rn_satsifies_leaf(v, x, off))
+ return x;
+ }
+ } while ((m = m->rm_mklist));
+ }
+ } while (t != top);
+ return 0;
+}
+
+#ifdef RN_DEBUG
+int rn_nodenum;
+struct radix_node *rn_clist;
+int rn_saveinfo;
+int rn_debug = 1;
+#endif
+
+struct radix_node *
+rn_newpair(void *v, int b, struct radix_node nodes[2])
+{
+ register struct radix_node *tt = nodes, *t = tt + 1;
+ t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7);
+ t->rn_l = tt; t->rn_off = b >> 3;
+ tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t;
+ tt->rn_flags = t->rn_flags = RNF_ACTIVE;
+#ifdef RN_DEBUG
+ tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ return t;
+}
+
+struct radix_node *
+rn_insert(void* v_arg,
+ struct radix_node_head *head,
+ int *dupentry,
+ struct radix_node nodes[2])
+{
+ caddr_t v = v_arg;
+ struct radix_node *top = head->rnh_treetop;
+ int head_off = top->rn_off, vlen = (int)*((u_char *)v);
+ register struct radix_node *t = rn_search(v_arg, top);
+ register caddr_t cp = v + head_off;
+ register int b;
+ struct radix_node *tt;
+
+ /*
+ * Find first bit at which v and t->rn_key differ
+ */
+ {
+ register caddr_t cp2 = t->rn_key + head_off;
+ register int cmp_res;
+ caddr_t cplim = v + vlen;
+
+ while (cp < cplim)
+ if (*cp2++ != *cp++)
+ goto on1;
+ /* handle adding 255.255.255.255 */
+ if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) {
+ *dupentry = 1;
+ return t;
+ }
+on1:
+ *dupentry = 0;
+ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
+ for (b = (cp - v) << 3; cmp_res; b--)
+ cmp_res >>= 1;
+ }
+ {
+ register struct radix_node *p, *x = top;
+ cp = v;
+ do {
+ p = x;
+ if (cp[x->rn_off] & x->rn_bmask)
+ x = x->rn_r;
+ else x = x->rn_l;
+ } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */
+#ifdef RN_DEBUG
+ if (rn_debug)
+ log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p);
+#endif
+ t = rn_newpair(v_arg, b, nodes); tt = t->rn_l;
+ if ((cp[p->rn_off] & p->rn_bmask) == 0)
+ p->rn_l = t;
+ else
+ p->rn_r = t;
+ x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */
+ if ((cp[t->rn_off] & t->rn_bmask) == 0) {
+ t->rn_r = x;
+ } else {
+ t->rn_r = tt; t->rn_l = x;
+ }
+#ifdef RN_DEBUG
+ if (rn_debug)
+ log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p);
+#endif
+ }
+ return (tt);
+}
+
+struct radix_node *
+rn_addmask(void *n_arg, int search, int skip)
+{
+ caddr_t netmask = (caddr_t)n_arg;
+ register struct radix_node *x;
+ register caddr_t cp, cplim;
+ register int b = 0, mlen, j;
+ int maskduplicated, m0, isnormal;
+ struct radix_node *saved_x;
+ static int last_zeroed = 0;
+
+ if ((mlen = *(u_char *)netmask) > max_keylen)
+ mlen = max_keylen;
+ if (skip == 0)
+ skip = 1;
+ if (mlen <= skip)
+ return (mask_rnhead->rnh_nodes);
+ if (skip > 1)
+ Bcopy(rn_ones + 1, addmask_key + 1, skip - 1);
+ if ((m0 = mlen) > skip)
+ Bcopy(netmask + skip, addmask_key + skip, mlen - skip);
+ /*
+ * Trim trailing zeroes.
+ */
+ for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;)
+ cp--;
+ mlen = cp - addmask_key;
+ if (mlen <= skip) {
+ if (m0 >= last_zeroed)
+ last_zeroed = mlen;
+ return (mask_rnhead->rnh_nodes);
+ }
+ if (m0 < last_zeroed)
+ Bzero(addmask_key + m0, last_zeroed - m0);
+ *addmask_key = last_zeroed = mlen;
+ x = rn_search(addmask_key, rn_masktop);
+ if (Bcmp(addmask_key, x->rn_key, mlen) != 0)
+ x = 0;
+ if (x || search)
+ return (x);
+ R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x));
+ if ((saved_x = x) == 0)
+ return (0);
+ Bzero(x, max_keylen + 2 * sizeof (*x));
+ netmask = cp = (caddr_t)(x + 2);
+ Bcopy(addmask_key, cp, mlen);
+ x = rn_insert(cp, mask_rnhead, &maskduplicated, x);
+ if (maskduplicated) {
+ log(LOG_ERR, "rn_addmask: mask impossibly already in tree");
+ Free(saved_x);
+ return (x);
+ }
+ /*
+ * Calculate index of mask, and check for normalcy.
+ */
+ cplim = netmask + mlen; isnormal = 1;
+ for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;)
+ cp++;
+ if (cp != cplim) {
+ for (j = 0x80; (j & *cp) != 0; j >>= 1)
+ b++;
+ if (*cp != normal_chars[b] || cp != (cplim - 1))
+ isnormal = 0;
+ }
+ b += (cp - netmask) << 3;
+ x->rn_b = -1 - b;
+ if (isnormal)
+ x->rn_flags |= RNF_NORMAL;
+ return (x);
+}
+
+static int /* XXX: arbitrary ordering for non-contiguous masks */
+rn_lexobetter(void *m_arg, void *n_arg)
+{
+ register u_char *mp = m_arg, *np = n_arg, *lim;
+
+ if (*mp > *np)
+ return 1; /* not really, but need to check longer one first */
+ if (*mp == *np)
+ for (lim = mp + *mp; mp < lim;)
+ if (*mp++ > *np++)
+ return 1;
+ return 0;
+}
+
+static struct radix_mask *
+rn_new_radix_mask(register struct radix_node *tt,
+ register struct radix_mask *next)
+{
+ register struct radix_mask *m;
+
+ MKGet(m);
+ if (m == 0) {
+ log(LOG_ERR, "Mask for route not entered\n");
+ return (0);
+ }
+ Bzero(m, sizeof *m);
+ m->rm_b = tt->rn_b;
+ m->rm_flags = tt->rn_flags;
+ if (tt->rn_flags & RNF_NORMAL)
+ m->rm_leaf = tt;
+ else
+ m->rm_mask = tt->rn_mask;
+ m->rm_mklist = next;
+ tt->rn_mklist = m;
+ return m;
+}
+
+struct radix_node *
+rn_addroute(void *v_arg,
+ void *n_arg,
+ struct radix_node_head *head,
+ struct radix_node treenodes[2])
+{
+ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg;
+ register struct radix_node *t, *x = 0, *tt;
+ struct radix_node *saved_tt, *top = head->rnh_treetop;
+ short b = 0, b_leaf = 0;
+ int keyduplicated;
+ caddr_t mmask;
+ struct radix_mask *m, **mp;
+
+ /*
+ * In dealing with non-contiguous masks, there may be
+ * many different routes which have the same mask.
+ * We will find it useful to have a unique pointer to
+ * the mask to speed avoiding duplicate references at
+ * nodes and possibly save time in calculating indices.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0)
+ return (0);
+ b_leaf = x->rn_b;
+ b = -1 - x->rn_b;
+ netmask = x->rn_key;
+ }
+ /*
+ * Deal with duplicated keys: attach node to previous instance
+ */
+ saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes);
+ if (keyduplicated) {
+ for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) {
+ if (tt->rn_mask == netmask)
+ return (0);
+ if (netmask == 0 ||
+ (tt->rn_mask &&
+ ((b_leaf < tt->rn_b) || /* index(netmask) > node */
+ rn_refines(netmask, tt->rn_mask) ||
+ rn_lexobetter(netmask, tt->rn_mask))))
+ break;
+ }
+ /*
+ * If the mask is not duplicated, we wouldn't
+ * find it among possible duplicate key entries
+ * anyway, so the above test doesn't hurt.
+ *
+ * We sort the masks for a duplicated key the same way as
+ * in a masklist -- most specific to least specific.
+ * This may require the unfortunate nuisance of relocating
+ * the head of the list.
+ */
+ if (tt == saved_tt) {
+ struct radix_node *xx = x;
+ /* link in at head of list */
+ (tt = treenodes)->rn_dupedkey = t;
+ tt->rn_flags = t->rn_flags;
+ tt->rn_p = x = t->rn_p;
+ if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt;
+ saved_tt = tt; x = xx;
+ } else {
+ (tt = treenodes)->rn_dupedkey = t->rn_dupedkey;
+ t->rn_dupedkey = tt;
+ }
+#ifdef RN_DEBUG
+ t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++;
+ tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt;
+#endif
+ tt->rn_key = (caddr_t) v;
+ tt->rn_b = -1;
+ tt->rn_flags = RNF_ACTIVE;
+ }
+ /*
+ * Put mask in tree.
+ */
+ if (netmask) {
+ tt->rn_mask = netmask;
+ tt->rn_b = x->rn_b;
+ tt->rn_flags |= x->rn_flags & RNF_NORMAL;
+ }
+ t = saved_tt->rn_p;
+ if (keyduplicated)
+ goto on2;
+ b_leaf = -1 - t->rn_b;
+ if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r;
+ /* Promote general routes from below */
+ if (x->rn_b < 0) {
+ for (mp = &t->rn_mklist; x; x = x->rn_dupedkey)
+ if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) {
+ if ((*mp = m = rn_new_radix_mask(x, 0)))
+ mp = &m->rm_mklist;
+ }
+ } else if (x->rn_mklist) {
+ /*
+ * Skip over masks whose index is > that of new node
+ */
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
+ if (m->rm_b >= b_leaf)
+ break;
+ t->rn_mklist = m; *mp = 0;
+ }
+on2:
+ /* Add new route to highest possible ancestor's list */
+ if ((netmask == 0) || (b > t->rn_b ))
+ return tt; /* can't lift at all */
+ b_leaf = tt->rn_b;
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ /*
+ * Search through routes associated with node to
+ * insert new route according to index.
+ * Need same criteria as when sorting dupedkeys to avoid
+ * double loop on deletion.
+ */
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) {
+ if (m->rm_b < b_leaf)
+ continue;
+ if (m->rm_b > b_leaf)
+ break;
+ if (m->rm_flags & RNF_NORMAL) {
+ mmask = m->rm_leaf->rn_mask;
+ if (tt->rn_flags & RNF_NORMAL) {
+ log(LOG_ERR,
+ "Non-unique normal route, mask not entered");
+ return tt;
+ }
+ } else
+ mmask = m->rm_mask;
+ if (mmask == netmask) {
+ m->rm_refs++;
+ tt->rn_mklist = m;
+ return tt;
+ }
+ if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask))
+ break;
+ }
+ *mp = rn_new_radix_mask(tt, *mp);
+ return tt;
+}
+
+struct radix_node *
+rn_delete(void *v_arg,
+ void *netmask_arg,
+ struct radix_node_head *head)
+{
+ register struct radix_node *t, *p, *x, *tt;
+ struct radix_mask *m, *saved_m, **mp;
+ struct radix_node *dupedkey, *saved_tt, *top;
+ caddr_t v, netmask;
+ int b, head_off, vlen;
+
+ v = v_arg;
+ netmask = netmask_arg;
+ x = head->rnh_treetop;
+ tt = rn_search(v, x);
+ head_off = x->rn_off;
+ vlen = *(u_char *)v;
+ saved_tt = tt;
+ top = x;
+ if (tt == 0 ||
+ Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off))
+ return (0);
+ /*
+ * Delete our route from mask lists.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 1, head_off)) == 0)
+ return (0);
+ netmask = x->rn_key;
+ while (tt->rn_mask != netmask)
+ if ((tt = tt->rn_dupedkey) == 0)
+ return (0);
+ }
+ if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0)
+ goto on1;
+ if (tt->rn_flags & RNF_NORMAL) {
+ if (m->rm_leaf != tt || m->rm_refs > 0) {
+ log(LOG_ERR, "rn_delete: inconsistent annotation\n");
+ return 0; /* dangling ref could cause disaster */
+ }
+ } else {
+ if (m->rm_mask != tt->rn_mask) {
+ log(LOG_ERR, "rn_delete: inconsistent annotation\n");
+ goto on1;
+ }
+ if (--m->rm_refs >= 0)
+ goto on1;
+ }
+ b = -1 - tt->rn_b;
+ t = saved_tt->rn_p;
+ if (b > t->rn_b)
+ goto on1; /* Wasn't lifted at all */
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
+ if (m == saved_m) {
+ *mp = m->rm_mklist;
+ MKFree(m);
+ break;
+ }
+ if (m == 0) {
+ log(LOG_ERR, "rn_delete: couldn't find our annotation\n");
+ if (tt->rn_flags & RNF_NORMAL)
+ return (0); /* Dangling ref to us */
+ }
+on1:
+ /*
+ * Eliminate us from tree
+ */
+ if (tt->rn_flags & RNF_ROOT)
+ return (0);
+#ifdef RN_DEBUG
+ /* Get us out of the creation list */
+ for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {}
+ if (t) t->rn_ybro = tt->rn_ybro;
+#endif
+ t = tt->rn_p;
+ if ((dupedkey = saved_tt->rn_dupedkey)) {
+ if (tt == saved_tt) {
+ x = dupedkey; x->rn_p = t;
+ if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x;
+ } else {
+ for (x = p = saved_tt; p && p->rn_dupedkey != tt;)
+ p = p->rn_dupedkey;
+ if (p) p->rn_dupedkey = tt->rn_dupedkey;
+ else log(LOG_ERR, "rn_delete: couldn't find us\n");
+ }
+ t = tt + 1;
+ if (t->rn_flags & RNF_ACTIVE) {
+#ifndef RN_DEBUG
+ *++x = *t; p = t->rn_p;
+#else
+ b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p;
+#endif
+ if (p->rn_l == t) p->rn_l = x; else p->rn_r = x;
+ x->rn_l->rn_p = x; x->rn_r->rn_p = x;
+ }
+ goto out;
+ }
+ if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l;
+ p = t->rn_p;
+ if (p->rn_r == t) p->rn_r = x; else p->rn_l = x;
+ x->rn_p = p;
+ /*
+ * Demote routes attached to us.
+ */
+ if (t->rn_mklist) {
+ if (x->rn_b >= 0) {
+ for (mp = &x->rn_mklist; (m = *mp);)
+ mp = &m->rm_mklist;
+ *mp = t->rn_mklist;
+ } else {
+ /* If there are any key,mask pairs in a sibling
+ duped-key chain, some subset will appear sorted
+ in the same order attached to our mklist */
+ for (m = t->rn_mklist; m && x; x = x->rn_dupedkey)
+ if (m == x->rn_mklist) {
+ struct radix_mask *mm = m->rm_mklist;
+ x->rn_mklist = 0;
+ if (--(m->rm_refs) < 0)
+ MKFree(m);
+ m = mm;
+ }
+ if (m)
+ syslog(LOG_ERR, "%s %lx at %lx\n",
+ "rn_delete: Orphaned Mask",
+ (unsigned long)m,
+ (unsigned long)x);
+ }
+ }
+ /*
+ * We may be holding an active internal node in the tree.
+ */
+ x = tt + 1;
+ if (t != x) {
+#ifndef RN_DEBUG
+ *t = *x;
+#else
+ b = t->rn_info; *t = *x; t->rn_info = b;
+#endif
+ t->rn_l->rn_p = t; t->rn_r->rn_p = t;
+ p = x->rn_p;
+ if (p->rn_l == x) p->rn_l = t; else p->rn_r = t;
+ }
+out:
+ tt->rn_flags &= ~RNF_ACTIVE;
+ tt[1].rn_flags &= ~RNF_ACTIVE;
+ return (tt);
+}
+
+int
+rn_walktree(struct radix_node_head *h,
+ register int (*f)(struct radix_node *, struct walkarg*),
+ struct walkarg *w)
+{
+ int error;
+ struct radix_node *base, *next;
+ register struct radix_node *rn = h->rnh_treetop;
+ /*
+ * This gets complicated because we may delete the node
+ * while applying the function f to it, so we need to calculate
+ * the successor node in advance.
+ */
+ /* First time through node, go left */
+ while (rn->rn_b >= 0)
+ rn = rn->rn_l;
+ for (;;) {
+ base = rn;
+ /* If at right child go back up, otherwise, go right */
+ while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0)
+ rn = rn->rn_p;
+ /* Find the next *leaf* since next node might vanish, too */
+ for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;)
+ rn = rn->rn_l;
+ next = rn;
+ /* Process leaves */
+ while ((rn = base)) {
+ base = rn->rn_dupedkey;
+ if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
+ return (error);
+ }
+ rn = next;
+ if (rn->rn_flags & RNF_ROOT)
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+int
+rn_inithead(void **head, int off)
+{
+ register struct radix_node_head *rnh;
+ register struct radix_node *t, *tt, *ttt;
+ if (*head)
+ return (1);
+ R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh));
+ if (rnh == 0)
+ return (0);
+ Bzero(rnh, sizeof (*rnh));
+ *head = rnh;
+ t = rn_newpair(rn_zeros, off, rnh->rnh_nodes);
+ ttt = rnh->rnh_nodes + 2;
+ t->rn_r = ttt;
+ t->rn_p = t;
+ tt = t->rn_l;
+ tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE;
+ tt->rn_b = -1 - off;
+ *ttt = *tt;
+ ttt->rn_key = rn_ones;
+ rnh->rnh_addaddr = rn_addroute;
+ rnh->rnh_deladdr = rn_delete;
+ rnh->rnh_matchaddr = rn_match;
+ rnh->rnh_lookup = rn_lookup;
+ rnh->rnh_walktree = rn_walktree;
+ rnh->rnh_treetop = t;
+ return (1);
+}
+
+void
+rn_init(void)
+{
+ char *cp, *cplim;
+ if (max_keylen == 0) {
+ printf("rn_init: radix functions require max_keylen be set\n");
+ return;
+ }
+ R_Malloc(rn_zeros, char *, 3 * max_keylen);
+ if (rn_zeros == NULL)
+ panic("rn_init");
+ Bzero(rn_zeros, 3 * max_keylen);
+ rn_ones = cp = rn_zeros + max_keylen;
+ addmask_key = cplim = rn_ones + max_keylen;
+ while (cp < cplim)
+ *cp++ = -1;
+ if (rn_inithead((void **)&mask_rnhead, 0) == 0)
+ panic("rn_init 2");
+}
+
diff --git a/sbin/routed/radix.h b/sbin/routed/radix.h
new file mode 100644
index 0000000..fddf02e
--- /dev/null
+++ b/sbin/routed/radix.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1988, 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.
+ *
+ * @(#)radix.h 8.2 (Berkeley) 10/31/94
+ */
+
+#ifndef __RADIX_H_
+#define __RADIX_H_
+
+#include <sys/cdefs.h>
+struct walkarg;
+
+/*
+ * Radix search tree node layout.
+ */
+
+struct radix_node {
+ struct radix_mask *rn_mklist; /* list of masks contained in subtree */
+ struct radix_node *rn_p; /* parent */
+ short rn_b; /* bit offset; -1-index(netmask) */
+ char rn_bmask; /* node: mask for bit test*/
+ u_char rn_flags; /* enumerated next */
+#define RNF_NORMAL 1 /* leaf contains normal route */
+#define RNF_ROOT 2 /* leaf is root leaf for tree */
+#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
+ union {
+ struct { /* leaf only data: */
+ caddr_t rn_Key; /* object of search */
+ caddr_t rn_Mask; /* netmask, if present */
+ struct radix_node *rn_Dupedkey;
+ } rn_leaf;
+ struct { /* node only data: */
+ int rn_Off; /* where to start compare */
+ struct radix_node *rn_L;/* progeny */
+ struct radix_node *rn_R;/* progeny */
+ }rn_node;
+ } rn_u;
+#ifdef RN_DEBUG
+ int rn_info;
+ struct radix_node *rn_twin;
+ struct radix_node *rn_ybro;
+#endif
+};
+
+#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
+#define rn_key rn_u.rn_leaf.rn_Key
+#define rn_mask rn_u.rn_leaf.rn_Mask
+#define rn_off rn_u.rn_node.rn_Off
+#define rn_l rn_u.rn_node.rn_L
+#define rn_r rn_u.rn_node.rn_R
+
+/*
+ * Annotations to tree concerning potential routes applying to subtrees.
+ */
+
+extern struct radix_mask {
+ short rm_b; /* bit offset; -1-index(netmask) */
+ char rm_unused; /* cf. rn_bmask */
+ u_char rm_flags; /* cf. rn_flags */
+ struct radix_mask *rm_mklist; /* more masks to try */
+ union {
+ caddr_t rmu_mask; /* the mask */
+ struct radix_node *rmu_leaf; /* for normal routes */
+ } rm_rmu;
+ int rm_refs; /* # of references to this struct */
+} *rn_mkfreelist;
+
+#define rm_mask rm_rmu.rmu_mask
+#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
+
+#define MKGet(m) {\
+ if (rn_mkfreelist) {\
+ m = rn_mkfreelist; \
+ rn_mkfreelist = (m)->rm_mklist; \
+ } else \
+ R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\
+
+#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
+
+struct radix_node_head {
+ struct radix_node *rnh_treetop;
+ int rnh_addrsize; /* permit, but not require fixed keys */
+ int rnh_pktsize; /* permit, but not require fixed keys */
+ struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
+ __P((void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]));
+ struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
+ __P((void *v, void *mask,
+ struct radix_node_head *head, struct radix_node nodes[]));
+ struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
+ __P((void *v, void *mask, struct radix_node_head *head));
+ struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
+ __P((void *v, void *mask, struct radix_node_head *head));
+ struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
+ __P((void *v, struct radix_node_head *head));
+ struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
+ __P((void *v, void *mask, struct radix_node_head *head));
+ struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
+ __P((void *v, struct radix_node_head *head));
+ int (*rnh_walktree) /* traverse tree */
+ (struct radix_node_head *head,
+ int (*f)(struct radix_node *, struct walkarg *),
+ struct walkarg *w);
+ struct radix_node rnh_nodes[3]; /* empty tree for common case */
+};
+
+
+#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n))
+#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n))
+#define Bzero(p, n) bzero((char *)(p), (int)(n));
+#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n)))
+#define Free(p) free((char *)p);
+
+void rn_init __P((void));
+int rn_inithead __P((void **, int));
+int rn_refines __P((void *, void *));
+int rn_walktree __P((struct radix_node_head *,
+ int (*)__P((struct radix_node *, struct walkarg*)),
+ struct walkarg*));
+struct radix_node
+ *rn_addmask __P((void *, int, int)),
+ *rn_addroute __P((void *, void *, struct radix_node_head *,
+ struct radix_node [2])),
+ *rn_delete __P((void *, void *, struct radix_node_head *)),
+ *rn_insert __P((void *, struct radix_node_head *, int *,
+ struct radix_node [2])),
+ *rn_match __P((void *, struct radix_node_head *)),
+ *rn_newpair __P((void *, int, struct radix_node[2])),
+ *rn_search __P((void *, struct radix_node *)),
+ *rn_search_m __P((void *, struct radix_node *, void *));
+
+#endif /* __RADIX_H_ */
diff --git a/sbin/routed/rdisc.c b/sbin/routed/rdisc.c
new file mode 100644
index 0000000..bccb3d9
--- /dev/null
+++ b/sbin/routed/rdisc.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (c) 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.20 $"
+
+#include "defs.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+/* router advertisement ICMP packet */
+struct icmp_ad {
+ u_int8_t icmp_type; /* type of message */
+ u_int8_t icmp_code; /* type sub code */
+ u_int16_t icmp_cksum; /* ones complement cksum of struct */
+ u_int8_t icmp_ad_num; /* # of following router addresses */
+ u_int8_t icmp_ad_asize; /* 2--words in each advertisement */
+ u_int16_t icmp_ad_life; /* seconds of validity */
+ struct icmp_ad_info {
+ n_long icmp_ad_addr;
+ n_long icmp_ad_pref;
+ } icmp_ad_info[1];
+};
+
+/* router solicitation ICMP packet */
+struct icmp_so {
+ u_int8_t icmp_type; /* type of message */
+ u_int8_t icmp_code; /* type sub code */
+ u_int16_t icmp_cksum; /* ones complement cksum of struct */
+ n_long icmp_so_rsvd;
+};
+
+union ad_u {
+ struct icmp icmp;
+ struct icmp_ad ad;
+ struct icmp_so so;
+};
+
+
+int rdisc_sock = -1; /* router-discovery raw socket */
+struct interface *rdisc_sock_mcast; /* current multicast interface */
+
+struct timeval rdisc_timer;
+int rdisc_ok; /* using solicited route */
+
+
+#define MAX_ADS 5
+struct dr { /* accumulated advertisements */
+ struct interface *dr_ifp;
+ naddr dr_gate; /* gateway */
+ time_t dr_ts; /* when received */
+ time_t dr_life; /* lifetime */
+ n_long dr_recv_pref; /* received but biased preference */
+ n_long dr_pref; /* preference adjusted by metric */
+} *cur_drp, drs[MAX_ADS];
+
+/* adjust preference by interface metric without driving it to infinity */
+#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
+ : (p) - ((ifp)->int_metric))
+
+static void rdisc_sort(void);
+
+
+/* dump an ICMP Router Discovery Advertisement Message
+ */
+static void
+trace_rdisc(char *act,
+ naddr from,
+ naddr to,
+ struct interface *ifp,
+ union ad_u *p,
+ u_int len)
+{
+ int i;
+ n_long *wp, *lim;
+
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ (void)fprintf(ftrace, "%s Router Ad"
+ " from %s to %s via %s life=%d\n",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ ntohs(p->ad.icmp_ad_life));
+ if (!TRACECONTENTS)
+ return;
+
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
+ for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
+ (void)fprintf(ftrace, "\t%s preference=%d",
+ naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
+ wp += p->ad.icmp_ad_asize;
+ }
+ (void)fputc('\n',ftrace);
+
+ } else {
+ trace_act("%s Router Solic. from %s to %s via %s value=%#x",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ ntohl(p->so.icmp_so_rsvd));
+ }
+}
+
+/* prepare Router Discovery socket.
+ */
+static void
+get_rdisc_sock(void)
+{
+ if (rdisc_sock < 0) {
+ rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (rdisc_sock < 0)
+ BADERR(1,"rdisc_sock = socket()");
+ fix_sock(rdisc_sock,"rdisc_sock");
+ fix_select();
+ }
+}
+
+
+/* Pick multicast group for router-discovery socket
+ */
+void
+set_rdisc_mg(struct interface *ifp,
+ int on) /* 0=turn it off */
+{
+ struct ip_mreq m;
+
+ if (rdisc_sock < 0) {
+ /* Create the raw socket so that we can hear at least
+ * broadcast router discovery packets.
+ */
+ if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
+ || !on)
+ return;
+ get_rdisc_sock();
+ }
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST)) {
+ ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
+ return;
+ }
+
+#ifdef MCAST_PPP_BUG
+ if (ifp->int_if_flags & IFF_POINTOPOINT)
+ return;
+#endif
+ bzero(&m, sizeof(m));
+ m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : ifp->int_addr);
+ if (supplier
+ || (ifp->int_state & IS_NO_ADV_IN)
+ || !on) {
+ /* stop listening to advertisements
+ */
+ if (ifp->int_state & IS_ALL_HOSTS) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
+ ifp->int_state &= ~IS_ALL_HOSTS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_HOSTS)) {
+ /* start listening to advertisements
+ */
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
+ } else {
+ ifp->int_state |= IS_ALL_HOSTS;
+ }
+ }
+
+ if (!supplier
+ || (ifp->int_state & IS_NO_ADV_OUT)
+ || !on) {
+ /* stop listening to solicitations
+ */
+ if (ifp->int_state & IS_ALL_ROUTERS) {
+ m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &m, sizeof(m)) < 0)
+ LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
+ ifp->int_state &= ~IS_ALL_ROUTERS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
+ /* start hearing solicitations
+ */
+ m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof(m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
+ } else {
+ ifp->int_state |= IS_ALL_ROUTERS;
+ }
+ }
+}
+
+
+/* start supplying routes
+ */
+void
+set_supplier(void)
+{
+ struct interface *ifp;
+ struct dr *drp;
+
+ if (supplier_set)
+ return;
+
+ trace_act("start suppying routes");
+
+ /* Forget discovered routes.
+ */
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ drp->dr_recv_pref = 0;
+ drp->dr_life = 0;
+ }
+ rdisc_age(0);
+
+ supplier_set = 1;
+ supplier = 1;
+
+ /* Do not start advertising until we have heard some RIP routes */
+ LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
+
+ /* Switch router discovery multicast groups from soliciting
+ * to advertising.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_state & IS_BROKE)
+ continue;
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
+ set_rdisc_mg(ifp, 1);
+ }
+
+ /* get rid of any redirects */
+ del_redirects(0,0);
+}
+
+
+/* age discovered routes and find the best one
+ */
+void
+rdisc_age(naddr bad_gate)
+{
+ time_t sec;
+ struct dr *drp;
+
+
+ /* If only adverising, then do only that. */
+ if (supplier) {
+ /* if switching from client to server, get rid of old
+ * default routes.
+ */
+ if (cur_drp != 0)
+ rdisc_sort();
+ rdisc_adv();
+ return;
+ }
+
+ /* If we are being told about a bad router,
+ * then age the discovered default route, and if there is
+ * no alternative, solicite a replacement.
+ */
+ if (bad_gate != 0) {
+ /* Look for the bad discovered default route.
+ * Age it and note its interface.
+ */
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+
+ /* When we find the bad router, then age the route
+ * to at most SUPPLY_INTERVAL.
+ * This is contrary to RFC 1256, but defends against
+ * black holes.
+ */
+ if (drp->dr_gate == bad_gate) {
+ sec = (now.tv_sec - drp->dr_life
+ + SUPPLY_INTERVAL);
+ if (drp->dr_ts > sec) {
+ trace_act("age 0.0.0.0 --> %s via %s",
+ naddr_ntoa(drp->dr_gate),
+ drp->dr_ifp->int_name);
+ drp->dr_ts = sec;
+ }
+ break;
+ }
+ }
+ }
+
+ /* delete old redirected routes to keep the kernel table small
+ */
+ sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life;
+ del_redirects(bad_gate, now.tv_sec-sec);
+
+ rdisc_sol();
+
+ rdisc_sort();
+}
+
+
+/* Zap all routes discovered via an interface that has gone bad
+ * This should only be called when !(ifp->int_state & IS_ALIAS)
+ */
+void
+if_bad_rdisc(struct interface *ifp)
+{
+ struct dr *drp;
+
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ifp != ifp)
+ continue;
+ drp->dr_recv_pref = 0;
+ drp->dr_life = 0;
+ }
+
+ rdisc_sort();
+}
+
+
+/* mark an interface ok for router discovering.
+ */
+void
+if_ok_rdisc(struct interface *ifp)
+{
+ set_rdisc_mg(ifp, 1);
+
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
+ ? MIN_WAITTIME
+ : MAX_SOLICITATION_DELAY);
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+}
+
+
+/* get rid of a dead discovered router
+ */
+static void
+del_rdisc(struct dr *drp)
+{
+ struct interface *ifp;
+ int i;
+
+
+ del_redirects(drp->dr_gate, 0);
+ drp->dr_ts = 0;
+ drp->dr_life = 0;
+
+
+ /* Count the other discovered routes on the interface.
+ */
+ i = 0;
+ ifp = drp->dr_ifp;
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts != 0
+ && drp->dr_ifp == ifp)
+ i++;
+ }
+
+ /* If that was the last good discovered router on the interface,
+ * then solicit a new one.
+ * This is contrary to RFC 1256, but defends against black holes.
+ */
+ if (i == 0
+ && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
+ trace_act("discovered route is bad--re-solicit routers via %s",
+ ifp->int_name);
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = 0;
+ rdisc_sol();
+ }
+}
+
+
+/* Find the best discovered route,
+ * and discard stale routers.
+ */
+static void
+rdisc_sort(void)
+{
+ struct dr *drp, *new_drp;
+ struct rt_entry *rt;
+ struct interface *ifp;
+ u_int new_st;
+ n_long new_pref;
+
+
+ /* Find the best discovered route.
+ */
+ new_drp = 0;
+ for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+ ifp = drp->dr_ifp;
+
+ /* Get rid of expired discovered routers.
+ */
+ if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
+ del_rdisc(drp);
+ continue;
+ }
+
+ LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
+
+ /* Update preference with possibly changed interface
+ * metric.
+ */
+ drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
+
+ /* Prefer the current route to prevent thrashing.
+ * Prefer shorter lifetimes to speed the detection of
+ * bad routers.
+ * Avoid sick interfaces.
+ */
+ if (new_drp == 0
+ || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
+ && (new_pref < drp->dr_pref
+ || (new_pref == drp->dr_pref
+ && (drp == cur_drp
+ || (new_drp != cur_drp
+ && new_drp->dr_life > drp->dr_life)))))
+ || ((new_st & IS_SICK)
+ && !(drp->dr_ifp->int_state & IS_SICK))) {
+ new_drp = drp;
+ new_st = drp->dr_ifp->int_state;
+ new_pref = drp->dr_pref;
+ }
+ }
+
+ /* switch to a better default route
+ */
+ if (new_drp != cur_drp) {
+ rt = rtget(RIP_DEFAULT, 0);
+
+ /* Stop using discovered routes if they are all bad
+ */
+ if (new_drp == 0) {
+ trace_act("turn off Router Discovery client");
+ rdisc_ok = 0;
+
+ if (rt != 0
+ && (rt->rt_state & RS_RDISC)) {
+ rtchange(rt, rt->rt_state & ~RS_RDISC,
+ rt->rt_gate, rt->rt_router,
+ HOPCNT_INFINITY, 0, rt->rt_ifp,
+ now.tv_sec - GARBAGE_TIME, 0);
+ rtswitch(rt, 0);
+ }
+
+ /* turn on RIP if permitted */
+ rip_on(0);
+
+ } else {
+ if (cur_drp == 0) {
+ trace_act("turn on Router Discovery client"
+ " using %s via %s",
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+
+ rdisc_ok = 1;
+
+ } else {
+ trace_act("switch Router Discovery from"
+ " %s via %s to %s via %s",
+ naddr_ntoa(cur_drp->dr_gate),
+ cur_drp->dr_ifp->int_name,
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+ }
+
+ if (rt != 0) {
+ rtchange(rt, rt->rt_state | RS_RDISC,
+ new_drp->dr_gate, new_drp->dr_gate,
+ 0,0, new_drp->dr_ifp,
+ now.tv_sec, 0);
+ } else {
+ rtadd(RIP_DEFAULT, 0,
+ new_drp->dr_gate, new_drp->dr_gate,
+ HOPCNT_INFINITY-1, 0,
+ RS_RDISC, new_drp->dr_ifp);
+ }
+
+ /* Now turn off RIP and delete RIP routes,
+ * which might otherwise include the default
+ * we just modified.
+ */
+ rip_off();
+ }
+
+ cur_drp = new_drp;
+ }
+}
+
+
+/* handle a single address in an advertisement
+ */
+static void
+parse_ad(naddr from,
+ naddr gate,
+ n_long pref,
+ u_short life,
+ struct interface *ifp)
+{
+ static struct msg_limit bad_gate;
+ struct dr *drp, *new_drp;
+
+
+ if (gate == RIP_DEFAULT
+ || !check_dst(gate)) {
+ msglim(&bad_gate, from,"router %s advertising bad gateway %s",
+ naddr_ntoa(from),
+ naddr_ntoa(gate));
+ return;
+ }
+
+ /* ignore pointers to ourself and routes via unreachable networks
+ */
+ if (ifwithaddr(gate, 1, 0) != 0) {
+ trace_pkt(" discard Router Discovery Ad pointing at us");
+ return;
+ }
+ if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
+ trace_pkt(" discard Router Discovery Ad"
+ " toward unreachable net");
+ return;
+ }
+
+ /* Convert preference to an unsigned value
+ * and later bias it by the metric of the interface.
+ */
+ pref = ntohl(pref) ^ MIN_PreferenceLevel;
+
+ if (pref == 0 || life == 0) {
+ pref = 0;
+ life = 0;
+ }
+
+ for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
+ /* accept new info for a familiar entry
+ */
+ if (drp->dr_gate == gate) {
+ new_drp = drp;
+ break;
+ }
+
+ if (life == 0)
+ continue; /* do not worry about dead ads */
+
+ if (drp->dr_ts == 0) {
+ new_drp = drp; /* use unused entry */
+
+ } else if (new_drp == 0) {
+ /* look for an entry worse than the new one to
+ * reuse.
+ */
+ if ((!(ifp->int_state & IS_SICK)
+ && (drp->dr_ifp->int_state & IS_SICK))
+ || (pref > drp->dr_pref
+ && !((ifp->int_state ^ drp->dr_ifp->int_state)
+ & IS_SICK)))
+ new_drp = drp;
+
+ } else if (new_drp->dr_ts != 0) {
+ /* look for the least valueable entry to reuse
+ */
+ if ((!(new_drp->dr_ifp->int_state & IS_SICK)
+ && (drp->dr_ifp->int_state & IS_SICK))
+ || (new_drp->dr_pref > drp->dr_pref
+ && !((new_drp->dr_ifp->int_state
+ ^ drp->dr_ifp->int_state)
+ & IS_SICK)))
+ new_drp = drp;
+ }
+ }
+
+ /* forget it if all of the current entries are better */
+ if (new_drp == 0)
+ return;
+
+ new_drp->dr_ifp = ifp;
+ new_drp->dr_gate = gate;
+ new_drp->dr_ts = now.tv_sec;
+ new_drp->dr_life = ntohs(life);
+ new_drp->dr_recv_pref = pref;
+ /* bias functional preference by metric of the interface */
+ new_drp->dr_pref = PREF(pref,ifp);
+
+ /* after hearing a good advertisement, stop asking
+ */
+ if (!(ifp->int_state & IS_SICK))
+ ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
+}
+
+
+/* Compute the IP checksum
+ * This assumes the packet is less than 32K long.
+ */
+static u_short
+in_cksum(u_short *p,
+ u_int len)
+{
+ u_int sum = 0;
+ int nwords = len >> 1;
+
+ while (nwords-- != 0)
+ sum += *p++;
+
+ if (len & 1)
+ sum += *(u_char *)p;
+
+ /* end-around-carry */
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ return (~sum);
+}
+
+
+/* Send a router discovery advertisement or solicitation ICMP packet.
+ */
+static void
+send_rdisc(union ad_u *p,
+ int p_size,
+ struct interface *ifp,
+ naddr dst, /* 0 or unicast destination */
+ int type) /* 0=unicast, 1=bcast, 2=mcast */
+{
+ struct sockaddr_in sin;
+ int flags;
+ char *msg;
+ naddr tgt_mcast;
+
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_addr.s_addr = dst;
+ sin.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ sin.sin_len = sizeof(sin);
+#endif
+ flags = MSG_DONTROUTE;
+
+ switch (type) {
+ case 0: /* unicast */
+ default:
+ msg = "Send";
+ break;
+
+ case 1: /* broadcast */
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send pt-to-pt";
+ sin.sin_addr.s_addr = ifp->int_dstaddr;
+ } else {
+ msg = "Send broadcast";
+ sin.sin_addr.s_addr = ifp->int_brdaddr;
+ }
+ break;
+
+ case 2: /* multicast */
+ msg = "Send multicast";
+ if (ifp->int_state & IS_DUP) {
+ trace_act("abort multicast output via %s"
+ " with duplicate address",
+ ifp->int_name);
+ return;
+ }
+ if (rdisc_sock_mcast != ifp) {
+ /* select the right interface. */
+#ifdef MCAST_PPP_BUG
+ /* Do not specifiy the primary interface explicitly
+ * if we have the multicast point-to-point kernel
+ * bug, since the kernel will do the wrong thing
+ * if the local address of a point-to-point link
+ * is the same as the address of an ordinary
+ * interface.
+ */
+ if (ifp->int_addr == myaddr) {
+ tgt_mcast = 0;
+ } else
+#endif
+ tgt_mcast = ifp->int_addr;
+ if (0 > setsockopt(rdisc_sock,
+ IPPROTO_IP, IP_MULTICAST_IF,
+ &tgt_mcast, sizeof(tgt_mcast))) {
+ LOGERR("setsockopt(rdisc_sock,"
+ "IP_MULTICAST_IF)");
+ rdisc_sock_mcast = 0;
+ return;
+ }
+ rdisc_sock_mcast = ifp;
+ }
+ flags = 0;
+ break;
+ }
+
+ if (rdisc_sock < 0)
+ get_rdisc_sock();
+
+ trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp,
+ p, p_size);
+
+ if (0 > sendto(rdisc_sock, p, p_size, flags,
+ (struct sockaddr *)&sin, sizeof(sin))) {
+ if (ifp == 0 || !(ifp->int_state & IS_BROKE))
+ msglog("sendto(%s%s%s): %s",
+ ifp != 0 ? ifp->int_name : "",
+ ifp != 0 ? ", " : "",
+ inet_ntoa(sin.sin_addr),
+ strerror(errno));
+ if (ifp != 0)
+ if_sick(ifp);
+ }
+}
+
+
+/* Send an advertisement
+ */
+static void
+send_adv(struct interface *ifp,
+ naddr dst, /* 0 or unicast destination */
+ int type) /* 0=unicast, 1=bcast, 2=mcast */
+{
+ union ad_u u;
+ n_long pref;
+
+
+ bzero(&u,sizeof(u.ad));
+
+ u.ad.icmp_type = ICMP_ROUTERADVERT;
+ u.ad.icmp_ad_num = 1;
+ u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
+
+ u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
+ pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel;
+ pref = PREF(pref, ifp) ^ MIN_PreferenceLevel;
+ u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref);
+
+ u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
+
+ u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
+
+ send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
+}
+
+
+/* Advertise for Router Discovery
+ */
+void
+rdisc_adv(void)
+{
+ struct interface *ifp;
+
+ if (!supplier)
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, >)
+ || stopint) {
+ send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
+ (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
+ ifp->int_rdisc_cnt++;
+
+ intvl_random(&ifp->int_rdisc_timer,
+ (ifp->int_rdisc_int*3)/4,
+ ifp->int_rdisc_int);
+ if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
+ && (ifp->int_rdisc_timer.tv_sec
+ > MAX_INITIAL_ADVERT_INTERVAL)) {
+ ifp->int_rdisc_timer.tv_sec
+ = MAX_INITIAL_ADVERT_INTERVAL;
+ }
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/* Solicit for Router Discovery
+ */
+void
+rdisc_sol(void)
+{
+ struct interface *ifp;
+ union ad_u u;
+
+
+ if (supplier)
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
+ || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
+ bzero(&u,sizeof(u.so));
+ u.so.icmp_type = ICMP_ROUTERSOLICIT;
+ u.so.icmp_cksum = in_cksum((u_short*)&u.so,
+ sizeof(u.so));
+ send_rdisc(&u, sizeof(u.so), ifp,
+ htonl(INADDR_ALLROUTERS_GROUP),
+ ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
+
+ if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
+ ifp->int_rdisc_timer.tv_usec = 0;
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/* check the IP header of a possible Router Discovery ICMP packet */
+static struct interface * /* 0 if bad */
+ck_icmp(char *act,
+ naddr from,
+ struct interface *ifp,
+ naddr to,
+ union ad_u *p,
+ u_int len)
+{
+ char *type;
+
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ type = "advertisement";
+ } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
+ type = "solicitation";
+ } else {
+ return 0;
+ }
+
+ if (p->icmp.icmp_code != 0) {
+ trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
+ type, p->icmp.icmp_code,
+ naddr_ntoa(from), naddr_ntoa(to));
+ return 0;
+ }
+
+ trace_rdisc(act, from, to, ifp, p, len);
+
+ if (ifp == 0)
+ trace_pkt("unknown interface for router-discovery %s"
+ " from %s to %s",
+ type, naddr_ntoa(from), naddr_ntoa(to));
+
+ return ifp;
+}
+
+
+/* read packets from the router discovery socket
+ */
+void
+read_d(void)
+{
+ static struct msg_limit bad_asize, bad_len;
+#ifdef USE_PASSIFNAME
+ static struct msg_limit bad_name;
+#endif
+ struct sockaddr_in from;
+ int n, fromlen, cc, hlen;
+ struct {
+#ifdef USE_PASSIFNAME
+ char ifname[IFNAMSIZ];
+#endif
+ union {
+ struct ip ip;
+ u_short s[512/2];
+ u_char b[512];
+ } pkt;
+ } buf;
+ union ad_u *p;
+ n_long *wp;
+ struct interface *ifp;
+
+
+ for (;;) {
+ fromlen = sizeof(from);
+ cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
+ (struct sockaddr*)&from,
+ &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("recvfrom(rdisc_sock)");
+ break;
+ }
+ if (fromlen != sizeof(struct sockaddr_in))
+ logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
+ fromlen);
+#ifdef USE_PASSIFNAME
+ if ((cc -= sizeof(buf.ifname)) < 0)
+ logbad(0,"missing USE_PASSIFNAME; only %d bytes",
+ cc+sizeof(buf.ifname));
+#endif
+
+ hlen = buf.pkt.ip.ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN)
+ continue;
+ p = (union ad_u *)&buf.pkt.b[hlen];
+ cc -= hlen;
+
+#ifdef USE_PASSIFNAME
+ ifp = ifwithname(buf.ifname, 0);
+ if (ifp == 0)
+ msglim(&bad_name, from.sin_addr.s_addr,
+ "impossible rdisc if_ name %.*s",
+ IFNAMSIZ, buf.ifname);
+#else
+ /* If we could tell the interface on which a packet from
+ * address 0 arrived, we could deal with such solicitations.
+ */
+ ifp = ((from.sin_addr.s_addr == 0)
+ ? 0 : iflookup(from.sin_addr.s_addr));
+#endif
+ ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
+ buf.pkt.ip.ip_dst.s_addr, p, cc);
+ if (ifp == 0)
+ continue;
+ if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
+ trace_pkt(" "
+ "discard our own Router Discovery message");
+ continue;
+ }
+
+ switch (p->icmp.icmp_type) {
+ case ICMP_ROUTERADVERT:
+ if (p->ad.icmp_ad_asize*4
+ < sizeof(p->ad.icmp_ad_info[0])) {
+ msglim(&bad_asize, from.sin_addr.s_addr,
+ "intolerable rdisc address size=%d",
+ p->ad.icmp_ad_asize);
+ continue;
+ }
+ if (p->ad.icmp_ad_num == 0) {
+ trace_pkt(" empty?");
+ continue;
+ }
+ if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info)
+ + (p->ad.icmp_ad_num
+ * sizeof(p->ad.icmp_ad_info[0])))) {
+ msglim(&bad_len, from.sin_addr.s_addr,
+ "rdisc length %d does not match ad_num"
+ " %d", cc, p->ad.icmp_ad_num);
+ continue;
+ }
+ if (supplier)
+ continue;
+ if (ifp->int_state & IS_NO_ADV_IN)
+ continue;
+
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ for (n = 0; n < p->ad.icmp_ad_num; n++) {
+ parse_ad(from.sin_addr.s_addr,
+ wp[0], wp[1],
+ ntohs(p->ad.icmp_ad_life),
+ ifp);
+ wp += p->ad.icmp_ad_asize;
+ }
+ break;
+
+
+ case ICMP_ROUTERSOLICIT:
+ if (!supplier)
+ continue;
+ if (ifp->int_state & IS_NO_ADV_OUT)
+ continue;
+
+ /* XXX
+ * We should handle messages from address 0.
+ */
+
+ /* Respond with a point-to-point advertisement */
+ send_adv(ifp, from.sin_addr.s_addr, 0);
+ break;
+ }
+ }
+
+ rdisc_sort();
+}
diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8
new file mode 100644
index 0000000..cbf65bf
--- /dev/null
+++ b/sbin/routed/routed.8
@@ -0,0 +1,650 @@
+.\" 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.
+.\"
+.\" @(#)routed.8 8.2 (Berkeley) 12/11/93
+.\" $Id: routed.8,v 1.8 1997/02/22 14:33:12 peter Exp $
+.\"
+.Dd June 1, 1996
+.Dt ROUTED 8
+.Os BSD 4.4
+.Sh NAME
+.Nm routed
+.Nd network RIP and router discovery routing daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl sqdghmAt
+.Op Fl T Ar tracefile
+.Oo
+.Fl F
+.Ar net Ns Op /mask Ns Op ,metric
+.Oc
+.Op Fl P Ar parms
+.Sh DESCRIPTION
+.Nm Routed
+is a dameon invoked at boot time to manage the network
+routing tables.
+It uses Routing Information Protocol, RIPv1 (RFC\ 1058),
+RIPv2 (RFC\ 1723),
+and Internet Router Discovery Protocol (RFC 1256)
+to maintain the kernel routing table.
+The RIPv1 protocol is based on the reference 4.3BSD daemon.
+.Pp
+It listens on the
+.Xr udp 4
+socket for the
+.Xr route 8
+service (see
+.Xr services 5 )
+for Routing Information Protocol packets.
+It also sends and receives multicast Router Discovery ICMP messages.
+If the host is a router,
+.Nm
+periodically supplies copies
+of its routing tables to any directly connected hosts and networks.
+It also advertise or solicits default routes using Router Discovery
+ICMP messages.
+.Pp
+When started (or when a network interface is later turned on),
+.Nm
+uses an AF_ROUTE address family facility to find those
+directly connected interfaces configured into the
+system and marked "up".
+It adds necessary routes for the interfaces
+to the kernel routing table.
+Soon after being first started, and provided there is at least one
+interface on which RIP has not been disabled,
+.Nm
+deletes all pre-existing
+non-static routes in kernel table.
+Static routes in the kernel table are preserved and
+included in RIP responses if they have a valid RIP metric
+(see
+.Xr route 8 ).
+.Pp
+If more than one interface is present (not counting the loopback interface),
+it is assumed that the host should forward packets among the
+connected networks.
+After transmitting a RIP
+.Em request
+and
+Router Discovery Advertisements or Solicitations on a new interface,
+the daemon enters a loop, listening for
+RIP request and response and Router Discovery packets from other hosts.
+.Pp
+When a
+.Em request
+packet is received,
+.Nm
+formulates a reply based on the information maintained in its
+internal tables.
+The
+.Em response
+packet generated contains a list of known routes, each marked
+with a "hop count" metric (a count of 16 or greater is
+considered "infinite").
+Advertised metrics reflect the metric associated with interface
+(see
+.Xr ifconfig 8 ),
+so setting the metric on an interface
+is an effective way to steer traffic.
+.Pp
+Responses do not contain routes with a first hop on the requesting
+network to implement in part
+.Em split-horizon .
+Requests from query programs
+such as
+.Xr rtquery 8
+are answered with the complete table.
+.Pp
+The routing table maintained by the daemon
+includes space for several gateways for each destination
+to speed recovery from a failing router.
+RIP
+.Em response
+packets received are used to update the routing tables provided they are
+from one of the several currently recognized gateways or
+advertise a better metric than at least one of the existing
+gateways.
+.Pp
+When an update is applied,
+.Nm
+records the change in its own tables and updates the kernel routing table
+if the best route to the destination changes.
+The change in the kernel routing table is reflected in the next batch of
+.Em response
+packets sent.
+If the next response is not scheduled for a while, a
+.Em flash update
+response containing only recently changed routes is sent.
+.Pp
+In addition to processing incoming packets,
+.Nm
+also periodically checks the routing table entries.
+If an entry has not been updated for 3 minutes, the entry's metric
+is set to infinity and marked for deletion.
+Deletions are delayed until the route has been advertised with
+an infinite metric to insure the invalidation
+is propagated throughout the local internet.
+This is a form of
+.Em poison reverse .
+.Pp
+Routes in the kernel table that are added or changed as a result
+of ICMP Redirect messages are deleted after a while to minimize
+.Em black-holes .
+When a TCP connection suffers a timeout,
+the kernel tells
+.Nm routed ,
+which deletes all redirected routes
+through the gateway involved, advances the age of all RIP routes through
+the gateway to allow an alternate to be chosen, and advances of the
+age of any relevant Router Discovery Protocol default routes.
+.Pp
+Hosts acting as internetwork routers gratuitously supply their
+routing tables every 30 seconds to all directly connected hosts
+and networks.
+These RIP responses are sent to the broadcast address on nets that support
+broadcasting,
+to the destination address on point-to-point links, and to the router's
+own address on other networks.
+If RIPv2 is enabled, multicast packets are sent on interfaces that
+support multicasting.
+.Pp
+If no response is received on a remote interface, if there are errors
+while sending responses,
+or if there are more errors than input or output (see
+.Xr netstat 8 ),
+then the cable or some other part of the interface is assumed to be
+disconnected or broken, and routes are adjusted appropriately.
+.Pp
+The
+.Em Internet Router Discovery Protocol
+is handled similarly.
+When the daemon is supplying RIP routes, it also listens for
+Router Discovery Solicitations and sends Advertisements.
+When it is quiet and only listening to other RIP routers, it
+sends Solicitations and listens for Advertisements.
+If it receives
+a good Advertisement, it stops listening for broadcast or multicast
+RIP responses.
+It tracks several advertising routers to speed recovery when the
+currently chosen router dies.
+If all discovered routers disappear,
+the daemon resumes listening to RIP responses.
+.Pp
+The Router Discovery standard requires that advertisements
+have a default "lifetime" of 30 minutes. That means should
+something happen, a client can be without a good route for
+30 minutes. It is a good idea to reduce the default to 45
+seconds using
+.Fl P Cm rdisc_interval=45
+on the command line or
+.Cm rdisc_interval=45
+in the
+.Pa /etc/gateways
+file.
+.Pp
+While using Router Discovery (which happens by default when
+the system has a single network interface and a Router Discover Advertisement
+is received), there is a single default route and a variable number of
+redirected host routes in the kernel table.
+On a host with more than one network interface,
+this default route will be via only one of the interfaces.
+Thus, multi-homed hosts running with
+.Fl q
+might need
+.Cm no_rdisc
+described below.
+.Pp
+See the
+.Cm pm_rdisc
+facility described below to support "legacy" systems
+that can handle neither RIPv2 nor Router Discovery.
+.Pp
+By default, neither Router Discovery advertisements nor solicitations
+are sent over point to point links (e.g. PPP).
+The netmask associated with point-to-point links (such as SLIP
+or PPP, with the IFF_POINTOPOINT flag) is used by
+.Nm routed
+to infer the netmask used by the remote system when RIPv1 is used.
+.Pp
+Options supported by
+.Nm routed :
+.Bl -tag -width Ds
+.It Fl s
+this option forces
+.Nm
+to supply routing information.
+This is the default if multiple network interfaces are present on which
+RIP or Router Discovery have not been disabled, and if the kernel switch
+ipforwarding=1.
+.It Fl q
+is the opposite of the
+.Fl s
+option.
+This is the default when only one interface is present.
+.It Fl d
+Do not run in the background.
+This option is meant for interactive use.
+.It Fl g
+This flag is used on internetwork routers to offer a route
+to the "default" destination.
+It is equivalent to
+.Fl F
+.Cm 0/0,1
+and is present mostly for historical reasons.
+A better choice is
+.Fl P Cm pm_rdisc
+on the command line or
+.Cm pm_rdisc
+in the
+.Pa /etc/gateways
+file.
+since a larger metric
+will be used, reducing the spread of the potentially dangerous
+default route.
+This is typically used on a gateway to the Internet,
+or on a gateway that uses another routing protocol whose routes
+are not reported to other local routers.
+Notice that because a metric of 1 is used, this feature is
+dangerous. It is more commonly accidentally used to create chaos with routing
+loop than to solve problems.
+.It Fl h
+This causes host or point-to-point routes to not be advertised,
+provided there is a network route going the same direction.
+That is a limited kind of aggregation.
+This option is useful on gateways to ethernets that have other gateway
+machines connected with point-to-point links such as SLIP.
+.It Fl m
+This causes the machine to advertise a host or point-to-point route to
+its primary interface.
+It is useful on multi-homed machines such as NFS servers.
+This option should not be used except when the cost of
+the host routes it generates is justified by the popularity of
+the server.
+It is effective only when the machine is supplying
+routing information, because there is more than one interface.
+The
+.Fl m
+option overrides the
+.Fl q
+option to the limited extent of advertising the host route.
+.It Fl A
+do not ignore RIPv2 authentication if we do not care about RIPv2
+authentication.
+This option is required for conformance with RFC 1723.
+However, it makes no sense and breaks using RIP as a discovery protocol
+to ignore all RIPv2 packets that carry authentication when this machine
+does not care about authentication.
+.It Fl T Ar tracefile
+increases the debugging level to at least 1 and
+causes debugging information to be appended to the trace file.
+Note that because of security concerns, it is wisest to not run
+.Nm routed
+routinely with tracing directed to a file.
+.It Fl t
+increases the debugging level, which causes more information to be logged
+on the tracefile specified with
+.Fl T
+or standard out.
+The debugging level can be increased or decreased
+with the
+.Em SIGUSR1
+or
+.Em SIGUSR2
+signals or with the
+.Xr rtquery 8
+command.
+.It Fl F Ar net[/mask][,metric]
+minimize routes in transmissions via interfaces with addresses that match
+.Em net/mask ,
+and synthesizes a default route to this machine with the
+.Em metric .
+The intent is to reduce RIP traffic on slow, point-to-point links
+such as PPP links by replacing many large UDP packets of RIP information
+with a single, small packet containing a "fake" default route.
+If
+.Em metric
+is absent, a value of 14 is assumed to limit
+the spread of the "fake" default route.
+
+This is a dangerous feature that when used carelessly can cause routing
+loops.
+Notice also that more than one interface can match the specified network
+number and mask.
+See also
+.Fl g .
+.It Fl P Ar parms
+is equivalent to adding the parameter
+line
+.Em parms
+to the
+.Pa /etc/gateways
+file.
+.El
+.Pp
+Any other argument supplied is interpreted as the name
+of a file in which the actions of
+.Nm
+should be logged.
+It is better to use
+.Fl T
+instead of
+appending the name of the trace file to the command.
+.Pp
+.Nm
+also supports the notion of
+"distant"
+.Em passive
+or
+.Em active
+gateways.
+When
+.Nm
+is started, it reads the file
+.Pa /etc/gateways
+to find such distant gateways which may not be located using
+only information from a routing socket, to discover if some
+of the local gateways are
+.Em passive ,
+and to obtain other parameters.
+Gateways specified in this manner should be marked passive
+if they are not expected to exchange routing information,
+while gateways marked active
+should be willing to exchange RIP packets.
+Routes through
+.Em passive
+gateways are installed in the
+kernel's routing tables once upon startup and are not included in
+transmitted RIP responses.
+.Pp
+Distant active gateways are treated like network interfaces.
+RIP responses are sent
+to the distant
+.Em active
+gateway.
+If no responses are received, the associated route is deleted from
+the kernel table and RIP responses advertised via other interfaces.
+If the distant gateway resumes sending RIP responses, the associated
+route is restored.
+.Pp
+Such gateways can be useful on media that do not support broadcasts
+or multicasts but otherwise act like classic shared media like
+Ethernets such as some ATM networks.
+One can list all RIP routers reachable on the ATM network in
+.Pa /etc/gateways
+with a series of
+"host" lines.
+.Pp
+Gateways marked
+.Em external
+are also passive, but are not placed in the kernel
+routing table nor are they included in routing updates.
+The function of external entries is to indicate
+that another routing process
+will install such a route if necessary,
+and that alternate routes to that destination should not be installed
+by
+.Nm routed .
+Such entries are only required when both routers may learn of routes
+to the same destination.
+.Pp
+The
+.Pa /etc/gateways
+file is comprised of a series of lines, each in
+one of the following formats or consist of parameters described below:
+.Pp
+.Bd -ragged
+.Cm net
+.Ar Nname[/mask]
+.Cm gateway
+.Ar Gname
+.Cm metric
+.Ar value
+.Pf < Cm passive No \&|
+.Cm active No \&|
+.Cm extern Ns >
+.Ed
+.Bd -ragged
+.Cm host
+.Ar Hname
+.Cm gateway
+.Ar Gname
+.Cm metric
+.Ar value
+.Pf < Cm passive No \&|
+.Cm active No \&|
+.Cm extern Ns >
+.Ed
+.Pp
+.Ar Nname
+or
+.Ar Hname
+is the name of the destination network or host.
+It may be a symbolic network name or an Internet address
+specified in "dot" notation (see
+.Xr inet 3 ).
+(If it is a name, then it must either be defined in
+.Pa /etc/networks
+or
+.Pa /etc/hosts ,
+or
+.Xr named 8 ,
+must have been started before
+.Nm routed Ns .)
+.Pp
+.Ar mask
+is an optional number between 1 and 32 indicating the netmask associated
+with
+.Ar Nname .
+.Pp
+.Ar Gname
+is the name or address of the gateway to which RIP responses should
+be forwarded.
+.Pp
+.Ar Value
+is the hop count to the destination host or network.
+.Ar " host hname "
+is equivalent to
+.Ar " net nname/32 ".
+.Pp
+One of the keywords
+.Cm passive ,
+.Cm active
+or
+.Cm external
+must be present to indicate whether the gateway should be treated as
+.Cm passive
+or
+.Cm active
+(as described above),
+or whether the gateway is
+.Cm external
+to the scope of the RIP protocol.
+.Pp
+Lines that start with neither "net" nor "host" must consist of one
+or more of the following parameter settings, separated by commas or
+blanks:
+.Bl -tag -width Ds
+.It Cm if Ns \&= Ns Ar ifname
+indicates that the other parameters on the line apply to the interface
+name
+.Ar ifname .
+.It Cm subnet Ns \&= Ns Ar nname[/mask][,metric]
+advertises a route to network
+.Ar nname
+with mask
+.Ar mask
+and the supplied metric (default 1).
+This is useful for filling "holes" in CIDR allocations.
+This parameter must appear by itself on a line.
+.Pp
+Do not use this feature unless necessary. It is dangerous.
+.It Cm passwd Ns \&= Ns Ar XXX
+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
+all RIPv2 responses sent, and checked on all RIPv2 responses received.
+Any blanks, tab characters, commas, or '#', '|', or NULL 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.
+If present,
+.Cm start
+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.
+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.
+Except that a
+.Cm KeyID
+is required, this keyword is similar to
+.Cm passwd .
+To protect the secrets, this parameter setting is valid only in the
+.Pa /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
+turns off aggregation of networks into supernets in RIPv2 responses.
+.It Cm passive
+marks the interface to not be advertised in updates sent via other
+interfaces, and turns off all RIP and router discovery through the interface.
+.It Cm no_rip
+disables all RIP processing on the specified interface.
+If no interfaces are allowed to process RIP packets,
+.Nm
+acts purely as a router discovery daemon.
+.Pp
+Note that turning off RIP without explicitly turning on router
+discovery advertisements with
+.Cm rdisc_adv
+or
+.Fl s
+causes
+.Nm routed
+to act as a client router discovery daemon, not advertising.
+.It Cm no_ripv1_in
+causes RIPv1 received responses to be ignored.
+.It Cm no_ripv2_in
+causes RIPv2 received responses to be ignored.
+.It Cm ripv2_out
+turns off RIPv1 output and causes RIPv2 advertisements to be
+multicast when possible.
+.It Cm ripv2
+is equivalent to
+.Cm no_ripv1_in
+and
+.Cm no_ripv1_out .
+.It Cm no_rdisc
+disables the Internet Router Discovery Protocol.
+.It Cm no_solicit
+disables the transmission of Router Discovery Solicitations.
+.It Cm send_solicit
+specifies that Router Discovery solicitations should be sent,
+even on point-to-point links,
+which by default only listen to Router Discovery messages.
+.It Cm no_rdisc_adv
+disables the transmission of Router Discovery Advertisements
+.It Cm rdisc_adv
+specifies that Router Discovery Advertisements should be sent,
+even on point-to-point links,
+which by default only listen to Router Discovery messages
+.It Cm bcast_rdisc
+specifies that Router Discovery packets should be broadcast instead of
+multicast.
+.It Cm rdisc_pref Ns \&= Ns Ar N
+sets the preference in Router Discovery Advertisements to the integer
+.Ar N .
+.It Cm rdisc_interval Ns \&= Ns Ar N
+sets the nominal interval with which Router Discovery Advertisements
+are transmitted to N seconds and their lifetime to 3*N.
+.It Cm fake_default Ns \&= Ns Ar metric
+has an identical effect to
+.Fl F Ar net[/mask][=metric]
+with the network and mask coming from the sepcified interface.
+.It Cm pm_rdisc
+is similar to
+.Cm fake_default .
+When RIPv2 routes are multicast, so that RIPv1 listeners cannot
+receive them, this feature causes a RIPv1 default route to be
+broadcast to RIPv1 listeners.
+Unless modified with
+.Cm fake_default ,
+the default route is broadcast with a metric of 14.
+That serves as a "poor man's router discovery" protocol.
+.It Cm trust_gateway Ns \&= Ns Ar rname
+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
+.Bl -tag -width /etc/gateways -compact
+.It Pa /etc/gateways
+for distant gateways
+.El
+.Sh SEE ALSO
+.Xr icmp 4 ,
+.Xr udp 4 ,
+.Xr gated 8 ,
+.Xr htable 8 ,
+.Xr rtquery 8 .
+.Rs
+.%T Internet Transport Protocols
+.%R XSIS 028112
+.%Q Xerox System Integration Standard
+.Re
+.Sh BUGS
+It does not always detect unidirectional failures in network interfaces
+(e.g., when the output side fails).
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile
new file mode 100644
index 0000000..adc9d15
--- /dev/null
+++ b/sbin/routed/rtquery/Makefile
@@ -0,0 +1,10 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id$
+
+PROG= rtquery
+MAN8= rtquery.8
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+#COPTS= -g -DDEBUG -Wall
+
+.include <bsd.prog.mk>
diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8
new file mode 100644
index 0000000..301c5d3
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.8
@@ -0,0 +1,130 @@
+.\" $Id: rtquery.8,v 1.6 1997/02/22 14:33:13 peter Exp $
+.Dd June 1, 1996
+.Dt RTQUERY 8
+.Os BSD 4.4
+.Sh NAME
+.Nm rtquery
+.Nd query routing daemons for their routing tables
+.Sh SYNOPSIS
+.Nm
+.Op Fl np1
+.Op Fl w Ar timeout
+.Op Fl r Ar addr
+.Op Fl a Ar secret
+.Ar host ...
+
+.Nm
+.Op Fl t Ar op
+.Ar host ...
+.Sh DESCRIPTION
+.Nm Rtquery
+is used to query a RIP network routing daemon,
+.Xr routed 8
+or
+.Xr gated 8 ,
+for its routing table by sending a
+.Em request
+or
+.Em poll
+command. The routing information in any routing
+.Em response
+packets returned is displayed numerically and symbolically.
+.Pp
+.Em Rtquery
+by default uses the
+.Em request
+command.
+When the
+.Ar -p
+option is specified,
+.Nm rtquery
+uses the
+.Em poll
+command, an
+undocumented extension to the RIP protocol supported by
+.Xr gated 8 .
+When querying gated, the
+.Em poll
+command is preferred over the
+.Em request
+command because the response is not subject to Split Horizon and/or
+Poisoned Reverse, and because some versions of
+.Xr gated 8
+do not answer
+the
+.Em request
+command.
+.Nm Routed
+does not answer the
+.Em poll
+command, but
+recognizes
+.Em request
+commands coming from
+.Nm
+and so answers completely.
+.Pp
+.Nm Rtquery
+is also used to turn tracing on or off in
+.Nm routed .
+.Pp
+Options supported by
+.Nm rtquery :
+.Bl -tag -width Ds
+.It Fl n
+Normally network and host numbers are displayed both symbolically
+and numerically.
+The
+.Fl n
+option displays only the numeric network and host numbers.
+.It Fl p
+Uses the
+.Em Poll
+command to request full routing information from
+.Xr gated 8 .
+This is an undocumented extension RIP protocol supported only by
+.Xr gated 8 .
+.It Fl 1
+query using RIP version 1 instead of RIP version 2.
+.It Fl w Ar timeout
+changes the delay for an answer from each host.
+By default, each host is given 15 seconds to respond.
+.It Fl r Ar addr
+ask about the route to destination
+.Em addr .
+.It Fl a Ar passwd=XXX
+.It Fl a Ar md5_passwd=XXX|KeyID
+cause the query to be sent with the indicated cleartext or MD5 password.
+.It Fl t Ar op
+change tracing, where
+.Em op
+is one of the following.
+Requests from processes not running with UID 0 or on distant networks
+are generally ignored by the daemon except for a message in the system log.
+.Xr gated 8
+is likely to ignore these debugging requests.
+.El
+.Bl -tag -width Ds -offset indent-two
+.It Em on=tracefile
+turn tracing on into the specified file. That file must usually
+have been specified when the daemon was started or be the same
+as a fixed name, often
+.Pa /etc/routed.trace .
+.It Em more
+increases the debugging level.
+.It Em off
+turns off tracing.
+.It Em dump
+dumps the daemon's routing table to the current tracefile.
+.El
+.Sh SEE ALSO
+.Xr gated 8 ,
+.Xr routed 8
+.Rs
+.%T Routing Information Protocol, RIPV1
+.%O RFC1058
+.Re
+.Rs
+.%T Routing Information Protocol, RIPv2
+.%O RFC1723
+.Re
diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c
new file mode 100644
index 0000000..e031c26
--- /dev/null
+++ b/sbin/routed/rtquery/rtquery.c
@@ -0,0 +1,843 @@
+/*-
+ * Copyright (c) 1982, 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.
+ */
+
+char copyright[] =
+"@(#) Copyright (c) 1982, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef sgi
+#include <strings.h>
+#include <bstring.h>
+#endif
+
+#ifndef sgi
+#define _HAVE_SIN_LEN
+#endif
+
+#include <md5.h>
+#define WTIME 15 /* Time to wait for all responses */
+#define STIME (250*1000) /* usec to wait for another response */
+
+int s;
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+MAXPATHLEN];
+} omsg_buf;
+#define OMSG omsg_buf.rip
+int omsg_len = sizeof(struct rip);
+
+union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+1024];
+ } imsg_buf;
+#define IMSG imsg_buf.rip
+
+int nflag; /* numbers, no names */
+int pflag; /* play the `gated` game */
+int ripv2 = 1; /* use RIP version 2 */
+int wtime = WTIME;
+int rflag; /* 1=ask about a particular route */
+int trace, not_trace; /* send trace command or not */
+int auth_type = RIP_AUTH_NONE;
+char passwd[RIP_AUTH_PW_LEN];
+u_long keyid;
+
+struct timeval sent; /* when query sent */
+
+static void rip_input(struct sockaddr_in*, int);
+static int out(char *);
+static void trace_loop(char *argv[]);
+static void query_loop(char *argv[], int);
+static int getnet(char *, struct netinfo *);
+static u_int std_mask(u_int);
+static int parse_quote(char **, char *, char *, char *, int);
+
+
+void
+main(int argc,
+ char *argv[])
+{
+ int ch, bsize;
+ char *p, *options, *value, delim;
+
+ OMSG.rip_nets[0].n_dst = RIP_DEFAULT;
+ OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != -1)
+ switch (ch) {
+ case 'n':
+ not_trace = 1;
+ nflag = 1;
+ break;
+
+ case 'p':
+ not_trace = 1;
+ pflag = 1;
+ break;
+
+ case '1':
+ ripv2 = 0;
+ break;
+
+ case 'w':
+ not_trace = 1;
+ wtime = (int)strtoul(optarg, &p, 0);
+ if (*p != '\0'
+ || wtime <= 0)
+ goto usage;
+ break;
+
+ case 'r':
+ not_trace = 1;
+ if (rflag)
+ goto usage;
+ rflag = getnet(optarg, &OMSG.rip_nets[0]);
+ if (!rflag) {
+ struct hostent *hp = gethostbyname(optarg);
+ if (hp == 0) {
+ fprintf(stderr, "rtquery: %s:", optarg);
+ herror(0);
+ exit(1);
+ }
+ bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst,
+ sizeof(OMSG.rip_nets[0].n_dst));
+ OMSG.rip_nets[0].n_family = RIP_AF_INET;
+ OMSG.rip_nets[0].n_mask = -1;
+ rflag = 1;
+ }
+ break;
+
+ case 't':
+ trace = 1;
+ options = optarg;
+ while (*options != '\0') {
+ char *traceopts[] = {
+# define TRACE_ON 0
+ "on",
+# define TRACE_MORE 1
+ "more",
+# define TRACE_OFF 2
+ "off",
+# define TRACE_DUMP 3
+ "dump",
+ 0
+ };
+ switch (getsubopt(&options,traceopts,&value)) {
+ case TRACE_ON:
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ if (!value
+ || strlen(value) > MAXPATHLEN)
+ goto usage;
+ break;
+ case TRACE_MORE:
+ if (value)
+ goto usage;
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ value = "";
+ break;
+ case TRACE_OFF:
+ if (value)
+ goto usage;
+ OMSG.rip_cmd = RIPCMD_TRACEOFF;
+ value = "";
+ break;
+ case TRACE_DUMP:
+ if (value)
+ goto usage;
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ value = "dump/../table";
+ break;
+ default:
+ goto usage;
+ }
+ strcpy((char*)OMSG.rip_tracefile, value);
+ omsg_len += strlen(value) - sizeof(OMSG.ripun);
+ }
+ break;
+
+ case 'a':
+ not_trace = 1;
+ p = strchr(optarg,'=');
+ if (!p)
+ goto usage;
+ *p++ = '\0';
+ if (!strcasecmp("passwd",optarg))
+ auth_type = RIP_AUTH_PW;
+ else if (!strcasecmp("md5_passwd",optarg))
+ auth_type = RIP_AUTH_MD5;
+ else
+ goto usage;
+ if (0 > parse_quote(&p,"|",&delim,
+ passwd,sizeof(passwd)))
+ goto usage;
+ if (auth_type == RIP_AUTH_MD5
+ && delim == '|') {
+ keyid = strtoul(p+1,&p,0);
+ if (keyid > 255 || *p != '\0')
+ goto usage;
+ } else if (delim != '\0') {
+ goto usage;
+ }
+ break;
+
+ default:
+ goto usage;
+ }
+ argv += optind;
+ argc -= optind;
+ if ((not_trace && trace) || argc == 0) {
+usage: fprintf(stderr, "%s\n%s\n",
+ "usage: rtquery [-np1v] [-r addr] [-w timeout] [-a secret] host ...",
+ " rtquery [-t op] host ...");
+ exit(1);
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(2);
+ }
+
+ /* be prepared to receive a lot of routes */
+ for (bsize = 127*1024; ; bsize -= 1024) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+ &bsize, sizeof(bsize)) == 0)
+ break;
+ if (bsize <= 4*1024) {
+ perror("setsockopt SO_RCVBUF");
+ break;
+ }
+ }
+
+ if (trace)
+ trace_loop(argv);
+ else
+ query_loop(argv, argc);
+ /* NOTREACHED */
+}
+
+
+/* tell the target hosts about tracing
+ */
+static void
+trace_loop(char *argv[])
+{
+ struct sockaddr_in myaddr;
+ int res;
+
+ if (geteuid() != 0) {
+ (void)fprintf(stderr, "-t requires UID 0\n");
+ exit(1);
+ }
+
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ } else {
+ OMSG.rip_vers = RIPv1;
+ }
+
+ bzero(&myaddr, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ myaddr.sin_len = sizeof(myaddr);
+#endif
+ myaddr.sin_port = htons(IPPORT_RESERVED-1);
+ while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
+ if (errno != EADDRINUSE
+ || myaddr.sin_port == 0) {
+ perror("bind");
+ exit(2);
+ }
+ myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1);
+ }
+
+ res = 1;
+ while (*argv != 0) {
+ if (out(*argv++) <= 0)
+ res = 0;
+ }
+ exit(res);
+}
+
+
+/* query all of the listed hosts
+ */
+static void
+query_loop(char *argv[], int argc)
+{
+# define NA0 (OMSG.rip_auths[0])
+# define NA2 (OMSG.rip_auths[2])
+ struct seen {
+ struct seen *next;
+ struct in_addr addr;
+ } *seen, *sp;
+ int answered = 0;
+ int cc;
+ fd_set bits;
+ struct timeval now, delay;
+ struct sockaddr_in from;
+ int fromlen;
+ MD5_CTX md5_ctx;
+
+
+ OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ if (auth_type == RIP_AUTH_PW) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_PW;
+ bcopy(passwd, NA0.au.au_pw,
+ RIP_AUTH_PW_LEN);
+ omsg_len += sizeof(OMSG.rip_nets[0]);
+
+ } else if (auth_type == RIP_AUTH_MD5) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_MD5;
+ NA0.au.a_md5.md5_keyid = (int8_t)keyid;
+ NA0.au.a_md5.md5_auth_len = RIP_AUTH_PW_LEN;
+ NA0.au.a_md5.md5_seqno = 0;
+ NA0.au.a_md5.md5_pkt_len = sizeof(OMSG.rip_nets[1]);
+ NA2.a_family = RIP_AF_AUTH;
+ NA2.a_type = 1;
+ bcopy(passwd, NA2.au.au_pw, sizeof(NA2.au.au_pw));
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (u_char *)&NA0,
+ (char *)(&NA2+1) - (char *)&NA0);
+ MD5Final(NA2.au.au_pw, &md5_ctx);
+ omsg_len += 2*sizeof(OMSG.rip_nets[0]);
+ }
+
+ } else {
+ OMSG.rip_vers = RIPv1;
+ OMSG.rip_nets[0].n_mask = 0;
+ }
+
+ /* ask the first (valid) host */
+ seen = 0;
+ while (0 > out(*argv++)) {
+ if (*argv == 0)
+ exit(-1);
+ answered++;
+ }
+
+ FD_ZERO(&bits);
+ for (;;) {
+ FD_SET(s, &bits);
+ delay.tv_sec = 0;
+ delay.tv_usec = STIME;
+ cc = select(s+1, &bits, 0,0, &delay);
+ if (cc > 0) {
+ fromlen = sizeof(from);
+ cc = recvfrom(s, imsg_buf.packet,
+ sizeof(imsg_buf.packet), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (cc < 0) {
+ perror("recvfrom");
+ exit(1);
+ }
+ /* count the distinct responding hosts.
+ * You cannot match responding hosts with
+ * addresses to which queries were transmitted,
+ * because a router might respond with a
+ * different source address.
+ */
+ for (sp = seen; sp != 0; sp = sp->next) {
+ if (sp->addr.s_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (sp == 0) {
+ sp = malloc(sizeof(*sp));
+ sp->addr = from.sin_addr;
+ sp->next = seen;
+ seen = sp;
+ answered++;
+ }
+
+ rip_input(&from, cc);
+ continue;
+ }
+
+ if (cc < 0) {
+ if ( errno == EINTR)
+ continue;
+ perror("select");
+ exit(1);
+ }
+
+ /* After a pause in responses, probe another host.
+ * This reduces the intermingling of answers.
+ */
+ while (*argv != 0 && 0 > out(*argv++))
+ answered++;
+
+ /* continue until no more packets arrive
+ * or we have heard from all hosts
+ */
+ if (answered >= argc)
+ break;
+
+ /* or until we have waited a long time
+ */
+ if (gettimeofday(&now, 0) < 0) {
+ perror("gettimeofday(now)");
+ exit(1);
+ }
+ if (sent.tv_sec + wtime <= now.tv_sec)
+ break;
+ }
+
+ /* fail if there was no answer */
+ exit (answered >= argc ? 0 : 1);
+}
+
+
+/* send to one host
+ */
+static int
+out(char *host)
+{
+ struct sockaddr_in router;
+ struct hostent *hp;
+
+ if (gettimeofday(&sent, 0) < 0) {
+ perror("gettimeofday(sent)");
+ return -1;
+ }
+
+ bzero(&router, sizeof(router));
+ router.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ router.sin_len = sizeof(router);
+#endif
+ if (!inet_aton(host, &router.sin_addr)) {
+ hp = gethostbyname(host);
+ if (hp == 0) {
+ herror(host);
+ return -1;
+ }
+ bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr));
+ }
+ router.sin_port = htons(RIP_PORT);
+
+ if (sendto(s, &omsg_buf, omsg_len, 0,
+ (struct sockaddr *)&router, sizeof(router)) < 0) {
+ perror(host);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Convert string to printable characters
+ */
+static char *
+qstring(u_char *s, int len)
+{
+ static char buf[8*20+1];
+ char *p;
+ u_char *s2, c;
+
+
+ for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) {
+ c = *s++;
+ if (c == '\0') {
+ for (s2 = s+1; s2 < &s[len]; s2++) {
+ if (*s2 != '\0')
+ break;
+ }
+ if (s2 >= &s[len])
+ goto exit;
+ }
+
+ if (c >= ' ' && c < 0x7f && c != '\\') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch (c) {
+ case '\\':
+ *p++ = '\\';
+ break;
+ case '\n':
+ *p++= 'n';
+ break;
+ case '\r':
+ *p++= 'r';
+ break;
+ case '\t':
+ *p++ = 't';
+ break;
+ case '\b':
+ *p++ = 'b';
+ break;
+ default:
+ p += sprintf(p,"%o",c);
+ break;
+ }
+ }
+exit:
+ *p = '\0';
+ return buf;
+}
+
+
+/*
+ * Handle an incoming RIP packet.
+ */
+static void
+rip_input(struct sockaddr_in *from,
+ int size)
+{
+ struct netinfo *n, *lim;
+ struct in_addr in;
+ char *name;
+ char net_buf[80];
+ u_int mask, dmask;
+ char *sp;
+ int i;
+ struct hostent *hp;
+ struct netent *np;
+ struct netauth *na;
+
+
+ if (nflag) {
+ printf("%s:", inet_ntoa(from->sin_addr));
+ } else {
+ hp = gethostbyaddr((char*)&from->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp == 0) {
+ printf("%s:",
+ inet_ntoa(from->sin_addr));
+ } else {
+ printf("%s (%s):", hp->h_name,
+ inet_ntoa(from->sin_addr));
+ }
+ }
+ if (IMSG.rip_cmd != RIPCMD_RESPONSE) {
+ printf("\n unexpected response type %d\n", IMSG.rip_cmd);
+ return;
+ }
+ printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers,
+ (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "",
+ size);
+ if (size > MAXPACKETSIZE) {
+ if (size > sizeof(imsg_buf) - sizeof(*n)) {
+ printf(" at least %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ size = sizeof(imsg_buf) - sizeof(*n);
+ } else {
+ printf(" %d bytes too long\n",
+ size-MAXPACKETSIZE);
+ }
+ } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
+ printf(" response of bad length=%d\n", size);
+ }
+
+ n = IMSG.rip_nets;
+ lim = (struct netinfo *)((char*)n + size) - 1;
+ for (; n <= lim; n++) {
+ name = "";
+ if (n->n_family == RIP_AF_INET) {
+ in.s_addr = n->n_dst;
+ (void)strcpy(net_buf, inet_ntoa(in));
+
+ mask = ntohl(n->n_mask);
+ dmask = mask & -mask;
+ if (mask != 0) {
+ sp = &net_buf[strlen(net_buf)];
+ if (IMSG.rip_vers == RIPv1) {
+ (void)sprintf(sp," mask=%#x ? ",mask);
+ mask = 0;
+ } else if (mask + dmask == 0) {
+ for (i = 0;
+ (i != 32
+ && ((1<<i)&mask) == 0);
+ i++)
+ continue;
+ (void)sprintf(sp, "/%d",32-i);
+ } else {
+ (void)sprintf(sp," (mask %#x)", mask);
+ }
+ }
+
+ if (!nflag) {
+ if (mask == 0) {
+ mask = std_mask(in.s_addr);
+ if ((ntohl(in.s_addr) & ~mask) != 0)
+ mask = 0;
+ }
+ /* Without a netmask, do not worry about
+ * whether the destination is a host or a
+ * network. Try both and use the first name
+ * we get.
+ *
+ * If we have a netmask we can make a
+ * good guess.
+ */
+ if ((in.s_addr & ~mask) == 0) {
+ np = getnetbyaddr((long)in.s_addr,
+ AF_INET);
+ if (np != 0)
+ name = np->n_name;
+ else if (in.s_addr == 0)
+ name = "default";
+ }
+ if (name[0] == '\0'
+ && ((in.s_addr & ~mask) != 0
+ || mask == 0xffffffff)) {
+ hp = gethostbyaddr((char*)&in,
+ sizeof(in),
+ AF_INET);
+ if (hp != 0)
+ name = hp->h_name;
+ }
+ }
+
+ } else if (n->n_family == RIP_AF_AUTH) {
+ na = (struct netauth*)n;
+ if (na->a_type == RIP_AUTH_PW
+ && n == IMSG.rip_nets) {
+ (void)printf(" Password Authentication:"
+ " \"%s\"\n",
+ qstring(na->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (na->a_type == RIP_AUTH_MD5
+ && n == IMSG.rip_nets) {
+ (void)printf(" MD5 Authentication"
+ " len=%d KeyID=%d"
+ " seqno=%d"
+ " rsvd=%#x,%#x\n",
+ na->au.a_md5.md5_pkt_len,
+ na->au.a_md5.md5_keyid,
+ na->au.a_md5.md5_seqno,
+ na->au.a_md5.rsvd[0],
+ na->au.a_md5.rsvd[1]);
+ continue;
+ }
+ (void)printf(" Authentication type %d: ",
+ ntohs(na->a_type));
+ for (i = 0; i < sizeof(na->au.au_pw); i++)
+ (void)printf("%02x ", na->au.au_pw[i]);
+ putc('\n', stdout);
+ continue;
+
+ } else {
+ (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d",
+ ntohs(n->n_family),
+ (char)(n->n_dst >> 24),
+ (char)(n->n_dst >> 16),
+ (char)(n->n_dst >> 8),
+ (char)n->n_dst);
+ }
+
+ (void)printf(" %-18s metric %2d %-10s",
+ net_buf, ntohl(n->n_metric), name);
+
+ if (n->n_nhop != 0) {
+ in.s_addr = n->n_nhop;
+ if (nflag)
+ hp = 0;
+ else
+ hp = gethostbyaddr((char*)&in, sizeof(in),
+ AF_INET);
+ (void)printf(" nhop=%-15s%s",
+ (hp != 0) ? hp->h_name : inet_ntoa(in),
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ }
+ if (n->n_tag != 0)
+ (void)printf(" tag=%#x%s", n->n_tag,
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ putc('\n', stdout);
+ }
+}
+
+
+/* Return the classical netmask for an IP address.
+ */
+static u_int
+std_mask(u_int addr) /* in network order */
+{
+ NTOHL(addr); /* was a host, not a network */
+
+ if (addr == 0) /* default route has mask 0 */
+ return 0;
+ if (IN_CLASSA(addr))
+ return IN_CLASSA_NET;
+ if (IN_CLASSB(addr))
+ return IN_CLASSB_NET;
+ return IN_CLASSC_NET;
+}
+
+
+/* get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+static int /* 0=bad */
+getnet(char *name,
+ struct netinfo *rt)
+{
+ int i;
+ struct netent *nentp;
+ u_int mask;
+ struct in_addr in;
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /* Detect and separate "1.2.3.4/24"
+ */
+ if (0 != (mname = rindex(name,'/'))) {
+ i = (int)(mname - name);
+ if (i > sizeof(hname)-1) /* name too long */
+ return 0;
+ bcopy(name, hname, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ nentp = getnetbyname(name);
+ if (nentp != 0) {
+ in.s_addr = nentp->n_net;
+ } else if (inet_aton(name, &in) == 1) {
+ NTOHL(in.s_addr);
+ } else {
+ return 0;
+ }
+
+ if (mname == 0) {
+ mask = std_mask(in.s_addr);
+ if ((~mask & in.s_addr) != 0)
+ mask = 0xffffffff;
+ } else {
+ mask = (u_int)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32)
+ return 0;
+ mask = 0xffffffff << (32-mask);
+ }
+
+ rt->n_dst = htonl(in.s_addr);
+ rt->n_family = RIP_AF_INET;
+ rt->n_mask = htonl(mask);
+ return 1;
+}
+
+
+/* strtok(), but honoring backslash
+ */
+static int /* -1=bad */
+parse_quote(char **linep,
+ char *delims,
+ char *delimp,
+ char *buf,
+ int lim)
+{
+ char c, *pc, *p;
+
+
+ pc = *linep;
+ if (*pc == '\0')
+ return -1;
+
+ for (;;) {
+ if (lim == 0)
+ return -1;
+ c = *pc++;
+ if (c == '\0')
+ break;
+
+ if (c == '\\' && pc != '\0') {
+ if ((c = *pc++) == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c >= '0' && c <= '7') {
+ c -= '0';
+ if (*pc >= '0' && *pc <= '7') {
+ c = (c<<3)+(*pc++ - '0');
+ if (*pc >= '0' && *pc <= '7')
+ c = (c<<3)+(*pc++ - '0');
+ }
+ }
+
+ } else {
+ for (p = delims; *p != '\0'; ++p) {
+ if (*p == c)
+ goto exit;
+ }
+ }
+
+ *buf++ = c;
+ --lim;
+ }
+exit:
+ if (delimp != 0)
+ *delimp = c;
+ *linep = pc-1;
+ if (lim != 0)
+ *buf = '\0';
+ return 0;
+}
diff --git a/sbin/routed/table.c b/sbin/routed/table.c
new file mode 100644
index 0000000..a7c2303
--- /dev/null
+++ b/sbin/routed/table.c
@@ -0,0 +1,2001 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+
+#include "defs.h"
+
+static struct rt_spare *rts_better(struct rt_entry *);
+
+struct radix_node_head *rhead; /* root of the radix tree */
+
+int need_flash = 1; /* flash update needed
+ * start =1 to suppress the 1st
+ */
+
+struct timeval age_timer; /* next check of old routes */
+struct timeval need_kern = { /* need to update kernel table */
+ EPOCH+MIN_WAITTIME-1
+};
+
+int stopint;
+
+int total_routes;
+
+/* zap any old routes through this gateway */
+naddr age_bad_gate;
+
+
+/* It is desirable to "aggregate" routes, to combine differing routes of
+ * the same metric and next hop into a common route with a smaller netmask
+ * or to suppress redundant routes, routes that add no information to
+ * routes with smaller netmasks.
+ *
+ * A route is redundant if and only if any and all routes with smaller
+ * but matching netmasks and nets are the same. Since routes are
+ * kept sorted in the radix tree, redundant routes always come second.
+ *
+ * There are two kinds of aggregations. First, two routes of the same bit
+ * mask and differing only in the least significant bit of the network
+ * number can be combined into a single route with a coarser mask.
+ *
+ * Second, a route can be suppressed in favor of another route with a more
+ * coarse mask provided no incompatible routes with intermediate masks
+ * are present. The second kind of aggregation involves suppressing routes.
+ * A route must not be suppressed if an incompatible route exists with
+ * an intermediate mask, since the suppressed route would be covered
+ * by the intermediate.
+ *
+ * This code relies on the radix tree walk encountering routes
+ * sorted first by address, with the smallest address first.
+ */
+
+struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest;
+
+/* #define DEBUG_AG */
+#ifdef DEBUG_AG
+#define CHECK_AG() {int acnt = 0; struct ag_info *cag; \
+ for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \
+ acnt++; \
+ for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \
+ acnt++; \
+ if (acnt != NUM_AG_SLOTS) { \
+ (void)fflush(stderr); \
+ abort(); \
+ } \
+}
+#else
+#define CHECK_AG()
+#endif
+
+
+/* Output the contents of an aggregation table slot.
+ * This function must always be immediately followed with the deletion
+ * of the target slot.
+ */
+static void
+ag_out(struct ag_info *ag,
+ void (*out)(struct ag_info *))
+{
+ struct ag_info *ag_cors;
+ naddr bit;
+
+
+ /* If we output both the even and odd twins, then the immediate parent,
+ * if it is present, is redundant, unless the parent manages to
+ * aggregate into something coarser.
+ * On successive calls, this code detects the even and odd twins,
+ * and marks the parent.
+ *
+ * Note that the order in which the radix tree code emits routes
+ * ensures that the twins are seen before the parent is emitted.
+ */
+ ag_cors = ag->ag_cors;
+ if (ag_cors != 0
+ && ag_cors->ag_mask == ag->ag_mask<<1
+ && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) {
+ ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h)
+ ? AGS_REDUN0
+ : AGS_REDUN1);
+ }
+
+ /* Skip it if this route is itself redundant.
+ *
+ * It is ok to change the contents of the slot here, since it is
+ * always deleted next.
+ */
+ if (ag->ag_state & AGS_REDUN0) {
+ if (ag->ag_state & AGS_REDUN1)
+ return;
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_dst_h |= bit;
+ ag->ag_mask |= bit;
+
+ } else if (ag->ag_state & AGS_REDUN1) {
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_mask |= bit;
+ }
+ out(ag);
+}
+
+
+static void
+ag_del(struct ag_info *ag)
+{
+ CHECK_AG();
+
+ if (ag->ag_cors == 0)
+ ag_corsest = ag->ag_fine;
+ else
+ ag->ag_cors->ag_fine = ag->ag_fine;
+
+ if (ag->ag_fine == 0)
+ ag_finest = ag->ag_cors;
+ else
+ ag->ag_fine->ag_cors = ag->ag_cors;
+
+ ag->ag_fine = ag_avail;
+ ag_avail = ag;
+
+ CHECK_AG();
+}
+
+
+/* Flush routes waiting for aggretation.
+ * This must not suppress a route unless it is known that among all
+ * routes with coarser masks that match it, the one with the longest
+ * mask is appropriate. This is ensured by scanning the routes
+ * in lexical order, and with the most restritive mask first
+ * among routes to the same destination.
+ */
+void
+ag_flush(naddr lim_dst_h, /* flush routes to here */
+ naddr lim_mask, /* matching this mask */
+ void (*out)(struct ag_info *))
+{
+ struct ag_info *ag, *ag_cors;
+ naddr dst_h;
+
+
+ for (ag = ag_finest;
+ ag != 0 && ag->ag_mask >= lim_mask;
+ ag = ag_cors) {
+ ag_cors = ag->ag_cors;
+
+ /* work on only the specified routes */
+ dst_h = ag->ag_dst_h;
+ if ((dst_h & lim_mask) != lim_dst_h)
+ continue;
+
+ if (!(ag->ag_state & AGS_SUPPRESS))
+ ag_out(ag, out);
+
+ else for ( ; ; ag_cors = ag_cors->ag_cors) {
+ /* Look for a route that can suppress the
+ * current route */
+ if (ag_cors == 0) {
+ /* failed, so output it and look for
+ * another route to work on
+ */
+ ag_out(ag, out);
+ break;
+ }
+
+ if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) {
+ /* We found a route with a coarser mask that
+ * aggregates the current target.
+ *
+ * If it has a different next hop, it
+ * cannot replace the target, so output
+ * the target.
+ */
+ if (ag->ag_gate != ag_cors->ag_gate
+ && !(ag->ag_state & AGS_FINE_GATE)
+ && !(ag_cors->ag_state & AGS_CORS_GATE)) {
+ ag_out(ag, out);
+ break;
+ }
+
+ /* If the coarse route has a good enough
+ * metric, it suppresses the target.
+ */
+ if (ag_cors->ag_pref <= ag->ag_pref) {
+ if (ag_cors->ag_seqno > ag->ag_seqno)
+ ag_cors->ag_seqno = ag->ag_seqno;
+ if (AG_IS_REDUN(ag->ag_state)
+ && ag_cors->ag_mask==ag->ag_mask<<1) {
+ if (ag_cors->ag_dst_h == dst_h)
+ ag_cors->ag_state |= AGS_REDUN0;
+ else
+ ag_cors->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_cors->ag_tag)
+ ag_cors->ag_tag = 0;
+ if (ag->ag_nhop != ag_cors->ag_nhop)
+ ag_cors->ag_nhop = 0;
+ break;
+ }
+ }
+ }
+
+ /* That route has either been output or suppressed */
+ ag_cors = ag->ag_cors;
+ ag_del(ag);
+ }
+
+ CHECK_AG();
+}
+
+
+/* Try to aggregate a route with previous routes.
+ */
+void
+ag_check(naddr dst,
+ naddr mask,
+ naddr gate,
+ naddr nhop,
+ char metric,
+ char pref,
+ u_int seqno,
+ u_short tag,
+ u_short state,
+ void (*out)(struct ag_info *)) /* output using this */
+{
+ struct ag_info *ag, *nag, *ag_cors;
+ naddr xaddr;
+ int x;
+
+ NTOHL(dst);
+
+ /* Punt non-contiguous subnet masks.
+ *
+ * (X & -X) contains a single bit if and only if X is a power of 2.
+ * (X + (X & -X)) == 0 if and only if X is a power of 2.
+ */
+ if ((mask & -mask) + mask != 0) {
+ struct ag_info nc_ag;
+
+ nc_ag.ag_dst_h = dst;
+ nc_ag.ag_mask = mask;
+ nc_ag.ag_gate = gate;
+ nc_ag.ag_nhop = nhop;
+ nc_ag.ag_metric = metric;
+ nc_ag.ag_pref = pref;
+ nc_ag.ag_tag = tag;
+ nc_ag.ag_state = state;
+ nc_ag.ag_seqno = seqno;
+ out(&nc_ag);
+ return;
+ }
+
+ /* Search for the right slot in the aggregation table.
+ */
+ ag_cors = 0;
+ ag = ag_corsest;
+ while (ag != 0) {
+ if (ag->ag_mask >= mask)
+ break;
+
+ /* Suppress old routes (i.e. combine with compatible routes
+ * with coarser masks) as we look for the right slot in the
+ * aggregation table for the new route.
+ * A route to an address less than the current destination
+ * will not be affected by the current route or any route
+ * seen hereafter. That means it is safe to suppress it.
+ * This check keeps poor routes (eg. with large hop counts)
+ * from preventing suppresion of finer routes.
+ */
+ if (ag_cors != 0
+ && ag->ag_dst_h < dst
+ && (ag->ag_state & AGS_SUPPRESS)
+ && ag_cors->ag_pref <= ag->ag_pref
+ && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h
+ && (ag_cors->ag_gate == ag->ag_gate
+ || (ag->ag_state & AGS_FINE_GATE)
+ || (ag_cors->ag_state & AGS_CORS_GATE))) {
+ if (ag_cors->ag_seqno > ag->ag_seqno)
+ ag_cors->ag_seqno = ag->ag_seqno;
+ if (AG_IS_REDUN(ag->ag_state)
+ && ag_cors->ag_mask==ag->ag_mask<<1) {
+ if (ag_cors->ag_dst_h == dst)
+ ag_cors->ag_state |= AGS_REDUN0;
+ else
+ ag_cors->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_cors->ag_tag)
+ ag_cors->ag_tag = 0;
+ if (ag->ag_nhop != ag_cors->ag_nhop)
+ ag_cors->ag_nhop = 0;
+ ag_del(ag);
+ CHECK_AG();
+ } else {
+ ag_cors = ag;
+ }
+ ag = ag_cors->ag_fine;
+ }
+
+ /* If we find the even/odd twin of the new route, and if the
+ * masks and so forth are equal, we can aggregate them.
+ * We can probably promote one of the pair.
+ *
+ * Since the routes are encountered in lexical order,
+ * the new route must be odd. However, the second or later
+ * times around this loop, it could be the even twin promoted
+ * from the even/odd pair of twins of the finer route.
+ */
+ while (ag != 0
+ && ag->ag_mask == mask
+ && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) {
+
+ /* Here we know the target route and the route in the current
+ * slot have the same netmasks and differ by at most the
+ * last bit. They are either for the same destination, or
+ * for an even/odd pair of destinations.
+ */
+ if (ag->ag_dst_h == dst) {
+ /* We have two routes to the same destination.
+ * Routes are encountered in lexical order, so a
+ * route is never promoted until the parent route is
+ * already present. So we know that the new route is
+ * a promoted pair and the route already in the slot
+ * is the explicit route.
+ *
+ * Prefer the best route if their metrics differ,
+ * or the promoted one if not, following a sort
+ * of longest-match rule.
+ */
+ if (pref <= ag->ag_pref) {
+ ag->ag_gate = gate;
+ ag->ag_nhop = nhop;
+ ag->ag_tag = tag;
+ ag->ag_metric = metric;
+ ag->ag_pref = pref;
+ x = ag->ag_state;
+ ag->ag_state = state;
+ state = x;
+ }
+
+ /* The sequence number controls flash updating,
+ * and should be the smaller of the two.
+ */
+ if (ag->ag_seqno > seqno)
+ ag->ag_seqno = seqno;
+
+ /* some bits are set if they are set on either route */
+ ag->ag_state |= (state & (AGS_PROMOTE_EITHER
+ | AGS_REDUN0 | AGS_REDUN1));
+ return;
+ }
+
+ /* If one of the routes can be promoted and the other can
+ * be suppressed, it may be possible to combine them or
+ * worthwhile to promote one.
+ *
+ * Note that any route that can be promoted is always
+ * marked to be eligible to be suppressed.
+ */
+ if (!((state & AGS_PROMOTE)
+ && (ag->ag_state & AGS_SUPPRESS))
+ && !((ag->ag_state & AGS_PROMOTE)
+ && (state & AGS_SUPPRESS)))
+ break;
+
+ /* A pair of even/odd twin routes can be combined
+ * if either is redundant, or if they are via the
+ * same gateway and have the same metric.
+ */
+ if (AG_IS_REDUN(ag->ag_state)
+ || AG_IS_REDUN(state)
+ || (ag->ag_gate == gate
+ && ag->ag_pref == pref
+ && (state & ag->ag_state & AGS_PROMOTE) != 0)) {
+
+ /* We have both the even and odd pairs.
+ * Since the routes are encountered in order,
+ * the route in the slot must be the even twin.
+ *
+ * Combine and promote the pair of routes.
+ */
+ if (seqno > ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ if (AG_IS_REDUN(ag->ag_state))
+ state |= AGS_REDUN0;
+ else
+ state &= ~AGS_REDUN0;
+ state |= (ag->ag_state & AGS_PROMOTE_EITHER);
+ if (ag->ag_tag != tag)
+ tag = 0;
+ if (ag->ag_nhop != nhop)
+ nhop = 0;
+
+ /* Get rid of the even twin that was already
+ * in the slot.
+ */
+ ag_del(ag);
+
+ } else if (ag->ag_pref >= pref
+ && (ag->ag_state & AGS_PROMOTE)) {
+ /* If we cannot combine the pair, maybe the route
+ * with the worse metric can be promoted.
+ *
+ * Promote the old, even twin, by giving its slot
+ * in the table to the new, odd twin.
+ */
+ ag->ag_dst_h = dst;
+
+ xaddr = ag->ag_gate;
+ ag->ag_gate = gate;
+ gate = xaddr;
+
+ xaddr = ag->ag_nhop;
+ ag->ag_nhop = nhop;
+ nhop = xaddr;
+
+ x = ag->ag_tag;
+ ag->ag_tag = tag;
+ tag = x;
+
+ x = ag->ag_state;
+ ag->ag_state = state;
+ state = x;
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN0;
+
+ x = ag->ag_metric;
+ ag->ag_metric = metric;
+ metric = x;
+
+ x = ag->ag_pref;
+ ag->ag_pref = pref;
+ pref = x;
+
+ if (seqno >= ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = seqno;
+
+ } else {
+ if (!(state & AGS_PROMOTE))
+ break; /* cannot promote either twin */
+
+ /* promote the new, odd twin by shaving its
+ * mask and address.
+ */
+ if (seqno > ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = seqno;
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ }
+
+ mask <<= 1;
+ dst &= mask;
+
+ if (ag_cors == 0) {
+ ag = ag_corsest;
+ break;
+ }
+ ag = ag_cors;
+ ag_cors = ag->ag_cors;
+ }
+
+ /* When we can no longer promote and combine routes,
+ * flush the old route in the target slot. Also flush
+ * any finer routes that we know will never be aggregated by
+ * the new route.
+ *
+ * In case we moved toward coarser masks,
+ * get back where we belong
+ */
+ if (ag != 0
+ && ag->ag_mask < mask) {
+ ag_cors = ag;
+ ag = ag->ag_fine;
+ }
+
+ /* Empty the target slot
+ */
+ if (ag != 0 && ag->ag_mask == mask) {
+ ag_flush(ag->ag_dst_h, ag->ag_mask, out);
+ ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine;
+ }
+
+#ifdef DEBUG_AG
+ (void)fflush(stderr);
+ if (ag == 0 && ag_cors != ag_finest)
+ abort();
+ if (ag_cors == 0 && ag != ag_corsest)
+ abort();
+ if (ag != 0 && ag->ag_cors != ag_cors)
+ abort();
+ if (ag_cors != 0 && ag_cors->ag_fine != ag)
+ abort();
+ CHECK_AG();
+#endif
+
+ /* Save the new route on the end of the table.
+ */
+ nag = ag_avail;
+ ag_avail = nag->ag_fine;
+
+ nag->ag_dst_h = dst;
+ nag->ag_mask = mask;
+ nag->ag_gate = gate;
+ nag->ag_nhop = nhop;
+ nag->ag_metric = metric;
+ nag->ag_pref = pref;
+ nag->ag_tag = tag;
+ nag->ag_state = state;
+ nag->ag_seqno = seqno;
+
+ nag->ag_fine = ag;
+ if (ag != 0)
+ ag->ag_cors = nag;
+ else
+ ag_finest = nag;
+ nag->ag_cors = ag_cors;
+ if (ag_cors == 0)
+ ag_corsest = nag;
+ else
+ ag_cors->ag_fine = nag;
+ CHECK_AG();
+}
+
+
+static char *
+rtm_type_name(u_char type)
+{
+ static char *rtm_types[] = {
+ "RTM_ADD",
+ "RTM_DELETE",
+ "RTM_CHANGE",
+ "RTM_GET",
+ "RTM_LOSING",
+ "RTM_REDIRECT",
+ "RTM_MISS",
+ "RTM_LOCK",
+ "RTM_OLDADD",
+ "RTM_OLDDEL",
+ "RTM_RESOLVE",
+ "RTM_NEWADDR",
+ "RTM_DELADDR",
+ "RTM_IFINFO"
+ };
+ static char name0[10];
+
+
+ if (type > sizeof(rtm_types)/sizeof(rtm_types[0])
+ || type == 0) {
+ sprintf(name0, "RTM type %#x", type);
+ return name0;
+ } else {
+ return rtm_types[type-1];
+ }
+}
+
+
+/* Trim a mask in a sockaddr
+ * Produce a length of 0 for an address of 0.
+ * Otherwise produce the index of the first zero byte.
+ */
+void
+#ifdef _HAVE_SIN_LEN
+masktrim(struct sockaddr_in *ap)
+#else
+masktrim(struct sockaddr_in_new *ap)
+#endif
+{
+ register char *cp;
+
+ if (ap->sin_addr.s_addr == 0) {
+ ap->sin_len = 0;
+ return;
+ }
+ cp = (char *)(&ap->sin_addr.s_addr+1);
+ while (*--cp == 0)
+ continue;
+ ap->sin_len = cp - (char*)ap + 1;
+}
+
+
+/* Tell the kernel to add, delete or change a route
+ */
+static void
+rtioctl(int action, /* RTM_DELETE, etc */
+ naddr dst,
+ naddr gate,
+ naddr mask,
+ int metric,
+ int flags)
+{
+ struct {
+ struct rt_msghdr w_rtm;
+ struct sockaddr_in w_dst;
+ struct sockaddr_in w_gate;
+#ifdef _HAVE_SA_LEN
+ struct sockaddr_in w_mask;
+#else
+ struct sockaddr_in_new w_mask;
+#endif
+ } w;
+ long cc;
+
+again:
+ bzero(&w, sizeof(w));
+ w.w_rtm.rtm_msglen = sizeof(w);
+ w.w_rtm.rtm_version = RTM_VERSION;
+ w.w_rtm.rtm_type = action;
+ w.w_rtm.rtm_flags = flags;
+ w.w_rtm.rtm_seq = ++rt_sock_seqno;
+ w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ if (metric != 0) {
+ w.w_rtm.rtm_rmx.rmx_hopcount = metric;
+ w.w_rtm.rtm_inits |= RTV_HOPCOUNT;
+ }
+ w.w_dst.sin_family = AF_INET;
+ w.w_dst.sin_addr.s_addr = dst;
+ w.w_gate.sin_family = AF_INET;
+ w.w_gate.sin_addr.s_addr = gate;
+#ifdef _HAVE_SA_LEN
+ w.w_dst.sin_len = sizeof(w.w_dst);
+ w.w_gate.sin_len = sizeof(w.w_gate);
+#endif
+ if (mask == HOST_MASK) {
+ w.w_rtm.rtm_flags |= RTF_HOST;
+ w.w_rtm.rtm_msglen -= sizeof(w.w_mask);
+ } else {
+ w.w_rtm.rtm_addrs |= RTA_NETMASK;
+ w.w_mask.sin_addr.s_addr = htonl(mask);
+#ifdef _HAVE_SA_LEN
+ masktrim(&w.w_mask);
+ if (w.w_mask.sin_len == 0)
+ w.w_mask.sin_len = sizeof(long);
+ w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len);
+#endif
+ }
+
+ if (TRACEKERNEL)
+ trace_kernel("write kernel %s %s->%s metric=%d flags=%#x\n",
+ rtm_type_name(action),
+ addrname(dst, mask, 0), naddr_ntoa(gate),
+ metric, flags);
+
+#ifndef NO_INSTALL
+ cc = write(rt_sock, &w, w.w_rtm.rtm_msglen);
+ if (cc == w.w_rtm.rtm_msglen)
+ return;
+ if (cc < 0) {
+ if (errno == ESRCH
+ && (action == RTM_CHANGE || action == RTM_DELETE)) {
+ trace_act("route to %s disappeared before %s",
+ addrname(dst, mask, 0),
+ rtm_type_name(action));
+ if (action == RTM_CHANGE) {
+ action = RTM_ADD;
+ goto again;
+ }
+ return;
+ }
+ msglog("write(rt_sock) %s %s --> %s: %s",
+ rtm_type_name(action),
+ addrname(dst, mask, 0), naddr_ntoa(gate),
+ strerror(errno));
+ } else {
+ msglog("write(rt_sock) wrote %d instead of %d",
+ cc, w.w_rtm.rtm_msglen);
+ }
+#endif
+}
+
+
+#define KHASH_SIZE 71 /* should be prime */
+#define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE]
+static struct khash {
+ struct khash *k_next;
+ naddr k_dst;
+ naddr k_mask;
+ naddr k_gate;
+ short k_metric;
+ u_short k_state;
+#define KS_NEW 0x001
+#define KS_DELETE 0x002
+#define KS_ADD 0x004 /* add to the kernel */
+#define KS_CHANGE 0x008 /* tell kernel to change the route */
+#define KS_DEL_ADD 0x010 /* delete & add to change the kernel */
+#define KS_STATIC 0x020 /* Static flag in kernel */
+#define KS_GATEWAY 0x040 /* G flag in kernel */
+#define KS_DYNAMIC 0x080 /* result of redirect */
+#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 */
+} *khash_bins[KHASH_SIZE];
+
+
+static struct khash*
+kern_find(naddr dst, naddr mask, struct khash ***ppk)
+{
+ struct khash *k, **pk;
+
+ for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) {
+ if (k->k_dst == dst && k->k_mask == mask)
+ break;
+ }
+ if (ppk != 0)
+ *ppk = pk;
+ return k;
+}
+
+
+static struct khash*
+kern_add(naddr dst, naddr mask)
+{
+ struct khash *k, **pk;
+
+ k = kern_find(dst, mask, &pk);
+ if (k != 0)
+ return k;
+
+ k = (struct khash *)malloc(sizeof(*k));
+
+ bzero(k, sizeof(*k));
+ k->k_dst = dst;
+ k->k_mask = mask;
+ k->k_state = KS_NEW;
+ k->k_keep = now.tv_sec;
+ *pk = k;
+
+ return k;
+}
+
+
+/* If a kernel route has a non-zero metric, check that it is still in the
+ * daemon table, and not deleted by interfaces coming and going.
+ */
+static void
+kern_check_static(struct khash *k,
+ struct interface *ifp)
+{
+ struct rt_entry *rt;
+ naddr int_addr;
+
+ if (k->k_metric == 0)
+ return;
+
+ int_addr = (ifp != 0) ? ifp->int_addr : loopaddr;
+
+ rt = rtget(k->k_dst, k->k_mask);
+ if (rt != 0) {
+ if (!(rt->rt_state & RS_STATIC))
+ rtchange(rt, rt->rt_state | RS_STATIC,
+ k->k_gate, int_addr,
+ k->k_metric, 0, ifp, now.tv_sec, 0);
+ } else {
+ rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr,
+ k->k_metric, 0, RS_STATIC, ifp);
+ }
+}
+
+
+/* add a route the kernel told us
+ */
+static void
+rtm_add(struct rt_msghdr *rtm,
+ struct rt_addrinfo *info,
+ time_t keep)
+{
+ struct khash *k;
+ struct interface *ifp;
+ naddr mask;
+
+
+ if (rtm->rtm_flags & RTF_HOST) {
+ mask = HOST_MASK;
+ } 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));
+ return;
+ }
+
+ if (INFO_GATE(info) == 0
+ || INFO_GATE(info)->sa_family != AF_INET) {
+ msglog("ignore %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ return;
+ }
+
+ k = kern_add(S_ADDR(INFO_DST(info)), mask);
+ if (k->k_state & KS_NEW)
+ k->k_keep = now.tv_sec+keep;
+ k->k_gate = S_ADDR(INFO_GATE(info));
+ k->k_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (k->k_metric < 0)
+ k->k_metric = 0;
+ else if (k->k_metric > HOPCNT_INFINITY)
+ k->k_metric = HOPCNT_INFINITY;
+ k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW);
+ if (rtm->rtm_flags & RTF_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ if (rtm->rtm_flags & RTF_STATIC)
+ 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))) {
+ /* 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.
+ */
+ 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",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate),
+ ifp ? ifp->int_name : "unknown interface");
+ } 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;
+ }
+
+ /* If it is not a static route, quit until the next comparison
+ * between the kernel and daemon tables, when it will be deleted.
+ */
+ if (!(k->k_state & KS_STATIC)) {
+ k->k_state |= KS_DELETE;
+ LIM_SEC(need_kern, k->k_keep);
+ return;
+ }
+
+ /* Put static routes with real metrics into the daemon table so
+ * they can be advertised.
+ *
+ * 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));
+
+ kern_check_static(k, ifp);
+}
+
+
+/* deal with packet loss
+ */
+static void
+rtm_lose(struct rt_msghdr *rtm,
+ struct rt_addrinfo *info)
+{
+ if (INFO_GATE(info) == 0
+ || INFO_GATE(info)->sa_family != AF_INET) {
+ trace_act("ignore %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ return;
+ }
+
+ if (!supplier)
+ rdisc_age(S_ADDR(INFO_GATE(info)));
+
+ age(S_ADDR(INFO_GATE(info)));
+}
+
+
+/* Clean the kernel table by copying it to the daemon image.
+ * Eventually the daemon will delete any extra routes.
+ */
+void
+flush_kern(void)
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct interface *ifp;
+ static struct sockaddr_in gate_sa;
+ struct rt_addrinfo info;
+
+
+ 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, 0, &needed, 0, 0) < 0) {
+ DBGERR(1,"RT_DUMP-sysctl-estimate");
+ return;
+ }
+ buf = malloc(needed);
+ if (sysctl(mib, 6, buf, &needed, 0, 0) < 0)
+ BADERR(1,"RT_DUMP");
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+
+ rt_xaddrs(&info,
+ (struct sockaddr *)(rtm+1),
+ (struct sockaddr *)(next + rtm->rtm_msglen),
+ rtm->rtm_addrs);
+
+ if (INFO_DST(&info) == 0
+ || INFO_DST(&info)->sa_family != AF_INET)
+ continue;
+
+ /* ignore ARP table entries on systems with a merged route
+ * and ARP table.
+ */
+ if (rtm->rtm_flags & RTF_LLINFO)
+ continue;
+
+ if (INFO_GATE(&info) == 0)
+ continue;
+ if (INFO_GATE(&info)->sa_family != AF_INET) {
+ if (INFO_GATE(&info)->sa_family != AF_LINK)
+ continue;
+ ifp = ifwithindex(((struct sockaddr_dl *)
+ INFO_GATE(&info))->sdl_index);
+ if (ifp == 0)
+ continue;
+ if ((ifp->int_if_flags & IFF_POINTOPOINT)
+ || S_ADDR(INFO_DST(&info)) == ifp->int_addr)
+ gate_sa.sin_addr.s_addr = ifp->int_addr;
+ else
+ gate_sa.sin_addr.s_addr = htonl(ifp->int_net);
+#ifdef _HAVE_SA_LEN
+ gate_sa.sin_len = sizeof(gate_sa);
+#endif
+ gate_sa.sin_family = AF_INET;
+ INFO_GATE(&info) = (struct sockaddr *)&gate_sa;
+ }
+
+ /* ignore multicast addresses
+ */
+ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info)))))
+ continue;
+
+ /* Note static routes and interface routes, and also
+ * preload the image of the kernel table so that
+ * we can later clean it, as well as avoid making
+ * unneeded changes. Keep the old kernel routes for a
+ * few seconds to allow a RIP or router-discovery
+ * response to be heard.
+ */
+ rtm_add(rtm,&info,MIN_WAITTIME);
+ }
+ free(buf);
+}
+
+
+/* Listen to announcements from the kernel
+ */
+void
+read_rt(void)
+{
+ long cc;
+ struct interface *ifp;
+ naddr mask;
+ union {
+ struct {
+ struct rt_msghdr rtm;
+ struct sockaddr addrs[RTAX_MAX];
+ } r;
+ struct if_msghdr ifm;
+ } m;
+ char str[100], *strp;
+ struct rt_addrinfo info;
+
+
+ for (;;) {
+ cc = read(rt_sock, &m, sizeof(m));
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("read(rt_sock)");
+ return;
+ }
+
+ if (m.r.rtm.rtm_version != RTM_VERSION) {
+ msglog("bogus routing message version %d",
+ m.r.rtm.rtm_version);
+ continue;
+ }
+
+ /* Ignore our own results.
+ */
+ if (m.r.rtm.rtm_type <= RTM_CHANGE
+ && m.r.rtm.rtm_pid == mypid) {
+ static int complained = 0;
+ if (!complained) {
+ msglog("receiving our own change messages");
+ complained = 1;
+ }
+ continue;
+ }
+
+ if (m.r.rtm.rtm_type == RTM_IFINFO
+ || m.r.rtm.rtm_type == RTM_NEWADDR
+ || m.r.rtm.rtm_type == RTM_DELADDR) {
+ ifp = ifwithindex(m.ifm.ifm_index);
+ if (ifp == 0)
+ trace_act("note %s with flags %#x"
+ " for index #%d",
+ rtm_type_name(m.r.rtm.rtm_type),
+ m.ifm.ifm_flags,
+ m.ifm.ifm_index);
+ else
+ trace_act("note %s with flags %#x for %s",
+ rtm_type_name(m.r.rtm.rtm_type),
+ m.ifm.ifm_flags,
+ ifp->int_name);
+
+ /* After being informed of a change to an interface,
+ * check them all now if the check would otherwise
+ * be a long time from now, if the interface is
+ * not known, or if the interface has been turned
+ * off or on.
+ */
+ if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL
+ || ifp == 0
+ || ((ifp->int_if_flags ^ m.ifm.ifm_flags)
+ & IFF_UP_RUNNING) != 0)
+ ifinit_timer.tv_sec = now.tv_sec;
+ continue;
+ }
+
+ strcpy(str, rtm_type_name(m.r.rtm.rtm_type));
+ strp = &str[strlen(str)];
+ if (m.r.rtm.rtm_type <= RTM_CHANGE)
+ strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid);
+
+ rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX],
+ m.r.rtm.rtm_addrs);
+
+ if (INFO_DST(&info) == 0) {
+ trace_act("ignore %s without dst", str);
+ continue;
+ }
+
+ if (INFO_DST(&info)->sa_family != AF_INET) {
+ trace_act("ignore %s for AF %d", str,
+ INFO_DST(&info)->sa_family);
+ continue;
+ }
+
+ mask = ((INFO_MASK(&info) != 0)
+ ? ntohl(S_ADDR(INFO_MASK(&info)))
+ : (m.r.rtm.rtm_flags & RTF_HOST)
+ ? HOST_MASK
+ : std_mask(S_ADDR(INFO_DST(&info))));
+
+ strp += sprintf(strp, ": %s",
+ addrname(S_ADDR(INFO_DST(&info)), mask, 0));
+
+ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) {
+ trace_act("ignore multicast %s", str);
+ continue;
+ }
+
+ if (INFO_GATE(&info) != 0
+ && INFO_GATE(&info)->sa_family == AF_INET)
+ strp += sprintf(strp, " --> %s",
+ saddr_ntoa(INFO_GATE(&info)));
+
+ if (INFO_AUTHOR(&info) != 0)
+ strp += sprintf(strp, " by authority of %s",
+ saddr_ntoa(INFO_AUTHOR(&info)));
+
+ switch (m.r.rtm.rtm_type) {
+ case RTM_ADD:
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ if (m.r.rtm.rtm_errno != 0) {
+ trace_act("ignore %s with \"%s\" error",
+ str, strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ rtm_add(&m.r.rtm,&info,0);
+ }
+ break;
+
+ case RTM_DELETE:
+ if (m.r.rtm.rtm_errno != 0) {
+ trace_act("ignore %s with \"%s\" error",
+ str, strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ del_static(S_ADDR(INFO_DST(&info)), mask, 1);
+ }
+ break;
+
+ case RTM_LOSING:
+ trace_act("%s", str);
+ rtm_lose(&m.r.rtm,&info);
+ break;
+
+ default:
+ trace_act("ignore %s", str);
+ break;
+ }
+ }
+}
+
+
+/* after aggregating, note routes that belong in the kernel
+ */
+static void
+kern_out(struct ag_info *ag)
+{
+ struct khash *k;
+
+
+ /* Do not install bad routes if they are not already present.
+ * This includes routes that had RS_NET_SYN for interfaces that
+ * recently died.
+ */
+ if (ag->ag_metric == HOPCNT_INFINITY) {
+ k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0);
+ if (k == 0)
+ return;
+ } else {
+ k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask);
+ }
+
+ if (k->k_state & KS_NEW) {
+ /* will need to add new entry to the kernel table */
+ k->k_state = KS_ADD;
+ if (ag->ag_state & AGS_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ k->k_gate = ag->ag_gate;
+ k->k_metric = ag->ag_metric;
+ return;
+ }
+
+ if (k->k_state & KS_STATIC)
+ return;
+
+ /* modify existing kernel entry if necessary */
+ if (k->k_gate != ag->ag_gate
+ || k->k_metric != ag->ag_metric) {
+ k->k_gate = ag->ag_gate;
+ k->k_metric = ag->ag_metric;
+ k->k_state |= KS_CHANGE;
+ }
+
+ if (k->k_state & KS_DYNAMIC) {
+ k->k_state &= ~KS_DYNAMIC;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ if ((k->k_state & KS_GATEWAY)
+ && !(ag->ag_state & AGS_GATEWAY)) {
+ k->k_state &= ~KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ } else if (!(k->k_state & KS_GATEWAY)
+ && (ag->ag_state & AGS_GATEWAY)) {
+ k->k_state |= KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ /* Deleting-and-adding is necessary to change aspects of a route.
+ * Just delete instead of deleting and then adding a bad route.
+ * Otherwise, we want to keep the route in the kernel.
+ */
+ if (k->k_metric == HOPCNT_INFINITY
+ && (k->k_state & KS_DEL_ADD))
+ k->k_state |= KS_DELETE;
+ else
+ k->k_state &= ~KS_DELETE;
+#undef RT
+}
+
+
+/* ARGSUSED */
+static int
+walk_kern(struct radix_node *rn,
+ struct walkarg *w)
+{
+#define RT ((struct rt_entry *)rn)
+ char metric, pref;
+ u_int ags = 0;
+
+
+ /* Do not install synthetic routes */
+ if (RT->rt_state & RS_NET_SYN)
+ return 0;
+
+ if (!(RT->rt_state & RS_IF)) {
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE);
+
+ } else {
+ /* Do not install routes for "external" remote interfaces.
+ */
+ if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL))
+ return 0;
+
+ ags |= AGS_IF;
+
+ /* If it is not an interface, or an alias for an interface,
+ * it must be a "gateway."
+ *
+ * If it is a "remote" interface, it is also a "gateway" to
+ * the kernel if is not a alias.
+ */
+ if (RT->rt_ifp == 0
+ || (RT->rt_ifp->int_state & IS_REMOTE))
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE);
+ }
+
+ if (RT->rt_state & RS_RDISC)
+ ags |= AGS_CORS_GATE;
+
+ /* aggregate good routes without regard to their metric */
+ pref = 1;
+ metric = RT->rt_metric;
+ if (metric == HOPCNT_INFINITY) {
+ /* if the route is dead, so try hard to aggregate. */
+ pref = HOPCNT_INFINITY;
+ ags |= (AGS_FINE_GATE | AGS_SUPPRESS);
+ }
+
+ ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0,
+ metric,pref, 0, 0, ags, kern_out);
+ return 0;
+#undef RT
+}
+
+
+/* Update the kernel table to match the daemon table.
+ */
+static void
+fix_kern(void)
+{
+ int i, flags;
+ struct khash *k, **pk;
+
+
+ need_kern = age_timer;
+
+ /* Walk daemon table, updating the copy of the kernel table.
+ */
+ (void)rn_walktree(rhead, walk_kern, 0);
+ ag_flush(0,0,kern_out);
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (pk = &khash_bins[i]; (k = *pk) != 0; ) {
+ /* Do not touch static routes */
+ if (k->k_state & KS_STATIC) {
+ kern_check_static(k,0);
+ pk = &k->k_next;
+ continue;
+ }
+
+ /* check hold on routes deleted by the operator */
+ if (k->k_keep > now.tv_sec) {
+ LIM_SEC(need_kern, k->k_keep);
+ k->k_state |= KS_DELETE;
+ pk = &k->k_next;
+ continue;
+ }
+
+ if ((k->k_state & (KS_DELETE | KS_DYNAMIC))
+ == KS_DELETE) {
+ if (!(k->k_state & KS_DELETED))
+ rtioctl(RTM_DELETE,
+ k->k_dst, k->k_gate, k->k_mask,
+ 0, 0);
+ *pk = k->k_next;
+ free(k);
+ continue;
+ }
+
+ if (0 != (k->k_state&(KS_ADD|KS_CHANGE|KS_DEL_ADD))) {
+ if (k->k_state & KS_DEL_ADD) {
+ rtioctl(RTM_DELETE,
+ k->k_dst,k->k_gate,k->k_mask,
+ 0, 0);
+ k->k_state &= ~KS_DYNAMIC;
+ }
+
+ flags = 0;
+ if (0 != (k->k_state&(KS_GATEWAY|KS_DYNAMIC)))
+ flags |= RTF_GATEWAY;
+
+ if (k->k_state & KS_ADD) {
+ rtioctl(RTM_ADD,
+ k->k_dst, k->k_gate, k->k_mask,
+ k->k_metric, flags);
+ } else if (k->k_state & KS_CHANGE) {
+ rtioctl(RTM_CHANGE,
+ k->k_dst,k->k_gate,k->k_mask,
+ k->k_metric, flags);
+ }
+ k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD);
+ }
+
+ /* Mark this route to be deleted in the next cycle.
+ * This deletes routes that disappear from the
+ * daemon table, since the normal aging code
+ * will clear the bit for routes that have not
+ * disappeared from the daemon table.
+ */
+ k->k_state |= KS_DELETE;
+ pk = &k->k_next;
+ }
+ }
+}
+
+
+/* Delete a static route in the image of the kernel table.
+ */
+void
+del_static(naddr dst,
+ naddr mask,
+ int gone)
+{
+ struct khash *k;
+ struct rt_entry *rt;
+
+ /* Just mark it in the table to be deleted next time the kernel
+ * table is updated.
+ * If it has already been deleted, mark it as such, and set its
+ * keep-timer so that it will not be deleted again for a while.
+ * This lets the operator delete a route added by the daemon
+ * and add a replacement.
+ */
+ k = kern_find(dst, mask, 0);
+ if (k != 0) {
+ k->k_state &= ~(KS_STATIC | KS_DYNAMIC);
+ k->k_state |= KS_DELETE;
+ if (gone) {
+ k->k_state |= KS_DELETED;
+ k->k_keep = now.tv_sec + K_KEEP_LIM;
+ }
+ }
+
+ rt = rtget(dst, mask);
+ if (rt != 0 && (rt->rt_state & RS_STATIC))
+ rtbad(rt);
+}
+
+
+/* Delete all routes generated from ICMP Redirects that use a given gateway,
+ * as well as old redirected routes.
+ */
+void
+del_redirects(naddr bad_gate,
+ time_t old)
+{
+ int i;
+ struct khash *k;
+
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != 0; k = k->k_next) {
+ if (!(k->k_state & KS_DYNAMIC)
+ || (k->k_state & KS_STATIC))
+ continue;
+
+ if (k->k_gate != bad_gate
+ && k->k_redirect_time > old
+ && !supplier)
+ continue;
+
+ k->k_state |= KS_DELETE;
+ k->k_state &= ~KS_DYNAMIC;
+ need_kern.tv_sec = now.tv_sec;
+ trace_act("mark redirected %s --> %s for deletion",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate));
+ }
+ }
+}
+
+
+/* Start the daemon tables.
+ */
+void
+rtinit(void)
+{
+ extern int max_keylen;
+ int i;
+ struct ag_info *ag;
+
+ /* Initialize the radix trees */
+ max_keylen = sizeof(struct sockaddr_in);
+ rn_init();
+ rn_inithead((void**)&rhead, 32);
+
+ /* mark all of the slots in the table free */
+ ag_avail = ag_slots;
+ for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) {
+ ag->ag_fine = ag+1;
+ ag++;
+ }
+}
+
+
+#ifdef _HAVE_SIN_LEN
+static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET};
+static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET};
+#else
+static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET};
+static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET};
+#endif
+
+
+void
+set_need_flash(void)
+{
+ if (!need_flash) {
+ need_flash = 1;
+ /* Do not send the flash update immediately. Wait a little
+ * while to hear from other routers.
+ */
+ no_flash.tv_sec = now.tv_sec + MIN_WAITTIME;
+ }
+}
+
+
+/* Get a particular routing table entry
+ */
+struct rt_entry *
+rtget(naddr dst, naddr mask)
+{
+ struct rt_entry *rt;
+
+ dst_sock.sin_addr.s_addr = dst;
+ mask_sock.sin_addr.s_addr = mask;
+ masktrim(&mask_sock);
+ rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead);
+ if (!rt
+ || rt->rt_dst != dst
+ || rt->rt_mask != mask)
+ return 0;
+
+ return rt;
+}
+
+
+/* Find a route to dst as the kernel would.
+ */
+struct rt_entry *
+rtfind(naddr dst)
+{
+ dst_sock.sin_addr.s_addr = dst;
+ return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead);
+}
+
+
+/* add a route to the table
+ */
+void
+rtadd(naddr dst,
+ naddr mask,
+ naddr gate, /* forward packets here */
+ naddr router, /* on the authority of this router */
+ int metric,
+ u_short tag,
+ u_int state, /* rs_state for the entry */
+ struct interface *ifp)
+{
+ struct rt_entry *rt;
+ naddr smask;
+ int i;
+ struct rt_spare *rts;
+
+ rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd");
+ bzero(rt, sizeof(*rt));
+ for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++)
+ rts->rts_metric = HOPCNT_INFINITY;
+
+ rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock;
+ rt->rt_dst = dst;
+ rt->rt_dst_sock.sin_family = AF_INET;
+#ifdef _HAVE_SIN_LEN
+ rt->rt_dst_sock.sin_len = dst_sock.sin_len;
+#endif
+ if (mask != HOST_MASK) {
+ smask = std_mask(dst);
+ if ((smask & ~mask) == 0 && mask > smask)
+ state |= RS_SUBNET;
+ }
+ mask_sock.sin_addr.s_addr = mask;
+ masktrim(&mask_sock);
+ rt->rt_mask = mask;
+ rt->rt_state = state;
+ rt->rt_gate = gate;
+ rt->rt_router = router;
+ rt->rt_time = now.tv_sec;
+ rt->rt_metric = metric;
+ rt->rt_poison_metric = HOPCNT_INFINITY;
+ rt->rt_tag = tag;
+ rt->rt_ifp = ifp;
+ rt->rt_seqno = update_seqno;
+
+ if (++total_routes == MAX_ROUTES)
+ msglog("have maximum (%d) routes", total_routes);
+ if (TRACEACTIONS)
+ trace_add_del("Add", rt);
+
+ need_kern.tv_sec = now.tv_sec;
+ set_need_flash();
+
+ 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
+ }
+}
+
+
+/* notice a changed route
+ */
+void
+rtchange(struct rt_entry *rt,
+ u_int state, /* new state bits */
+ naddr gate, /* now forward packets here */
+ naddr router, /* on the authority of this router */
+ int metric, /* new metric */
+ u_short tag,
+ struct interface *ifp,
+ time_t new_time,
+ char *label)
+{
+ if (rt->rt_metric != metric) {
+ /* Fix the kernel immediately if it seems the route
+ * has gone bad, since there may be a working route that
+ * aggregates this route.
+ */
+ if (metric == HOPCNT_INFINITY) {
+ need_kern.tv_sec = now.tv_sec;
+ if (new_time >= now.tv_sec - EXPIRE_TIME)
+ new_time = now.tv_sec - EXPIRE_TIME;
+ }
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ if (rt->rt_gate != gate) {
+ need_kern.tv_sec = now.tv_sec;
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ state |= (rt->rt_state & RS_SUBNET);
+
+ /* Keep various things from deciding ageless routes are stale.
+ */
+ if (!AGE_RT(state, ifp))
+ new_time = now.tv_sec;
+
+ if (TRACEACTIONS)
+ trace_change(rt, state, gate, router, metric, tag, ifp,
+ new_time,
+ label ? label : "Chg ");
+
+ rt->rt_state = state;
+ rt->rt_gate = gate;
+ rt->rt_router = router;
+ rt->rt_metric = metric;
+ rt->rt_tag = tag;
+ rt->rt_ifp = ifp;
+ rt->rt_time = new_time;
+}
+
+
+/* check for a better route among the spares
+ */
+static struct rt_spare *
+rts_better(struct rt_entry *rt)
+{
+ struct rt_spare *rts, *rts1;
+ int i;
+
+ /* find the best alternative among the spares */
+ rts = rt->rt_spares+1;
+ for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) {
+ if (BETTER_LINK(rt,rts1,rts))
+ rts = rts1;
+ }
+
+ return rts;
+}
+
+
+/* switch to a backup route
+ */
+void
+rtswitch(struct rt_entry *rt,
+ struct rt_spare *rts)
+{
+ struct rt_spare swap;
+ char label[10];
+
+
+ /* Do not change permanent routes */
+ if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC
+ | RS_NET_SYN | RS_IF)))
+ return;
+
+ /* find the best alternative among the spares */
+ if (rts == 0)
+ rts = rts_better(rt);
+
+ /* Do not bother if it is not worthwhile.
+ */
+ if (!BETTER_LINK(rt, rts, rt->rt_spares))
+ return;
+
+ swap = rt->rt_spares[0];
+ (void)sprintf(label, "Use #%d", rts - rt->rt_spares);
+ rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC),
+ rts->rts_gate, rts->rts_router, rts->rts_metric,
+ rts->rts_tag, rts->rts_ifp, rts->rts_time, label);
+ *rts = swap;
+}
+
+
+void
+rtdelete(struct rt_entry *rt)
+{
+ struct khash *k;
+
+
+ if (TRACEACTIONS)
+ trace_add_del("Del", rt);
+
+ k = kern_find(rt->rt_dst, rt->rt_mask, 0);
+ if (k != 0) {
+ k->k_state |= KS_DELETE;
+ need_kern.tv_sec = now.tv_sec;
+ }
+
+ dst_sock.sin_addr.s_addr = rt->rt_dst;
+ mask_sock.sin_addr.s_addr = rt->rt_mask;
+ masktrim(&mask_sock);
+ if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock,
+ rhead)) {
+ msglog("rnh_deladdr() failed");
+ } else {
+ free(rt);
+ total_routes--;
+ }
+}
+
+
+/* Get rid of a bad route, and try to switch to a replacement.
+ */
+void
+rtbad(struct rt_entry *rt)
+{
+ /* Poison the route */
+ rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC),
+ rt->rt_gate, rt->rt_router, HOPCNT_INFINITY, rt->rt_tag,
+ 0, rt->rt_time, 0);
+
+ rtswitch(rt, 0);
+}
+
+
+/* Junk a RS_NET_SYN or RS_LOCAL route,
+ * unless it is needed by another interface.
+ */
+void
+rtbad_sub(struct rt_entry *rt)
+{
+ struct interface *ifp, *ifp1;
+ struct intnet *intnetp;
+ u_int state;
+
+
+ ifp1 = 0;
+ state = 0;
+
+ if (rt->rt_state & RS_LOCAL) {
+ /* Is this the route through loopback for the interface?
+ * If so, see if it is used by any other interfaces, such
+ * as a point-to-point interface with the same local address.
+ */
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ /* Retain it if another interface needs it.
+ */
+ if (ifp->int_addr == rt->rt_ifp->int_addr) {
+ state |= RS_LOCAL;
+ ifp1 = ifp;
+ break;
+ }
+ }
+
+ }
+
+ if (!(state & RS_LOCAL)) {
+ /* Retain RIPv1 logical network route if there is another
+ * interface that justifies it.
+ */
+ if (rt->rt_state & RS_NET_SYN) {
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
+ if ((ifp->int_state & IS_NEED_NET_SYN)
+ && rt->rt_mask == ifp->int_std_mask
+ && rt->rt_dst == ifp->int_std_addr) {
+ state |= RS_NET_SYN;
+ ifp1 = ifp;
+ break;
+ }
+ }
+ }
+
+ /* or if there is an authority route that needs it. */
+ for (intnetp = intnets;
+ intnetp != 0;
+ intnetp = intnetp->intnet_next) {
+ if (intnetp->intnet_addr == rt->rt_dst
+ && intnetp->intnet_mask == rt->rt_mask) {
+ state |= (RS_NET_SYN | RS_NET_INT);
+ break;
+ }
+ }
+ }
+
+ if (ifp1 != 0 || (state & RS_NET_SYN)) {
+ rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL))
+ | state),
+ rt->rt_gate, rt->rt_router, rt->rt_metric,
+ rt->rt_tag, ifp1, rt->rt_time, 0);
+ } else {
+ rtbad(rt);
+ }
+}
+
+
+/* Called while walking the table looking for sick interfaces
+ * or after a time change.
+ */
+/* ARGSUSED */
+int
+walk_bad(struct radix_node *rn,
+ struct walkarg *w)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i;
+ time_t new_time;
+
+
+ /* fix any spare routes through the interface
+ */
+ rts = RT->rt_spares;
+ for (i = NUM_SPARES; i != 1; i--) {
+ rts++;
+
+ if (rts->rts_ifp != 0
+ && (rts->rts_ifp->int_state & IS_BROKE)) {
+ /* mark the spare route to be deleted immediately */
+ new_time = rts->rts_time;
+ if (new_time >= now_garbage)
+ new_time = now_garbage-1;
+ trace_upslot(RT, rts, rts->rts_gate,
+ rts->rts_router, 0,
+ HOPCNT_INFINITY, rts->rts_tag,
+ new_time);
+ rts->rts_ifp = 0;
+ rts->rts_metric = HOPCNT_INFINITY;
+ rts->rts_time = new_time;
+ }
+ }
+
+ /* Deal with the main route
+ */
+ /* finished if it has been handled before or if its interface is ok
+ */
+ if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE))
+ return 0;
+
+ /* Bad routes for other than interfaces are easy.
+ */
+ if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) {
+ rtbad(RT);
+ return 0;
+ }
+
+ rtbad_sub(RT);
+ return 0;
+#undef RT
+}
+
+
+/* Check the age of an individual route.
+ */
+/* ARGSUSED */
+static int
+walk_age(struct radix_node *rn,
+ struct walkarg *w)
+{
+#define RT ((struct rt_entry *)rn)
+ struct interface *ifp;
+ struct rt_spare *rts;
+ int i;
+
+
+ /* age all of the spare routes, including the primary route
+ * currently in use
+ */
+ rts = RT->rt_spares;
+ for (i = NUM_SPARES; i != 0; i--, rts++) {
+
+ ifp = rts->rts_ifp;
+ if (i == NUM_SPARES) {
+ if (!AGE_RT(RT->rt_state, ifp)) {
+ /* Keep various things from deciding ageless
+ * routes are stale
+ */
+ rts->rts_time = now.tv_sec;
+ continue;
+ }
+
+ /* forget RIP routes after RIP has been turned off.
+ */
+ if (rip_sock < 0) {
+ rtdelete(RT);
+ return 0;
+ }
+ }
+
+ /* age failing routes
+ */
+ if (age_bad_gate == rts->rts_gate
+ && rts->rts_time >= now_stale) {
+ rts->rts_time -= SUPPLY_INTERVAL;
+ }
+
+ /* trash the spare routes when they go bad */
+ if (rts->rts_metric < HOPCNT_INFINITY
+ && now_garbage > rts->rts_time) {
+ trace_upslot(RT, rts, rts->rts_gate,
+ rts->rts_router, rts->rts_ifp,
+ HOPCNT_INFINITY, rts->rts_tag,
+ rts->rts_time);
+ rts->rts_metric = HOPCNT_INFINITY;
+ }
+ }
+
+
+ /* finished if the active route is still fresh */
+ if (now_stale <= RT->rt_time)
+ return 0;
+
+ /* try to switch to an alternative */
+ rtswitch(RT, 0);
+
+ /* Delete a dead route after it has been publically mourned. */
+ if (now_garbage > RT->rt_time) {
+ rtdelete(RT);
+ return 0;
+ }
+
+ /* Start poisoning a bad route before deleting it. */
+ if (now.tv_sec - RT->rt_time > EXPIRE_TIME)
+ rtchange(RT, RT->rt_state, RT->rt_gate, RT->rt_router,
+ HOPCNT_INFINITY, RT->rt_tag, RT->rt_ifp,
+ RT->rt_time, 0);
+ return 0;
+}
+
+
+/* Watch for dead routes and interfaces.
+ */
+void
+age(naddr bad_gate)
+{
+ struct interface *ifp;
+ int need_query = 0;
+
+ /* If not listening to RIP, there is no need to age the routes in
+ * the table.
+ */
+ age_timer.tv_sec = (now.tv_sec
+ + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL));
+
+ /* Check for dead IS_REMOTE interfaces by timing their
+ * transmissions.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (!(ifp->int_state & IS_REMOTE))
+ continue;
+
+ /* ignore unreachable remote interfaces */
+ if (!check_remote(ifp))
+ continue;
+ /* Restore remote interface that has become reachable
+ */
+ if (ifp->int_state & IS_BROKE)
+ if_ok(ifp, "remote ");
+
+ if (ifp->int_act_time != NEVER
+ && now.tv_sec - ifp->int_act_time > EXPIRE_TIME) {
+ msglog("remote interface %s to %s timed out after"
+ " %d:%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ (now.tv_sec - ifp->int_act_time)/60,
+ (now.tv_sec - ifp->int_act_time)%60);
+ if_sick(ifp);
+ }
+
+ /* If we have not heard from the other router
+ * recently, ask it.
+ */
+ if (now.tv_sec >= ifp->int_query_time) {
+ ifp->int_query_time = NEVER;
+ need_query = 1;
+ }
+ }
+
+ /* Age routes. */
+ age_bad_gate = bad_gate;
+ (void)rn_walktree(rhead, walk_age, 0);
+
+ /* Update the kernel routing table. */
+ fix_kern();
+
+ /* poke reticent remote gateways */
+ if (need_query)
+ rip_query();
+}
diff --git a/sbin/routed/trace.c b/sbin/routed/trace.c
new file mode 100644
index 0000000..28c65f3
--- /dev/null
+++ b/sbin/routed/trace.c
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
+static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
+#elif defined(__NetBSD__)
+static char rcsid[] = "$NetBSD$";
+#endif
+#ident "$Revision: 1.17 $"
+
+#define RIPCMDS
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+
+
+#ifdef sgi
+/* use *stat64 for files on large filesystems */
+#define stat stat64
+#endif
+
+#define NRECORDS 50 /* size of circular trace buffer */
+
+int tracelevel, new_tracelevel;
+FILE *ftrace = stdout; /* output trace file */
+static char *sigtrace_pat = "%s\n";
+static char savetracename[MAXPATHLEN+1];
+char inittracename[MAXPATHLEN+1];
+int file_trace; /* 1=tracing to file, not stdout */
+
+static void trace_dump(void);
+
+
+/* convert string to printable characters
+ */
+static char *
+qstring(u_char *s, int len)
+{
+ static char buf[8*20+1];
+ char *p;
+ u_char *s2, c;
+
+
+ for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) {
+ c = *s++;
+ if (c == '\0') {
+ for (s2 = s+1; s2 < &s[len]; s2++) {
+ if (*s2 != '\0')
+ break;
+ }
+ if (s2 >= &s[len])
+ goto exit;
+ }
+
+ if (c >= ' ' && c < 0x7f && c != '\\') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch (c) {
+ case '\\':
+ *p++ = '\\';
+ break;
+ case '\n':
+ *p++= 'n';
+ break;
+ case '\r':
+ *p++= 'r';
+ break;
+ case '\t':
+ *p++ = 't';
+ break;
+ case '\b':
+ *p++ = 'b';
+ break;
+ default:
+ p += sprintf(p,"%o",c);
+ break;
+ }
+ }
+exit:
+ *p = '\0';
+ return buf;
+}
+
+
+/* convert IP address to a string, but not into a single buffer
+ */
+char *
+naddr_ntoa(naddr a)
+{
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ char str[16]; /* xxx.xxx.xxx.xxx\0 */
+ } bufs[NUM_BUFS];
+ char *s;
+ struct in_addr addr;
+
+ addr.s_addr = a;
+ s = strcpy(bufs[bufno].str, inet_ntoa(addr));
+ bufno = (bufno+1) % NUM_BUFS;
+ return s;
+#undef NUM_BUFS
+}
+
+
+char *
+saddr_ntoa(struct sockaddr *sa)
+{
+ return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa));
+}
+
+
+static char *
+ts(time_t secs) {
+ static char s[20];
+
+ secs += epoch.tv_sec;
+#ifdef sgi
+ (void)cftime(s, "%T", &secs);
+#else
+ bcopy(ctime(&secs)+11, s, 8);
+ s[8] = '\0';
+#endif
+ return s;
+}
+
+
+/* On each event, display a time stamp.
+ * This assumes that 'now' is update once for each event, and
+ * that at least now.tv_usec changes.
+ */
+static struct timeval lastlog_time;
+
+void
+lastlog(void)
+{
+ if (lastlog_time.tv_sec != now.tv_sec
+ || lastlog_time.tv_usec != now.tv_usec) {
+ (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec));
+ lastlog_time = now;
+ }
+}
+
+
+static void
+tmsg(char *p, ...)
+{
+ va_list args;
+
+ if (ftrace != 0) {
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ fflush(ftrace);
+ }
+}
+
+
+static void
+trace_close(void)
+{
+ int fd;
+
+
+ fflush(stdout);
+ fflush(stderr);
+
+ if (ftrace != 0 && file_trace) {
+ if (ftrace != stdout)
+ fclose(ftrace);
+ ftrace = 0;
+ fd = open(_PATH_DEVNULL, O_RDWR);
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ (void)close(fd);
+ }
+ lastlog_time.tv_sec = 0;
+}
+
+
+void
+trace_flush(void)
+{
+ if (ftrace != 0) {
+ fflush(ftrace);
+ if (ferror(ftrace))
+ trace_off("tracing off: ", strerror(ferror(ftrace)));
+ }
+}
+
+
+void
+trace_off(char *p, ...)
+{
+ va_list args;
+
+
+ if (ftrace != 0) {
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ }
+ trace_close();
+
+ new_tracelevel = tracelevel = 0;
+}
+
+
+/* log a change in tracing
+ */
+void
+tracelevel_msg(char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ static char *off_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions stopped",
+ "Tracing packets stopped",
+ "Tracing packet contents stopped",
+ "Tracing kernel changes stopped",
+ };
+ static char *on_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions started",
+ "Tracing packets started",
+ "Tracing packet contents started",
+ "Tracing kernel changes started",
+ };
+ u_int old_tracelevel = tracelevel;
+
+
+ if (new_tracelevel < 0)
+ new_tracelevel = 0;
+ else if (new_tracelevel > MAX_TRACELEVEL)
+ new_tracelevel = MAX_TRACELEVEL;
+
+ if (new_tracelevel < tracelevel) {
+ if (new_tracelevel <= 0) {
+ trace_off(pat, off_msgs[0]);
+ } else do {
+ tmsg(pat, off_msgs[tracelevel]);
+ }
+ while (--tracelevel != new_tracelevel);
+
+ } else if (new_tracelevel > tracelevel) {
+ do {
+ tmsg(pat, on_msgs[tracelevel++]);
+ } while (tracelevel != new_tracelevel);
+ }
+
+ if (dump > 0
+ || (dump == 0 && old_tracelevel == 0 && tracelevel != 0))
+ trace_dump();
+}
+
+
+void
+set_tracefile(char *filename,
+ char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ struct stat stbuf;
+ FILE *n_ftrace;
+ char *fn;
+
+
+ /* Allow a null filename to increase the level if the trace file
+ * is already open or if coming from a trusted source, such as
+ * a signal or the command line.
+ */
+ if (filename == 0 || filename[0] == '\0') {
+ filename = 0;
+ if (ftrace == 0) {
+ if (inittracename[0] == '\0') {
+ msglog("missing trace file name");
+ return;
+ }
+ fn = inittracename;
+ } else {
+ fn = 0;
+ }
+
+ } else if (!strcmp(filename,"dump/../table")) {
+ trace_dump();
+ return;
+
+ } else {
+ /* Allow the file specified with "-T file" to be reopened,
+ * but require all other names specified over the net to
+ * match the official path. The path can specify a directory
+ * in which the file is to be created.
+ */
+ if (strcmp(filename, inittracename)
+#ifdef _PATH_TRACE
+ && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)
+ || strstr(filename,"../")
+ || 0 > stat(_PATH_TRACE, &stbuf))
+#endif
+ ) {
+ msglog("wrong trace file \"%s\"", filename);
+ return;
+ }
+
+ /* If the new tracefile exists, it must be a regular file.
+ */
+ if (stat(filename, &stbuf) >= 0
+ && (stbuf.st_mode & S_IFMT) != S_IFREG) {
+ msglog("wrong type (%#x) of trace file \"%s\"",
+ stbuf.st_mode, filename);
+ return;
+ }
+
+ fn = filename;
+ }
+
+ if (fn != 0) {
+ n_ftrace = fopen(fn, "a");
+ if (n_ftrace == 0) {
+ msglog("failed to open trace file \"%s\" %s",
+ fn, strerror(errno));
+ if (fn == inittracename)
+ inittracename[0] = '\0';
+ return;
+ }
+
+ tmsg("switch to trace file %s\n", fn);
+
+ file_trace = 1;
+ trace_close();
+
+ if (fn != savetracename)
+ strncpy(savetracename, fn, sizeof(savetracename)-1);
+ ftrace = n_ftrace;
+
+ fflush(stdout);
+ fflush(stderr);
+ dup2(fileno(ftrace), STDOUT_FILENO);
+ dup2(fileno(ftrace), STDERR_FILENO);
+ }
+
+ if (new_tracelevel == 0 || filename == 0)
+ new_tracelevel++;
+ tracelevel_msg(pat, dump != 0 ? dump : (filename != 0));
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_on(int s)
+{
+ new_tracelevel++;
+ sigtrace_pat = "SIGUSR1: %s\n";
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_off(int s)
+{
+ new_tracelevel--;
+ sigtrace_pat = "SIGUSR2: %s\n";
+}
+
+
+/* Set tracing after a signal.
+ */
+void
+set_tracelevel(void)
+{
+ if (new_tracelevel == tracelevel)
+ return;
+
+ /* If tracing entirely off, and there was no tracefile specified
+ * on the command line, then leave it off.
+ */
+ if (new_tracelevel > tracelevel && ftrace == 0) {
+ if (savetracename[0] != '\0') {
+ set_tracefile(savetracename,sigtrace_pat,0);
+ } else if (inittracename[0] != '\0') {
+ set_tracefile(inittracename,sigtrace_pat,0);
+ } else {
+ new_tracelevel = 0;
+ return;
+ }
+ } else {
+ tracelevel_msg(sigtrace_pat, 0);
+ }
+}
+
+
+/* display an address
+ */
+char *
+addrname(naddr addr, /* in network byte order */
+ naddr mask,
+ int force) /* 0=show mask if nonstandard, */
+{ /* 1=always show mask, 2=never */
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ char str[15+20];
+ } bufs[NUM_BUFS];
+ char *s, *sp;
+ naddr dmask;
+ int i;
+
+ s = strcpy(bufs[bufno].str, naddr_ntoa(addr));
+ bufno = (bufno+1) % NUM_BUFS;
+
+ if (force == 1 || (force == 0 && mask != std_mask(addr))) {
+ sp = &s[strlen(s)];
+
+ dmask = mask & -mask;
+ if (mask + dmask == 0) {
+ for (i = 0; i != 32 && ((1<<i) & mask) == 0; i++)
+ continue;
+ (void)sprintf(sp, "/%d", 32-i);
+
+ } else {
+ (void)sprintf(sp, " (mask %#x)", (u_int)mask);
+ }
+ }
+
+ return s;
+#undef NUM_BUFS
+}
+
+
+/* display a bit-field
+ */
+struct bits {
+ int bits_mask;
+ int bits_clear;
+ char *bits_name;
+};
+
+static struct bits if_bits[] = {
+ { IFF_LOOPBACK, 0, "LOOPBACK" },
+ { IFF_POINTOPOINT, 0, "PT-TO-PT" },
+ { 0, 0, 0}
+};
+
+static struct bits is_bits[] = {
+ { IS_ALIAS, 0, "ALIAS" },
+ { IS_SUBNET, 0, "" },
+ { IS_REMOTE, (IS_NO_RDISC
+ | IS_BCAST_RDISC), "REMOTE" },
+ { IS_PASSIVE, (IS_NO_RDISC
+ | IS_NO_RIP
+ | IS_NO_SUPER_AG
+ | IS_PM_RDISC
+ | IS_NO_AG), "PASSIVE" },
+ { IS_EXTERNAL, 0, "EXTERNAL" },
+ { IS_CHECKED, 0, "" },
+ { IS_ALL_HOSTS, 0, "" },
+ { IS_ALL_ROUTERS, 0, "" },
+ { IS_DISTRUST, 0, "DISTRUST" },
+ { IS_BROKE, IS_SICK, "BROKEN" },
+ { IS_SICK, 0, "SICK" },
+ { IS_DUP, 0, "DUPLICATE" },
+ { IS_REDIRECT_OK, 0, "REDIRECT_OK" },
+ { IS_NEED_NET_SYN, 0, "" },
+ { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" },
+ { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" },
+ { (IS_NO_RIPV1_IN
+ | IS_NO_RIPV2_IN
+ | IS_NO_RIPV1_OUT
+ | IS_NO_RIPV2_OUT), 0, "NO_RIP" },
+ { (IS_NO_RIPV1_IN
+ | IS_NO_RIPV1_OUT), 0, "RIPV2" },
+ { IS_NO_RIPV1_IN, 0, "NO_RIPV1_IN" },
+ { IS_NO_RIPV2_IN, 0, "NO_RIPV2_IN" },
+ { IS_NO_RIPV1_OUT, 0, "NO_RIPV1_OUT" },
+ { IS_NO_RIPV2_OUT, 0, "NO_RIPV2_OUT" },
+ { (IS_NO_ADV_IN
+ | IS_NO_SOL_OUT
+ | IS_NO_ADV_OUT), IS_BCAST_RDISC, "NO_RDISC" },
+ { IS_NO_SOL_OUT, 0, "NO_SOLICIT" },
+ { IS_SOL_OUT, 0, "SEND_SOLICIT" },
+ { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" },
+ { IS_ADV_OUT, 0, "RDISC_ADV" },
+ { IS_BCAST_RDISC, 0, "BCAST_RDISC" },
+ { IS_PM_RDISC, 0, "" },
+ { 0, 0, "%#x"}
+};
+
+static struct bits rs_bits[] = {
+ { RS_IF, 0, "IF" },
+ { RS_NET_INT, RS_NET_SYN, "NET_INT" },
+ { RS_NET_SYN, 0, "NET_SYN" },
+ { RS_SUBNET, 0, "" },
+ { RS_LOCAL, 0, "LOCAL" },
+ { RS_MHOME, 0, "MHOME" },
+ { RS_STATIC, 0, "STATIC" },
+ { RS_RDISC, 0, "RDISC" },
+ { 0, 0, "%#x"}
+};
+
+
+static void
+trace_bits(struct bits *tbl,
+ u_int field,
+ int force)
+{
+ int b;
+ char c;
+
+ if (force) {
+ (void)putc('<', ftrace);
+ c = 0;
+ } else {
+ c = '<';
+ }
+
+ while (field != 0
+ && (b = tbl->bits_mask) != 0) {
+ if ((b & field) == b) {
+ if (tbl->bits_name[0] != '\0') {
+ if (c)
+ (void)putc(c, ftrace);
+ (void)fprintf(ftrace, "%s", tbl->bits_name);
+ c = '|';
+ }
+ if (0 == (field &= ~(b | tbl->bits_clear)))
+ break;
+ }
+ tbl++;
+ }
+ if (field != 0 && tbl->bits_name != 0) {
+ if (c)
+ (void)putc(c, ftrace);
+ (void)fprintf(ftrace, tbl->bits_name, field);
+ c = '|';
+ }
+
+ if (c != '<' || force)
+ (void)fputs("> ", ftrace);
+}
+
+
+static char *
+trace_pair(naddr dst,
+ naddr mask,
+ char *gate)
+{
+ static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */
+ +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */
+ int i;
+
+ i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0));
+ (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), gate);
+ return buf;
+}
+
+
+static void
+print_rts(struct rt_spare *rts,
+ int force_metric, /* -1=suppress, 0=default */
+ int force_ifp, /* -1=suppress, 0=default */
+ int force_router, /* -1=suppress, 0=default, 1=display */
+ int force_tag, /* -1=suppress, 0=default, 1=display */
+ int force_time) /* 0=suppress, 1=display */
+{
+ if (force_metric >= 0)
+ (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
+ if (force_ifp >= 0)
+ (void)fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ?
+ "if?" : rts->rts_ifp->int_name));
+ if (force_router > 0
+ || (force_router == 0 && rts->rts_router != rts->rts_gate))
+ (void)fprintf(ftrace, "router=%s ",
+ naddr_ntoa(rts->rts_router));
+ if (force_time > 0)
+ (void)fprintf(ftrace, "%s ", ts(rts->rts_time));
+ if (force_tag > 0
+ || (force_tag == 0 && rts->rts_tag != 0))
+ (void)fprintf(ftrace, "tag=%#x ",
+ ntohs(rts->rts_tag));
+}
+
+
+void
+trace_if(char *act,
+ struct interface *ifp)
+{
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ lastlog();
+ (void)fprintf(ftrace, "%-3s interface %-4s ", act, ifp->int_name);
+ (void)fprintf(ftrace, "%-15s-->%-15s ",
+ naddr_ntoa(ifp->int_addr),
+ addrname(((ifp->int_if_flags & IFF_POINTOPOINT)
+ ? ifp->int_dstaddr
+ : htonl(ifp->int_net)),
+ ifp->int_mask, 1));
+ if (ifp->int_metric != 0)
+ (void)fprintf(ftrace, "metric=%d ", ifp->int_metric);
+ if (!IS_RIP_OUT_OFF(ifp->int_state)
+ && ifp->int_d_metric != 0)
+ (void)fprintf(ftrace, "fake_default=%d ", ifp->int_d_metric);
+ trace_bits(if_bits, ifp->int_if_flags, 0);
+ trace_bits(is_bits, ifp->int_state, 0);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_upslot(struct rt_entry *rt,
+ struct rt_spare *rts,
+ naddr gate,
+ naddr router,
+ struct interface *ifp,
+ int metric,
+ u_short tag,
+ time_t new_time)
+{
+ struct rt_spare new;
+
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ if (rts->rts_gate == gate
+ && rts->rts_router == router
+ && rts->rts_metric == metric
+ && rts->rts_tag == tag)
+ return;
+ new.rts_ifp = ifp;
+ new.rts_gate = gate;
+ new.rts_router = router;
+ new.rts_metric = metric;
+ new.rts_time = new_time;
+ new.rts_tag = tag;
+
+ lastlog();
+ if (rts->rts_gate != RIP_DEFAULT) {
+ (void)fprintf(ftrace, "Chg #%d %-35s ",
+ rts - rt->rt_spares,
+ trace_pair(rt->rt_dst, rt->rt_mask,
+ naddr_ntoa(rts->rts_gate)));
+ print_rts(rts, 0,0,
+ rts->rts_gate != gate,
+ rts->rts_tag != tag,
+ rts != rt->rt_spares || AGE_RT(rt->rt_state,
+ rt->rt_ifp));
+
+ (void)fprintf(ftrace, "\n %19s%-16s ", "",
+ gate != rts->rts_gate ? naddr_ntoa(gate) : "");
+ print_rts(&new,
+ -(metric == rts->rts_metric),
+ -(ifp == rts->rts_ifp),
+ 0,
+ rts->rts_tag != tag,
+ new_time != rts->rts_time && (rts != rt->rt_spares
+ || AGE_RT(rt->rt_state,
+ ifp)));
+
+ } else {
+ (void)fprintf(ftrace, "Add #%d %-35s ",
+ rts - rt->rt_spares,
+ trace_pair(rt->rt_dst, rt->rt_mask,
+ naddr_ntoa(gate)));
+ print_rts(&new, 0,0,0,0,
+ rts != rt->rt_spares || AGE_RT(rt->rt_state,ifp));
+ }
+ (void)fputc('\n',ftrace);
+}
+
+
+/* talk about a change made to the kernel table
+ */
+void
+trace_kernel(char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEKERNEL || ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+}
+
+
+/* display a message if tracing actions
+ */
+void
+trace_act(char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEACTIONS || ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ (void)fputc('\n',ftrace);
+}
+
+
+/* display a message if tracing packets
+ */
+void
+trace_pkt(char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ vfprintf(ftrace, p, args);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_change(struct rt_entry *rt,
+ u_int state,
+ naddr gate, /* forward packets here */
+ naddr router, /* on the authority of this router */
+ int metric,
+ u_short tag,
+ struct interface *ifp,
+ time_t new_time,
+ char *label)
+{
+ struct rt_spare new;
+
+ if (ftrace == 0)
+ return;
+
+ if (rt->rt_metric == metric
+ && rt->rt_gate == gate
+ && rt->rt_router == router
+ && rt->rt_state == state
+ && rt->rt_tag == tag)
+ return;
+ new.rts_ifp = ifp;
+ new.rts_gate = gate;
+ new.rts_router = router;
+ new.rts_metric = metric;
+ new.rts_time = new_time;
+ new.rts_tag = tag;
+
+ lastlog();
+ (void)fprintf(ftrace, "%s %-35s ",
+ label,
+ trace_pair(rt->rt_dst, rt->rt_mask,
+ naddr_ntoa(rt->rt_gate)));
+ print_rts(rt->rt_spares,
+ 0,0,0,0, AGE_RT(rt->rt_state, rt->rt_ifp));
+ trace_bits(rs_bits, rt->rt_state, rt->rt_state != state);
+
+ (void)fprintf(ftrace, "\n%*s %19s%-16s ",
+ strlen(label), "", "",
+ rt->rt_gate != gate ? naddr_ntoa(gate) : "");
+ print_rts(&new,
+ -(metric == rt->rt_metric),
+ -(ifp == rt->rt_ifp),
+ 0,
+ rt->rt_tag != tag,
+ rt->rt_time != new_time && AGE_RT(rt->rt_state,ifp));
+ if (rt->rt_state != state)
+ trace_bits(rs_bits, state, 1);
+ (void)fputc('\n',ftrace);
+}
+
+
+void
+trace_add_del(char * action, struct rt_entry *rt)
+{
+ if (ftrace == 0)
+ return;
+
+ lastlog();
+ (void)fprintf(ftrace, "%s %-35s ",
+ action,
+ trace_pair(rt->rt_dst, rt->rt_mask,
+ naddr_ntoa(rt->rt_gate)));
+ print_rts(rt->rt_spares, 0,0,0,0,AGE_RT(rt->rt_state,rt->rt_ifp));
+ trace_bits(rs_bits, rt->rt_state, 0);
+ (void)fputc('\n',ftrace);
+}
+
+
+/* ARGSUSED */
+static int
+walk_trace(struct radix_node *rn,
+ struct walkarg *w)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i, age = AGE_RT(RT->rt_state, RT->rt_ifp);
+
+ (void)fprintf(ftrace, " %-35s ", trace_pair(RT->rt_dst, RT->rt_mask,
+ naddr_ntoa(RT->rt_gate)));
+ print_rts(&RT->rt_spares[0], 0,0,0,0,age);
+ trace_bits(rs_bits, RT->rt_state, 0);
+ if (RT->rt_poison_time >= now_garbage
+ && RT->rt_poison_metric < RT->rt_metric)
+ (void)fprintf(ftrace, "pm=%d@%s",
+ RT->rt_poison_metric,
+ ts(RT->rt_poison_time));
+
+ rts = &RT->rt_spares[1];
+ for (i = 1; i < NUM_SPARES; i++, rts++) {
+ if (rts->rts_gate != RIP_DEFAULT) {
+ (void)fprintf(ftrace,"\n #%d%15s%-16s ",
+ i, "", naddr_ntoa(rts->rts_gate));
+ print_rts(rts, 0,0,0,0,1);
+ }
+ }
+ (void)fputc('\n',ftrace);
+
+ return 0;
+}
+
+
+static void
+trace_dump(void)
+{
+ struct interface *ifp;
+
+ if (ftrace == 0)
+ return;
+ lastlog();
+
+ (void)fputs("current daemon state:\n", ftrace);
+ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next)
+ trace_if("", ifp);
+ (void)rn_walktree(rhead, walk_trace, 0);
+}
+
+
+void
+trace_rip(char *dir1, char *dir2,
+ struct sockaddr_in *who,
+ struct interface *ifp,
+ struct rip *msg,
+ int size) /* total size of message */
+{
+ struct netinfo *n, *lim;
+# define NA (msg->rip_auths)
+ int i, seen_route;
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+ if (msg->rip_cmd >= RIPCMD_MAX
+ || msg->rip_vers == 0) {
+ (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s"
+ " %s.%d size=%d\n",
+ dir1, msg->rip_vers, msg->rip_cmd, dir2,
+ naddr_ntoa(who->sin_addr.s_addr),
+ ntohs(who->sin_port),
+ size);
+ return;
+ }
+
+ (void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
+ dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
+ naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
+ ifp ? " via " : "", ifp ? ifp->int_name : "");
+ if (!TRACECONTENTS)
+ return;
+
+ seen_route = 0;
+ switch (msg->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ n = msg->rip_nets;
+ lim = (struct netinfo *)((char*)msg + size);
+ for (; n < lim; n++) {
+ if (!seen_route
+ && n->n_family == RIP_AF_UNSPEC
+ && ntohl(n->n_metric) == HOPCNT_INFINITY
+ && msg->rip_cmd == RIPCMD_REQUEST
+ && (n+1 == lim
+ || (n+2 == lim
+ && (n+1)->n_family == RIP_AF_AUTH))) {
+ (void)fputs("\tQUERY ", ftrace);
+ if (n->n_dst != 0)
+ (void)fprintf(ftrace, "%s ",
+ naddr_ntoa(n->n_dst));
+ if (n->n_mask != 0)
+ (void)fprintf(ftrace, "mask=%#x ",
+ (u_int)ntohl(n->n_mask));
+ if (n->n_nhop != 0)
+ (void)fprintf(ftrace, "nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void)fprintf(ftrace, "tag=%#x ",
+ ntohs(n->n_tag));
+ (void)fputc('\n',ftrace);
+ continue;
+ }
+
+ if (n->n_family == RIP_AF_AUTH) {
+ if (NA->a_type == RIP_AUTH_PW
+ && n == msg->rip_nets) {
+ (void)fprintf(ftrace, "\tPassword"
+ " Authentication:"
+ " \"%s\"\n",
+ qstring(NA->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (NA->a_type == RIP_AUTH_MD5
+ && n == msg->rip_nets) {
+ (void)fprintf(ftrace,
+ "\tMD5 Authentication"
+ " len=%d KeyID=%u"
+ " seqno=%u"
+ " rsvd=%#x,%#x\n",
+ NA->au.a_md5.md5_pkt_len,
+ NA->au.a_md5.md5_keyid,
+ NA->au.a_md5.md5_seqno,
+ NA->au.a_md5.rsvd[0],
+ NA->au.a_md5.rsvd[1]);
+ continue;
+ }
+ (void)fprintf(ftrace,
+ "\tAuthentication"
+ " type %d: ",
+ ntohs(NA->a_type));
+ for (i = 0;
+ i < sizeof(NA->au.au_pw);
+ i++)
+ (void)fprintf(ftrace, "%02x ",
+ NA->au.au_pw[i]);
+ (void)fputc('\n',ftrace);
+ continue;
+ }
+
+ seen_route = 1;
+ if (n->n_family != RIP_AF_INET) {
+ (void)fprintf(ftrace,
+ "\t(af %d) %-18s mask=%#x ",
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst),
+ (u_int)ntohl(n->n_mask));
+ } else if (msg->rip_vers == RIPv1) {
+ (void)fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask==0 ? 2 : 1));
+ } else {
+ (void)fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask==0 ? 2 : 0));
+ }
+ (void)fprintf(ftrace, "metric=%-2d ",
+ (u_int)ntohl(n->n_metric));
+ if (n->n_nhop != 0)
+ (void)fprintf(ftrace, " nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void)fprintf(ftrace, "tag=%#x",
+ ntohs(n->n_tag));
+ (void)fputc('\n',ftrace);
+ }
+ if (size != (char *)n - (char *)msg)
+ (void)fprintf(ftrace, "truncated record, len %d\n",
+ size);
+ break;
+
+ case RIPCMD_TRACEON:
+ fprintf(ftrace, "\tfile=\"%.*s\"\n", size-4,
+ msg->rip_tracefile);
+ break;
+
+ case RIPCMD_TRACEOFF:
+ break;
+ }
+}
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..216cd7f
--- /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$
+.\"
+.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..d78a55a
--- /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")) != -1)
+ 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..dc9f6b2
--- /dev/null
+++ b/sbin/scsi/scsi.8
@@ -0,0 +1,300 @@
+.\"
+.\" 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.10 1997/03/19 22:42:02 mpp Exp $
+.\"
+.Dd October 11, 1993
+.Dt SCSI 8
+.Os BSD 4
+.Sh NAME
+.Nm scsi
+.Nd program to assist with scsi devices
+.Sh SYNOPSIS
+.Nm scsi
+.Fl f Ar device
+.Fl d Ar debug_level
+.Nm scsi
+.Fl f Ar device
+.Fl z Ar seconds
+.Op Fl v
+.Nm scsi
+.Fl f Ar device
+.Fl m Ar page
+.Op Fl P Ar pc
+.Op Fl e
+.Nm scsi
+.Fl f Ar device
+.Fl p
+.Op Fl b Ar bus
+.Op Fl l Ar lun
+.Nm scsi
+.Fl f Ar device
+.Fl r
+.Op Fl b Ar bus
+.Op Fl t Ar targ
+.Op Fl l Ar lun
+.Nm scsi
+.Fl f Ar device
+.Fl c Ar cmd_fmt
+.Op Ar arg0 ... argn
+.Op Fl s Ar seconds
+.Op Fl v
+.Fl o Ar count out_fmt
+.Op Ar arg0 ... argn
+.Fl i Ar count in_fmt
+.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
+.Fl d
+option sets the SCSI kernel debug level. The kernel must have been compiled
+with the
+.Dv SCSIDEBUG
+option. See
+.Pa /sys/scsi/scsi_debug.h
+to figure out what to set the kernel debug level to.
+.Pp
+The
+.Fl z
+option freezes all activity on all SCSI busses for a given number of
+seconds. If
+.Fl 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
+.Dv SCSI_FREEZE
+kernel option.
+This kernel code is not committed yet.
+.Pp
+The
+.Fl m
+option is used to read a device mode page. The file
+.Pa /usr/share/misc/scsi_modes
+is read to look at for how to interpret the mode data. The environment
+variable
+.Ev SCSI_MODES
+can specify a different file to use.
+.Pp
+The
+.Fl P
+option can be used to specify a page control field. The page control
+fields are:
+.Pp
+.Bl -tag -width xxxx -indent offset -compact
+.It 0
+Current Values
+.It 1
+Changeable Values
+.It 2
+Default Values
+.It 3
+Saved Values
+.El
+.Pp
+The
+.Fl e
+option permits you to edit the fields. It will use the editor specified
+by your
+.Ev EDITOR
+environment variable. To store changes permanently,
+edit page control 3 using the
+.Fl P
+flag.
+.Pp
+The
+.Fl p
+option can be used against the "super scsi" device
+.Pa /dev/scsi/super
+to probe all devices with a given SCSI lun on a given SCSI bus.
+The bus can be selected with the
+.Fl b
+option and the default is 0.
+The lun can be selected with the
+.Fl l
+option and the default is 0.
+See
+.Xr scsi 4
+for a description of the "super scsi" device.
+.Pp
+The
+.Fl 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
+.Fl p
+option should bring on line any newly found devices.
+See
+.Xr scsi 4
+for a description of fixed scsi devices.
+.Pp
+The
+.Fl c
+option permits you to send user level SCSI commands specified on
+the command line to a
+device. The command is sent using the
+.Dv 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.
+.Pp
+.Fl v
+turns on more verbose information.
+.Pp
+.Fl s
+sets the command timeout in seconds. The default is two seconds.
+.Pp
+.Fl c Ar 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
+.Fl o
+.Ar count
+.Ar out_fmt
+.Op Ar 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.
+.Ar out_fmt
+can be specified as a hyphen ("-") to indicate that the
+.Ar count
+bytes of data should be read from the standard input.
+.Pp
+.Fl i Ar count Ar in_fmt
+indicates that this is a data in command (i.e., data will be read from
+the device into the system) with
+.Ar count
+bytes of data read in. The information is extracted according to
+.Ar in_fmt
+using the facilities described in
+.Xr scsi 3
+and displayed on the standard output.
+.Ar in_fmt
+can be specified as a hyphen ("-") to indicate that the
+.Ar count
+bytes of data input should be written to the standard output.
+.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
+.Ev SU_DEBUG_OUTPUT
+variable can be set to a file to send debugging
+output to that file.
+.Pp
+The
+.Ev 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
+.Ev 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
+.Ev EDITOR
+variable determines the editor to use for the mode editor.
+.Sh SEE ALSO
+.Xr scsi 3 ,
+.Xr scsi 4
+.Sh BUGS
+.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
+.Fl i
+option to do an inquiry went away in
+.Fx 2.1 .
+The new facilities
+provided by
+.Fl c
+supercede that.
+.Pp
+Check your permissions carefully.
+.Ql scsi -f /dev/rsd0c -c "4 0 0 0 0 0
+permits anyone who can open
+.Pa /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 to support the new reprobe
+and user SCSI commands. It first appeared in
+.Tn FreeBSD
+in
+.Fx 2.0.5 .
diff --git a/sbin/scsi/scsi.c b/sbin/scsi/scsi.c
new file mode 100644
index 0000000..fdbb901
--- /dev/null
+++ b/sbin/scsi/scsi.c
@@ -0,0 +1,958 @@
+/*
+ * 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.15 1997/03/29 03:33:04 imp 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>
+#include <err.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)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: scsi -f device -d debug_level",
+ " scsi -f device [-v] -z seconds",
+ " scsi -f device -m page [-P pc] [-e]",
+ " scsi -f device -p [-b bus] [-l lun]",
+ " scsi -f device -r [-b bus] [-t targ] [-l lun]",
+ " scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn]",
+ " -o count out_fmt [arg0 ... argn]",
+ " -i count in_fmt");
+ 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:")) != -1) {
+ 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)
+ err(errno, "unable to open device %s", optarg);
+ 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)
+{
+ 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) {
+ 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..ba15f65
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.8
@@ -0,0 +1,109 @@
+.\" 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: scsiformat.8,v 1.7 1997/02/22 14:33:17 peter Exp $
+.\"
+.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 qyw
+.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 y
+Yes; do format without asking any question. If this option is not
+provided,
+.Nm scsiformat
+will ask back before actually starting to format the device. At this
+point, hitting the interrupt key (normally Control-C) will abort the
+program.
+.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..8039255
--- /dev/null
+++ b/sbin/scsiformat/scsiformat.sh
@@ -0,0 +1,144 @@
+#!/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.4 1997/02/22 14:33:17 peter Exp $
+#
+
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"; export PATH
+
+READONLY=yes
+DOIT=no
+QUIET=no
+RAW=
+PAGE=0
+
+usage()
+{
+ echo "usage: scsiformat [-qyw] [-p page-control] raw-device-name" 1>&2
+ exit 2
+}
+
+while getopts "qwyp:" option
+do
+ case $option in
+ q)
+ QUIET=yes
+ ;;
+ y)
+ DOIT=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
+ if [ "$DOIT" != "yes" ]
+ then
+ echo "This will destroy all data on this drive!"
+ echo -n "Hit return to continue, or INTR (^C) to abort: "
+ read dummy
+ fi
+ # 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..f892fcf
--- /dev/null
+++ b/sbin/shutdown/shutdown.c
@@ -0,0 +1,455 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 <err.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())
+ errx(1, "NOT super-user");
+#endif
+ nosync = NULL;
+ readstdin = 0;
+ while ((ch = getopt(argc, argv, "-hknr")) != -1)
+ 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 (doreboot && dohalt) {
+ warnx("incompatible switches -h and -r.");
+ 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)
+ err(1, "fork");
+ if (forkpid)
+ errx(0, "[pid %d]", forkpid);
+ }
+#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)
+ errx(1, "that time is already past.");
+ 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()
+{
+ errx(1, "bad time format.");
+}
+
+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..e0e1a0a
--- /dev/null
+++ b/sbin/slattach/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 5.4 (Berkeley) 5/11/90
+#
+# $Header: /home/ncvs/src/sbin/slattach/Makefile,v 1.6 1995/09/20 12:56:23 ache Exp $
+
+PROG= slattach
+SRCS= slattach.c
+MAN8= slattach.8
+MLINKS= slattach.8 slip.8
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/slattach/slattach.8 b/sbin/slattach/slattach.8
new file mode 100644
index 0000000..9afa051
--- /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.12 1997/01/13 00:25:46 wosch 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 order to send a signal to
+.Nm slattach .
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr startslip 1 ,
+.Xr uustat 1 ,
+.Xr netintro 4 ,
+.Xr ifconfig 8 ,
+.Xr rc 8 ,
+.Xr sliplogin 8 ,
+/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..085b5c4
--- /dev/null
+++ b/sbin/slattach/slattach.c
@@ -0,0 +1,596 @@
+/*
+ * 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.27 1997/03/31 22:50:10 brian 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 <libutil.h>
+#include <err.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_var.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 */
+static void usage __P((void));
+
+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 void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: slattach [-acfhlnz] [-e command] [-r command] [-s speed] [-u command]",
+" [-L] [-K timeout] [-O timeout] [-S unit] device");
+ /* do not exit here */
+}
+
+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:")) != -1) {
+ 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;
+ case '?':
+ default:
+ usage();
+ exit_handler(1);
+ }
+ }
+
+ if (optind == argc - 1)
+ dev = argv[optind];
+
+ if (optind < (argc - 1))
+ warnx("too many args, first='%s'", argv[optind]);
+ if (optind > (argc - 1))
+ warnx("not enough args");
+ if (dev == (char *)0) {
+ usage();
+ 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 */
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ 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) {
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ 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..26a8aaa
--- /dev/null
+++ b/sbin/startslip/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= startslip
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/sbin/startslip/startslip.1 b/sbin/startslip/startslip.1
new file mode 100644
index 0000000..7b39ca7
--- /dev/null
+++ b/sbin/startslip/startslip.1
@@ -0,0 +1,201 @@
+.\" 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
+.\" $Id$
+.\"
+.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 uustat 1 ,
+.Xr slattach 8 ,
+.Xr sliplogin 8 ,
+/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..aa92219
--- /dev/null
+++ b/sbin/startslip/startslip.c
@@ -0,0 +1,604 @@
+/*-
+ * 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.24 1997/03/31 22:50:30 brian 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 <libutil.h>
+#include <err.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_var.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
+
+int carrier __P((void));
+void down __P((int));
+int getline __P((char *, int, int, time_t));
+static void usage __P((void));
+
+int
+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")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ speed = atoi(optarg);
+ break;
+ case 's':
+ if (diali >= MAXDIALS)
+ errx(1, "max dial strings number (%d) exceeded", MAXDIALS);
+ 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) {
+ int res;
+ if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
+ 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;
+ return(0); /* not reached */
+}
+
+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;
+}
+
+int
+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);
+}
+
+int
+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);
+}
+
+void
+down(code)
+{
+ if (fd > -1)
+ close(fd);
+ if (pfd)
+ unlink(pidfile);
+ if (uucp_lock && locked)
+ uu_unlock(dvname);
+ exit(code);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: startslip [-d] [-b speed] [-s string1 [-s string2 [...]]] [-h] [-l]",
+" [-L] [-A annexname] [-U upscript] [-D downscript]",
+" [-t script_timeout] [-W maxtries] [-w retry_pause]",
+" [-K keepalive] [-O outfill] [-S unit] device user passwd");
+ exit(1);
+}
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..eb7e643
--- /dev/null
+++ b/sbin/swapon/swapon.c
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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>
+#include <string.h>
+#include <err.h>
+
+static 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")) != -1)
+ 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)
+ warnx("%s: device already in use", name);
+ break;
+ default:
+ warn("%s", name);
+ break;
+ }
+ return(1);
+ }
+ return(0);
+}
+
+static 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..8dcaf42
--- /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")) != -1) {
+ 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..9265995
--- /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$
+.\"
+.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..b1629f0
--- /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.12 1997/02/22 16:13:58 peter 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")) != -1) {
+ 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..1802f22
--- /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..ec628cd
--- /dev/null
+++ b/sbin/umount/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.4 (Berkeley) 6/22/95
+
+PROG= umount
+SRCS= umount.c vfslist.c
+MAN8= umount.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH: ${MOUNT}
+
+CFLAGS+= -D_NEW_VFSCONF
+
+.include <bsd.prog.mk>
diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8
new file mode 100644
index 0000000..a2742c5
--- /dev/null
+++ b/sbin/umount/umount.8
@@ -0,0 +1,148 @@
+.\" 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.2 (Berkeley) 5/8/95
+.\"
+.Dd May 8, 1995
+.Dt UMOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm umount
+.Nd unmount filesystems
+.Sh SYNOPSIS
+.Nm umount
+.Op Fl fv
+.Ar special | node
+.Nm umount
+.Fl a | A
+.Op Fl fv
+.Op Fl h Ar host
+.Op Fl t Ar 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 filesystem 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 the filesystems described in
+.Xr fstab 5
+are unmounted.
+.It Fl A
+All the currently mounted filesystems except
+the root are unmounted.
+.It Fl f
+The filesystem is forcibly unmounted.
+Active special devices continue to work,
+but all other files return errors if further accesses are attempted.
+The root filesystem 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 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 filesystem
+is unmounted.
+.El
+.Sh FILES
+.Bl -tag -width /etc/fstab -compact
+.It Pa /etc/fstab
+filesystem 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..f4951e4
--- /dev/null
+++ b/sbin/umount/umount.c
@@ -0,0 +1,384 @@
+/*-
+ * 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.8 (Berkeley) 5/8/95";
+#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;
+char *nfshost;
+
+int checkvfsname __P((const char *, char **));
+char *getmntname __P((char *, mntwhat, char **));
+char **makevfslist __P((char *));
+int selected __P((int));
+int namematch __P((struct hostent *));
+int umountall __P((char **));
+int umountfs __P((char *, char **));
+void usage __P((void));
+int xdr_dir __P((XDR *, char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int all, ch, errs, mnts;
+ char **typelist = NULL;
+ struct statfs *mntbuf;
+
+ /* Start disks transferring immediately. */
+ sync();
+
+ all = 0;
+ while ((ch = getopt(argc, argv, "AaFfh:t:v")) != -1)
+ switch (ch) {
+ case 'A':
+ all = 2;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ case 'F':
+ fake = 1;
+ break;
+ case 'f':
+ fflag = MNT_FORCE;
+ break;
+ case 'h': /* -h implies -A. */
+ all = 2;
+ nfshost = optarg;
+ break;
+ case 't':
+ if (typelist != NULL)
+ errx(1, "only one -t option may be specified.");
+ typelist = makevfslist(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))
+ typelist = makevfslist("nfs");
+
+ switch (all) {
+ case 2:
+ if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
+ warn("getmntinfo");
+ errs = 1;
+ break;
+ }
+ for (errs = 0, mnts--; mnts > 0; mnts--) {
+ if (checkvfsname(mntbuf[mnts].f_fstypename, typelist))
+ continue;
+ if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0)
+ errs = 1;
+ }
+ break;
+ case 1:
+ if (setfsent() == 0)
+ err(1, "%s", _PATH_FSTAB);
+ errs = umountall(typelist);
+ break;
+ case 0:
+ for (errs = 0; *argv != NULL; ++argv)
+ if (umountfs(*argv, typelist) != 0)
+ errs = 1;
+ break;
+ }
+ exit(errs);
+}
+
+int
+umountall(typelist)
+ char **typelist;
+{
+ struct fstab *fs;
+ int rval, type;
+ char *cp;
+ struct vfsconf vfc;
+
+ 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 (getvfsbyname(fs->fs_vfstype, &vfc) < 0) {
+ warnx("%s: unknown mount type", fs->fs_vfstype);
+ continue;
+ }
+ if (checkvfsname(fs->fs_vfstype, typelist))
+ 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(typelist);
+ return (umountfs(cp, typelist) || rval);
+ }
+ return (0);
+}
+
+int
+umountfs(name, typelist)
+ char *name;
+ char **typelist;
+{
+ enum clnt_stat clnt_stat;
+ struct hostent *hp;
+ struct sockaddr_in saddr;
+ struct stat sb;
+ struct timeval pertry, try;
+ CLIENT *clp;
+ int so;
+ char *type, *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 (checkvfsname(type, typelist))
+ return (1);
+
+ hp = NULL;
+ if (!strcmp(type, "nfs")) {
+ 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 = ':';
+ }
+ }
+
+ if (!namematch(hp))
+ return (1);
+
+ 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;
+ char **type;
+{
+ static struct statfs *mntbuf;
+ static int mntsize;
+ int i;
+
+ if (mntbuf == NULL &&
+ (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_fstypename;
+ return (mntbuf[i].f_mntonname);
+ }
+ if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) {
+ if (type)
+ *type = mntbuf[i].f_fstypename;
+ return (mntbuf[i].f_mntfromname);
+ }
+ }
+ return (NULL);
+}
+
+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, "%s\n%s\n",
+ "usage: umount [-fv] special | node",
+ " umount -a | -A [-fv] [-h host] [-t type]");
+ exit(1);
+}
OpenPOWER on IntegriCloud