From 8fae3551ec46402adf7ab034cf9e02bcbc7ca8ee Mon Sep 17 00:00:00 2001 From: "Rodney W. Grimes" Date: Thu, 26 May 1994 06:35:07 +0000 Subject: BSD 4.4 Lite sbin Sources Note: XNSrouted and routed NOT imported here, they shall be imported with usr.sbin. --- sbin/Makefile | 10 + sbin/Makefile.inc | 3 + sbin/badsect/Makefile | 6 + sbin/badsect/badsect.8 | 132 +++ sbin/badsect/badsect.c | 209 ++++ sbin/bsdlabel/Makefile | 14 + sbin/bsdlabel/bsdlabel.8 | 343 ++++++ sbin/bsdlabel/bsdlabel.c | 1314 +++++++++++++++++++++ sbin/bsdlabel/disklabel.5.5 | 384 ++++++ sbin/bsdlabel/dkcksum.c | 53 + sbin/bsdlabel/pathnames.h | 40 + sbin/clri/Makefile | 6 + sbin/clri/clri.8 | 79 ++ sbin/clri/clri.c | 140 +++ sbin/disklabel/Makefile | 14 + sbin/disklabel/disklabel.5.5 | 384 ++++++ sbin/disklabel/disklabel.8 | 343 ++++++ sbin/disklabel/disklabel.c | 1314 +++++++++++++++++++++ sbin/disklabel/dkcksum.c | 53 + sbin/disklabel/pathnames.h | 40 + sbin/dmesg/Makefile | 10 + sbin/dmesg/dmesg.8 | 63 + sbin/dmesg/dmesg.c | 157 +++ sbin/dump/Makefile | 25 + sbin/dump/dump.8 | 335 ++++++ sbin/dump/dump.h | 212 ++++ sbin/dump/dumprmt.c | 380 ++++++ sbin/dump/itime.c | 271 +++++ sbin/dump/main.c | 560 +++++++++ sbin/dump/optr.c | 538 +++++++++ sbin/dump/pathnames.h | 42 + sbin/dump/tape.c | 859 ++++++++++++++ sbin/dump/traverse.c | 613 ++++++++++ sbin/dump/unctime.c | 157 +++ sbin/dumpfs/Makefile | 6 + sbin/dumpfs/dumpfs.8 | 62 + sbin/dumpfs/dumpfs.c | 315 +++++ sbin/dumplfs/Makefile | 9 + sbin/dumplfs/dumplfs.8 | 59 + sbin/dumplfs/dumplfs.c | 612 ++++++++++ sbin/dumplfs/extern.h | 39 + sbin/dumplfs/misc.c | 90 ++ sbin/fastboot/Makefile | 12 + sbin/fastboot/fastboot.8 | 69 ++ sbin/fastboot/fastboot.sh | 38 + sbin/fastboot/fasthalt.sh | 38 + sbin/fsck/Makefile | 9 + sbin/fsck/SMM.doc/0.t | 150 +++ sbin/fsck/SMM.doc/1.t | 83 ++ sbin/fsck/SMM.doc/2.t | 265 +++++ sbin/fsck/SMM.doc/3.t | 439 +++++++ sbin/fsck/SMM.doc/4.t | 1424 +++++++++++++++++++++++ sbin/fsck/SMM.doc/Makefile | 7 + sbin/fsck/dir.c | 681 +++++++++++ sbin/fsck/fsck.8 | 291 +++++ sbin/fsck/fsck.h | 215 ++++ sbin/fsck/inode.c | 543 +++++++++ sbin/fsck/main.c | 318 +++++ sbin/fsck/pass1.c | 314 +++++ sbin/fsck/pass1b.c | 99 ++ sbin/fsck/pass2.c | 430 +++++++ sbin/fsck/pass3.c | 71 ++ sbin/fsck/pass4.c | 133 +++ sbin/fsck/pass5.c | 319 +++++ sbin/fsck/preen.c | 354 ++++++ sbin/fsck/setup.c | 466 ++++++++ sbin/fsck/utilities.c | 566 +++++++++ sbin/fsck_ffs/Makefile | 9 + sbin/fsck_ffs/SMM.doc/0.t | 150 +++ sbin/fsck_ffs/SMM.doc/1.t | 83 ++ sbin/fsck_ffs/SMM.doc/2.t | 265 +++++ sbin/fsck_ffs/SMM.doc/3.t | 439 +++++++ sbin/fsck_ffs/SMM.doc/4.t | 1424 +++++++++++++++++++++++ sbin/fsck_ffs/SMM.doc/Makefile | 7 + sbin/fsck_ffs/dir.c | 681 +++++++++++ sbin/fsck_ffs/fsck.h | 215 ++++ sbin/fsck_ffs/fsck_ffs.8 | 291 +++++ sbin/fsck_ffs/inode.c | 543 +++++++++ sbin/fsck_ffs/main.c | 318 +++++ sbin/fsck_ffs/pass1.c | 314 +++++ sbin/fsck_ffs/pass1b.c | 99 ++ sbin/fsck_ffs/pass2.c | 430 +++++++ sbin/fsck_ffs/pass3.c | 71 ++ sbin/fsck_ffs/pass4.c | 133 +++ sbin/fsck_ffs/pass5.c | 319 +++++ sbin/fsck_ffs/preen.c | 354 ++++++ sbin/fsck_ffs/setup.c | 466 ++++++++ sbin/fsck_ffs/utilities.c | 566 +++++++++ sbin/fsck_ifs/Makefile | 9 + sbin/fsck_ifs/dir.c | 681 +++++++++++ sbin/fsck_ifs/fsck.h | 215 ++++ sbin/fsck_ifs/fsck_ifs.8 | 291 +++++ sbin/fsck_ifs/inode.c | 543 +++++++++ sbin/fsck_ifs/main.c | 318 +++++ sbin/fsck_ifs/pass1.c | 314 +++++ sbin/fsck_ifs/pass1b.c | 99 ++ sbin/fsck_ifs/pass2.c | 430 +++++++ sbin/fsck_ifs/pass3.c | 71 ++ sbin/fsck_ifs/pass4.c | 133 +++ sbin/fsck_ifs/pass5.c | 319 +++++ sbin/fsck_ifs/preen.c | 354 ++++++ sbin/fsck_ifs/setup.c | 466 ++++++++ sbin/fsck_ifs/utilities.c | 566 +++++++++ sbin/ifconfig/Makefile | 6 + sbin/ifconfig/ifconfig.8 | 275 +++++ sbin/ifconfig/ifconfig.c | 679 +++++++++++ sbin/init/Makefile | 11 + sbin/init/NOTES | 112 ++ sbin/init/init.8 | 291 +++++ sbin/init/init.c | 1297 +++++++++++++++++++++ sbin/init/pathnames.h | 42 + sbin/mknod/Makefile | 6 + sbin/mknod/mknod.8 | 99 ++ sbin/mknod/mknod.c | 82 ++ sbin/mount/Makefile | 8 + sbin/mount/getmntopts.3 | 163 +++ sbin/mount/getmntopts.c | 87 ++ sbin/mount/mntopts.h | 72 ++ sbin/mount/mount.8 | 264 +++++ sbin/mount/mount.c | 512 ++++++++ sbin/mount/mount_ufs.c | 131 +++ sbin/mount/pathnames.h | 38 + sbin/mount_cd9660/Makefile | 11 + sbin/mount_cd9660/mount_cd9660.8 | 99 ++ sbin/mount_cd9660/mount_cd9660.c | 130 +++ sbin/mount_fdesc/Makefile | 11 + sbin/mount_fdesc/mount_fdesc.8 | 171 +++ sbin/mount_fdesc/mount_fdesc.c | 100 ++ sbin/mount_ifs/Makefile | 8 + sbin/mount_ifs/getmntopts.3 | 163 +++ sbin/mount_ifs/getmntopts.c | 87 ++ sbin/mount_ifs/mntopts.h | 72 ++ sbin/mount_ifs/mount.8 | 264 +++++ sbin/mount_ifs/mount.c | 512 ++++++++ sbin/mount_ifs/mount_ufs.c | 131 +++ sbin/mount_ifs/pathnames.h | 38 + sbin/mount_kernfs/Makefile | 11 + sbin/mount_kernfs/mount_kernfs.8 | 131 +++ sbin/mount_kernfs/mount_kernfs.c | 100 ++ sbin/mount_lfs/Makefile | 11 + sbin/mount_lfs/mount_lfs.8 | 125 ++ sbin/mount_lfs/mount_lfs.c | 147 +++ sbin/mount_lfs/pathnames.h | 36 + sbin/mount_nfs/Makefile | 14 + sbin/mount_nfs/mount_nfs.8 | 222 ++++ sbin/mount_nfs/mount_nfs.c | 551 +++++++++ sbin/mount_null/Makefile | 11 + sbin/mount_null/mount_null.8 | 220 ++++ sbin/mount_null/mount_null.c | 129 ++ sbin/mount_nullfs/Makefile | 11 + sbin/mount_nullfs/mount_nullfs.8 | 220 ++++ sbin/mount_nullfs/mount_nullfs.c | 129 ++ sbin/mount_portal/Makefile | 15 + sbin/mount_portal/activate.c | 215 ++++ sbin/mount_portal/conf.c | 329 ++++++ sbin/mount_portal/mount_portal.8 | 136 +++ sbin/mount_portal/mount_portal.c | 261 +++++ sbin/mount_portal/pathnames.h | 44 + sbin/mount_portal/portal.conf | 7 + sbin/mount_portal/portald.h | 82 ++ sbin/mount_portal/pt_conf.c | 51 + sbin/mount_portal/pt_exec.c | 61 + sbin/mount_portal/pt_file.c | 106 ++ sbin/mount_portal/pt_tcp.c | 158 +++ sbin/mount_portalfs/Makefile | 15 + sbin/mount_portalfs/activate.c | 215 ++++ sbin/mount_portalfs/conf.c | 329 ++++++ sbin/mount_portalfs/mount_portalfs.8 | 136 +++ sbin/mount_portalfs/mount_portalfs.c | 261 +++++ sbin/mount_portalfs/pathnames.h | 44 + sbin/mount_portalfs/portal.conf | 7 + sbin/mount_portalfs/portald.h | 82 ++ sbin/mount_portalfs/pt_conf.c | 51 + sbin/mount_portalfs/pt_exec.c | 61 + sbin/mount_portalfs/pt_file.c | 106 ++ sbin/mount_portalfs/pt_tcp.c | 158 +++ sbin/mount_procfs/Makefile | 11 + sbin/mount_procfs/mount_procfs.8 | 253 ++++ sbin/mount_procfs/mount_procfs.c | 100 ++ sbin/mount_umap/Makefile | 11 + sbin/mount_umap/mount_umap.8 | 131 +++ sbin/mount_umap/mount_umap.c | 232 ++++ sbin/mount_umap/sample.group.mapfile | 2 + sbin/mount_umap/sample.user.mapfile | 3 + sbin/mount_umap/umap_manual | 175 +++ sbin/mount_umapfs/Makefile | 11 + sbin/mount_umapfs/mount_umapfs.8 | 131 +++ sbin/mount_umapfs/mount_umapfs.c | 232 ++++ sbin/mount_umapfs/sample.group.mapfile | 2 + sbin/mount_umapfs/sample.user.mapfile | 3 + sbin/mount_umapfs/umap_manual | 175 +++ sbin/mount_union/Makefile | 14 + sbin/mount_union/mount_union.8 | 204 ++++ sbin/mount_union/mount_union.c | 140 +++ sbin/mount_unionfs/Makefile | 14 + sbin/mount_unionfs/mount_unionfs.8 | 204 ++++ sbin/mount_unionfs/mount_unionfs.c | 140 +++ sbin/mountd/Makefile | 10 + sbin/mountd/exports.5 | 250 ++++ sbin/mountd/mountd.8 | 100 ++ sbin/mountd/mountd.c | 2005 ++++++++++++++++++++++++++++++++ sbin/mountd/netgroup.5 | 91 ++ sbin/mountd/pathnames.h | 39 + sbin/newfs/Makefile | 14 + sbin/newfs/mkfs.c | 1227 +++++++++++++++++++ sbin/newfs/newfs.8 | 282 +++++ sbin/newfs/newfs.c | 678 +++++++++++ sbin/newlfs/Makefile | 9 + sbin/newlfs/config.h | 134 +++ sbin/newlfs/extern.h | 45 + sbin/newlfs/lfs.c | 673 +++++++++++ sbin/newlfs/misc.c | 83 ++ sbin/newlfs/newfs.c | 466 ++++++++ sbin/newlfs/newlfs.8 | 95 ++ sbin/nfsd/Makefile | 9 + sbin/nfsd/nfsd.8 | 114 ++ sbin/nfsd/nfsd.c | 589 ++++++++++ sbin/nfsiod/Makefile | 7 + sbin/nfsiod/nfsiod.8 | 74 ++ sbin/nfsiod/nfsiod.c | 167 +++ sbin/nologin/Makefile | 14 + sbin/nologin/nologin.8 | 54 + sbin/nologin/nologin.sh | 38 + sbin/ping/Makefile | 8 + sbin/ping/ping.8 | 327 ++++++ sbin/ping/ping.c | 986 ++++++++++++++++ sbin/quotacheck/Makefile | 8 + sbin/quotacheck/preen.c | 354 ++++++ sbin/quotacheck/quotacheck.8 | 157 +++ sbin/quotacheck/quotacheck.c | 636 ++++++++++ sbin/reboot/Makefile | 18 + sbin/reboot/boot_hp300.8 | 117 ++ sbin/reboot/boot_i386.8 | 126 ++ sbin/reboot/boot_sparc.8 | 89 ++ sbin/reboot/boot_tahoe.8 | 152 +++ sbin/reboot/boot_vax.8 | 322 +++++ sbin/reboot/reboot.8 | 88 ++ sbin/reboot/reboot.c | 204 ++++ sbin/restore/Makefile | 17 + sbin/restore/dirs.c | 747 ++++++++++++ sbin/restore/extern.h | 108 ++ sbin/restore/interactive.c | 755 ++++++++++++ sbin/restore/main.c | 345 ++++++ sbin/restore/pathnames.h | 43 + sbin/restore/restore.8 | 405 +++++++ sbin/restore/restore.c | 819 +++++++++++++ sbin/restore/restore.h | 137 +++ sbin/restore/symtab.c | 629 ++++++++++ sbin/restore/tape.c | 1348 +++++++++++++++++++++ sbin/restore/utilities.c | 395 +++++++ sbin/route/Makefile | 25 + sbin/route/ccitt_addr.c | 175 +++ sbin/route/keywords | 44 + sbin/route/route.8 | 324 ++++++ sbin/route/route.c | 1415 ++++++++++++++++++++++ sbin/savecore/Makefile | 8 + sbin/savecore/savecore.8 | 124 ++ sbin/savecore/savecore.c | 649 +++++++++++ sbin/scsiformat/Makefile | 7 + sbin/scsiformat/scsiformat.8 | 82 ++ sbin/scsiformat/scsiformat.c | 664 +++++++++++ sbin/shutdown/Makefile | 9 + sbin/shutdown/pathnames.h | 41 + sbin/shutdown/shutdown.8 | 162 +++ sbin/shutdown/shutdown.c | 492 ++++++++ sbin/slattach/Makefile | 7 + sbin/slattach/slattach.8 | 90 ++ sbin/slattach/slattach.c | 173 +++ sbin/startslip/Makefile | 6 + sbin/startslip/startslip.1 | 105 ++ sbin/startslip/startslip.c | 452 +++++++ sbin/swapon/Makefile | 6 + sbin/swapon/swapon.8 | 90 ++ sbin/swapon/swapon.c | 120 ++ sbin/tunefs/Makefile | 6 + sbin/tunefs/tunefs.8 | 138 +++ sbin/tunefs/tunefs.c | 284 +++++ sbin/umount/Makefile | 7 + sbin/umount/umount.8 | 123 ++ sbin/umount/umount.c | 421 +++++++ 280 files changed, 65629 insertions(+) create mode 100644 sbin/Makefile create mode 100644 sbin/Makefile.inc create mode 100644 sbin/badsect/Makefile create mode 100644 sbin/badsect/badsect.8 create mode 100644 sbin/badsect/badsect.c create mode 100644 sbin/bsdlabel/Makefile create mode 100644 sbin/bsdlabel/bsdlabel.8 create mode 100644 sbin/bsdlabel/bsdlabel.c create mode 100644 sbin/bsdlabel/disklabel.5.5 create mode 100644 sbin/bsdlabel/dkcksum.c create mode 100644 sbin/bsdlabel/pathnames.h create mode 100644 sbin/clri/Makefile create mode 100644 sbin/clri/clri.8 create mode 100644 sbin/clri/clri.c create mode 100644 sbin/disklabel/Makefile create mode 100644 sbin/disklabel/disklabel.5.5 create mode 100644 sbin/disklabel/disklabel.8 create mode 100644 sbin/disklabel/disklabel.c create mode 100644 sbin/disklabel/dkcksum.c create mode 100644 sbin/disklabel/pathnames.h create mode 100644 sbin/dmesg/Makefile create mode 100644 sbin/dmesg/dmesg.8 create mode 100644 sbin/dmesg/dmesg.c create mode 100644 sbin/dump/Makefile create mode 100644 sbin/dump/dump.8 create mode 100644 sbin/dump/dump.h create mode 100644 sbin/dump/dumprmt.c create mode 100644 sbin/dump/itime.c create mode 100644 sbin/dump/main.c create mode 100644 sbin/dump/optr.c create mode 100644 sbin/dump/pathnames.h create mode 100644 sbin/dump/tape.c create mode 100644 sbin/dump/traverse.c create mode 100644 sbin/dump/unctime.c create mode 100644 sbin/dumpfs/Makefile create mode 100644 sbin/dumpfs/dumpfs.8 create mode 100644 sbin/dumpfs/dumpfs.c create mode 100644 sbin/dumplfs/Makefile create mode 100644 sbin/dumplfs/dumplfs.8 create mode 100644 sbin/dumplfs/dumplfs.c create mode 100644 sbin/dumplfs/extern.h create mode 100644 sbin/dumplfs/misc.c create mode 100644 sbin/fastboot/Makefile create mode 100644 sbin/fastboot/fastboot.8 create mode 100644 sbin/fastboot/fastboot.sh create mode 100644 sbin/fastboot/fasthalt.sh create mode 100644 sbin/fsck/Makefile create mode 100644 sbin/fsck/SMM.doc/0.t create mode 100644 sbin/fsck/SMM.doc/1.t create mode 100644 sbin/fsck/SMM.doc/2.t create mode 100644 sbin/fsck/SMM.doc/3.t create mode 100644 sbin/fsck/SMM.doc/4.t create mode 100644 sbin/fsck/SMM.doc/Makefile create mode 100644 sbin/fsck/dir.c create mode 100644 sbin/fsck/fsck.8 create mode 100644 sbin/fsck/fsck.h create mode 100644 sbin/fsck/inode.c create mode 100644 sbin/fsck/main.c create mode 100644 sbin/fsck/pass1.c create mode 100644 sbin/fsck/pass1b.c create mode 100644 sbin/fsck/pass2.c create mode 100644 sbin/fsck/pass3.c create mode 100644 sbin/fsck/pass4.c create mode 100644 sbin/fsck/pass5.c create mode 100644 sbin/fsck/preen.c create mode 100644 sbin/fsck/setup.c create mode 100644 sbin/fsck/utilities.c create mode 100644 sbin/fsck_ffs/Makefile create mode 100644 sbin/fsck_ffs/SMM.doc/0.t create mode 100644 sbin/fsck_ffs/SMM.doc/1.t create mode 100644 sbin/fsck_ffs/SMM.doc/2.t create mode 100644 sbin/fsck_ffs/SMM.doc/3.t create mode 100644 sbin/fsck_ffs/SMM.doc/4.t create mode 100644 sbin/fsck_ffs/SMM.doc/Makefile create mode 100644 sbin/fsck_ffs/dir.c create mode 100644 sbin/fsck_ffs/fsck.h create mode 100644 sbin/fsck_ffs/fsck_ffs.8 create mode 100644 sbin/fsck_ffs/inode.c create mode 100644 sbin/fsck_ffs/main.c create mode 100644 sbin/fsck_ffs/pass1.c create mode 100644 sbin/fsck_ffs/pass1b.c create mode 100644 sbin/fsck_ffs/pass2.c create mode 100644 sbin/fsck_ffs/pass3.c create mode 100644 sbin/fsck_ffs/pass4.c create mode 100644 sbin/fsck_ffs/pass5.c create mode 100644 sbin/fsck_ffs/preen.c create mode 100644 sbin/fsck_ffs/setup.c create mode 100644 sbin/fsck_ffs/utilities.c create mode 100644 sbin/fsck_ifs/Makefile create mode 100644 sbin/fsck_ifs/dir.c create mode 100644 sbin/fsck_ifs/fsck.h create mode 100644 sbin/fsck_ifs/fsck_ifs.8 create mode 100644 sbin/fsck_ifs/inode.c create mode 100644 sbin/fsck_ifs/main.c create mode 100644 sbin/fsck_ifs/pass1.c create mode 100644 sbin/fsck_ifs/pass1b.c create mode 100644 sbin/fsck_ifs/pass2.c create mode 100644 sbin/fsck_ifs/pass3.c create mode 100644 sbin/fsck_ifs/pass4.c create mode 100644 sbin/fsck_ifs/pass5.c create mode 100644 sbin/fsck_ifs/preen.c create mode 100644 sbin/fsck_ifs/setup.c create mode 100644 sbin/fsck_ifs/utilities.c create mode 100644 sbin/ifconfig/Makefile create mode 100644 sbin/ifconfig/ifconfig.8 create mode 100644 sbin/ifconfig/ifconfig.c create mode 100644 sbin/init/Makefile create mode 100644 sbin/init/NOTES create mode 100644 sbin/init/init.8 create mode 100644 sbin/init/init.c create mode 100644 sbin/init/pathnames.h create mode 100644 sbin/mknod/Makefile create mode 100644 sbin/mknod/mknod.8 create mode 100644 sbin/mknod/mknod.c create mode 100644 sbin/mount/Makefile create mode 100644 sbin/mount/getmntopts.3 create mode 100644 sbin/mount/getmntopts.c create mode 100644 sbin/mount/mntopts.h create mode 100644 sbin/mount/mount.8 create mode 100644 sbin/mount/mount.c create mode 100644 sbin/mount/mount_ufs.c create mode 100644 sbin/mount/pathnames.h create mode 100644 sbin/mount_cd9660/Makefile create mode 100644 sbin/mount_cd9660/mount_cd9660.8 create mode 100644 sbin/mount_cd9660/mount_cd9660.c create mode 100644 sbin/mount_fdesc/Makefile create mode 100644 sbin/mount_fdesc/mount_fdesc.8 create mode 100644 sbin/mount_fdesc/mount_fdesc.c create mode 100644 sbin/mount_ifs/Makefile create mode 100644 sbin/mount_ifs/getmntopts.3 create mode 100644 sbin/mount_ifs/getmntopts.c create mode 100644 sbin/mount_ifs/mntopts.h create mode 100644 sbin/mount_ifs/mount.8 create mode 100644 sbin/mount_ifs/mount.c create mode 100644 sbin/mount_ifs/mount_ufs.c create mode 100644 sbin/mount_ifs/pathnames.h create mode 100644 sbin/mount_kernfs/Makefile create mode 100644 sbin/mount_kernfs/mount_kernfs.8 create mode 100644 sbin/mount_kernfs/mount_kernfs.c create mode 100644 sbin/mount_lfs/Makefile create mode 100644 sbin/mount_lfs/mount_lfs.8 create mode 100644 sbin/mount_lfs/mount_lfs.c create mode 100644 sbin/mount_lfs/pathnames.h create mode 100644 sbin/mount_nfs/Makefile create mode 100644 sbin/mount_nfs/mount_nfs.8 create mode 100644 sbin/mount_nfs/mount_nfs.c create mode 100644 sbin/mount_null/Makefile create mode 100644 sbin/mount_null/mount_null.8 create mode 100644 sbin/mount_null/mount_null.c create mode 100644 sbin/mount_nullfs/Makefile create mode 100644 sbin/mount_nullfs/mount_nullfs.8 create mode 100644 sbin/mount_nullfs/mount_nullfs.c create mode 100644 sbin/mount_portal/Makefile create mode 100644 sbin/mount_portal/activate.c create mode 100644 sbin/mount_portal/conf.c create mode 100644 sbin/mount_portal/mount_portal.8 create mode 100644 sbin/mount_portal/mount_portal.c create mode 100644 sbin/mount_portal/pathnames.h create mode 100644 sbin/mount_portal/portal.conf create mode 100644 sbin/mount_portal/portald.h create mode 100644 sbin/mount_portal/pt_conf.c create mode 100644 sbin/mount_portal/pt_exec.c create mode 100644 sbin/mount_portal/pt_file.c create mode 100644 sbin/mount_portal/pt_tcp.c create mode 100644 sbin/mount_portalfs/Makefile create mode 100644 sbin/mount_portalfs/activate.c create mode 100644 sbin/mount_portalfs/conf.c create mode 100644 sbin/mount_portalfs/mount_portalfs.8 create mode 100644 sbin/mount_portalfs/mount_portalfs.c create mode 100644 sbin/mount_portalfs/pathnames.h create mode 100644 sbin/mount_portalfs/portal.conf create mode 100644 sbin/mount_portalfs/portald.h create mode 100644 sbin/mount_portalfs/pt_conf.c create mode 100644 sbin/mount_portalfs/pt_exec.c create mode 100644 sbin/mount_portalfs/pt_file.c create mode 100644 sbin/mount_portalfs/pt_tcp.c create mode 100644 sbin/mount_procfs/Makefile create mode 100644 sbin/mount_procfs/mount_procfs.8 create mode 100644 sbin/mount_procfs/mount_procfs.c create mode 100644 sbin/mount_umap/Makefile create mode 100644 sbin/mount_umap/mount_umap.8 create mode 100644 sbin/mount_umap/mount_umap.c create mode 100644 sbin/mount_umap/sample.group.mapfile create mode 100644 sbin/mount_umap/sample.user.mapfile create mode 100644 sbin/mount_umap/umap_manual create mode 100644 sbin/mount_umapfs/Makefile create mode 100644 sbin/mount_umapfs/mount_umapfs.8 create mode 100644 sbin/mount_umapfs/mount_umapfs.c create mode 100644 sbin/mount_umapfs/sample.group.mapfile create mode 100644 sbin/mount_umapfs/sample.user.mapfile create mode 100644 sbin/mount_umapfs/umap_manual create mode 100644 sbin/mount_union/Makefile create mode 100644 sbin/mount_union/mount_union.8 create mode 100644 sbin/mount_union/mount_union.c create mode 100644 sbin/mount_unionfs/Makefile create mode 100644 sbin/mount_unionfs/mount_unionfs.8 create mode 100644 sbin/mount_unionfs/mount_unionfs.c create mode 100644 sbin/mountd/Makefile create mode 100644 sbin/mountd/exports.5 create mode 100644 sbin/mountd/mountd.8 create mode 100644 sbin/mountd/mountd.c create mode 100644 sbin/mountd/netgroup.5 create mode 100644 sbin/mountd/pathnames.h create mode 100644 sbin/newfs/Makefile create mode 100644 sbin/newfs/mkfs.c create mode 100644 sbin/newfs/newfs.8 create mode 100644 sbin/newfs/newfs.c create mode 100644 sbin/newlfs/Makefile create mode 100644 sbin/newlfs/config.h create mode 100644 sbin/newlfs/extern.h create mode 100644 sbin/newlfs/lfs.c create mode 100644 sbin/newlfs/misc.c create mode 100644 sbin/newlfs/newfs.c create mode 100644 sbin/newlfs/newlfs.8 create mode 100644 sbin/nfsd/Makefile create mode 100644 sbin/nfsd/nfsd.8 create mode 100644 sbin/nfsd/nfsd.c create mode 100644 sbin/nfsiod/Makefile create mode 100644 sbin/nfsiod/nfsiod.8 create mode 100644 sbin/nfsiod/nfsiod.c create mode 100644 sbin/nologin/Makefile create mode 100644 sbin/nologin/nologin.8 create mode 100644 sbin/nologin/nologin.sh create mode 100644 sbin/ping/Makefile create mode 100644 sbin/ping/ping.8 create mode 100644 sbin/ping/ping.c create mode 100644 sbin/quotacheck/Makefile create mode 100644 sbin/quotacheck/preen.c create mode 100644 sbin/quotacheck/quotacheck.8 create mode 100644 sbin/quotacheck/quotacheck.c create mode 100644 sbin/reboot/Makefile create mode 100644 sbin/reboot/boot_hp300.8 create mode 100644 sbin/reboot/boot_i386.8 create mode 100644 sbin/reboot/boot_sparc.8 create mode 100644 sbin/reboot/boot_tahoe.8 create mode 100644 sbin/reboot/boot_vax.8 create mode 100644 sbin/reboot/reboot.8 create mode 100644 sbin/reboot/reboot.c create mode 100644 sbin/restore/Makefile create mode 100644 sbin/restore/dirs.c create mode 100644 sbin/restore/extern.h create mode 100644 sbin/restore/interactive.c create mode 100644 sbin/restore/main.c create mode 100644 sbin/restore/pathnames.h create mode 100644 sbin/restore/restore.8 create mode 100644 sbin/restore/restore.c create mode 100644 sbin/restore/restore.h create mode 100644 sbin/restore/symtab.c create mode 100644 sbin/restore/tape.c create mode 100644 sbin/restore/utilities.c create mode 100644 sbin/route/Makefile create mode 100644 sbin/route/ccitt_addr.c create mode 100644 sbin/route/keywords create mode 100644 sbin/route/route.8 create mode 100644 sbin/route/route.c create mode 100644 sbin/savecore/Makefile create mode 100644 sbin/savecore/savecore.8 create mode 100644 sbin/savecore/savecore.c create mode 100644 sbin/scsiformat/Makefile create mode 100644 sbin/scsiformat/scsiformat.8 create mode 100644 sbin/scsiformat/scsiformat.c create mode 100644 sbin/shutdown/Makefile create mode 100644 sbin/shutdown/pathnames.h create mode 100644 sbin/shutdown/shutdown.8 create mode 100644 sbin/shutdown/shutdown.c create mode 100644 sbin/slattach/Makefile create mode 100644 sbin/slattach/slattach.8 create mode 100644 sbin/slattach/slattach.c create mode 100644 sbin/startslip/Makefile create mode 100644 sbin/startslip/startslip.1 create mode 100644 sbin/startslip/startslip.c create mode 100644 sbin/swapon/Makefile create mode 100644 sbin/swapon/swapon.8 create mode 100644 sbin/swapon/swapon.c create mode 100644 sbin/tunefs/Makefile create mode 100644 sbin/tunefs/tunefs.8 create mode 100644 sbin/tunefs/tunefs.c create mode 100644 sbin/umount/Makefile create mode 100644 sbin/umount/umount.8 create mode 100644 sbin/umount/umount.c (limited to 'sbin') diff --git a/sbin/Makefile b/sbin/Makefile new file mode 100644 index 000000000000..919fba6dec9f --- /dev/null +++ b/sbin/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.5 (Berkeley) 3/31/94 + +SUBDIR= XNSrouted badsect clri disklabel dmesg dump dumpfs dumplfs fastboot \ + fsck fsdb icheck ifconfig init mknod mount mount_cd9660 mount_fdesc \ + mount_kernfs mount_lfs mount_nfs mount_null mount_portal \ + mount_procfs mount_umap mount_union mountd ncheck newfs newlfs nfsd \ + nfsiod nologin ping quotacheck reboot restore route routed savecore \ + scsiformat shutdown slattach startslip swapon tunefs umount + +.include diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc new file mode 100644 index 000000000000..79c318e633f1 --- /dev/null +++ b/sbin/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93 + +BINDIR?= /sbin diff --git a/sbin/badsect/Makefile b/sbin/badsect/Makefile new file mode 100644 index 000000000000..ff9f4f1d22cb --- /dev/null +++ b/sbin/badsect/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= badsect +MAN8= badsect.0 + +.include diff --git a/sbin/badsect/badsect.8 b/sbin/badsect/badsect.8 new file mode 100644 index 000000000000..840011d00225 --- /dev/null +++ b/sbin/badsect/badsect.8 @@ -0,0 +1,132 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)badsect.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt BADSECT 8 +.Os BSD 4 +.Sh NAME +.Nm badsect +.Nd create files to contain bad sectors +.Sh SYNOPSIS +.Nm /etc/badsect +.Ar bbdir sector ... +.Sh DESCRIPTION +.Nm Badsect +makes a file to contain a bad sector. Normally, bad sectors +are made inaccessible by the standard formatter, which provides +a forwarding table for bad sectors to the driver; see +.Xr bad144 8 +for details. +If a driver supports the bad blocking standard it is much preferable to +use that method to isolate bad blocks, since the bad block forwarding +makes the pack appear perfect, and such packs can then be copied with +.Xr dd 1 . +The technique used by this program is also less general than +bad block forwarding, as +.Nm badsect +can't make amends for +bad blocks in the i-list of file systems or in swap areas. +.Pp +On some disks, +adding a sector which is suddenly bad to the bad sector table +currently requires the running of the standard +.Tn DEC +formatter. +Thus to deal with a newly bad block +or on disks where the drivers +do not support the bad-blocking standard +.Nm badsect +may be used to good effect. +.Pp +.Nm Badsect +is used on a quiet file system in the following way: +First mount the file system, and change to its root directory. +Make a directory +.Li BAD +there. Run +.Nm badsect +giving as argument the +.Ar BAD +directory followed by +all the bad sectors you wish to add. +(The sector numbers must be relative to the beginning of +the file system, but this is not hard as the system reports +relative sector numbers in its console error messages.) +Then change back to the root directory, unmount the file system +and run +.Xr fsck 8 +on the file system. The bad sectors should show up in two files +or in the bad sector files and the free list. Have +.Xr fsck +remove files containing the offending bad sectors, but +.Em do not +have it remove the +.Pa BAD/ Ns Em nnnnn +files. +This will leave the bad sectors in only the +.Li BAD +files. +.Pp +.Nm Badsect +works by giving the specified sector numbers in a +.Xr mknod 2 +system call, +creating an illegal file whose first block address is the block containing +bad sector and whose name is the bad sector number. +When it is discovered by +.Xr fsck +it will ask +.Dq Li "HOLD BAD BLOCK ?" +A positive response will cause +.Xr fsck +to convert the inode to a regular file containing the bad block. +.Sh SEE ALSO +.Xr bad144 8 , +.Xr fsck 8 , +.Xr format 8 +.Sh DIAGNOSTICS +.Nm Badsect +refuses to attach a block that +resides in a critical area or is out of range of the file system. +A warning is issued if the block is already in use. +.Sh BUGS +If more than one sector which comprise a file system fragment are bad, +you should specify only one of them to +.Nm badsect , +as the blocks in the bad sector files actually cover all the sectors in a +file system fragment. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.1 . diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c new file mode 100644 index 000000000000..fa978b10a79f --- /dev/null +++ b/sbin/badsect/badsect.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1981, 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1981, 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)badsect.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +/* + * badsect + * + * Badsect takes a list of file-system relative sector numbers + * and makes files containing the blocks of which these sectors are a part. + * It can be used to contain sectors which have problems if these sectors + * are not part of the bad file for the pack (see bad144). For instance, + * this program can be used if the driver for the file system in question + * does not support bad block forwarding. + */ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +union { + struct fs fs; + char fsx[SBSIZE]; +} ufs; +#define sblock ufs.fs +union { + struct cg cg; + char cgx[MAXBSIZE]; +} ucg; +#define acg ucg.cg +struct fs *fs; +int fso, fsi; +int errs; +long dev_bsize = 1; + +char buf[MAXBSIZE]; + +void rdfs __P((daddr_t, int, char *)); +int chkuse __P((daddr_t, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + daddr_t number; + struct stat stbuf, devstat; + register struct direct *dp; + DIR *dirp; + char name[BUFSIZ]; + + if (argc < 3) { + fprintf(stderr, "usage: badsect bbdir blkno [ blkno ]\n"); + exit(1); + } + if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0) { + perror(argv[1]); + exit(2); + } + strcpy(name, _PATH_DEV); + if ((dirp = opendir(name)) == NULL) { + perror(name); + exit(3); + } + while ((dp = readdir(dirp)) != NULL) { + strcpy(&name[5], dp->d_name); + if (stat(name, &devstat) < 0) { + perror(name); + exit(4); + } + if (stbuf.st_dev == devstat.st_rdev && + (devstat.st_mode & IFMT) == IFBLK) + break; + } + closedir(dirp); + if (dp == NULL) { + printf("Cannot find dev 0%o corresponding to %s\n", + stbuf.st_rdev, argv[1]); + exit(5); + } + if ((fsi = open(name, 0)) < 0) { + perror(name); + exit(6); + } + fs = &sblock; + rdfs(SBOFF, SBSIZE, (char *)fs); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + for (argc -= 2, argv += 2; argc > 0; argc--, argv++) { + number = atoi(*argv); + if (chkuse(number, 1)) + continue; + if (mknod(*argv, IFMT|0600, dbtofsb(fs, number)) < 0) { + perror(*argv); + errs++; + } + } + printf("Don't forget to run ``fsck %s''\n", name); + exit(errs); +} + +int +chkuse(blkno, cnt) + daddr_t blkno; + int cnt; +{ + int cg; + daddr_t fsbn, bn; + + fsbn = dbtofsb(fs, blkno); + if ((unsigned)(fsbn+cnt) > fs->fs_size) { + printf("block %d out of range of file system\n", blkno); + return (1); + } + cg = dtog(fs, fsbn); + if (fsbn < cgdmin(fs, cg)) { + if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } else { + if ((fsbn+cnt) > cgbase(fs, cg+1)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } + rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)sblock.fs_cgsize, + (char *)&acg); + if (!cg_chkmagic(&acg)) { + fprintf(stderr, "cg %d: bad magic number\n", cg); + errs++; + return (1); + } + bn = dtogd(fs, fsbn); + if (isclr(cg_blksfree(&acg), bn)) + printf("Warning: sector %d is in use\n", blkno); + return (0); +} + +/* + * read a block from the file system + */ +void +rdfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (lseek(fsi, (off_t)bno * dev_bsize, SEEK_SET) < 0) { + printf("seek error: %ld\n", bno); + perror("rdfs"); + exit(1); + } + n = read(fsi, bf, size); + if (n != size) { + printf("read error: %ld\n", bno); + perror("rdfs"); + exit(1); + } +} diff --git a/sbin/bsdlabel/Makefile b/sbin/bsdlabel/Makefile new file mode 100644 index 000000000000..3f4749c70f09 --- /dev/null +++ b/sbin/bsdlabel/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/17/94 + +PROG= disklabel +SRCS= disklabel.c dkcksum.c +MAN8= disklabel.0 +CLEANFILES=disklabel.5.0 + +all: ${PROG} disklabel.5.0 ${MAN8} + +beforeinstall: + install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} disklabel.5.0 \ + ${DESTDIR}${MANDIR}5/disklabel.0 + +.include diff --git a/sbin/bsdlabel/bsdlabel.8 b/sbin/bsdlabel/bsdlabel.8 new file mode 100644 index 000000000000..bf14e0f23b04 --- /dev/null +++ b/sbin/bsdlabel/bsdlabel.8 @@ -0,0 +1,343 @@ +.\" Copyright (c) 1987, 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd "April 19, 1994" +.Dt DISKLABEL 8 +.Os BSD 4.2 +.Sh NAME +.Nm disklabel +.Nd read and write disk pack label +.Sh SYNOPSIS +.Nm disklabel +.Op Fl r +.Ar disk +.Nm disklabel +.Fl w +.Op Fl r +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl e +.Op Fl r +.Ar disk +.Nm disklabel +.Fl R +.Op Fl r +.Ar disk Ar protofile +.Nm disklabel +.Op Fl NW +.Ar disk +.sp +.Nm disklabel +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk +.Oo Ar disktype Oc +.Nm disklabel +.Fl w +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl R +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar protofile +.Oo Ar disktype Oc +.Sh DESCRIPTION +.Nm Disklabel +can be used to install, examine or modify the label on a disk drive or pack. +When writing the label, it can be used +to change the drive identification, +the disk partitions on the drive, +or to replace a damaged label. +On some systems, +.Nm disklabel +can be used to install bootstrap code as well. +There are several forms of the command that read (display), install or edit +the label on a disk. +Each form has an additional option, +.Fl r , +which causes the label to be read from or written to the disk directly, +rather than going through the system's in-core copy of the label. +This option may allow a label to be installed on a disk +without kernel support for a label, such as when labels are first installed +on a system; it must be used when first installing a label on a disk. +The specific effect of +.Fl r +is described under each command. +The read and install forms also support the +.Fl B +option to install bootstrap code. +These variants are described later. +.Pp +The first form of the command (read) is used to examine the label on the named +disk drive (e.g. sd0 or /dev/rsd0c). +It will display all of the parameters associated with the drive +and its partition layout. +Unless the +.Fl r +flag is given, +the kernel's in-core copy of the label is displayed; +if the disk has no label, or the partition types on the disk are incorrect, +the kernel may have constructed or modified the label. +If the +.Fl r +flag is given, the label from the raw disk will be displayed rather +than the in-core label. +.Pp +The second form of the command, with the +.Fl w +flag, is used to write a standard label on the designated drive. +The required arguments to +.Nm disklabel +are the drive to be labelled (e.g. sd0), and +the drive type as described in the +.Xr disktab 5 +file. +The drive parameters and partitions are taken from that file. +If different disks of the same physical type are to have different +partitions, it will be necessary to have separate disktab entries +describing each, or to edit the label after installation as described below. +The optional argument is a pack identification string, +up to 16 characters long. +The pack id must be quoted if it contains blanks. +If the +.Fl r +flag is given, the disk sectors containing the label and bootstrap +will be written directly. +A side-effect of this is that any existing bootstrap code will be overwritten +and the disk rendered unbootable. +If +.Fl r +is not specified, +the existing label will be updated via the in-core copy and any bootstrap +code will be unaffected. +If the disk does not already have a label, the +.Fl r +flag must be used. +In either case, the kernel's in-core label is replaced. +.Pp +An existing disk label may be edited by using the +.Fl e +flag. +The label is read from the in-core kernel copy, +or directly from the disk if the +.Fl r +flag is also given. +The label is formatted and then supplied to an editor for changes. +If no editor is specified in an +.Ev EDITOR +environment variable, +.Xr vi 1 +is used. +When the editor terminates, the formatted label is reread +and used to rewrite the disk label. +Existing bootstrap code is unchanged regardless of whether +.Fl r +was specified. +.Pp +With the +.Fl R +flag, +.Nm disklabel +is capable of restoring a disk label that was formatted +in a prior operation and saved in an ascii file. +The prototype file used to create the label should be in the same format +as that produced when reading or editing a label. +Comments are delimited by +.Ar \&# +and newline. +As with +.Fl w , +any existing bootstrap code will be clobbered if +.Fl r +is specified and will be unaffected otherwise. +.Pp +The +.Fl NW +flags for +.Nm disklabel +explicitly disallow and +allow, respectively, writing of the pack label area on the selected disk. +.Pp +The final three forms of +.Nm disklabel +are used to install boostrap code on machines where the bootstrap is part +of the label. +The bootstrap code is comprised of one or two boot programs depending on +the machine. +The +.Fl B +option is used to denote that bootstrap code is to be installed. +The +.Fl r +flag is implied by +.Fl B +and never needs to be specified. +The name of the boot program(s) to be installed can be selected in a +variety of ways. +First, the names can be specified explicitly via the +.Fl b +and +.Fl s +flags. +On machines with only a single level of boot program, +.Fl b +is the name of that program. +For machines with a two-level bootstrap, +.Fl b +indicates the primary boot program and +.Fl s +the secondary boot program. +If the names are not explicitly given, standard boot programs will be used. +The boot programs are located in +.Pa /usr/mdec . +The names of the programs are taken from the ``b0'' and ``b1'' parameters +of the +.Xr disktab 5 +entry for the disk if +.Ar disktype +was given and its disktab entry exists and includes those parameters. +Otherwise, boot program names are derived from the name of the disk. +These names are of the form +.Pa basename Ns boot +for the primary (or only) bootstrap, and +.Pf boot Pa basename +for the secondary bootstrap; +for example, +.Pa /usr/mdec/sdboot +and +.Pa /usr/mdec/bootsd +if the disk device is +.Em sd0 . +.Pp +The first of the three boot-installation forms is used to install +bootstrap code without changing the existing label. +It is essentially a read command with respect to the disk label +itself and all options are related to the specification of the boot +program as described previously. +The final two forms are analogous to the basic write and restore versions +except that they will install bootstrap code in addition to a new label. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/disktab +.It Pa /usr/mdec/ Ns Em xx Ns boot +.It Pa /usr/mdec/boot Ns Em xx +.El +.Sh EXAMPLES +.Dl disklabel sd0 +.Pp +Display the in-core label for sd0 as obtained via +.Pa /dev/rsd0c . +.Pp +.Dl disklabel -w -r /dev/rsd0c sd2212 foo +.Pp +Create a label for sd0 based on information for ``sd2212'' found in +.Pa /etc/disktab . +Any existing bootstrap code will be clobbered. +.Pp +.Dl disklabel -e -r sd0 +.Pp +Read the on-disk label for sd0, edit it and reinstall in-core as well +as on-disk. +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -R sd0 mylabel +.Pp +Restore the on-disk and in-core label for sd0 from information in +.Pa mylabel . +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -B sd0 +.Pp +Install a new bootstrap on sd0. +The boot code comes from +.Pa /usr/mdec/sdboot +and possibly +.Pa /usr/mdec/bootsd . +On-disk and in-core labels are unchanged. +.Pp +.Dl disklabel -w -B /dev/rsd0c -b newboot sd2212 +.Pp +Install a new label and bootstrap. +The label is derived from disktab information for ``sd2212'' and +installed both in-core and on-disk. +The bootstrap code comes from the file +.Pa /usr/mdec/newboot . +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 5 +.Sh DIAGNOSTICS +The kernel device drivers will not allow the size of a disk partition +to be decreased or the offset of a partition to be changed while it is open. +Some device drivers create a label containing only a single large partition +if a disk is unlabeled; thus, the label must be written to the ``a'' +partition of the disk while it is open. +This sometimes requires the desired label to be set in two steps, +the first one creating at least one other partition, +and the second setting the label on the new partition +while shrinking the ``a'' partition. +.Pp +On some machines the bootstrap code may not fit entirely in the area +allocated for it by some filesystems. +As a result, it may not be possible to have filesystems on some partitions +of a ``bootable'' disk. +When installing bootstrap code, +.Nm disklabel +checks for these cases. +If the installed boot code would overlap a partition of type FS_UNUSED +it is marked as type FS_BOOT. +The +.Xr newfs 8 +utility will disallow creation of filesystems on FS_BOOT partitions. +Conversely, if a partition has a type other than FS_UNUSED or FS_BOOT, +.Nm disklabel +will not install bootstrap code that overlaps it. +.Sh BUGS +When a disk name is given without a full pathname, +the constructed device name uses the ``a'' partition on the tahoe, +the ``c'' partition on all others. diff --git a/sbin/bsdlabel/bsdlabel.c b/sbin/bsdlabel/bsdlabel.c new file mode 100644 index 000000000000..4a2934078584 --- /dev/null +++ b/sbin/bsdlabel/bsdlabel.c @@ -0,0 +1,1314 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Symmetric Computer Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; +/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * Disklabel: read and write disklabels. + * The label is usually placed on one of the first sectors of the disk. + * Many machines also place a bootstrap in the same area, + * in which case the label is embedded in the bootstrap. + * The bootstrap source must leave space at the proper offset + * for the label on such machines. + */ + +#ifdef tahoe +#define RAWPARTITION 'a' +#else +#define RAWPARTITION 'c' +#endif + +#ifndef BBSIZE +#define BBSIZE 8192 /* size of boot area, with label */ +#endif + +#ifdef tahoe +#define NUMBOOT 0 +#else +#if defined(hp300) || defined(hp800) +#define NUMBOOT 1 +#else +#define NUMBOOT 2 +#endif +#endif + +#define DEFEDITOR _PATH_VI +#define streq(a,b) (strcmp(a,b) == 0) + +char *dkname; +char *specname; +char tmpfil[] = _PATH_TMP; + +extern int errno; +char namebuf[BBSIZE], *np = namebuf; +struct disklabel lab; +struct disklabel *readlabel(), *makebootarea(); +char bootarea[BBSIZE]; + +#if NUMBOOT > 0 +int installboot; /* non-zero if we should install a boot program */ +char *bootbuf; /* pointer to buffer with remainder of boot prog */ +int bootsize; /* size of remaining boot program */ +char *xxboot; /* primary boot */ +char *bootxx; /* secondary boot */ +char boot0[MAXPATHLEN]; +char boot1[MAXPATHLEN]; +#endif + +enum { + UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT +} op = UNSPEC; + +int rflag; + +#ifdef DEBUG +int debug; +#define OPTIONS "BNRWb:ders:w" +#else +#define OPTIONS "BNRWb:ers:w" +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register struct disklabel *lp; + FILE *t; + int ch, f, flag, error = 0; + char *name = 0; + + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { +#if NUMBOOT > 0 + case 'B': + ++installboot; + break; + case 'b': + xxboot = optarg; + break; +#if NUMBOOT > 1 + case 's': + bootxx = optarg; + break; +#endif +#endif + case 'N': + if (op != UNSPEC) + usage(); + op = NOWRITE; + break; + case 'R': + if (op != UNSPEC) + usage(); + op = RESTORE; + break; + case 'W': + if (op != UNSPEC) + usage(); + op = WRITEABLE; + break; + case 'e': + if (op != UNSPEC) + usage(); + op = EDIT; + break; + case 'r': + ++rflag; + break; + case 'w': + if (op != UNSPEC) + usage(); + op = WRITE; + break; +#ifdef DEBUG + case 'd': + debug++; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; +#if NUMBOOT > 0 + if (installboot) { + rflag++; + if (op == UNSPEC) + op = WRITEBOOT; + } else { + if (op == UNSPEC) + op = READ; + xxboot = bootxx = 0; + } +#else + if (op == UNSPEC) + op = READ; +#endif + if (argc < 1) + usage(); + + dkname = argv[0]; + if (dkname[0] != '/') { + (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); + specname = np; + np += strlen(specname) + 1; + } else + specname = dkname; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + if (f < 0 && errno == ENOENT && dkname[0] != '/') { + (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); + np = namebuf + strlen(specname) + 1; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + } + if (f < 0) + Perror(specname); + + switch(op) { + + case EDIT: + if (argc != 1) + usage(); + lp = readlabel(f); + error = edit(lp, f); + break; + + case NOWRITE: + flag = 0; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + + case READ: + if (argc != 1) + usage(); + lp = readlabel(f); + display(stdout, lp); + error = checklabel(lp); + break; + + case RESTORE: +#if NUMBOOT > 0 + if (installboot && argc == 3) { + makelabel(argv[2], 0, &lab); + argc--; + } +#endif + if (argc != 2) + usage(); + lp = makebootarea(bootarea, &lab, f); + if (!(t = fopen(argv[1], "r"))) + Perror(argv[1]); + if (getasciilabel(t, lp)) + error = writelabel(f, bootarea, lp); + break; + + case WRITE: + if (argc == 3) { + name = argv[2]; + argc--; + } + if (argc != 2) + usage(); + makelabel(argv[1], name, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = lab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + + case WRITEABLE: + flag = 1; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + +#if NUMBOOT > 0 + case WRITEBOOT: + { + struct disklabel tlab; + + lp = readlabel(f); + tlab = *lp; + if (argc == 2) + makelabel(argv[1], 0, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = tlab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + } +#endif + } + exit(error); +} + +/* + * Construct a prototype disklabel from /etc/disktab. As a side + * effect, set the names of the primary and secondary boot files + * if specified. + */ +makelabel(type, name, lp) + char *type, *name; + register struct disklabel *lp; +{ + register struct disklabel *dp; + char *strcpy(); + + dp = getdiskbyname(type); + if (dp == NULL) { + fprintf(stderr, "%s: unknown disk type\n", type); + exit(1); + } + *lp = *dp; +#if NUMBOOT > 0 + /* + * Set bootstrap name(s). + * 1. If set from command line, use those, + * 2. otherwise, check if disktab specifies them (b0 or b1), + * 3. otherwise, makebootarea() will choose ones based on the name + * of the disk special file. E.g. /dev/ra0 -> raboot, bootra + */ + if (!xxboot && lp->d_boot0) { + if (*lp->d_boot0 != '/') + (void)sprintf(boot0, "%s/%s", + _PATH_BOOTDIR, lp->d_boot0); + else + (void)strcpy(boot0, lp->d_boot0); + xxboot = boot0; + } +#if NUMBOOT > 1 + if (!bootxx && lp->d_boot1) { + if (*lp->d_boot1 != '/') + (void)sprintf(boot1, "%s/%s", + _PATH_BOOTDIR, lp->d_boot1); + else + (void)strcpy(boot1, lp->d_boot1); + bootxx = boot1; + } +#endif +#endif + /* d_packname is union d_boot[01], so zero */ + bzero(lp->d_packname, sizeof(lp->d_packname)); + if (name) + (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); +} + +writelabel(f, boot, lp) + int f; + char *boot; + register struct disklabel *lp; +{ + register int i; + int flag; + + setbootflag(lp); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (rflag) { + /* + * First set the kernel disk label, + * then write a label to the raw disk. + * If the SDINFO ioctl fails because it is unimplemented, + * keep going; otherwise, the kernel consistency checks + * may prevent us from changing the current (in-core) + * label. + */ + if (ioctl(f, DIOCSDINFO, lp) < 0 && + errno != ENODEV && errno != ENOTTY) { + l_perror("ioctl DIOCSDINFO"); + return (1); + } + (void)lseek(f, (off_t)0, SEEK_SET); + /* + * write enable label sector before write (if necessary), + * disable after writing. + */ + flag = 1; + if (ioctl(f, DIOCWLABEL, &flag) < 0) + perror("ioctl DIOCWLABEL"); + if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { + perror("write"); + return (1); + } +#if NUMBOOT > 0 + /* + * Output the remainder of the disklabel + */ + if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { + perror("write"); + return(1); + } +#endif + flag = 0; + (void) ioctl(f, DIOCWLABEL, &flag); + } else if (ioctl(f, DIOCWDINFO, lp) < 0) { + l_perror("ioctl DIOCWDINFO"); + return (1); + } +#ifdef vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + daddr_t alt; + + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), + SEEK_SET); + if (write(f, boot, lp->d_secsize) < lp->d_secsize) { + int oerrno = errno; + fprintf(stderr, "alternate label %d ", i/2); + errno = oerrno; + perror("write"); + } + } + } +#endif + return (0); +} + +l_perror(s) + char *s; +{ + int saverrno = errno; + + fprintf(stderr, "disklabel: %s: ", s); + + switch (saverrno) { + + case ESRCH: + fprintf(stderr, "No disk label on disk;\n"); + fprintf(stderr, + "use \"disklabel -r\" to install initial label\n"); + break; + + case EINVAL: + fprintf(stderr, "Label magic number or checksum is wrong!\n"); + fprintf(stderr, "(disklabel or kernel is out of date?)\n"); + break; + + case EBUSY: + fprintf(stderr, "Open partition would move or shrink\n"); + break; + + case EXDEV: + fprintf(stderr, + "Labeled partition or 'a' partition must start at beginning of disk\n"); + break; + + default: + errno = saverrno; + perror((char *)NULL); + break; + } +} + +/* + * Fetch disklabel for disk. + * Use ioctl to get label unless -r flag is given. + */ +struct disklabel * +readlabel(f) + int f; +{ + register struct disklabel *lp; + + if (rflag) { + if (read(f, bootarea, BBSIZE) < BBSIZE) + Perror(specname); + for (lp = (struct disklabel *)bootarea; + lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); + lp = (struct disklabel *)((char *)lp + 16)) + if (lp->d_magic == DISKMAGIC && + lp->d_magic2 == DISKMAGIC) + break; + if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || + lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || + dkcksum(lp) != 0) { + fprintf(stderr, + "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); + /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ + exit (1); + } + } else { + lp = &lab; + if (ioctl(f, DIOCGDINFO, lp) < 0) + Perror("ioctl DIOCGDINFO"); + } + return (lp); +} + +/* + * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' + * Returns a pointer to the disklabel portion of the bootarea. + */ +struct disklabel * +makebootarea(boot, dp, f) + char *boot; + register struct disklabel *dp; + int f; +{ + struct disklabel *lp; + register char *p; + int b; +#if NUMBOOT > 0 + char *dkbasename; + struct stat sb; +#endif + + /* XXX */ + if (dp->d_secsize == 0) { + dp->d_secsize = DEV_BSIZE; + dp->d_bbsize = BBSIZE; + } + lp = (struct disklabel *) + (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); + bzero((char *)lp, sizeof *lp); +#if NUMBOOT > 0 + /* + * If we are not installing a boot program but we are installing a + * label on disk then we must read the current bootarea so we don't + * clobber the existing boot. + */ + if (!installboot) { + if (rflag) { + if (read(f, boot, BBSIZE) < BBSIZE) + Perror(specname); + bzero((char *)lp, sizeof *lp); + } + return (lp); + } + /* + * We are installing a boot program. Determine the name(s) and + * read them into the appropriate places in the boot area. + */ + if (!xxboot || !bootxx) { + dkbasename = np; + if ((p = rindex(dkname, '/')) == NULL) + p = dkname; + else + p++; + while (*p && !isdigit(*p)) + *np++ = *p++; + *np++ = '\0'; + + if (!xxboot) { + (void)sprintf(np, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + xxboot = np; + (void)sprintf(xxboot, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + np += strlen(xxboot) + 1; + } +#if NUMBOOT > 1 + if (!bootxx) { + (void)sprintf(np, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + bootxx = np; + (void)sprintf(bootxx, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + np += strlen(bootxx) + 1; + } +#endif + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", + xxboot, bootxx ? bootxx : "NONE"); +#endif + + /* + * Strange rules: + * 1. One-piece bootstrap (hp300/hp800) + * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest + * is remembered and written later following the bootarea. + * 2. Two-piece bootstraps (vax/i386?/mips?) + * up to d_secsize bytes of ``xxboot'' go in first d_secsize + * bytes of bootarea, remaining d_bbsize-d_secsize filled + * from ``bootxx''. + */ + b = open(xxboot, O_RDONLY); + if (b < 0) + Perror(xxboot); +#if NUMBOOT > 1 + if (read(b, boot, (int)dp->d_secsize) < 0) + Perror(xxboot); + (void)close(b); + b = open(bootxx, O_RDONLY); + if (b < 0) + Perror(bootxx); + if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0) + Perror(bootxx); +#else + if (read(b, boot, (int)dp->d_bbsize) < 0) + Perror(xxboot); + (void)fstat(b, &sb); + bootsize = (int)sb.st_size - dp->d_bbsize; + if (bootsize > 0) { + /* XXX assume d_secsize is a power of two */ + bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); + bootbuf = (char *)malloc((size_t)bootsize); + if (bootbuf == 0) + Perror(xxboot); + if (read(b, bootbuf, bootsize) < 0) { + free(bootbuf); + Perror(xxboot); + } + } +#endif + (void)close(b); +#endif + /* + * Make sure no part of the bootstrap is written in the area + * reserved for the label. + */ + for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) + if (*p) { + fprintf(stderr, + "Bootstrap doesn't leave room for disk label\n"); + exit(2); + } + return (lp); +} + +display(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register int i, j; + register struct partition *pp; + + fprintf(f, "# %s:\n", specname); + if ((unsigned) lp->d_type < DKMAXTYPES) + fprintf(f, "type: %s\n", dktypenames[lp->d_type]); + else + fprintf(f, "type: %d\n", lp->d_type); + fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename); + fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname); + fprintf(f, "flags:"); + if (lp->d_flags & D_REMOVABLE) + fprintf(f, " removeable"); + if (lp->d_flags & D_ECC) + fprintf(f, " ecc"); + if (lp->d_flags & D_BADSECT) + fprintf(f, " badsect"); + fprintf(f, "\n"); + fprintf(f, "bytes/sector: %d\n", lp->d_secsize); + fprintf(f, "sectors/track: %d\n", lp->d_nsectors); + fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks); + fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl); + fprintf(f, "cylinders: %d\n", lp->d_ncylinders); + fprintf(f, "rpm: %d\n", lp->d_rpm); + fprintf(f, "interleave: %d\n", lp->d_interleave); + fprintf(f, "trackskew: %d\n", lp->d_trackskew); + fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); + fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch); + fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek); + fprintf(f, "drivedata: "); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fprintf(f, "%d ", lp->d_drivedata[j]); + fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); + fprintf(f, + "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8d %8d ", 'a' + i, + pp->p_size, pp->p_offset); + if ((unsigned) pp->p_fstype < FSMAXTYPES) + fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); + else + fprintf(f, "%8d", pp->p_fstype); + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + fprintf(f, " %5d %5d %5.5s ", + pp->p_fsize, pp->p_fsize * pp->p_frag, ""); + break; + + case FS_BSDFFS: + fprintf(f, " %5d %5d %5d ", + pp->p_fsize, pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + fprintf(f, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4d", + pp->p_offset / lp->d_secpercyl); + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %d", + (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / + lp->d_secpercyl - 1); + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } + fflush(f); +} + +edit(lp, f) + struct disklabel *lp; + int f; +{ + register int c; + struct disklabel label; + FILE *fd; + char *mktemp(); + + (void) mktemp(tmpfil); + fd = fopen(tmpfil, "w"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't create\n", tmpfil); + return (1); + } + (void)fchmod(fileno(fd), 0600); + display(fd, lp); + fclose(fd); + for (;;) { + if (!editit()) + break; + fd = fopen(tmpfil, "r"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't reopen for reading\n", + tmpfil); + break; + } + bzero((char *)&label, sizeof(label)); + if (getasciilabel(fd, &label)) { + *lp = label; + if (writelabel(f, bootarea, lp) == 0) { + (void) unlink(tmpfil); + return (0); + } + } + printf("re-edit the label? [y]: "); fflush(stdout); + c = getchar(); + if (c != EOF && c != (int)'\n') + while (getchar() != (int)'\n') + ; + if (c == (int)'n') + break; + } + (void) unlink(tmpfil); + return (1); +} + +editit() +{ + register int pid, xpid; + int stat, omask; + extern char *getenv(); + + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = fork()) < 0) { + extern int errno; + + if (errno == EPROCLIM) { + fprintf(stderr, "You have too many processes\n"); + return(0); + } + if (errno != EAGAIN) { + perror("fork"); + return(0); + } + sleep(1); + } + if (pid == 0) { + register char *ed; + + sigsetmask(omask); + setgid(getgid()); + setuid(getuid()); + if ((ed = getenv("EDITOR")) == (char *)0) + ed = DEFEDITOR; + execlp(ed, ed, tmpfil, 0); + perror(ed); + exit(1); + } + while ((xpid = wait(&stat)) >= 0) + if (xpid == pid) + break; + sigsetmask(omask); + return(!stat); +} + +char * +skip(cp) + register char *cp; +{ + + while (*cp != '\0' && isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return ((char *)NULL); + return (cp); +} + +char * +word(cp) + register char *cp; +{ + register char c; + + while (*cp != '\0' && !isspace(*cp) && *cp != '#') + cp++; + if ((c = *cp) != '\0') { + *cp++ = '\0'; + if (c != '#') + return (skip(cp)); + } + return ((char *)NULL); +} + +/* + * Read an ascii label in from fd f, + * in the same format as that put out by display(), + * and fill in lp. + */ +getasciilabel(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register char **cpp, *cp; + register struct partition *pp; + char *tp, *s, line[BUFSIZ]; + int v, lineno = 0, errors = 0; + + lp->d_bbsize = BBSIZE; /* XXX */ + lp->d_sbsize = SBSIZE; /* XXX */ + while (fgets(line, sizeof(line) - 1, f)) { + lineno++; + if (cp = index(line,'\n')) + *cp = '\0'; + cp = skip(line); + if (cp == NULL) + continue; + tp = index(cp, ':'); + if (tp == NULL) { + fprintf(stderr, "line %d: syntax error\n", lineno); + errors++; + continue; + } + *tp++ = '\0', tp = skip(tp); + if (streq(cp, "type")) { + if (tp == NULL) + tp = "unknown"; + cpp = dktypenames; + for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, tp)) { + lp->d_type = cpp - dktypenames; + goto next; + } + v = atoi(tp); + if ((unsigned)v >= DKMAXTYPES) + fprintf(stderr, "line %d:%s %d\n", lineno, + "Warning, unknown disk type", v); + lp->d_type = v; + continue; + } + if (streq(cp, "flags")) { + for (v = 0; (cp = tp) && *cp != '\0';) { + tp = word(cp); + if (streq(cp, "removeable")) + v |= D_REMOVABLE; + else if (streq(cp, "ecc")) + v |= D_ECC; + else if (streq(cp, "badsect")) + v |= D_BADSECT; + else { + fprintf(stderr, + "line %d: %s: bad flag\n", + lineno, cp); + errors++; + } + } + lp->d_flags = v; + continue; + } + if (streq(cp, "drivedata")) { + register int i; + + for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { + lp->d_drivedata[i++] = atoi(cp); + tp = word(cp); + } + continue; + } + if (sscanf(cp, "%d partitions", &v) == 1) { + if (v == 0 || (unsigned)v > MAXPARTITIONS) { + fprintf(stderr, + "line %d: bad # of partitions\n", lineno); + lp->d_npartitions = MAXPARTITIONS; + errors++; + } else + lp->d_npartitions = v; + continue; + } + if (tp == NULL) + tp = ""; + if (streq(cp, "disk")) { + strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); + continue; + } + if (streq(cp, "label")) { + strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); + continue; + } + if (streq(cp, "bytes/sector")) { + v = atoi(tp); + if (v <= 0 || (v % 512) != 0) { + fprintf(stderr, + "line %d: %s: bad sector size\n", + lineno, tp); + errors++; + } else + lp->d_secsize = v; + continue; + } + if (streq(cp, "sectors/track")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_nsectors = v; + continue; + } + if (streq(cp, "sectors/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_secpercyl = v; + continue; + } + if (streq(cp, "tracks/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ntracks = v; + continue; + } + if (streq(cp, "cylinders")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ncylinders = v; + continue; + } + if (streq(cp, "rpm")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_rpm = v; + continue; + } + if (streq(cp, "interleave")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_interleave = v; + continue; + } + if (streq(cp, "trackskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trackskew = v; + continue; + } + if (streq(cp, "cylinderskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_cylskew = v; + continue; + } + if (streq(cp, "headswitch")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_headswitch = v; + continue; + } + if (streq(cp, "track-to-track seek")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trkseek = v; + continue; + } + if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { + unsigned part = *cp - 'a'; + + if (part > lp->d_npartitions) { + fprintf(stderr, + "line %d: bad partition name\n", lineno); + errors++; + continue; + } + pp = &lp->d_partitions[part]; +#define NXTNUM(n) { \ + cp = tp, tp = word(cp); \ + if (tp == NULL) \ + tp = cp; \ + (n) = atoi(cp); \ + } + + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition size\n", + lineno, cp); + errors++; + } else + pp->p_size = v; + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition offset\n", + lineno, cp); + errors++; + } else + pp->p_offset = v; + cp = tp, tp = word(cp); + cpp = fstypenames; + for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, cp)) { + pp->p_fstype = cpp - fstypenames; + goto gottype; + } + if (isdigit(*cp)) + v = atoi(cp); + else + v = FSMAXTYPES; + if ((unsigned)v >= FSMAXTYPES) { + fprintf(stderr, "line %d: %s %s\n", lineno, + "Warning, unknown filesystem type", cp); + v = FS_UNUSED; + } + pp->p_fstype = v; + gottype: + + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + break; + + case FS_BSDFFS: + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + NXTNUM(pp->p_cpg); + break; + + default: + break; + } + continue; + } + fprintf(stderr, "line %d: %s: Unknown disklabel field\n", + lineno, cp); + errors++; + next: + ; + } + errors += checklabel(lp); + return (errors == 0); +} + +/* + * Check disklabel for errors and fill in + * derived fields according to supplied values. + */ +checklabel(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + + if (lp->d_secsize == 0) { + fprintf(stderr, "sector size %d\n", lp->d_secsize); + return (1); + } + if (lp->d_nsectors == 0) { + fprintf(stderr, "sectors/track %d\n", lp->d_nsectors); + return (1); + } + if (lp->d_ntracks == 0) { + fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks); + return (1); + } + if (lp->d_ncylinders == 0) { + fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders); + errors++; + } + if (lp->d_rpm == 0) + Warning("revolutions/minute %d", lp->d_rpm); + if (lp->d_secpercyl == 0) + lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; + if (lp->d_secperunit == 0) + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + if (lp->d_bbsize == 0) { + fprintf(stderr, "boot block size %d\n", lp->d_bbsize); + errors++; + } else if (lp->d_bbsize % lp->d_secsize) + Warning("boot block size %% sector-size != 0"); + if (lp->d_sbsize == 0) { + fprintf(stderr, "super block size %d\n", lp->d_sbsize); + errors++; + } else if (lp->d_sbsize % lp->d_secsize) + Warning("super block size %% sector-size != 0"); + if (lp->d_npartitions > MAXPARTITIONS) + Warning("number of partitions (%d) > MAXPARTITIONS (%d)", + lp->d_npartitions, MAXPARTITIONS); + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0 && pp->p_offset != 0) + Warning("partition %c: size 0, but offset %d", + part, pp->p_offset); +#ifdef notdef + if (pp->p_size % lp->d_secpercyl) + Warning("partition %c: size %% cylinder-size != 0", + part); + if (pp->p_offset % lp->d_secpercyl) + Warning("partition %c: offset %% cylinder-size != 0", + part); +#endif + if (pp->p_offset > lp->d_secperunit) { + fprintf(stderr, + "partition %c: offset past end of unit\n", part); + errors++; + } + if (pp->p_offset + pp->p_size > lp->d_secperunit) { + fprintf(stderr, + "partition %c: partition extends past end of unit\n", + part); + errors++; + } + } + for (; i < MAXPARTITIONS; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size || pp->p_offset) + Warning("unused partition %c: size %d offset %d", + 'a' + i, pp->p_size, pp->p_offset); + } + return (errors); +} + +/* + * If we are installing a boot program that doesn't fit in d_bbsize + * we need to mark those partitions that the boot overflows into. + * This allows newfs to prevent creation of a filesystem where it might + * clobber bootstrap code. + */ +setbootflag(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + u_long boffset; + + if (bootbuf == 0) + return; + boffset = bootsize / lp->d_secsize; + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0) + continue; + if (boffset <= pp->p_offset) { + if (pp->p_fstype == FS_BOOT) + pp->p_fstype = FS_UNUSED; + } else if (pp->p_fstype != FS_BOOT) { + if (pp->p_fstype != FS_UNUSED) { + fprintf(stderr, + "boot overlaps used partition %c\n", + part); + errors++; + } else { + pp->p_fstype = FS_BOOT; + Warning("boot overlaps partition %c, %s", + part, "marked as FS_BOOT"); + } + } + } + if (errors) { + fprintf(stderr, "Cannot install boot program\n"); + exit(4); + } +} + +/*VARARGS1*/ +Warning(fmt, a1, a2, a3, a4, a5) + char *fmt; +{ + + fprintf(stderr, "Warning, "); + fprintf(stderr, fmt, a1, a2, a3, a4, a5); + fprintf(stderr, "\n"); +} + +Perror(str) + char *str; +{ + fputs("disklabel: ", stderr); perror(str); + exit(4); +} + +usage() +{ +#if NUMBOOT > 0 + fprintf(stderr, +"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n", +"usage: disklabel [-r] disk", + "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", + "(to write label with existing boot program)", +"or disklabel -e [-r] disk", + "(to edit label)", +"or disklabel -R [-r] disk protofile", + "(to restore label with existing boot program)", +#if NUMBOOT > 1 +"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", + "(to install boot program with existing label)", +"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", + "(to write label and boot program)", +"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", + "(to restore label and boot program)", +#else +"or disklabel -B [ -b bootprog ] disk [ type ]", + "(to install boot program with existing on-disk label)", +"or disklabel -w -B [ -b bootprog ] disk type [ packid ]", + "(to write label and install boot program)", +"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]", + "(to restore label and install boot program)", +#endif +"or disklabel [-NW] disk", + "(to write disable/enable label)"); +#else + fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n", +"usage: disklabel [-r] disk", "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", "(to write label)", +"or disklabel -e [-r] disk", "(to edit label)", +"or disklabel -R [-r] disk protofile", "(to restore label)", +"or disklabel [-NW] disk", "(to write disable/enable label)"); +#endif + exit(1); +} diff --git a/sbin/bsdlabel/disklabel.5.5 b/sbin/bsdlabel/disklabel.5.5 new file mode 100644 index 000000000000..fb6f6cd14d58 --- /dev/null +++ b/sbin/bsdlabel/disklabel.5.5 @@ -0,0 +1,384 @@ +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.5.5 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DISKLABEL 5 +.Os +.Sh NAME +.Nm disklabel +.Nd disk pack label +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +Each disk or disk pack on a system may contain a disk label +which provides detailed information +about the geometry of the disk and the partitions into which the disk +is divided. +It should be initialized when the disk is formatted, +and may be changed later with the +.Xr disklabel 8 +program. +This information is used by the system disk driver and by the bootstrap +program to determine how to program the drive +and where to find the filesystems on the disk partitions. +Additional information is used by the filesystem in order +to use the disk most efficiently and to locate important filesystem information. +The description of each partition contains an identifier for the partition +type (standard filesystem, swap area, etc.). +The filesystem updates the in-core copy of the label if it contains +incomplete information about the filesystem. +.Pp +The label is located in sector number +.Dv LABELSECTOR +of the drive, usually sector 0 where it may be found +without any information about the disk geometry. +It is at an offset +.Dv LABELOFFSET +from the beginning of the sector, to allow room for the initial bootstrap. +The disk sector containing the label is normally made read-only +so that it is not accidentally overwritten by pack-to-pack copies +or swap operations; +the +.Dv DIOCWLABEL +.Xr ioctl 2 , +which is done as needed by the +.Xr disklabel +program. +.Pp +A copy of the in-core label for a disk can be obtained with the +.Dv DIOCGDINFO +.Xr ioctl ; +this works with a file descriptor for a block or character (``raw'') device +for any partition of the disk. +The in-core copy of the label is set by the +.Dv DIOCSDINFO +.Xr ioctl . +The offset of a partition cannot generally be changed while it is open, +nor can it be made smaller while it is open. +One exception is that any change is allowed if no label was found +on the disk, and the driver was able to construct only a skeletal label +without partition information. +Finally, the +.Dv DIOCWDINFO +.Xr ioctl +operation sets the in-core label and then updates the on-disk label; +there must be an existing label on the disk for this operation to succeed. +Thus, the initial label for a disk or disk pack must be installed +by writing to the raw disk. +All of these operations are normally done using +.Xr disklabel . +.Pp +The format of the disk label, as specified in +.Aw Pa sys/disklabel.h , +is +.Bd -literal +/* +* Disk description table, see disktab(5) +*/ +#define DISKTAB "/etc/disktab" + +/* +* Each disk has a label which includes information about the hardware +* disk geometry, filesystem partitions, and drive specific information. +* The label is in block 0 or 1, possibly offset from the beginning +* to leave room for a bootstrap, etc. +*/ + +#ifndef LABELSECTOR +#define LABELSECTOR 0 /* sector containing label */ +#endif + +#ifndef LABELOFFSET +#define LABELOFFSET 64 /* offset of label in sector */ +#endif + +#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + +#ifndef LOCORE +struct disklabel { + u_long d_magic; /* the magic number */ + short d_type; /* drive type */ + short d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) + to retrieve the values from /etc/disktab. + */ +#if defined(KERNEL) || defined(STANDALONE) + char d_packname[16]; /* pack identifier */ +#else + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; + +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 +#endif /* ! KERNEL or STANDALONE */ + + /* disk geometry: */ + u_long d_secsize; /* # of bytes per sector */ + u_long d_nsectors; /* # of data sectors per track */ + u_long d_ntracks; /* # of tracks per cylinder */ + u_long d_ncylinders; /* # of data cylinders per unit */ + u_long d_secpercyl; /* # of data sectors per cylinder */ + u_long d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + u_short d_sparespertrack; /* # of spare sectors per track */ + u_short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + u_long d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + u_short d_rpm; /* rotational speed */ + u_short d_interleave; /* hardware sector interleave */ + u_short d_trackskew; /* sector 0 skew, per track */ + u_short d_cylskew; /* sector 0 skew, per cylinder */ + u_long d_headswitch; /* head switch time, usec */ + u_long d_trkseek; /* track-to-track seek, usec */ + u_long d_flags; /* generic flags */ +#define NDDATA 5 + u_long d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + u_long d_spare[NSPARE]; /* reserved for future use */ + u_long d_magic2; /* the magic number (again) */ + u_short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + u_short d_npartitions; /* number of partitions in following */ + u_long d_bbsize; /* size of boot area at sn0, bytes */ + u_long d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + u_long p_size; /* number of sectors in partition */ + u_long p_offset; /* starting sector */ + u_long p_fsize; /* filesystem basic fragment size */ + u_char p_fstype; /* filesystem type, see below */ + u_char p_frag; /* filesystem fragments per block */ + union { + u_short cpg; /* UFS: FS cylinders per group */ + u_short sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + u_short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define DTYPE_MSCP 2 /* MSCP */ +#define DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define DTYPE_SCSI 4 /* SCSI */ +#define DTYPE_ESDI 5 /* ESDI interface */ +#define DTYPE_ST506 6 /* ST506 etc. */ +#define DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define DTYPE_HPFL 8 /* HP Fiber-link */ +#define DTYPE_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1) +#endif + +/* +* Filesystem type and version. +* Used to interpret other filesystem-specific +* per-partition information. +*/ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "MSDOS", + "4.4LFS", + "unknown", + "HPFS", + "ISO9660", + "boot", + 0 +}; +#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1) +#endif + +/* +* flags shared by various drives: +*/ +#define D_REMOVABLE 0x01 /* removable media */ +#define D_ECC 0x02 /* supports ECC */ +#define D_BADSECT 0x04 /* supports bad sector forw. */ +#define D_RAMDISK 0x08 /* disk emulator */ +#define D_CHAIN 0x10 /* can do back-back transfers */ + +/* +* Drive data for SMD. +*/ + +#define d_smdflags d_drivedata[0] +#define D_SSE 0x1 /* supports skip sectoring */ +#define d_mindist d_drivedata[1] +#define d_maxdist d_drivedata[2] +#define d_sdist d_drivedata[3] + +/* +* Drive data for ST506. +*/ +#define d_precompcyl d_drivedata[0] +#define d_gap3 d_drivedata[1] /* used only when formatting */ + +/* + * Drive data for SCSI. + */ +#define d_blind d_drivedata[0] + +#ifndef LOCORE +/* +* Structure used to perform a format +* or other raw operation, returning data +* and/or register values. +* Register identification and format +* are device- and driver-dependent. +*/ +struct format_op { + char *df_buf; + int df_count; /* value-result */ + daddr_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* +* Structure used internally to retrieve +* information about a partition on a disk. +*/ +struct partinfo { + struct disklabel *disklab; + struct partition *part; +}; + +/* +* Disk-specific ioctls. +*/ + /* get and set disklabel; DIOCGPART used internally */ +#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */ +#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */ +#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */ +#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */ + +/* do format operation, read or write */ +#define DIOCRFORMAT _IOWR('d', 105, struct format_op) +#define DIOCWFORMAT _IOWR('d', 106, struct format_op) + +#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */ +#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */ +#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */ + +#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */ + +#endif LOCORE +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 +.Sh HISTORY diff --git a/sbin/bsdlabel/dkcksum.c b/sbin/bsdlabel/dkcksum.c new file mode 100644 index 000000000000..f7a1e4917df6 --- /dev/null +++ b/sbin/bsdlabel/dkcksum.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include + +u_short +dkcksum(lp) + register struct disklabel *lp; +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *)lp; + end = (u_short *)&lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} diff --git a/sbin/bsdlabel/pathnames.h b/sbin/bsdlabel/pathnames.h new file mode 100644 index 000000000000..def3297d205e --- /dev/null +++ b/sbin/bsdlabel/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include + +#define _PATH_BOOTDIR "/usr/mdec" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/EdDk.aXXXXXX" diff --git a/sbin/clri/Makefile b/sbin/clri/Makefile new file mode 100644 index 000000000000..deb3cbb17266 --- /dev/null +++ b/sbin/clri/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= clri +MAN8= clri.0 + +.include diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8 new file mode 100644 index 000000000000..df313a778cf3 --- /dev/null +++ b/sbin/clri/clri.8 @@ -0,0 +1,79 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)clri.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt CLRI 8 +.Os BSD 4 +.Sh NAME +.Nm clri +.Nd clear an inode +.Sh SYNOPSIS +.Nm clri +.Ar special_device inode_number ... +.Sh DESCRIPTION +.Bf -symbolic +.Nm Clri +is obsoleted for normal file system repair work by +.Xr fsck 8 . +.Ef +.Pp +.Nm Clri +zeros out the inodes with the specified inode number(s) +on the filesystem residing on the given +.Ar special_device . +The +.Xr fsck 8 +utility is usually run after +.Nm clri +to reclaim the zero'ed inode(s) and the +blocks previously claimed by those inode(s). +Both read and write permission are required on the specified +.Ar special_device . +.Pp +The primary purpose of this routine +is to remove a file which +for some reason is not being properly handled by +.Xr fsck 8 . +Once removed, +it is anticipated that +.Xr fsck 8 +will be able to clean up the resulting mess. +.Sh "SEE ALSO" +.Xr fsck 8 , +.Xr fsdb 8 , +.Xr icheck 8 , +.Xr ncheck 8 +.Sh BUGS +If the file is open, the work of +.Nm clri +will be lost when the inode is written back to disk from the inode cache. diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c new file mode 100644 index 000000000000..c2bdcccc1d9d --- /dev/null +++ b/sbin/clri/clri.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rich $alz of BBN Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93"; +#endif /* not lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fs *sbp; + register struct dinode *ip; + register int fd; + struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)]; + long generation, bsize; + off_t offset; + int inonum; + char *fs, sblock[SBSIZE]; + + if (argc < 3) { + (void)fprintf(stderr, "usage: clri filesystem inode ...\n"); + exit(1); + } + + fs = *++argv; + + /* get the superblock. */ + if ((fd = open(fs, O_RDWR, 0)) < 0) + err(1, "%s", fs); + if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) { + (void)fprintf(stderr, + "clri: %s: can't read the superblock.\n", fs); + exit(1); + } + + sbp = (struct fs *)sblock; + if (sbp->fs_magic != FS_MAGIC) { + (void)fprintf(stderr, + "clri: %s: superblock magic number 0x%x, not 0x%x.\n", + fs, sbp->fs_magic, FS_MAGIC); + exit(1); + } + bsize = sbp->fs_bsize; + + /* remaining arguments are inode numbers. */ + while (*++argv) { + /* get the inode number. */ + if ((inonum = atoi(*argv)) <= 0) { + (void)fprintf(stderr, + "clri: %s is not a valid inode number.\n", *argv); + exit(1); + } + (void)printf("clearing %d\n", inonum); + + /* read in the appropriate block. */ + offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */ + offset = fsbtodb(sbp, offset); /* fs blk disk blk */ + offset *= DEV_BSIZE; /* disk blk to bytes */ + + /* seek and read the block */ + if (lseek(fd, offset, SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + + /* get the inode within the block. */ + ip = &ibuf[ino_to_fsbo(sbp, inonum)]; + + /* clear the inode, and bump the generation count. */ + generation = ip->di_gen + 1; + memset(ip, 0, sizeof(*ip)); + ip->di_gen = generation; + + /* backup and write the block */ + if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0) + err(1, "%s", fs); + if (write(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + (void)fsync(fd); + } + (void)close(fd); + exit(0); +} diff --git a/sbin/disklabel/Makefile b/sbin/disklabel/Makefile new file mode 100644 index 000000000000..3f4749c70f09 --- /dev/null +++ b/sbin/disklabel/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/17/94 + +PROG= disklabel +SRCS= disklabel.c dkcksum.c +MAN8= disklabel.0 +CLEANFILES=disklabel.5.0 + +all: ${PROG} disklabel.5.0 ${MAN8} + +beforeinstall: + install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} disklabel.5.0 \ + ${DESTDIR}${MANDIR}5/disklabel.0 + +.include diff --git a/sbin/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5.5 new file mode 100644 index 000000000000..fb6f6cd14d58 --- /dev/null +++ b/sbin/disklabel/disklabel.5.5 @@ -0,0 +1,384 @@ +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.5.5 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DISKLABEL 5 +.Os +.Sh NAME +.Nm disklabel +.Nd disk pack label +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +Each disk or disk pack on a system may contain a disk label +which provides detailed information +about the geometry of the disk and the partitions into which the disk +is divided. +It should be initialized when the disk is formatted, +and may be changed later with the +.Xr disklabel 8 +program. +This information is used by the system disk driver and by the bootstrap +program to determine how to program the drive +and where to find the filesystems on the disk partitions. +Additional information is used by the filesystem in order +to use the disk most efficiently and to locate important filesystem information. +The description of each partition contains an identifier for the partition +type (standard filesystem, swap area, etc.). +The filesystem updates the in-core copy of the label if it contains +incomplete information about the filesystem. +.Pp +The label is located in sector number +.Dv LABELSECTOR +of the drive, usually sector 0 where it may be found +without any information about the disk geometry. +It is at an offset +.Dv LABELOFFSET +from the beginning of the sector, to allow room for the initial bootstrap. +The disk sector containing the label is normally made read-only +so that it is not accidentally overwritten by pack-to-pack copies +or swap operations; +the +.Dv DIOCWLABEL +.Xr ioctl 2 , +which is done as needed by the +.Xr disklabel +program. +.Pp +A copy of the in-core label for a disk can be obtained with the +.Dv DIOCGDINFO +.Xr ioctl ; +this works with a file descriptor for a block or character (``raw'') device +for any partition of the disk. +The in-core copy of the label is set by the +.Dv DIOCSDINFO +.Xr ioctl . +The offset of a partition cannot generally be changed while it is open, +nor can it be made smaller while it is open. +One exception is that any change is allowed if no label was found +on the disk, and the driver was able to construct only a skeletal label +without partition information. +Finally, the +.Dv DIOCWDINFO +.Xr ioctl +operation sets the in-core label and then updates the on-disk label; +there must be an existing label on the disk for this operation to succeed. +Thus, the initial label for a disk or disk pack must be installed +by writing to the raw disk. +All of these operations are normally done using +.Xr disklabel . +.Pp +The format of the disk label, as specified in +.Aw Pa sys/disklabel.h , +is +.Bd -literal +/* +* Disk description table, see disktab(5) +*/ +#define DISKTAB "/etc/disktab" + +/* +* Each disk has a label which includes information about the hardware +* disk geometry, filesystem partitions, and drive specific information. +* The label is in block 0 or 1, possibly offset from the beginning +* to leave room for a bootstrap, etc. +*/ + +#ifndef LABELSECTOR +#define LABELSECTOR 0 /* sector containing label */ +#endif + +#ifndef LABELOFFSET +#define LABELOFFSET 64 /* offset of label in sector */ +#endif + +#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + +#ifndef LOCORE +struct disklabel { + u_long d_magic; /* the magic number */ + short d_type; /* drive type */ + short d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) + to retrieve the values from /etc/disktab. + */ +#if defined(KERNEL) || defined(STANDALONE) + char d_packname[16]; /* pack identifier */ +#else + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; + +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 +#endif /* ! KERNEL or STANDALONE */ + + /* disk geometry: */ + u_long d_secsize; /* # of bytes per sector */ + u_long d_nsectors; /* # of data sectors per track */ + u_long d_ntracks; /* # of tracks per cylinder */ + u_long d_ncylinders; /* # of data cylinders per unit */ + u_long d_secpercyl; /* # of data sectors per cylinder */ + u_long d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + u_short d_sparespertrack; /* # of spare sectors per track */ + u_short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + u_long d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + u_short d_rpm; /* rotational speed */ + u_short d_interleave; /* hardware sector interleave */ + u_short d_trackskew; /* sector 0 skew, per track */ + u_short d_cylskew; /* sector 0 skew, per cylinder */ + u_long d_headswitch; /* head switch time, usec */ + u_long d_trkseek; /* track-to-track seek, usec */ + u_long d_flags; /* generic flags */ +#define NDDATA 5 + u_long d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + u_long d_spare[NSPARE]; /* reserved for future use */ + u_long d_magic2; /* the magic number (again) */ + u_short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + u_short d_npartitions; /* number of partitions in following */ + u_long d_bbsize; /* size of boot area at sn0, bytes */ + u_long d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + u_long p_size; /* number of sectors in partition */ + u_long p_offset; /* starting sector */ + u_long p_fsize; /* filesystem basic fragment size */ + u_char p_fstype; /* filesystem type, see below */ + u_char p_frag; /* filesystem fragments per block */ + union { + u_short cpg; /* UFS: FS cylinders per group */ + u_short sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + u_short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define DTYPE_MSCP 2 /* MSCP */ +#define DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define DTYPE_SCSI 4 /* SCSI */ +#define DTYPE_ESDI 5 /* ESDI interface */ +#define DTYPE_ST506 6 /* ST506 etc. */ +#define DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define DTYPE_HPFL 8 /* HP Fiber-link */ +#define DTYPE_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1) +#endif + +/* +* Filesystem type and version. +* Used to interpret other filesystem-specific +* per-partition information. +*/ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "MSDOS", + "4.4LFS", + "unknown", + "HPFS", + "ISO9660", + "boot", + 0 +}; +#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1) +#endif + +/* +* flags shared by various drives: +*/ +#define D_REMOVABLE 0x01 /* removable media */ +#define D_ECC 0x02 /* supports ECC */ +#define D_BADSECT 0x04 /* supports bad sector forw. */ +#define D_RAMDISK 0x08 /* disk emulator */ +#define D_CHAIN 0x10 /* can do back-back transfers */ + +/* +* Drive data for SMD. +*/ + +#define d_smdflags d_drivedata[0] +#define D_SSE 0x1 /* supports skip sectoring */ +#define d_mindist d_drivedata[1] +#define d_maxdist d_drivedata[2] +#define d_sdist d_drivedata[3] + +/* +* Drive data for ST506. +*/ +#define d_precompcyl d_drivedata[0] +#define d_gap3 d_drivedata[1] /* used only when formatting */ + +/* + * Drive data for SCSI. + */ +#define d_blind d_drivedata[0] + +#ifndef LOCORE +/* +* Structure used to perform a format +* or other raw operation, returning data +* and/or register values. +* Register identification and format +* are device- and driver-dependent. +*/ +struct format_op { + char *df_buf; + int df_count; /* value-result */ + daddr_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* +* Structure used internally to retrieve +* information about a partition on a disk. +*/ +struct partinfo { + struct disklabel *disklab; + struct partition *part; +}; + +/* +* Disk-specific ioctls. +*/ + /* get and set disklabel; DIOCGPART used internally */ +#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */ +#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */ +#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */ +#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */ + +/* do format operation, read or write */ +#define DIOCRFORMAT _IOWR('d', 105, struct format_op) +#define DIOCWFORMAT _IOWR('d', 106, struct format_op) + +#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */ +#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */ +#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */ + +#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */ + +#endif LOCORE +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 +.Sh HISTORY diff --git a/sbin/disklabel/disklabel.8 b/sbin/disklabel/disklabel.8 new file mode 100644 index 000000000000..bf14e0f23b04 --- /dev/null +++ b/sbin/disklabel/disklabel.8 @@ -0,0 +1,343 @@ +.\" Copyright (c) 1987, 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd "April 19, 1994" +.Dt DISKLABEL 8 +.Os BSD 4.2 +.Sh NAME +.Nm disklabel +.Nd read and write disk pack label +.Sh SYNOPSIS +.Nm disklabel +.Op Fl r +.Ar disk +.Nm disklabel +.Fl w +.Op Fl r +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl e +.Op Fl r +.Ar disk +.Nm disklabel +.Fl R +.Op Fl r +.Ar disk Ar protofile +.Nm disklabel +.Op Fl NW +.Ar disk +.sp +.Nm disklabel +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk +.Oo Ar disktype Oc +.Nm disklabel +.Fl w +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar disktype +.Oo Ar packid Oc +.Nm disklabel +.Fl R +.Fl B +.Oo +.Fl b Ar boot1 +.Op Fl s Ar boot2 +.Oc +.Ar disk Ar protofile +.Oo Ar disktype Oc +.Sh DESCRIPTION +.Nm Disklabel +can be used to install, examine or modify the label on a disk drive or pack. +When writing the label, it can be used +to change the drive identification, +the disk partitions on the drive, +or to replace a damaged label. +On some systems, +.Nm disklabel +can be used to install bootstrap code as well. +There are several forms of the command that read (display), install or edit +the label on a disk. +Each form has an additional option, +.Fl r , +which causes the label to be read from or written to the disk directly, +rather than going through the system's in-core copy of the label. +This option may allow a label to be installed on a disk +without kernel support for a label, such as when labels are first installed +on a system; it must be used when first installing a label on a disk. +The specific effect of +.Fl r +is described under each command. +The read and install forms also support the +.Fl B +option to install bootstrap code. +These variants are described later. +.Pp +The first form of the command (read) is used to examine the label on the named +disk drive (e.g. sd0 or /dev/rsd0c). +It will display all of the parameters associated with the drive +and its partition layout. +Unless the +.Fl r +flag is given, +the kernel's in-core copy of the label is displayed; +if the disk has no label, or the partition types on the disk are incorrect, +the kernel may have constructed or modified the label. +If the +.Fl r +flag is given, the label from the raw disk will be displayed rather +than the in-core label. +.Pp +The second form of the command, with the +.Fl w +flag, is used to write a standard label on the designated drive. +The required arguments to +.Nm disklabel +are the drive to be labelled (e.g. sd0), and +the drive type as described in the +.Xr disktab 5 +file. +The drive parameters and partitions are taken from that file. +If different disks of the same physical type are to have different +partitions, it will be necessary to have separate disktab entries +describing each, or to edit the label after installation as described below. +The optional argument is a pack identification string, +up to 16 characters long. +The pack id must be quoted if it contains blanks. +If the +.Fl r +flag is given, the disk sectors containing the label and bootstrap +will be written directly. +A side-effect of this is that any existing bootstrap code will be overwritten +and the disk rendered unbootable. +If +.Fl r +is not specified, +the existing label will be updated via the in-core copy and any bootstrap +code will be unaffected. +If the disk does not already have a label, the +.Fl r +flag must be used. +In either case, the kernel's in-core label is replaced. +.Pp +An existing disk label may be edited by using the +.Fl e +flag. +The label is read from the in-core kernel copy, +or directly from the disk if the +.Fl r +flag is also given. +The label is formatted and then supplied to an editor for changes. +If no editor is specified in an +.Ev EDITOR +environment variable, +.Xr vi 1 +is used. +When the editor terminates, the formatted label is reread +and used to rewrite the disk label. +Existing bootstrap code is unchanged regardless of whether +.Fl r +was specified. +.Pp +With the +.Fl R +flag, +.Nm disklabel +is capable of restoring a disk label that was formatted +in a prior operation and saved in an ascii file. +The prototype file used to create the label should be in the same format +as that produced when reading or editing a label. +Comments are delimited by +.Ar \&# +and newline. +As with +.Fl w , +any existing bootstrap code will be clobbered if +.Fl r +is specified and will be unaffected otherwise. +.Pp +The +.Fl NW +flags for +.Nm disklabel +explicitly disallow and +allow, respectively, writing of the pack label area on the selected disk. +.Pp +The final three forms of +.Nm disklabel +are used to install boostrap code on machines where the bootstrap is part +of the label. +The bootstrap code is comprised of one or two boot programs depending on +the machine. +The +.Fl B +option is used to denote that bootstrap code is to be installed. +The +.Fl r +flag is implied by +.Fl B +and never needs to be specified. +The name of the boot program(s) to be installed can be selected in a +variety of ways. +First, the names can be specified explicitly via the +.Fl b +and +.Fl s +flags. +On machines with only a single level of boot program, +.Fl b +is the name of that program. +For machines with a two-level bootstrap, +.Fl b +indicates the primary boot program and +.Fl s +the secondary boot program. +If the names are not explicitly given, standard boot programs will be used. +The boot programs are located in +.Pa /usr/mdec . +The names of the programs are taken from the ``b0'' and ``b1'' parameters +of the +.Xr disktab 5 +entry for the disk if +.Ar disktype +was given and its disktab entry exists and includes those parameters. +Otherwise, boot program names are derived from the name of the disk. +These names are of the form +.Pa basename Ns boot +for the primary (or only) bootstrap, and +.Pf boot Pa basename +for the secondary bootstrap; +for example, +.Pa /usr/mdec/sdboot +and +.Pa /usr/mdec/bootsd +if the disk device is +.Em sd0 . +.Pp +The first of the three boot-installation forms is used to install +bootstrap code without changing the existing label. +It is essentially a read command with respect to the disk label +itself and all options are related to the specification of the boot +program as described previously. +The final two forms are analogous to the basic write and restore versions +except that they will install bootstrap code in addition to a new label. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/disktab +.It Pa /usr/mdec/ Ns Em xx Ns boot +.It Pa /usr/mdec/boot Ns Em xx +.El +.Sh EXAMPLES +.Dl disklabel sd0 +.Pp +Display the in-core label for sd0 as obtained via +.Pa /dev/rsd0c . +.Pp +.Dl disklabel -w -r /dev/rsd0c sd2212 foo +.Pp +Create a label for sd0 based on information for ``sd2212'' found in +.Pa /etc/disktab . +Any existing bootstrap code will be clobbered. +.Pp +.Dl disklabel -e -r sd0 +.Pp +Read the on-disk label for sd0, edit it and reinstall in-core as well +as on-disk. +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -R sd0 mylabel +.Pp +Restore the on-disk and in-core label for sd0 from information in +.Pa mylabel . +Existing bootstrap code is unaffected. +.Pp +.Dl disklabel -B sd0 +.Pp +Install a new bootstrap on sd0. +The boot code comes from +.Pa /usr/mdec/sdboot +and possibly +.Pa /usr/mdec/bootsd . +On-disk and in-core labels are unchanged. +.Pp +.Dl disklabel -w -B /dev/rsd0c -b newboot sd2212 +.Pp +Install a new label and bootstrap. +The label is derived from disktab information for ``sd2212'' and +installed both in-core and on-disk. +The bootstrap code comes from the file +.Pa /usr/mdec/newboot . +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 5 +.Sh DIAGNOSTICS +The kernel device drivers will not allow the size of a disk partition +to be decreased or the offset of a partition to be changed while it is open. +Some device drivers create a label containing only a single large partition +if a disk is unlabeled; thus, the label must be written to the ``a'' +partition of the disk while it is open. +This sometimes requires the desired label to be set in two steps, +the first one creating at least one other partition, +and the second setting the label on the new partition +while shrinking the ``a'' partition. +.Pp +On some machines the bootstrap code may not fit entirely in the area +allocated for it by some filesystems. +As a result, it may not be possible to have filesystems on some partitions +of a ``bootable'' disk. +When installing bootstrap code, +.Nm disklabel +checks for these cases. +If the installed boot code would overlap a partition of type FS_UNUSED +it is marked as type FS_BOOT. +The +.Xr newfs 8 +utility will disallow creation of filesystems on FS_BOOT partitions. +Conversely, if a partition has a type other than FS_UNUSED or FS_BOOT, +.Nm disklabel +will not install bootstrap code that overlaps it. +.Sh BUGS +When a disk name is given without a full pathname, +the constructed device name uses the ``a'' partition on the tahoe, +the ``c'' partition on all others. diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c new file mode 100644 index 000000000000..4a2934078584 --- /dev/null +++ b/sbin/disklabel/disklabel.c @@ -0,0 +1,1314 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Symmetric Computer Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; +/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * Disklabel: read and write disklabels. + * The label is usually placed on one of the first sectors of the disk. + * Many machines also place a bootstrap in the same area, + * in which case the label is embedded in the bootstrap. + * The bootstrap source must leave space at the proper offset + * for the label on such machines. + */ + +#ifdef tahoe +#define RAWPARTITION 'a' +#else +#define RAWPARTITION 'c' +#endif + +#ifndef BBSIZE +#define BBSIZE 8192 /* size of boot area, with label */ +#endif + +#ifdef tahoe +#define NUMBOOT 0 +#else +#if defined(hp300) || defined(hp800) +#define NUMBOOT 1 +#else +#define NUMBOOT 2 +#endif +#endif + +#define DEFEDITOR _PATH_VI +#define streq(a,b) (strcmp(a,b) == 0) + +char *dkname; +char *specname; +char tmpfil[] = _PATH_TMP; + +extern int errno; +char namebuf[BBSIZE], *np = namebuf; +struct disklabel lab; +struct disklabel *readlabel(), *makebootarea(); +char bootarea[BBSIZE]; + +#if NUMBOOT > 0 +int installboot; /* non-zero if we should install a boot program */ +char *bootbuf; /* pointer to buffer with remainder of boot prog */ +int bootsize; /* size of remaining boot program */ +char *xxboot; /* primary boot */ +char *bootxx; /* secondary boot */ +char boot0[MAXPATHLEN]; +char boot1[MAXPATHLEN]; +#endif + +enum { + UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT +} op = UNSPEC; + +int rflag; + +#ifdef DEBUG +int debug; +#define OPTIONS "BNRWb:ders:w" +#else +#define OPTIONS "BNRWb:ers:w" +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register struct disklabel *lp; + FILE *t; + int ch, f, flag, error = 0; + char *name = 0; + + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { +#if NUMBOOT > 0 + case 'B': + ++installboot; + break; + case 'b': + xxboot = optarg; + break; +#if NUMBOOT > 1 + case 's': + bootxx = optarg; + break; +#endif +#endif + case 'N': + if (op != UNSPEC) + usage(); + op = NOWRITE; + break; + case 'R': + if (op != UNSPEC) + usage(); + op = RESTORE; + break; + case 'W': + if (op != UNSPEC) + usage(); + op = WRITEABLE; + break; + case 'e': + if (op != UNSPEC) + usage(); + op = EDIT; + break; + case 'r': + ++rflag; + break; + case 'w': + if (op != UNSPEC) + usage(); + op = WRITE; + break; +#ifdef DEBUG + case 'd': + debug++; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; +#if NUMBOOT > 0 + if (installboot) { + rflag++; + if (op == UNSPEC) + op = WRITEBOOT; + } else { + if (op == UNSPEC) + op = READ; + xxboot = bootxx = 0; + } +#else + if (op == UNSPEC) + op = READ; +#endif + if (argc < 1) + usage(); + + dkname = argv[0]; + if (dkname[0] != '/') { + (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); + specname = np; + np += strlen(specname) + 1; + } else + specname = dkname; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + if (f < 0 && errno == ENOENT && dkname[0] != '/') { + (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); + np = namebuf + strlen(specname) + 1; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + } + if (f < 0) + Perror(specname); + + switch(op) { + + case EDIT: + if (argc != 1) + usage(); + lp = readlabel(f); + error = edit(lp, f); + break; + + case NOWRITE: + flag = 0; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + + case READ: + if (argc != 1) + usage(); + lp = readlabel(f); + display(stdout, lp); + error = checklabel(lp); + break; + + case RESTORE: +#if NUMBOOT > 0 + if (installboot && argc == 3) { + makelabel(argv[2], 0, &lab); + argc--; + } +#endif + if (argc != 2) + usage(); + lp = makebootarea(bootarea, &lab, f); + if (!(t = fopen(argv[1], "r"))) + Perror(argv[1]); + if (getasciilabel(t, lp)) + error = writelabel(f, bootarea, lp); + break; + + case WRITE: + if (argc == 3) { + name = argv[2]; + argc--; + } + if (argc != 2) + usage(); + makelabel(argv[1], name, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = lab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + + case WRITEABLE: + flag = 1; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + +#if NUMBOOT > 0 + case WRITEBOOT: + { + struct disklabel tlab; + + lp = readlabel(f); + tlab = *lp; + if (argc == 2) + makelabel(argv[1], 0, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = tlab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + } +#endif + } + exit(error); +} + +/* + * Construct a prototype disklabel from /etc/disktab. As a side + * effect, set the names of the primary and secondary boot files + * if specified. + */ +makelabel(type, name, lp) + char *type, *name; + register struct disklabel *lp; +{ + register struct disklabel *dp; + char *strcpy(); + + dp = getdiskbyname(type); + if (dp == NULL) { + fprintf(stderr, "%s: unknown disk type\n", type); + exit(1); + } + *lp = *dp; +#if NUMBOOT > 0 + /* + * Set bootstrap name(s). + * 1. If set from command line, use those, + * 2. otherwise, check if disktab specifies them (b0 or b1), + * 3. otherwise, makebootarea() will choose ones based on the name + * of the disk special file. E.g. /dev/ra0 -> raboot, bootra + */ + if (!xxboot && lp->d_boot0) { + if (*lp->d_boot0 != '/') + (void)sprintf(boot0, "%s/%s", + _PATH_BOOTDIR, lp->d_boot0); + else + (void)strcpy(boot0, lp->d_boot0); + xxboot = boot0; + } +#if NUMBOOT > 1 + if (!bootxx && lp->d_boot1) { + if (*lp->d_boot1 != '/') + (void)sprintf(boot1, "%s/%s", + _PATH_BOOTDIR, lp->d_boot1); + else + (void)strcpy(boot1, lp->d_boot1); + bootxx = boot1; + } +#endif +#endif + /* d_packname is union d_boot[01], so zero */ + bzero(lp->d_packname, sizeof(lp->d_packname)); + if (name) + (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); +} + +writelabel(f, boot, lp) + int f; + char *boot; + register struct disklabel *lp; +{ + register int i; + int flag; + + setbootflag(lp); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (rflag) { + /* + * First set the kernel disk label, + * then write a label to the raw disk. + * If the SDINFO ioctl fails because it is unimplemented, + * keep going; otherwise, the kernel consistency checks + * may prevent us from changing the current (in-core) + * label. + */ + if (ioctl(f, DIOCSDINFO, lp) < 0 && + errno != ENODEV && errno != ENOTTY) { + l_perror("ioctl DIOCSDINFO"); + return (1); + } + (void)lseek(f, (off_t)0, SEEK_SET); + /* + * write enable label sector before write (if necessary), + * disable after writing. + */ + flag = 1; + if (ioctl(f, DIOCWLABEL, &flag) < 0) + perror("ioctl DIOCWLABEL"); + if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { + perror("write"); + return (1); + } +#if NUMBOOT > 0 + /* + * Output the remainder of the disklabel + */ + if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { + perror("write"); + return(1); + } +#endif + flag = 0; + (void) ioctl(f, DIOCWLABEL, &flag); + } else if (ioctl(f, DIOCWDINFO, lp) < 0) { + l_perror("ioctl DIOCWDINFO"); + return (1); + } +#ifdef vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + daddr_t alt; + + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), + SEEK_SET); + if (write(f, boot, lp->d_secsize) < lp->d_secsize) { + int oerrno = errno; + fprintf(stderr, "alternate label %d ", i/2); + errno = oerrno; + perror("write"); + } + } + } +#endif + return (0); +} + +l_perror(s) + char *s; +{ + int saverrno = errno; + + fprintf(stderr, "disklabel: %s: ", s); + + switch (saverrno) { + + case ESRCH: + fprintf(stderr, "No disk label on disk;\n"); + fprintf(stderr, + "use \"disklabel -r\" to install initial label\n"); + break; + + case EINVAL: + fprintf(stderr, "Label magic number or checksum is wrong!\n"); + fprintf(stderr, "(disklabel or kernel is out of date?)\n"); + break; + + case EBUSY: + fprintf(stderr, "Open partition would move or shrink\n"); + break; + + case EXDEV: + fprintf(stderr, + "Labeled partition or 'a' partition must start at beginning of disk\n"); + break; + + default: + errno = saverrno; + perror((char *)NULL); + break; + } +} + +/* + * Fetch disklabel for disk. + * Use ioctl to get label unless -r flag is given. + */ +struct disklabel * +readlabel(f) + int f; +{ + register struct disklabel *lp; + + if (rflag) { + if (read(f, bootarea, BBSIZE) < BBSIZE) + Perror(specname); + for (lp = (struct disklabel *)bootarea; + lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); + lp = (struct disklabel *)((char *)lp + 16)) + if (lp->d_magic == DISKMAGIC && + lp->d_magic2 == DISKMAGIC) + break; + if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || + lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || + dkcksum(lp) != 0) { + fprintf(stderr, + "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); + /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ + exit (1); + } + } else { + lp = &lab; + if (ioctl(f, DIOCGDINFO, lp) < 0) + Perror("ioctl DIOCGDINFO"); + } + return (lp); +} + +/* + * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' + * Returns a pointer to the disklabel portion of the bootarea. + */ +struct disklabel * +makebootarea(boot, dp, f) + char *boot; + register struct disklabel *dp; + int f; +{ + struct disklabel *lp; + register char *p; + int b; +#if NUMBOOT > 0 + char *dkbasename; + struct stat sb; +#endif + + /* XXX */ + if (dp->d_secsize == 0) { + dp->d_secsize = DEV_BSIZE; + dp->d_bbsize = BBSIZE; + } + lp = (struct disklabel *) + (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); + bzero((char *)lp, sizeof *lp); +#if NUMBOOT > 0 + /* + * If we are not installing a boot program but we are installing a + * label on disk then we must read the current bootarea so we don't + * clobber the existing boot. + */ + if (!installboot) { + if (rflag) { + if (read(f, boot, BBSIZE) < BBSIZE) + Perror(specname); + bzero((char *)lp, sizeof *lp); + } + return (lp); + } + /* + * We are installing a boot program. Determine the name(s) and + * read them into the appropriate places in the boot area. + */ + if (!xxboot || !bootxx) { + dkbasename = np; + if ((p = rindex(dkname, '/')) == NULL) + p = dkname; + else + p++; + while (*p && !isdigit(*p)) + *np++ = *p++; + *np++ = '\0'; + + if (!xxboot) { + (void)sprintf(np, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + xxboot = np; + (void)sprintf(xxboot, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + np += strlen(xxboot) + 1; + } +#if NUMBOOT > 1 + if (!bootxx) { + (void)sprintf(np, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + bootxx = np; + (void)sprintf(bootxx, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + np += strlen(bootxx) + 1; + } +#endif + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", + xxboot, bootxx ? bootxx : "NONE"); +#endif + + /* + * Strange rules: + * 1. One-piece bootstrap (hp300/hp800) + * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest + * is remembered and written later following the bootarea. + * 2. Two-piece bootstraps (vax/i386?/mips?) + * up to d_secsize bytes of ``xxboot'' go in first d_secsize + * bytes of bootarea, remaining d_bbsize-d_secsize filled + * from ``bootxx''. + */ + b = open(xxboot, O_RDONLY); + if (b < 0) + Perror(xxboot); +#if NUMBOOT > 1 + if (read(b, boot, (int)dp->d_secsize) < 0) + Perror(xxboot); + (void)close(b); + b = open(bootxx, O_RDONLY); + if (b < 0) + Perror(bootxx); + if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0) + Perror(bootxx); +#else + if (read(b, boot, (int)dp->d_bbsize) < 0) + Perror(xxboot); + (void)fstat(b, &sb); + bootsize = (int)sb.st_size - dp->d_bbsize; + if (bootsize > 0) { + /* XXX assume d_secsize is a power of two */ + bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); + bootbuf = (char *)malloc((size_t)bootsize); + if (bootbuf == 0) + Perror(xxboot); + if (read(b, bootbuf, bootsize) < 0) { + free(bootbuf); + Perror(xxboot); + } + } +#endif + (void)close(b); +#endif + /* + * Make sure no part of the bootstrap is written in the area + * reserved for the label. + */ + for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) + if (*p) { + fprintf(stderr, + "Bootstrap doesn't leave room for disk label\n"); + exit(2); + } + return (lp); +} + +display(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register int i, j; + register struct partition *pp; + + fprintf(f, "# %s:\n", specname); + if ((unsigned) lp->d_type < DKMAXTYPES) + fprintf(f, "type: %s\n", dktypenames[lp->d_type]); + else + fprintf(f, "type: %d\n", lp->d_type); + fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename); + fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname); + fprintf(f, "flags:"); + if (lp->d_flags & D_REMOVABLE) + fprintf(f, " removeable"); + if (lp->d_flags & D_ECC) + fprintf(f, " ecc"); + if (lp->d_flags & D_BADSECT) + fprintf(f, " badsect"); + fprintf(f, "\n"); + fprintf(f, "bytes/sector: %d\n", lp->d_secsize); + fprintf(f, "sectors/track: %d\n", lp->d_nsectors); + fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks); + fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl); + fprintf(f, "cylinders: %d\n", lp->d_ncylinders); + fprintf(f, "rpm: %d\n", lp->d_rpm); + fprintf(f, "interleave: %d\n", lp->d_interleave); + fprintf(f, "trackskew: %d\n", lp->d_trackskew); + fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); + fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch); + fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek); + fprintf(f, "drivedata: "); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fprintf(f, "%d ", lp->d_drivedata[j]); + fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); + fprintf(f, + "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8d %8d ", 'a' + i, + pp->p_size, pp->p_offset); + if ((unsigned) pp->p_fstype < FSMAXTYPES) + fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); + else + fprintf(f, "%8d", pp->p_fstype); + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + fprintf(f, " %5d %5d %5.5s ", + pp->p_fsize, pp->p_fsize * pp->p_frag, ""); + break; + + case FS_BSDFFS: + fprintf(f, " %5d %5d %5d ", + pp->p_fsize, pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + fprintf(f, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4d", + pp->p_offset / lp->d_secpercyl); + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %d", + (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / + lp->d_secpercyl - 1); + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } + fflush(f); +} + +edit(lp, f) + struct disklabel *lp; + int f; +{ + register int c; + struct disklabel label; + FILE *fd; + char *mktemp(); + + (void) mktemp(tmpfil); + fd = fopen(tmpfil, "w"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't create\n", tmpfil); + return (1); + } + (void)fchmod(fileno(fd), 0600); + display(fd, lp); + fclose(fd); + for (;;) { + if (!editit()) + break; + fd = fopen(tmpfil, "r"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't reopen for reading\n", + tmpfil); + break; + } + bzero((char *)&label, sizeof(label)); + if (getasciilabel(fd, &label)) { + *lp = label; + if (writelabel(f, bootarea, lp) == 0) { + (void) unlink(tmpfil); + return (0); + } + } + printf("re-edit the label? [y]: "); fflush(stdout); + c = getchar(); + if (c != EOF && c != (int)'\n') + while (getchar() != (int)'\n') + ; + if (c == (int)'n') + break; + } + (void) unlink(tmpfil); + return (1); +} + +editit() +{ + register int pid, xpid; + int stat, omask; + extern char *getenv(); + + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = fork()) < 0) { + extern int errno; + + if (errno == EPROCLIM) { + fprintf(stderr, "You have too many processes\n"); + return(0); + } + if (errno != EAGAIN) { + perror("fork"); + return(0); + } + sleep(1); + } + if (pid == 0) { + register char *ed; + + sigsetmask(omask); + setgid(getgid()); + setuid(getuid()); + if ((ed = getenv("EDITOR")) == (char *)0) + ed = DEFEDITOR; + execlp(ed, ed, tmpfil, 0); + perror(ed); + exit(1); + } + while ((xpid = wait(&stat)) >= 0) + if (xpid == pid) + break; + sigsetmask(omask); + return(!stat); +} + +char * +skip(cp) + register char *cp; +{ + + while (*cp != '\0' && isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return ((char *)NULL); + return (cp); +} + +char * +word(cp) + register char *cp; +{ + register char c; + + while (*cp != '\0' && !isspace(*cp) && *cp != '#') + cp++; + if ((c = *cp) != '\0') { + *cp++ = '\0'; + if (c != '#') + return (skip(cp)); + } + return ((char *)NULL); +} + +/* + * Read an ascii label in from fd f, + * in the same format as that put out by display(), + * and fill in lp. + */ +getasciilabel(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register char **cpp, *cp; + register struct partition *pp; + char *tp, *s, line[BUFSIZ]; + int v, lineno = 0, errors = 0; + + lp->d_bbsize = BBSIZE; /* XXX */ + lp->d_sbsize = SBSIZE; /* XXX */ + while (fgets(line, sizeof(line) - 1, f)) { + lineno++; + if (cp = index(line,'\n')) + *cp = '\0'; + cp = skip(line); + if (cp == NULL) + continue; + tp = index(cp, ':'); + if (tp == NULL) { + fprintf(stderr, "line %d: syntax error\n", lineno); + errors++; + continue; + } + *tp++ = '\0', tp = skip(tp); + if (streq(cp, "type")) { + if (tp == NULL) + tp = "unknown"; + cpp = dktypenames; + for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, tp)) { + lp->d_type = cpp - dktypenames; + goto next; + } + v = atoi(tp); + if ((unsigned)v >= DKMAXTYPES) + fprintf(stderr, "line %d:%s %d\n", lineno, + "Warning, unknown disk type", v); + lp->d_type = v; + continue; + } + if (streq(cp, "flags")) { + for (v = 0; (cp = tp) && *cp != '\0';) { + tp = word(cp); + if (streq(cp, "removeable")) + v |= D_REMOVABLE; + else if (streq(cp, "ecc")) + v |= D_ECC; + else if (streq(cp, "badsect")) + v |= D_BADSECT; + else { + fprintf(stderr, + "line %d: %s: bad flag\n", + lineno, cp); + errors++; + } + } + lp->d_flags = v; + continue; + } + if (streq(cp, "drivedata")) { + register int i; + + for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { + lp->d_drivedata[i++] = atoi(cp); + tp = word(cp); + } + continue; + } + if (sscanf(cp, "%d partitions", &v) == 1) { + if (v == 0 || (unsigned)v > MAXPARTITIONS) { + fprintf(stderr, + "line %d: bad # of partitions\n", lineno); + lp->d_npartitions = MAXPARTITIONS; + errors++; + } else + lp->d_npartitions = v; + continue; + } + if (tp == NULL) + tp = ""; + if (streq(cp, "disk")) { + strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); + continue; + } + if (streq(cp, "label")) { + strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); + continue; + } + if (streq(cp, "bytes/sector")) { + v = atoi(tp); + if (v <= 0 || (v % 512) != 0) { + fprintf(stderr, + "line %d: %s: bad sector size\n", + lineno, tp); + errors++; + } else + lp->d_secsize = v; + continue; + } + if (streq(cp, "sectors/track")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_nsectors = v; + continue; + } + if (streq(cp, "sectors/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_secpercyl = v; + continue; + } + if (streq(cp, "tracks/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ntracks = v; + continue; + } + if (streq(cp, "cylinders")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ncylinders = v; + continue; + } + if (streq(cp, "rpm")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_rpm = v; + continue; + } + if (streq(cp, "interleave")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_interleave = v; + continue; + } + if (streq(cp, "trackskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trackskew = v; + continue; + } + if (streq(cp, "cylinderskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_cylskew = v; + continue; + } + if (streq(cp, "headswitch")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_headswitch = v; + continue; + } + if (streq(cp, "track-to-track seek")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trkseek = v; + continue; + } + if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { + unsigned part = *cp - 'a'; + + if (part > lp->d_npartitions) { + fprintf(stderr, + "line %d: bad partition name\n", lineno); + errors++; + continue; + } + pp = &lp->d_partitions[part]; +#define NXTNUM(n) { \ + cp = tp, tp = word(cp); \ + if (tp == NULL) \ + tp = cp; \ + (n) = atoi(cp); \ + } + + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition size\n", + lineno, cp); + errors++; + } else + pp->p_size = v; + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition offset\n", + lineno, cp); + errors++; + } else + pp->p_offset = v; + cp = tp, tp = word(cp); + cpp = fstypenames; + for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, cp)) { + pp->p_fstype = cpp - fstypenames; + goto gottype; + } + if (isdigit(*cp)) + v = atoi(cp); + else + v = FSMAXTYPES; + if ((unsigned)v >= FSMAXTYPES) { + fprintf(stderr, "line %d: %s %s\n", lineno, + "Warning, unknown filesystem type", cp); + v = FS_UNUSED; + } + pp->p_fstype = v; + gottype: + + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + break; + + case FS_BSDFFS: + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + NXTNUM(pp->p_cpg); + break; + + default: + break; + } + continue; + } + fprintf(stderr, "line %d: %s: Unknown disklabel field\n", + lineno, cp); + errors++; + next: + ; + } + errors += checklabel(lp); + return (errors == 0); +} + +/* + * Check disklabel for errors and fill in + * derived fields according to supplied values. + */ +checklabel(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + + if (lp->d_secsize == 0) { + fprintf(stderr, "sector size %d\n", lp->d_secsize); + return (1); + } + if (lp->d_nsectors == 0) { + fprintf(stderr, "sectors/track %d\n", lp->d_nsectors); + return (1); + } + if (lp->d_ntracks == 0) { + fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks); + return (1); + } + if (lp->d_ncylinders == 0) { + fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders); + errors++; + } + if (lp->d_rpm == 0) + Warning("revolutions/minute %d", lp->d_rpm); + if (lp->d_secpercyl == 0) + lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; + if (lp->d_secperunit == 0) + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + if (lp->d_bbsize == 0) { + fprintf(stderr, "boot block size %d\n", lp->d_bbsize); + errors++; + } else if (lp->d_bbsize % lp->d_secsize) + Warning("boot block size %% sector-size != 0"); + if (lp->d_sbsize == 0) { + fprintf(stderr, "super block size %d\n", lp->d_sbsize); + errors++; + } else if (lp->d_sbsize % lp->d_secsize) + Warning("super block size %% sector-size != 0"); + if (lp->d_npartitions > MAXPARTITIONS) + Warning("number of partitions (%d) > MAXPARTITIONS (%d)", + lp->d_npartitions, MAXPARTITIONS); + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0 && pp->p_offset != 0) + Warning("partition %c: size 0, but offset %d", + part, pp->p_offset); +#ifdef notdef + if (pp->p_size % lp->d_secpercyl) + Warning("partition %c: size %% cylinder-size != 0", + part); + if (pp->p_offset % lp->d_secpercyl) + Warning("partition %c: offset %% cylinder-size != 0", + part); +#endif + if (pp->p_offset > lp->d_secperunit) { + fprintf(stderr, + "partition %c: offset past end of unit\n", part); + errors++; + } + if (pp->p_offset + pp->p_size > lp->d_secperunit) { + fprintf(stderr, + "partition %c: partition extends past end of unit\n", + part); + errors++; + } + } + for (; i < MAXPARTITIONS; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size || pp->p_offset) + Warning("unused partition %c: size %d offset %d", + 'a' + i, pp->p_size, pp->p_offset); + } + return (errors); +} + +/* + * If we are installing a boot program that doesn't fit in d_bbsize + * we need to mark those partitions that the boot overflows into. + * This allows newfs to prevent creation of a filesystem where it might + * clobber bootstrap code. + */ +setbootflag(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + u_long boffset; + + if (bootbuf == 0) + return; + boffset = bootsize / lp->d_secsize; + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0) + continue; + if (boffset <= pp->p_offset) { + if (pp->p_fstype == FS_BOOT) + pp->p_fstype = FS_UNUSED; + } else if (pp->p_fstype != FS_BOOT) { + if (pp->p_fstype != FS_UNUSED) { + fprintf(stderr, + "boot overlaps used partition %c\n", + part); + errors++; + } else { + pp->p_fstype = FS_BOOT; + Warning("boot overlaps partition %c, %s", + part, "marked as FS_BOOT"); + } + } + } + if (errors) { + fprintf(stderr, "Cannot install boot program\n"); + exit(4); + } +} + +/*VARARGS1*/ +Warning(fmt, a1, a2, a3, a4, a5) + char *fmt; +{ + + fprintf(stderr, "Warning, "); + fprintf(stderr, fmt, a1, a2, a3, a4, a5); + fprintf(stderr, "\n"); +} + +Perror(str) + char *str; +{ + fputs("disklabel: ", stderr); perror(str); + exit(4); +} + +usage() +{ +#if NUMBOOT > 0 + fprintf(stderr, +"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n", +"usage: disklabel [-r] disk", + "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", + "(to write label with existing boot program)", +"or disklabel -e [-r] disk", + "(to edit label)", +"or disklabel -R [-r] disk protofile", + "(to restore label with existing boot program)", +#if NUMBOOT > 1 +"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", + "(to install boot program with existing label)", +"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", + "(to write label and boot program)", +"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", + "(to restore label and boot program)", +#else +"or disklabel -B [ -b bootprog ] disk [ type ]", + "(to install boot program with existing on-disk label)", +"or disklabel -w -B [ -b bootprog ] disk type [ packid ]", + "(to write label and install boot program)", +"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]", + "(to restore label and install boot program)", +#endif +"or disklabel [-NW] disk", + "(to write disable/enable label)"); +#else + fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n", +"usage: disklabel [-r] disk", "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", "(to write label)", +"or disklabel -e [-r] disk", "(to edit label)", +"or disklabel -R [-r] disk protofile", "(to restore label)", +"or disklabel [-NW] disk", "(to write disable/enable label)"); +#endif + exit(1); +} diff --git a/sbin/disklabel/dkcksum.c b/sbin/disklabel/dkcksum.c new file mode 100644 index 000000000000..f7a1e4917df6 --- /dev/null +++ b/sbin/disklabel/dkcksum.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include + +u_short +dkcksum(lp) + register struct disklabel *lp; +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *)lp; + end = (u_short *)&lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} diff --git a/sbin/disklabel/pathnames.h b/sbin/disklabel/pathnames.h new file mode 100644 index 000000000000..def3297d205e --- /dev/null +++ b/sbin/disklabel/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include + +#define _PATH_BOOTDIR "/usr/mdec" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/EdDk.aXXXXXX" diff --git a/sbin/dmesg/Makefile b/sbin/dmesg/Makefile new file mode 100644 index 000000000000..fc5edce2ae16 --- /dev/null +++ b/sbin/dmesg/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= dmesg +MAN8= dmesg.0 +BINGRP= kmem +BINMODE=2555 +LDADD= -lkvm +DPADD= ${LIBKVM} + +.include diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8 new file mode 100644 index 000000000000..7dd0acf633ed --- /dev/null +++ b/sbin/dmesg/dmesg.8 @@ -0,0 +1,63 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DMESG 8 +.Os BSD 4 +.Sh NAME +.Nm dmesg +.Nd "display the system message buffer" +.Sh SYNOPSIS +.Nm dmesg +.Op Fl M Ar core +.Op Fl N Ar system +.Sh DESCRIPTION +.Nm Dmesg +displays the contents of the system message buffer. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl M +Extract values associated with the name list from the specified core +instead of the default ``/dev/kmem''. +.It Fl N +Extract the name list from the specified system instead of the default +``/vmunix''. +.El +.Sh SEE ALSO +.Xr syslogd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c new file mode 100644 index 000000000000..d789f5dcaa37 --- /dev/null +++ b/sbin/dmesg/dmesg.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nlist nl[] = { +#define X_MSGBUF 0 + { "_msgbufp" }, + { NULL }, +}; + +void usage __P((void)); + +#define KREAD(addr, var) \ + kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var) + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int ch, newl, skip; + register char *p, *ep; + struct msgbuf *bufp, cur; + char *memf, *nlistf; + kvm_t *kd; + char buf[5]; + + memf = nlistf = NULL; + while ((ch = getopt(argc, argv, "M:N:")) != EOF) + switch(ch) { + case 'M': + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (memf != NULL || nlistf != NULL) + setgid(getgid()); + + /* Read in kernel message buffer, do sanity checks. */ + if ((kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg")) == NULL) + exit (1); + if (kvm_nlist(kd, nl) == -1) + errx(1, "kvm_nlist: %s", kvm_geterr(kd)); + if (nl[X_MSGBUF].n_type == 0) + errx(1, "%s: msgbufp not found", nlistf ? nlistf : "namelist"); + if (KREAD(nl[X_MSGBUF].n_value, bufp) || KREAD((long)bufp, cur)) + errx(1, "kvm_read: %s", kvm_geterr(kd)); + kvm_close(kd); + if (cur.msg_magic != MSG_MAGIC) + errx(1, "magic number incorrect"); + if (cur.msg_bufx >= MSG_BSIZE) + cur.msg_bufx = 0; + + /* + * The message buffer is circular; start at the read pointer, and + * go to the write pointer - 1. + */ + p = cur.msg_bufc + cur.msg_bufx; + ep = cur.msg_bufc + cur.msg_bufx - 1; + for (newl = skip = 0; p != ep; ++p) { + if (p == cur.msg_bufc + MSG_BSIZE) + p = cur.msg_bufc; + ch = *p; + /* Skip "\n<.*>" syslog sequences. */ + if (skip) { + if (ch == '>') + newl = skip = 0; + continue; + } + if (newl && ch == '<') { + skip = 1; + continue; + } + if (ch == '\0') + continue; + newl = ch == '\n'; + (void)vis(buf, ch, 0, 0); + if (buf[1] == 0) + (void)putchar(buf[0]); + else + (void)printf("%s", buf); + } + if (!newl) + (void)putchar('\n'); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: dmesg [-M core] [-N system]\n"); + exit(1); +} diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile new file mode 100644 index 000000000000..78ab5d8be25d --- /dev/null +++ b/sbin/dump/Makefile @@ -0,0 +1,25 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +# dump.h header file +# itime.c reads /etc/dumpdates +# main.c driver +# optr.c operator interface +# dumprmt.c handles remote tape via rmt(8) +# tape.c handles the mag tape and opening/closing +# traverse.c traverses the file system +# unctime.c undo ctime +# +# DEBUG use local directory to find ddate and dumpdates +# TDEBUG trace out the process forking + +PROG= dump +LINKS= ${BINDIR}/dump ${BINDIR}/rdump +CFLAGS+=-DRDUMP +SRCS= itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c +BINOWN= root +BINGRP= tty +BINMODE=6555 +MAN8= dump.0 +MLINKS+=dump.8 rdump.8 + +.include diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8 new file mode 100644 index 000000000000..2cd9335c454e --- /dev/null +++ b/sbin/dump/dump.8 @@ -0,0 +1,335 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dump.8 8.1 (Berkeley) 6/16/93 +.\" +.Dd June 16, 1993 +.Dt DUMP 8 +.Os BSD 4 +.Sh NAME +.Nm dump +.Nd filesystem backup +.Sh SYNOPSIS +.Nm dump +.Op Cm 0123456789BbhfusTdWn Op Ar argument ... +.Op Ar filesystem +.Sh DESCRIPTION +.Nm Dump +examines files +on a filesystem +and determines which files +need to be backed up. These files +are copied to the given disk, tape or other +storage medium for safe keeping (see the +.Cm f +option below for doing remote backups). +A dump that is larger than the output medium is broken into +multiple volumes. +On most media the size is determined by writing until an +end-of-media indication is returned. +On media that cannot reliably return an end-of-media indication +(such as some cartridge tape drives) +each volume is of a fixed size; +the actual size is determined by the tape size and density and/or +block count options below. +By default, the same output file name is used for each volume +after prompting the operator to change media. +.Pp +The following options are supported by +.Nm dump: +.Bl -tag -width 4n +.It Cm 0\-9 +Dump levels. +A level 0, full backup, +guarantees the entire file system is copied +(but see also the +.Cm h +option below). +A level number above 0, +incremental backup, +tells dump to +copy all files new or modified since the +last dump of the same or lower level. The default +level is 9. +.It Cm B Ar records +The number of dump records per volume. +This option overrides the calculation of tape size +based on length and density. +.It Cm b Ar blocksize +The number of kilobytes per dump record. +.It Cm h Ar level +Honor the user +.Dq nodump +flag +.Dp Dv UF_NODUMP +only for dumps at or above the given +.Ar level . +The default honor level is 1, +so that incremental backups omit such files +but full backups retain them. +.It Cm f Ar file +Write the backup to +.Ar file ; +.Ar file +may be a special device file +like +.Pa /dev/rmt12 +(a tape drive), +.Pa /dev/rsd1c +(a disk drive), +an ordinary file, +or +.Ql Fl +(the standard output). +Multiple file names may be given as a single argument separated by commas. +Each file will be used for one dump volume in the order listed; +if the dump requires more volumes than the number of names given, +the last file name will used for all remaining volumes after prompting +for media changes. +If the name of the file is of the form +.Dq host:file , +or +.Dq user@host:file , +.Nm dump +writes to the named file on the remote host using +.Xr rmt 8 . +.It Cm d Ar density +Set tape density to +.Ar density . +The default is 1600BPI. +.It Cm n +Whenever +.Nm dump +requires operator attention, +notify all operators in the group +.Dq operator +by means similar to a +.Xr wall 1 . +.It Cm s Ar feet +Attempt to calculate the amount of tape needed +at a particular density. +If this amount is exceeded, +.Nm dump +prompts for a new tape. +It is recommended to be a bit conservative on this option. +The default tape length is 2300 feet. +.It Cm u +Update the file +.Pa /etc/dumpdates +after a successful dump. +The format of +.Pa /etc/dumpdates +is readable by people, consisting of one +free format record per line: +filesystem name, +increment level +and +.Xr ctime 3 +format dump date. +There may be only one entry per filesystem at each level. +The file +.Pa /etc/dumpdates +may be edited to change any of the fields, +if necessary. +.It Cm T Ar date +Use the specified date as the starting time for the dump +instead of the time determined from looking in +.Pa /etc/dumpdates . +The format of date is the same as that of +.Xr ctime 3 . +This option is useful for automated dump scripts that wish to +dump over a specific period of time. +The +.Cm T +option is mutually exclusive from the +.Cm u +option. +.It Cm W +.Nm Dump +tells the operator what file systems need to be dumped. +This information is gleaned from the files +.Pa /etc/dumpdates +and +.Pa /etc/fstab . +The +.Cm W +option causes +.Nm dump +to print out, for each file system in +.Pa /etc/dumpdates +the most recent dump date and level, +and highlights those file systems that should be dumped. +If the +.Cm W +option is set, all other options are ignored, and +.Nm dump +exits immediately. +.It Cm w +Is like W, but prints only those filesystems which need to be dumped. +.El +.Pp +.Nm Dump +requires operator intervention on these conditions: +end of tape, +end of dump, +tape write error, +tape open error or +disk read error (if there are more than a threshold of 32). +In addition to alerting all operators implied by the +.Cm n +key, +.Nm dump +interacts with the operator on +.Em dump's +control terminal at times when +.Nm dump +can no longer proceed, +or if something is grossly wrong. +All questions +.Nm dump +poses +.Em must +be answered by typing +.Dq yes +or +.Dq no , +appropriately. +.Pp +Since making a dump involves a lot of time and effort for full dumps, +.Nm dump +checkpoints itself at the start of each tape volume. +If writing that volume fails for some reason, +.Nm dump +will, +with operator permission, +restart itself from the checkpoint +after the old tape has been rewound and removed, +and a new tape has been mounted. +.Pp +.Nm Dump +tells the operator what is going on at periodic intervals, +including usually low estimates of the number of blocks to write, +the number of tapes it will take, the time to completion, and +the time to the tape change. +The output is verbose, +so that others know that the terminal +controlling +.Nm dump +is busy, +and will be for some time. +.Pp +In the event of a catastrophic disk event, the time required +to restore all the necessary backup tapes or files to disk +can be kept to a minimum by staggering the incremental dumps. +An efficient method of staggering incremental dumps +to minimize the number of tapes follows: +.Bl -bullet -offset indent +.It +Always start with a level 0 backup, for example: +.Bd -literal -offset indent +/etc/dump 0uf /dev/nrst1 /usr/src +.Ed +.Pp +This should be done at set intervals, say once a month or once every two months, +and on a set of fresh tapes that is saved forever. +.It +After a level 0, dumps of active file +systems are taken on a daily basis, +using a modified Tower of Hanoi algorithm, +with this sequence of dump levels: +.Bd -literal -offset indent +3 2 5 4 7 6 9 8 9 9 ... +.Ed +.Pp +For the daily dumps, it should be possible to use a fixed number of tapes +for each day, used on a weekly basis. +Each week, a level 1 dump is taken, and +the daily Hanoi sequence repeats beginning with 3. +For weekly dumps, another fixed set of tapes per dumped file system is +used, also on a cyclical basis. +.El +.Pp +After several months or so, the daily and weekly tapes should get +rotated out of the dump cycle and fresh tapes brought in. +.Sh FILES +.Bl -tag -width /etc/dumpdates -compact +.It Pa /dev/rmt8 +default tape unit to dump to +.It Pa /etc/dumpdates +dump date records +.It Pa /etc/fstab +dump table: file systems and frequency +.It Pa /etc/group +to find group +.Em operator +.El +.Sh SEE ALSO +.Xr restore 8 , +.Xr rmt 8 , +.Xr dump 5 , +.Xr fstab 5 +.Sh DIAGNOSTICS +Many, and verbose. +.Pp +Dump exits with zero status on success. +Startup errors are indicated with an exit code of 1; +abnormal termination is indicated with an exit code of 3. +.Sh BUGS +.Pp +Fewer than 32 read errors on the filesystem are ignored. +Each reel requires a new process, so parent processes for +reels already written just hang around until the entire tape +is written. +.Pp +.Nm Dump +with the +.Cm W +or +.Cm w +options does not report filesystems that have never been recorded +in +.Pa /etc/dumpdates , +even if listed in +.Pa /etc/fstab . +.Pp +It would be nice if +.Nm dump +knew about the dump sequence, +kept track of the tapes scribbled on, +told the operator which tape to mount when, +and provided more assistance +for the operator running +.Xr restore . +.Sh HISTORY +A +.Nm dump +command appeared in Version 6 AT&T UNIX. diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h new file mode 100644 index 000000000000..9060b00e9477 --- /dev/null +++ b/sbin/dump/dump.h @@ -0,0 +1,212 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dump.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXINOPB (MAXBSIZE / sizeof(struct dinode)) +#define MAXNINDIR (MAXBSIZE / sizeof(daddr_t)) + +/* + * Dump maps used to describe what is to be dumped. + */ +int mapsize; /* size of the state maps */ +char *usedinomap; /* map of allocated inodes */ +char *dumpdirmap; /* map of directories to be dumped */ +char *dumpinomap; /* map of files to be dumped */ +/* + * Map manipulation macros. + */ +#define SETINO(ino, map) \ + map[(u_int)((ino) - 1) / NBBY] |= 1 << ((u_int)((ino) - 1) % NBBY) +#define CLRINO(ino, map) \ + map[(u_int)((ino) - 1) / NBBY] &= ~(1 << ((u_int)((ino) - 1) % NBBY)) +#define TSTINO(ino, map) \ + (map[(u_int)((ino) - 1) / NBBY] & (1 << ((u_int)((ino) - 1) % NBBY))) + +/* + * All calculations done in 0.1" units! + */ +char *disk; /* name of the disk file */ +char *tape; /* name of the tape file */ +char *dumpdates; /* name of the file containing dump date information*/ +char *temp; /* name of the file for doing rewrite of dumpdates */ +char lastlevel; /* dump level of previous dump */ +char level; /* dump level of this dump */ +int uflag; /* update flag */ +int diskfd; /* disk file descriptor */ +int tapefd; /* tape file descriptor */ +int pipeout; /* true => output to standard output */ +ino_t curino; /* current inumber; used globally */ +int newtape; /* new tape flag */ +int density; /* density in 0.1" units */ +long tapesize; /* estimated tape size, blocks */ +long tsize; /* tape size in 0.1" units */ +long asize; /* number of 0.1" units written on current tape */ +int etapes; /* estimated number of tapes */ +int nonodump; /* if set, do not honor UF_NODUMP user flags */ + +int notify; /* notify operator flag */ +int blockswritten; /* number of blocks written on current tape */ +int tapeno; /* current tape number */ +time_t tstart_writing; /* when started writing the first tape block */ +struct fs *sblock; /* the file system super block */ +char sblock_buf[MAXBSIZE]; +long dev_bsize; /* block size of underlying disk device */ +int dev_bshift; /* log2(dev_bsize) */ +int tp_bshift; /* log2(TP_BSIZE) */ + +#ifndef __P +#include +#endif + +/* operator interface functions */ +void broadcast __P((char *message)); +void lastdump __P((int arg)); /* int should be char */ +void msg __P((const char *fmt, ...)); +void msgtail __P((const char *fmt, ...)); +int query __P((char *question)); +void quit __P((const char *fmt, ...)); +void set_operators __P((void)); +void timeest __P((void)); +time_t unctime __P((char *str)); + +/* mapping rouintes */ +struct dinode; +long blockest __P((struct dinode *dp)); +int mapfiles __P((ino_t maxino, long *tapesize)); +int mapdirs __P((ino_t maxino, long *tapesize)); + +/* file dumping routines */ +void blksout __P((daddr_t *blkp, int frags, ino_t ino)); +void bread __P((daddr_t blkno, char *buf, int size)); +void dumpino __P((struct dinode *dp, ino_t ino)); +void dumpmap __P((char *map, int type, ino_t ino)); +void writeheader __P((ino_t ino)); + +/* tape writing routines */ +int alloctape __P((void)); +void close_rewind __P((void)); +void dumpblock __P((daddr_t blkno, int size)); +void startnewtape __P((int top)); +void trewind __P((void)); +void writerec __P((char *dp, int isspcl)); + +__dead void Exit __P((int status)); +void dumpabort __P((int signo)); +void getfstab __P((void)); + +char *rawname __P((char *cp)); +struct dinode *getino __P((ino_t inum)); + +/* rdump routines */ +#ifdef RDUMP +void rmtclose __P((void)); +int rmthost __P((char *host)); +int rmtopen __P((char *tape, int mode)); +int rmtwrite __P((char *buf, int count)); +#endif /* RDUMP */ + +void interrupt __P((int signo)); /* in case operator bangs on console */ + +/* + * Exit status codes + */ +#define X_FINOK 0 /* normal exit */ +#define X_REWRITE 2 /* restart writing from the check point */ +#define X_ABORT 3 /* abort dump; don't attempt checkpointing */ + +#define OPGRENT "operator" /* group entry to notify */ +#define DIALUP "ttyd" /* prefix for dialups */ + +struct fstab *fstabsearch __P((char *key)); /* search fs_file and fs_spec */ + +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +/* + * The contents of the file _PATH_DUMPDATES is maintained both on + * a linked list, and then (eventually) arrayified. + */ +struct dumpdates { + char dd_name[NAME_MAX+3]; + char dd_level; + time_t dd_ddate; +}; +struct dumptime { + struct dumpdates dt_value; + struct dumptime *dt_next; +}; +struct dumptime *dthead; /* head of the list version */ +int nddates; /* number of records (might be zero) */ +int ddates_in; /* we have read the increment file */ +struct dumpdates **ddatev; /* the arrayfied version */ +void initdumptimes __P((void)); +void getdumptime __P((void)); +void putdumptime __P((void)); +#define ITITERATE(i, ddp) \ + for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i]) + +void sig __P((int signo)); + +/* + * Compatibility with old systems. + */ +#ifdef COMPAT +#include +extern char *index(), *rindex(), *strdup(); +extern char *ctime(); +extern int read(), write(); +extern int errno; +#endif + +#ifndef _PATH_UTMP +#define _PATH_UTMP "/etc/utmp" +#endif +#ifndef _PATH_FSTAB +#define _PATH_FSTAB "/etc/fstab" +#endif + +#ifdef sunos +extern char *calloc(); +extern char *malloc(); +extern long atol(); +extern char *strcpy(); +extern char *strncpy(); +extern char *strcat(); +extern time_t time(); +extern void endgrent(); +extern __dead void exit(); +extern off_t lseek(); +extern const char *strerror(); +#endif diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c new file mode 100644 index 000000000000..22acbfd443cb --- /dev/null +++ b/sbin/dump/dumprmt.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dumprmt.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#ifdef sunos +#include + +#include +#else +#include +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#include +#include +#endif + +#include "pathnames.h" +#include "dump.h" + +#define TS_CLOSED 0 +#define TS_OPEN 1 + +static int rmtstate = TS_CLOSED; +static int rmtape; +static char *rmtpeer; + +static int okname __P((char *)); +static int rmtcall __P((char *, char *)); +static void rmtconnaborted __P((/* int, int */)); +static int rmtgetb __P((void)); +static void rmtgetconn __P((void)); +static void rmtgets __P((char *, int)); +static int rmtreply __P((char *)); + +extern int ntrec; /* blocking factor on tape */ + +int +rmthost(host) + char *host; +{ + + rmtpeer = malloc(strlen(host) + 1); + if (rmtpeer) + strcpy(rmtpeer, host); + else + rmtpeer = host; + signal(SIGPIPE, rmtconnaborted); + rmtgetconn(); + if (rmtape < 0) + return (0); + return (1); +} + +static void +rmtconnaborted() +{ + + (void) fprintf(stderr, "rdump: Lost connection to remote host.\n"); + exit(1); +} + +void +rmtgetconn() +{ + register char *cp; + static struct servent *sp = NULL; + static struct passwd *pwd = NULL; +#ifdef notdef + static int on = 1; +#endif + char *tuser; + int size; + int maxseg; + + if (sp == NULL) { + sp = getservbyname("shell", "tcp"); + if (sp == NULL) { + (void) fprintf(stderr, + "rdump: shell/tcp: unknown service\n"); + exit(1); + } + pwd = getpwuid(getuid()); + if (pwd == NULL) { + (void) fprintf(stderr, "rdump: who are you?\n"); + exit(1); + } + } + if ((cp = index(rmtpeer, '@')) != NULL) { + tuser = rmtpeer; + *cp = '\0'; + if (!okname(tuser)) + exit(1); + rmtpeer = ++cp; + } else + tuser = pwd->pw_name; + rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser, + _PATH_RMT, (int *)0); + size = ntrec * TP_BSIZE; + if (size > 60 * 1024) /* XXX */ + size = 60 * 1024; + /* Leave some space for rmt request/response protocol */ + size += 2 * 1024; + while (size > TP_BSIZE && + setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) + size -= TP_BSIZE; + (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); + maxseg = 1024; + if (setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG, + &maxseg, sizeof (maxseg)) < 0) + perror("TCP_MAXSEG setsockopt"); + +#ifdef notdef + if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) + perror("TCP_NODELAY setsockopt"); +#endif +} + +static int +okname(cp0) + char *cp0; +{ + register char *cp; + register int c; + + for (cp = cp0; *cp; cp++) { + c = *cp; + if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { + (void) fprintf(stderr, "rdump: invalid user name %s\n", + cp0); + return (0); + } + } + return (1); +} + +int +rmtopen(tape, mode) + char *tape; + int mode; +{ + char buf[256]; + + (void)sprintf(buf, "O%s\n%d\n", tape, mode); + rmtstate = TS_OPEN; + return (rmtcall(tape, buf)); +} + +void +rmtclose() +{ + + if (rmtstate != TS_OPEN) + return; + rmtcall("close", "C\n"); + rmtstate = TS_CLOSED; +} + +int +rmtread(buf, count) + char *buf; + int count; +{ + char line[30]; + int n, i, cc; + extern errno; + + (void)sprintf(line, "R%d\n", count); + n = rmtcall("read", line); + if (n < 0) { + errno = n; + return (-1); + } + for (i = 0; i < n; i += cc) { + cc = read(rmtape, buf+i, n - i); + if (cc <= 0) { + rmtconnaborted(); + } + } + return (n); +} + +int +rmtwrite(buf, count) + char *buf; + int count; +{ + char line[30]; + + (void)sprintf(line, "W%d\n", count); + write(rmtape, line, strlen(line)); + write(rmtape, buf, count); + return (rmtreply("write")); +} + +void +rmtwrite0(count) + int count; +{ + char line[30]; + + (void)sprintf(line, "W%d\n", count); + write(rmtape, line, strlen(line)); +} + +void +rmtwrite1(buf, count) + char *buf; + int count; +{ + + write(rmtape, buf, count); +} + +int +rmtwrite2() +{ + + return (rmtreply("write")); +} + +int +rmtseek(offset, pos) + int offset, pos; +{ + char line[80]; + + (void)sprintf(line, "L%d\n%d\n", offset, pos); + return (rmtcall("seek", line)); +} + +struct mtget mts; + +struct mtget * +rmtstatus() +{ + register int i; + register char *cp; + + if (rmtstate != TS_OPEN) + return (NULL); + rmtcall("status", "S\n"); + for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++) + *cp++ = rmtgetb(); + return (&mts); +} + +int +rmtioctl(cmd, count) + int cmd, count; +{ + char buf[256]; + + if (count < 0) + return (-1); + (void)sprintf(buf, "I%d\n%d\n", cmd, count); + return (rmtcall("ioctl", buf)); +} + +static int +rmtcall(cmd, buf) + char *cmd, *buf; +{ + + if (write(rmtape, buf, strlen(buf)) != strlen(buf)) + rmtconnaborted(); + return (rmtreply(cmd)); +} + +static int +rmtreply(cmd) + char *cmd; +{ + register char *cp; + char code[30], emsg[BUFSIZ]; + + rmtgets(code, sizeof (code)); + if (*code == 'E' || *code == 'F') { + rmtgets(emsg, sizeof (emsg)); + msg("%s: %s", cmd, emsg); + if (*code == 'F') { + rmtstate = TS_CLOSED; + return (-1); + } + return (-1); + } + if (*code != 'A') { + /* Kill trailing newline */ + cp = code + strlen(code); + if (cp > code && *--cp == '\n') + *cp = '\0'; + + msg("Protocol to remote tape server botched (code \"%s\").\n", + code); + rmtconnaborted(); + } + return (atoi(code + 1)); +} + +int +rmtgetb() +{ + char c; + + if (read(rmtape, &c, 1) != 1) + rmtconnaborted(); + return (c); +} + +/* Get a line (guaranteed to have a trailing newline). */ +void +rmtgets(line, len) + char *line; + int len; +{ + register char *cp = line; + + while (len > 1) { + *cp = rmtgetb(); + if (*cp == '\n') { + cp[1] = '\0'; + return; + } + cp++; + len--; + } + *cp = '\0'; + msg("Protocol to remote tape server botched.\n"); + msg("(rmtgets got \"%s\").\n", line); + rmtconnaborted(); +} diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c new file mode 100644 index 000000000000..94656ac649e3 --- /dev/null +++ b/sbin/dump/itime.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#ifdef sunos +#include + +#include +#include +#include +#else +#include +#endif + +#include + +#include +#include +#include +#ifdef __STDC__ +#include +#include +#include +#endif + +#include "dump.h" + +struct dumpdates **ddatev = 0; +int nddates = 0; +int ddates_in = 0; +struct dumptime *dthead = 0; + +static void dumprecout __P((FILE *, struct dumpdates *)); +static int getrecord __P((FILE *, struct dumpdates *)); +static int makedumpdate __P((struct dumpdates *, char *)); +static void readdumptimes __P((FILE *)); + +void +initdumptimes() +{ + FILE *df; + + if ((df = fopen(dumpdates, "r")) == NULL) { + if (errno != ENOENT) { + quit("cannot read %s: %s\n", dumpdates, + strerror(errno)); + /* NOTREACHED */ + } + /* + * Dumpdates does not exist, make an empty one. + */ + msg("WARNING: no file `%s', making an empty one\n", dumpdates); + if ((df = fopen(dumpdates, "w")) == NULL) { + quit("cannot create %s: %s\n", dumpdates, + strerror(errno)); + /* NOTREACHED */ + } + (void) fclose(df); + if ((df = fopen(dumpdates, "r")) == NULL) { + quit("cannot read %s even after creating it: %s\n", + dumpdates, strerror(errno)); + /* NOTREACHED */ + } + } + (void) flock(fileno(df), LOCK_SH); + readdumptimes(df); + (void) fclose(df); +} + +static void +readdumptimes(df) + FILE *df; +{ + register int i; + register struct dumptime *dtwalk; + + for (;;) { + dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime)); + if (getrecord(df, &(dtwalk->dt_value)) < 0) + break; + nddates++; + dtwalk->dt_next = dthead; + dthead = dtwalk; + } + + ddates_in = 1; + /* + * arrayify the list, leaving enough room for the additional + * record that we may have to add to the ddate structure + */ + ddatev = (struct dumpdates **) + calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *)); + dtwalk = dthead; + for (i = nddates - 1; i >= 0; i--, dtwalk = dtwalk->dt_next) + ddatev[i] = &dtwalk->dt_value; +} + +void +getdumptime() +{ + register struct dumpdates *ddp; + register int i; + char *fname; + + fname = disk; +#ifdef FDEBUG + msg("Looking for name %s in dumpdates = %s for level = %c\n", + fname, dumpdates, level); +#endif + spcl.c_ddate = 0; + lastlevel = '0'; + + initdumptimes(); + /* + * Go find the entry with the same name for a lower increment + * and older date + */ + ITITERATE(i, ddp) { + if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0) + continue; + if (ddp->dd_level >= level) + continue; + if (ddp->dd_ddate <= spcl.c_ddate) + continue; + spcl.c_ddate = ddp->dd_ddate; + lastlevel = ddp->dd_level; + } +} + +void +putdumptime() +{ + FILE *df; + register struct dumpdates *dtwalk; + register int i; + int fd; + char *fname; + + if(uflag == 0) + return; + if ((df = fopen(dumpdates, "r+")) == NULL) + quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno)); + fd = fileno(df); + (void) flock(fd, LOCK_EX); + fname = disk; + free((char *)ddatev); + ddatev = 0; + nddates = 0; + dthead = 0; + ddates_in = 0; + readdumptimes(df); + if (fseek(df, 0L, 0) < 0) + quit("fseek: %s\n", strerror(errno)); + spcl.c_ddate = 0; + ITITERATE(i, dtwalk) { + if (strncmp(fname, dtwalk->dd_name, + sizeof (dtwalk->dd_name)) != 0) + continue; + if (dtwalk->dd_level != level) + continue; + goto found; + } + /* + * construct the new upper bound; + * Enough room has been allocated. + */ + dtwalk = ddatev[nddates] = + (struct dumpdates *)calloc(1, sizeof (struct dumpdates)); + nddates += 1; + found: + (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name)); + dtwalk->dd_level = level; + dtwalk->dd_ddate = spcl.c_date; + + ITITERATE(i, dtwalk) { + dumprecout(df, dtwalk); + } + if (fflush(df)) + quit("%s: %s\n", dumpdates, strerror(errno)); + if (ftruncate(fd, ftell(df))) + quit("ftruncate (%s): %s\n", dumpdates, strerror(errno)); + (void) fclose(df); + msg("level %c dump on %s", level, + spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date)); +} + +static void +dumprecout(file, what) + FILE *file; + struct dumpdates *what; +{ + + if (fprintf(file, DUMPOUTFMT, + what->dd_name, + what->dd_level, + ctime(&what->dd_ddate)) < 0) + quit("%s: %s\n", dumpdates, strerror(errno)); +} + +int recno; + +static int +getrecord(df, ddatep) + FILE *df; + struct dumpdates *ddatep; +{ + char tbuf[BUFSIZ]; + + recno = 0; + if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf) + return(-1); + recno++; + if (makedumpdate(ddatep, tbuf) < 0) + msg("Unknown intermediate format in %s, line %d\n", + dumpdates, recno); + +#ifdef FDEBUG + msg("getrecord: %s %c %s", ddatep->dd_name, ddatep->dd_level, + ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate)); +#endif + return(0); +} + +static int +makedumpdate(ddp, tbuf) + struct dumpdates *ddp; + char *tbuf; +{ + char un_buf[128]; + + (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf); + ddp->dd_ddate = unctime(un_buf); + if (ddp->dd_ddate < 0) + return(-1); + return(0); +} diff --git a/sbin/dump/main.c b/sbin/dump/main.c new file mode 100644 index 000000000000..40d668e13e82 --- /dev/null +++ b/sbin/dump/main.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 1980, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/15/94"; +#endif /* not lint */ + +#include +#include +#ifdef sunos +#include + +#include +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#include +#include +#endif + +#include "dump.h" +#include "pathnames.h" + +#ifndef SBOFF +#define SBOFF (SBLOCK * DEV_BSIZE) +#endif + +int notify = 0; /* notify operator flag */ +int blockswritten = 0; /* number of blocks written on current tape */ +int tapeno = 0; /* current tape number */ +int density = 0; /* density in bytes/0.1" */ +int ntrec = NTREC; /* # tape blocks in each tape record */ +int cartridge = 0; /* Assume non-cartridge tape */ +long dev_bsize = 1; /* recalculated below */ +long blocksperfile; /* output blocks per file */ +char *host = NULL; /* remote host (if any) */ + +static long numarg __P((int, char *, long, long, int *, char ***)); +static __dead void missingarg __P((int, char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + register ino_t ino; + register int dirty; + register struct dinode *dp; + register struct fstab *dt; + register char *map; + register char *cp; + int i, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; + ino_t maxino; + + spcl.c_date = 0; + (void)time((time_t *)&spcl.c_date); + + tsize = 0; /* Default later, based on 'c' option for cart tapes */ + tape = _PATH_DEFTAPE; + dumpdates = _PATH_DUMPDATES; + temp = _PATH_DTMP; + if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) + quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); + level = '0'; + if (argc == 1) { + (void) fprintf(stderr, "Must specify a key.\n"); + Exit(X_ABORT); + } + argv++; + argc -= 2; + for (cp = *argv++; cp != NULL && *cp != '\0'; cp++) { + switch (*cp) { + case '-': + break; + + case 'w': + lastdump('w'); /* tell us only what has to be done */ + exit(0); + + case 'W': /* what to do */ + lastdump('W'); /* tell us state of what is done */ + exit(0); /* do nothing else */ + + case 'f': /* output file */ + if (argc < 1) + missingarg('f', "output file"); + tape = *argv++; + argc--; + break; + + case 'd': /* density, in bits per inch */ + density = numarg('d', "density", + 10L, 327670L, &argc, &argv) / 10; + if (density >= 625 && !bflag) + ntrec = HIGHDENSITYTREC; + break; + + case 's': /* tape size, feet */ + tsize = numarg('s', "size", + 1L, 0L, &argc, &argv) * 12 * 10; + break; + + case 'T': /* time of last dump */ + if (argc < 1) + missingarg('T', "time of last dump"); + spcl.c_ddate = unctime(*argv); + if (spcl.c_ddate < 0) { + (void)fprintf(stderr, "bad time \"%s\"\n", + *argv); + exit(X_ABORT); + } + Tflag = 1; + lastlevel = '?'; + argc--; + argv++; + break; + + case 'b': /* blocks per tape write */ + ntrec = numarg('b', "number of blocks per write", + 1L, 1000L, &argc, &argv); + break; + + case 'B': /* blocks per output file */ + blocksperfile = numarg('B', "number of blocks per file", + 1L, 0L, &argc, &argv); + break; + + case 'c': /* Tape is cart. not 9-track */ + cartridge = 1; + break; + + /* dump level */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + level = *cp; + break; + + case 'u': /* update /etc/dumpdates */ + uflag = 1; + break; + + case 'n': /* notify operators */ + notify = 1; + break; + + case 'h': + honorlevel = numarg('h', "honor level", + 0L, 10L, &argc, &argv); + break; + + default: + (void)fprintf(stderr, "bad key '%c'\n", *cp); + exit(X_ABORT); + } + } + if (argc < 1) { + (void)fprintf(stderr, "Must specify disk or filesystem\n"); + exit(X_ABORT); + } + disk = *argv++; + argc--; + if (argc >= 1) { + (void)fprintf(stderr, "Unknown arguments to dump:"); + while (argc--) + (void)fprintf(stderr, " %s", *argv++); + (void)fprintf(stderr, "\n"); + exit(X_ABORT); + } + if (Tflag && uflag) { + (void)fprintf(stderr, + "You cannot use the T and u flags together.\n"); + exit(X_ABORT); + } + if (strcmp(tape, "-") == 0) { + pipeout++; + tape = "standard output"; + } + + if (blocksperfile) + blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ + else { + /* + * Determine how to default tape size and density + * + * density tape size + * 9-track 1600 bpi (160 bytes/.1") 2300 ft. + * 9-track 6250 bpi (625 bytes/.1") 2300 ft. + * cartridge 8000 bpi (100 bytes/.1") 1700 ft. + * (450*4 - slop) + */ + if (density == 0) + density = cartridge ? 100 : 160; + if (tsize == 0) + tsize = cartridge ? 1700L*120L : 2300L*120L; + } + + if (index(tape, ':')) { + host = tape; + tape = index(host, ':'); + *tape++ = '\0'; +#ifdef RDUMP + if (rmthost(host) == 0) + exit(X_ABORT); +#else + (void)fprintf(stderr, "remote dump not enabled\n"); + exit(X_ABORT); +#endif + } + (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ + + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, sig); + if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) + signal(SIGTRAP, sig); + if (signal(SIGFPE, SIG_IGN) != SIG_IGN) + signal(SIGFPE, sig); + if (signal(SIGBUS, SIG_IGN) != SIG_IGN) + signal(SIGBUS, sig); + if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) + signal(SIGSEGV, sig); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, sig); + if (signal(SIGINT, interrupt) == SIG_IGN) + signal(SIGINT, SIG_IGN); + + set_operators(); /* /etc/group snarfed */ + getfstab(); /* /etc/fstab snarfed */ + /* + * disk can be either the full special file name, + * the suffix of the special file name, + * the special name missing the leading '/', + * the file system name with or without the leading '/'. + */ + dt = fstabsearch(disk); + if (dt != NULL) { + disk = rawname(dt->fs_spec); + (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); + (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); + } else { + (void)strncpy(spcl.c_dev, disk, NAMELEN); + (void)strncpy(spcl.c_filesys, "an unlisted file system", + NAMELEN); + } + (void)strcpy(spcl.c_label, "none"); + (void)gethostname(spcl.c_host, NAMELEN); + spcl.c_level = level - '0'; + spcl.c_type = TS_TAPE; + if (!Tflag) + getdumptime(); /* /etc/dumpdates snarfed */ + + msg("Date of this level %c dump: %s", level, + spcl.c_date == 0 ? "the epoch\n" : ctime(&spcl.c_date)); + msg("Date of last level %c dump: %s", lastlevel, + spcl.c_ddate == 0 ? "the epoch\n" : ctime(&spcl.c_ddate)); + msg("Dumping %s ", disk); + if (dt != NULL) + msgtail("(%s) ", dt->fs_file); + if (host) + msgtail("to %s on host %s\n", tape, host); + else + msgtail("to %s\n", tape); + + if ((diskfd = open(disk, O_RDONLY)) < 0) { + msg("Cannot open %s\n", disk); + exit(X_ABORT); + } + sync(); + sblock = (struct fs *)sblock_buf; + bread(SBOFF, (char *) sblock, SBSIZE); + if (sblock->fs_magic != FS_MAGIC) + quit("bad sblock magic number\n"); + dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); + dev_bshift = ffs(dev_bsize) - 1; + if (dev_bsize != (1 << dev_bshift)) + quit("dev_bsize (%d) is not a power of 2", dev_bsize); + tp_bshift = ffs(TP_BSIZE) - 1; + if (TP_BSIZE != (1 << tp_bshift)) + quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); +#ifdef FS_44INODEFMT + if (sblock->fs_inodefmt >= FS_44INODEFMT) + spcl.c_flags |= DR_NEWINODEFMT; +#endif + maxino = sblock->fs_ipg * sblock->fs_ncg; + mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); + usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); + dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); + dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); + tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); + + nonodump = spcl.c_level < honorlevel; + + msg("mapping (Pass I) [regular files]\n"); + anydirskipped = mapfiles(maxino, &tapesize); + + msg("mapping (Pass II) [directories]\n"); + while (anydirskipped) { + anydirskipped = mapdirs(maxino, &tapesize); + } + + if (pipeout) { + tapesize += 10; /* 10 trailer blocks */ + msg("estimated %ld tape blocks.\n", tapesize); + } else { + double fetapes; + + if (blocksperfile) + fetapes = (double) tapesize / blocksperfile; + else if (cartridge) { + /* Estimate number of tapes, assuming streaming stops at + the end of each block written, and not in mid-block. + Assume no erroneous blocks; this can be compensated + for with an artificially low tape size. */ + fetapes = + ( tapesize /* blocks */ + * TP_BSIZE /* bytes/block */ + * (1.0/density) /* 0.1" / byte */ + + + tapesize /* blocks */ + * (1.0/ntrec) /* streaming-stops per block */ + * 15.48 /* 0.1" / streaming-stop */ + ) * (1.0 / tsize ); /* tape / 0.1" */ + } else { + /* Estimate number of tapes, for old fashioned 9-track + tape */ + int tenthsperirg = (density == 625) ? 3 : 7; + fetapes = + ( tapesize /* blocks */ + * TP_BSIZE /* bytes / block */ + * (1.0/density) /* 0.1" / byte */ + + + tapesize /* blocks */ + * (1.0/ntrec) /* IRG's / block */ + * tenthsperirg /* 0.1" / IRG */ + ) * (1.0 / tsize ); /* tape / 0.1" */ + } + etapes = fetapes; /* truncating assignment */ + etapes++; + /* count the dumped inodes map on each additional tape */ + tapesize += (etapes - 1) * + (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); + tapesize += etapes + 10; /* headers + 10 trailer blks */ + msg("estimated %ld tape blocks on %3.2f tape(s).\n", + tapesize, fetapes); + } + + /* + * Allocate tape buffer. + */ + if (!alloctape()) + quit("can't allocate tape buffers - try a smaller blocking factor.\n"); + + startnewtape(1); + (void)time((time_t *)&(tstart_writing)); + dumpmap(usedinomap, TS_CLRI, maxino - 1); + + msg("dumping (Pass III) [directories]\n"); + dirty = 0; /* XXX just to get gcc to shut up */ + for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + dirty = *map++; + else + dirty >>= 1; + if ((dirty & 1) == 0) + continue; + /* + * Skip directory inodes deleted and maybe reallocated + */ + dp = getino(ino); + if ((dp->di_mode & IFMT) != IFDIR) + continue; + (void)dumpino(dp, ino); + } + + msg("dumping (Pass IV) [regular files]\n"); + for (map = dumpinomap, ino = 1; ino < maxino; ino++) { + int mode; + + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + dirty = *map++; + else + dirty >>= 1; + if ((dirty & 1) == 0) + continue; + /* + * Skip inodes deleted and reallocated as directories. + */ + dp = getino(ino); + mode = dp->di_mode & IFMT; + if (mode == IFDIR) + continue; + (void)dumpino(dp, ino); + } + + spcl.c_type = TS_END; + for (i = 0; i < ntrec; i++) + writeheader(maxino - 1); + if (pipeout) + msg("DUMP: %ld tape blocks\n",spcl.c_tapea); + else + msg("DUMP: %ld tape blocks on %d volumes(s)\n", + spcl.c_tapea, spcl.c_volume); + putdumptime(); + trewind(); + broadcast("DUMP IS DONE!\7\7\n"); + msg("DUMP IS DONE\n"); + Exit(X_FINOK); + /* NOTREACHED */ +} + +/* + * Pick up a numeric argument. It must be nonnegative and in the given + * range (except that a vmax of 0 means unlimited). + */ +static long +numarg(letter, meaning, vmin, vmax, pargc, pargv) + int letter; + char *meaning; + long vmin, vmax; + int *pargc; + char ***pargv; +{ + register char *p; + long val; + char *str; + + if (--*pargc < 0) + missingarg(letter, meaning); + str = *(*pargv)++; + for (p = str; *p; p++) + if (!isdigit(*p)) + goto bad; + val = atol(str); + if (val < vmin || (vmax && val > vmax)) + goto bad; + return (val); + +bad: + (void)fprintf(stderr, "bad '%c' (%s) value \"%s\"\n", + letter, meaning, str); + exit(X_ABORT); +} + +static __dead void +missingarg(letter, meaning) + int letter; + char *meaning; +{ + + (void)fprintf(stderr, "The '%c' flag (%s) requires an argument\n", + letter, meaning); + exit(X_ABORT); +} + +void +sig(signo) + int signo; +{ + switch(signo) { + case SIGALRM: + case SIGBUS: + case SIGFPE: + case SIGHUP: + case SIGTERM: + case SIGTRAP: + if (pipeout) + quit("Signal on pipe: cannot recover\n"); + msg("Rewriting attempted as response to unknown signal.\n"); + (void)fflush(stderr); + (void)fflush(stdout); + close_rewind(); + exit(X_REWRITE); + /* NOTREACHED */ + case SIGSEGV: + msg("SIGSEGV: ABORTING!\n"); + (void)signal(SIGSEGV, SIG_DFL); + (void)kill(0, SIGSEGV); + /* NOTREACHED */ + } +} + +char * +rawname(cp) + char *cp; +{ + static char rawbuf[MAXPATHLEN]; + char *dp = rindex(cp, '/'); + + if (dp == NULL) + return (NULL); + *dp = '\0'; + (void)strcpy(rawbuf, cp); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, dp + 1); + return (rawbuf); +} + +#ifdef sunos +const char * +strerror(errnum) + int errnum; +{ + extern int sys_nerr; + extern const char *const sys_errlist[]; + + if (errnum < sys_nerr) + return (sys_errlist[errnum]); + else + return ("bogus errno in strerror"); +} +#endif diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c new file mode 100644 index 000000000000..6db09085095c --- /dev/null +++ b/sbin/dump/optr.c @@ -0,0 +1,538 @@ +/*- + * Copyright (c) 1980, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#include +#include +#endif +#include +#ifdef __STDC__ +#include +#endif +#include +#ifndef __STDC__ +#include +#endif + +#include "dump.h" +#include "pathnames.h" + +void alarmcatch __P((/* int, int */)); +int datesort __P((const void *, const void *)); +static void sendmes __P((char *, char *)); + +/* + * Query the operator; This previously-fascist piece of code + * no longer requires an exact response. + * It is intended to protect dump aborting by inquisitive + * people banging on the console terminal to see what is + * happening which might cause dump to croak, destroying + * a large number of hours of work. + * + * Every 2 minutes we reprint the message, alerting others + * that dump needs attention. + */ +static int timeout; +static char *attnmessage; /* attention message */ + +int +query(question) + char *question; +{ + char replybuffer[64]; + int back, errcount; + FILE *mytty; + + if ((mytty = fopen(_PATH_TTY, "r")) == NULL) + quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); + attnmessage = question; + timeout = 0; + alarmcatch(); + back = -1; + errcount = 0; + do { + if (fgets(replybuffer, 63, mytty) == NULL) { + clearerr(mytty); + if (++errcount > 30) /* XXX ugly */ + quit("excessive operator query failures\n"); + } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { + back = 1; + } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { + back = 0; + } else { + (void) fprintf(stderr, + " DUMP: \"Yes\" or \"No\"?\n"); + (void) fprintf(stderr, + " DUMP: %s: (\"yes\" or \"no\") ", question); + } + } while (back < 0); + + /* + * Turn off the alarm, and reset the signal to trap out.. + */ + (void) alarm(0); + if (signal(SIGALRM, sig) == SIG_IGN) + signal(SIGALRM, SIG_IGN); + (void) fclose(mytty); + return(back); +} + +char lastmsg[100]; + +/* + * Alert the console operator, and enable the alarm clock to + * sleep for 2 minutes in case nobody comes to satisfy dump + */ +void +alarmcatch() +{ + if (notify == 0) { + if (timeout == 0) + (void) fprintf(stderr, + " DUMP: %s: (\"yes\" or \"no\") ", + attnmessage); + else + msgtail("\7\7"); + } else { + if (timeout) { + msgtail("\n"); + broadcast(""); /* just print last msg */ + } + (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", + attnmessage); + } + signal(SIGALRM, alarmcatch); + (void) alarm(120); + timeout = 1; +} + +/* + * Here if an inquisitive operator interrupts the dump program + */ +void +interrupt(signo) + int signo; +{ + msg("Interrupt received.\n"); + if (query("Do you want to abort dump?")) + dumpabort(0); +} + +/* + * The following variables and routines manage alerting + * operators to the status of dump. + * This works much like wall(1) does. + */ +struct group *gp; + +/* + * Get the names from the group entry "operator" to notify. + */ +void +set_operators() +{ + if (!notify) /*not going to notify*/ + return; + gp = getgrnam(OPGRENT); + (void) endgrent(); + if (gp == NULL) { + msg("No group entry for %s.\n", OPGRENT); + notify = 0; + return; + } +} + +struct tm *localclock; + +/* + * We fork a child to do the actual broadcasting, so + * that the process control groups are not messed up + */ +void +broadcast(message) + char *message; +{ + time_t clock; + FILE *f_utmp; + struct utmp utmp; + char **np; + int pid, s; + + if (!notify || gp == NULL) + return; + + switch (pid = fork()) { + case -1: + return; + case 0: + break; + default: + while (wait(&s) != pid) + continue; + return; + } + + clock = time((time_t *)0); + localclock = localtime(&clock); + + if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { + msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); + return; + } + + while (!feof(f_utmp)) { + if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) + break; + if (utmp.ut_name[0] == 0) + continue; + for (np = gp->gr_mem; *np; np++) { + if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) + continue; + /* + * Do not send messages to operators on dialups + */ + if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) + continue; +#ifdef DEBUG + msg("Message to %s at %s\n", *np, utmp.ut_line); +#endif + sendmes(utmp.ut_line, message); + } + } + (void) fclose(f_utmp); + Exit(0); /* the wait in this same routine will catch this */ + /* NOTREACHED */ +} + +static void +sendmes(tty, message) + char *tty, *message; +{ + char t[50], buf[BUFSIZ]; + register char *cp; + int lmsg = 1; + FILE *f_tty; + + (void) strcpy(t, _PATH_DEV); + (void) strcat(t, tty); + + if ((f_tty = fopen(t, "w")) != NULL) { + setbuf(f_tty, buf); + (void) fprintf(f_tty, + "\n\ +\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ +DUMP: NEEDS ATTENTION: ", + localclock->tm_hour, localclock->tm_min); + for (cp = lastmsg; ; cp++) { + if (*cp == '\0') { + if (lmsg) { + cp = message; + if (*cp == '\0') + break; + lmsg = 0; + } else + break; + } + if (*cp == '\n') + (void) putc('\r', f_tty); + (void) putc(*cp, f_tty); + } + (void) fclose(f_tty); + } +} + +/* + * print out an estimate of the amount of time left to do the dump + */ + +time_t tschedule = 0; + +void +timeest() +{ + time_t tnow, deltat; + + (void) time((time_t *) &tnow); + if (tnow >= tschedule) { + tschedule = tnow + 300; + if (blockswritten < 500) + return; + deltat = tstart_writing - tnow + + (1.0 * (tnow - tstart_writing)) + / blockswritten * tapesize; + msg("%3.2f%% done, finished in %d:%02d\n", + (blockswritten * 100.0) / tapesize, + deltat / 3600, (deltat % 3600) / 60); + } +} + +void +#if __STDC__ +msg(const char *fmt, ...) +#else +msg(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + (void) fprintf(stderr," DUMP: "); +#ifdef TDEBUG + (void) fprintf(stderr, "pid=%d ", getpid()); +#endif +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + (void) fflush(stdout); + (void) fflush(stderr); + (void) vsprintf(lastmsg, fmt, ap); + va_end(ap); +} + +void +#if __STDC__ +msgtail(const char *fmt, ...) +#else +msgtail(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void +#if __STDC__ +quit(const char *fmt, ...) +#else +quit(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + (void) fprintf(stderr," DUMP: "); +#ifdef TDEBUG + (void) fprintf(stderr, "pid=%d ", getpid()); +#endif +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + (void) fflush(stdout); + (void) fflush(stderr); + dumpabort(0); +} + +/* + * Tell the operator what has to be done; + * we don't actually do it + */ + +struct fstab * +allocfsent(fs) + register struct fstab *fs; +{ + register struct fstab *new; + + new = (struct fstab *)malloc(sizeof (*fs)); + if (new == NULL || + (new->fs_file = strdup(fs->fs_file)) == NULL || + (new->fs_type = strdup(fs->fs_type)) == NULL || + (new->fs_spec = strdup(fs->fs_spec)) == NULL) + quit("%s\n", strerror(errno)); + new->fs_passno = fs->fs_passno; + new->fs_freq = fs->fs_freq; + return (new); +} + +struct pfstab { + struct pfstab *pf_next; + struct fstab *pf_fstab; +}; + +static struct pfstab *table; + +void +getfstab() +{ + register struct fstab *fs; + register struct pfstab *pf; + + if (setfsent() == 0) { + msg("Can't open %s for dump table information: %s\n", + _PATH_FSTAB, strerror(errno)); + return; + } + while ((fs = getfsent()) != NULL) { + if (strcmp(fs->fs_type, FSTAB_RW) && + strcmp(fs->fs_type, FSTAB_RO) && + strcmp(fs->fs_type, FSTAB_RQ)) + continue; + fs = allocfsent(fs); + if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) + quit("%s\n", strerror(errno)); + pf->pf_fstab = fs; + pf->pf_next = table; + table = pf; + } + (void) endfsent(); +} + +/* + * Search in the fstab for a file name. + * This file name can be either the special or the path file name. + * + * The entries in the fstab are the BLOCK special names, not the + * character special names. + * The caller of fstabsearch assures that the character device + * is dumped (that is much faster) + * + * The file name can omit the leading '/'. + */ +struct fstab * +fstabsearch(key) + char *key; +{ + register struct pfstab *pf; + register struct fstab *fs; + char *rn; + + for (pf = table; pf != NULL; pf = pf->pf_next) { + fs = pf->pf_fstab; + if (strcmp(fs->fs_file, key) == 0 || + strcmp(fs->fs_spec, key) == 0) + return (fs); + rn = rawname(fs->fs_spec); + if (rn != NULL && strcmp(rn, key) == 0) + return (fs); + if (key[0] != '/') { + if (*fs->fs_spec == '/' && + strcmp(fs->fs_spec + 1, key) == 0) + return (fs); + if (*fs->fs_file == '/' && + strcmp(fs->fs_file + 1, key) == 0) + return (fs); + } + } + return (NULL); +} + +/* + * Tell the operator what to do + */ +void +lastdump(arg) + char arg; /* w ==> just what to do; W ==> most recent dumps */ +{ + register int i; + register struct fstab *dt; + register struct dumpdates *dtwalk; + char *lastname, *date; + int dumpme; + time_t tnow; + + (void) time(&tnow); + getfstab(); /* /etc/fstab input */ + initdumptimes(); /* /etc/dumpdates input */ + qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); + + if (arg == 'w') + (void) printf("Dump these file systems:\n"); + else + (void) printf("Last dump(s) done (Dump '>' file systems):\n"); + lastname = "??"; + ITITERATE(i, dtwalk) { + if (strncmp(lastname, dtwalk->dd_name, + sizeof(dtwalk->dd_name)) == 0) + continue; + date = (char *)ctime(&dtwalk->dd_ddate); + date[16] = '\0'; /* blast away seconds and year */ + lastname = dtwalk->dd_name; + dt = fstabsearch(dtwalk->dd_name); + dumpme = (dt != NULL && + dt->fs_freq != 0 && + dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); + if (arg != 'w' || dumpme) + (void) printf( + "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", + dumpme && (arg != 'w') ? '>' : ' ', + dtwalk->dd_name, + dt ? dt->fs_file : "", + dtwalk->dd_level, + date); + } +} + +int +datesort(a1, a2) + const void *a1, *a2; +{ + struct dumpdates *d1 = *(struct dumpdates **)a1; + struct dumpdates *d2 = *(struct dumpdates **)a2; + int diff; + + diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); + if (diff == 0) + return (d2->dd_ddate - d1->dd_ddate); + return (diff); +} diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h new file mode 100644 index 000000000000..e4dff5336062 --- /dev/null +++ b/sbin/dump/pathnames.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include + +#define _PATH_DEFTAPE "/dev/rmt8" +#define _PATH_DTMP "/etc/dtmp" +#define _PATH_DUMPDATES "/etc/dumpdates" +#define _PATH_LOCK "/tmp/dumplockXXXXXX" +#define _PATH_RMT "rmt" diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c new file mode 100644 index 000000000000..f6a4e5b82bd3 --- /dev/null +++ b/sbin/dump/tape.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)tape.c 8.2 (Berkeley) 3/17/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#ifdef sunos +#include + +#include +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#include +#include +#else +int write(), read(); +#endif + +#include "dump.h" +#include "pathnames.h" + +int writesize; /* size of malloc()ed buffer for tape */ +long lastspclrec = -1; /* tape block number of last written header */ +int trecno = 0; /* next record to write in current block */ +extern long blocksperfile; /* number of blocks per output file */ +long blocksthisvol; /* number of blocks on current output file */ +extern int ntrec; /* blocking factor on tape */ +extern int cartridge; +extern char *host; +char *nexttape; + +static int atomic __P((int (*)(), int, char *, int)); +static void doslave __P((int, int)); +static void enslave __P((void)); +static void flushtape __P((void)); +static void killall __P((void)); +static void rollforward __P((void)); + +/* + * Concurrent dump mods (Caltech) - disk block reading and tape writing + * are exported to several slave processes. While one slave writes the + * tape, the others read disk blocks; they pass control of the tape in + * a ring via signals. The parent process traverses the filesystem and + * sends writeheader()'s and lists of daddr's to the slaves via pipes. + * The following structure defines the instruction packets sent to slaves. + */ +struct req { + daddr_t dblk; + int count; +}; +int reqsiz; + +#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ +struct slave { + int tapea; /* header number at start of this chunk */ + int count; /* count to next header (used for TS_TAPE */ + /* after EOT) */ + int inode; /* inode that we are currently dealing with */ + int fd; /* FD for this slave */ + int pid; /* PID for this slave */ + int sent; /* 1 == we've sent this slave requests */ + int firstrec; /* record number of this block */ + char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ + struct req *req; /* buffer for requests */ +} slaves[SLAVES+1]; +struct slave *slp; + +char (*nextblock)[TP_BSIZE]; + +int master; /* pid of master, for sending error signals */ +int tenths; /* length of tape used per block written */ +static int caught; /* have we caught the signal to proceed? */ +static int ready; /* have we reached the lock point without having */ + /* received the SIGUSR2 signal from the prev slave? */ +static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ + /* SIGUSR2 arrives from the previous slave */ + +int +alloctape() +{ + int pgoff = getpagesize() - 1; + char *buf; + int i; + + writesize = ntrec * TP_BSIZE; + reqsiz = (ntrec + 1) * sizeof(struct req); + /* + * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode + * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require + * repositioning after stopping, i.e, streaming mode, where the gap is + * variable, 0.30" to 0.45". The gap is maximal when the tape stops. + */ + if (blocksperfile == 0) + tenths = writesize / density + + (cartridge ? 16 : density == 625 ? 5 : 8); + /* + * Allocate tape buffer contiguous with the array of instruction + * packets, so flushtape() can write them together with one write(). + * Align tape buffer on page boundary to speed up tape write(). + */ + for (i = 0; i <= SLAVES; i++) { + buf = (char *) + malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); + if (buf == NULL) + return(0); + slaves[i].tblock = (char (*)[TP_BSIZE]) + (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); + slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; + } + slp = &slaves[0]; + slp->count = 1; + slp->tapea = 0; + slp->firstrec = 0; + nextblock = slp->tblock; + return(1); +} + +void +writerec(dp, isspcl) + char *dp; + int isspcl; +{ + + slp->req[trecno].dblk = (daddr_t)0; + slp->req[trecno].count = 1; + *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; + if (isspcl) + lastspclrec = spcl.c_tapea; + trecno++; + spcl.c_tapea++; + if (trecno >= ntrec) + flushtape(); +} + +void +dumpblock(blkno, size) + daddr_t blkno; + int size; +{ + int avail, tpblks, dblkno; + + dblkno = fsbtodb(sblock, blkno); + tpblks = size >> tp_bshift; + while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { + slp->req[trecno].dblk = dblkno; + slp->req[trecno].count = avail; + trecno += avail; + spcl.c_tapea += avail; + if (trecno >= ntrec) + flushtape(); + dblkno += avail << (tp_bshift - dev_bshift); + tpblks -= avail; + } +} + +int nogripe = 0; + +void +tperror(signo) + int signo; +{ + + if (pipeout) { + msg("write error on %s\n", tape); + quit("Cannot recover\n"); + /* NOTREACHED */ + } + msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno); + broadcast("DUMP WRITE ERROR!\n"); + if (!query("Do you want to restart?")) + dumpabort(0); + msg("Closing this volume. Prepare to restart with new media;\n"); + msg("this dump volume will be rewritten.\n"); + killall(); + nogripe = 1; + close_rewind(); + Exit(X_REWRITE); +} + +void +sigpipe(signo) + int signo; +{ + + quit("Broken pipe\n"); +} + +static void +flushtape() +{ + int i, blks, got; + long lastfirstrec; + + int siz = (char *)nextblock - (char *)slp->req; + + slp->req[trecno].count = 0; /* Sentinel */ + + if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) + quit("error writing command pipe: %s\n", strerror(errno)); + slp->sent = 1; /* we sent a request, read the response later */ + + lastfirstrec = slp->firstrec; + + if (++slp >= &slaves[SLAVES]) + slp = &slaves[0]; + + /* Read results back from next slave */ + if (slp->sent) { + if (atomic(read, slp->fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slp->sent = 0; + + /* Check for end of tape */ + if (got < writesize) { + msg("End of tape detected\n"); + + /* + * Drain the results, don't care what the values were. + * If we read them here then trewind won't... + */ + for (i = 0; i < SLAVES; i++) { + if (slaves[i].sent) { + if (atomic(read, slaves[i].fd, + (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slaves[i].sent = 0; + } + } + + close_rewind(); + rollforward(); + return; + } + } + + blks = 0; + if (spcl.c_type != TS_END) { + for (i = 0; i < spcl.c_count; i++) + if (spcl.c_addr[i] != 0) + blks++; + } + slp->count = lastspclrec + blks + 1 - spcl.c_tapea; + slp->tapea = spcl.c_tapea; + slp->firstrec = lastfirstrec + ntrec; + slp->inode = curino; + nextblock = slp->tblock; + trecno = 0; + asize += tenths; + blockswritten += ntrec; + blocksthisvol += ntrec; + if (!pipeout && (blocksperfile ? + (blocksthisvol >= blocksperfile) : (asize > tsize))) { + close_rewind(); + startnewtape(0); + } + timeest(); +} + +void +trewind() +{ + int f; + int got; + + for (f = 0; f < SLAVES; f++) { + /* + * Drain the results, but unlike EOT we DO (or should) care + * what the return values were, since if we detect EOT after + * we think we've written the last blocks to the tape anyway, + * we have to replay those blocks with rollforward. + * + * fixme: punt for now. + */ + if (slaves[f].sent) { + if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slaves[f].sent = 0; + if (got != writesize) { + msg("EOT detected in last 2 tape records!\n"); + msg("Use a longer tape, decrease the size estimate\n"); + quit("or use no size estimate at all.\n"); + } + } + (void) close(slaves[f].fd); + } + while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ + /* void */; + + if (pipeout) + return; + + msg("Closing %s\n", tape); + +#ifdef RDUMP + if (host) { + rmtclose(); + while (rmtopen(tape, 0) < 0) + sleep(10); + rmtclose(); + return; + } +#endif + (void) close(tapefd); + while ((f = open(tape, 0)) < 0) + sleep (10); + (void) close(f); +} + +void +close_rewind() +{ + trewind(); + if (nexttape) + return; + if (!nogripe) { + msg("Change Volumes: Mount volume #%d\n", tapeno+1); + broadcast("CHANGE DUMP VOLUMES!\7\7\n"); + } + while (!query("Is the new volume mounted and ready to go?")) + if (query("Do you want to abort?")) { + dumpabort(0); + /*NOTREACHED*/ + } +} + +void +rollforward() +{ + register struct req *p, *q, *prev; + register struct slave *tslp; + int i, size, savedtapea, got; + union u_spcl *ntb, *otb; + tslp = &slaves[SLAVES]; + ntb = (union u_spcl *)tslp->tblock[1]; + + /* + * Each of the N slaves should have requests that need to + * be replayed on the next tape. Use the extra slave buffers + * (slaves[SLAVES]) to construct request lists to be sent to + * each slave in turn. + */ + for (i = 0; i < SLAVES; i++) { + q = &tslp->req[1]; + otb = (union u_spcl *)slp->tblock; + + /* + * For each request in the current slave, copy it to tslp. + */ + + prev = NULL; + for (p = slp->req; p->count > 0; p += p->count) { + *q = *p; + if (p->dblk == 0) + *ntb++ = *otb++; /* copy the datablock also */ + prev = q; + q += q->count; + } + if (prev == NULL) + quit("rollforward: protocol botch"); + if (prev->dblk != 0) + prev->count -= 1; + else + ntb--; + q -= 1; + q->count = 0; + q = &tslp->req[0]; + if (i == 0) { + q->dblk = 0; + q->count = 1; + trecno = 0; + nextblock = tslp->tblock; + savedtapea = spcl.c_tapea; + spcl.c_tapea = slp->tapea; + startnewtape(0); + spcl.c_tapea = savedtapea; + lastspclrec = savedtapea - 1; + } + size = (char *)ntb - (char *)q; + if (atomic(write, slp->fd, (char *)q, size) != size) { + perror(" DUMP: error writing command pipe"); + dumpabort(0); + } + slp->sent = 1; + if (++slp >= &slaves[SLAVES]) + slp = &slaves[0]; + + q->count = 1; + + if (prev->dblk != 0) { + /* + * If the last one was a disk block, make the + * first of this one be the last bit of that disk + * block... + */ + q->dblk = prev->dblk + + prev->count * (TP_BSIZE / DEV_BSIZE); + ntb = (union u_spcl *)tslp->tblock; + } else { + /* + * It wasn't a disk block. Copy the data to its + * new location in the buffer. + */ + q->dblk = 0; + *((union u_spcl *)tslp->tblock) = *ntb; + ntb = (union u_spcl *)tslp->tblock[1]; + } + } + slp->req[0] = *q; + nextblock = slp->tblock; + if (q->dblk == 0) + nextblock++; + trecno = 1; + + /* + * Clear the first slaves' response. One hopes that it + * worked ok, otherwise the tape is much too short! + */ + if (slp->sent) { + if (atomic(read, slp->fd, (char *)&got, sizeof got) + != sizeof got) { + perror(" DUMP: error reading command pipe in master"); + dumpabort(0); + } + slp->sent = 0; + + if (got != writesize) { + quit("EOT detected at start of the tape!\n"); + } + } +} + +/* + * We implement taking and restoring checkpoints on the tape level. + * When each tape is opened, a new process is created by forking; this + * saves all of the necessary context in the parent. The child + * continues the dump; the parent waits around, saving the context. + * If the child returns X_REWRITE, then it had problems writing that tape; + * this causes the parent to fork again, duplicating the context, and + * everything continues as if nothing had happened. + */ +void +startnewtape(top) + int top; +{ + int parentpid; + int childpid; + int status; + int waitpid; + char *p; +#ifdef sunos + void (*interrupt_save)(); +#else + sig_t interrupt_save; +#endif + + interrupt_save = signal(SIGINT, SIG_IGN); + parentpid = getpid(); + +restore_check_point: + (void)signal(SIGINT, interrupt_save); + /* + * All signals are inherited... + */ + childpid = fork(); + if (childpid < 0) { + msg("Context save fork fails in parent %d\n", parentpid); + Exit(X_ABORT); + } + if (childpid != 0) { + /* + * PARENT: + * save the context by waiting + * until the child doing all of the work returns. + * don't catch the interrupt + */ + signal(SIGINT, SIG_IGN); +#ifdef TDEBUG + msg("Tape: %d; parent process: %d child process %d\n", + tapeno+1, parentpid, childpid); +#endif /* TDEBUG */ + while ((waitpid = wait(&status)) != childpid) + msg("Parent %d waiting for child %d has another child %d return\n", + parentpid, childpid, waitpid); + if (status & 0xFF) { + msg("Child %d returns LOB status %o\n", + childpid, status&0xFF); + } + status = (status >> 8) & 0xFF; +#ifdef TDEBUG + switch(status) { + case X_FINOK: + msg("Child %d finishes X_FINOK\n", childpid); + break; + case X_ABORT: + msg("Child %d finishes X_ABORT\n", childpid); + break; + case X_REWRITE: + msg("Child %d finishes X_REWRITE\n", childpid); + break; + default: + msg("Child %d finishes unknown %d\n", + childpid, status); + break; + } +#endif /* TDEBUG */ + switch(status) { + case X_FINOK: + Exit(X_FINOK); + case X_ABORT: + Exit(X_ABORT); + case X_REWRITE: + goto restore_check_point; + default: + msg("Bad return code from dump: %d\n", status); + Exit(X_ABORT); + } + /*NOTREACHED*/ + } else { /* we are the child; just continue */ +#ifdef TDEBUG + sleep(4); /* allow time for parent's message to get out */ + msg("Child on Tape %d has parent %d, my pid = %d\n", + tapeno+1, parentpid, getpid()); +#endif /* TDEBUG */ + /* + * If we have a name like "/dev/rmt0,/dev/rmt1", + * use the name before the comma first, and save + * the remaining names for subsequent volumes. + */ + tapeno++; /* current tape sequence */ + if (nexttape || index(tape, ',')) { + if (nexttape && *nexttape) + tape = nexttape; + if ((p = index(tape, ',')) != NULL) { + *p = '\0'; + nexttape = p + 1; + } else + nexttape = NULL; + msg("Dumping volume %d on %s\n", tapeno, tape); + } +#ifdef RDUMP + while ((tapefd = (host ? rmtopen(tape, 2) : + pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) +#else + while ((tapefd = (pipeout ? 1 : + open(tape, O_WRONLY|O_CREAT, 0666))) < 0) +#endif + { + msg("Cannot open output \"%s\".\n", tape); + if (!query("Do you want to retry the open?")) + dumpabort(0); + } + + enslave(); /* Share open tape file descriptor with slaves */ + + asize = 0; + blocksthisvol = 0; + if (top) + newtape++; /* new tape signal */ + spcl.c_count = slp->count; + /* + * measure firstrec in TP_BSIZE units since restore doesn't + * know the correct ntrec value... + */ + spcl.c_firstrec = slp->firstrec; + spcl.c_volume++; + spcl.c_type = TS_TAPE; + spcl.c_flags |= DR_NEWHEADER; + writeheader((ino_t)slp->inode); + spcl.c_flags &=~ DR_NEWHEADER; + if (tapeno > 1) + msg("Volume %d begins with blocks from inode %d\n", + tapeno, slp->inode); + } +} + +void +dumpabort(signo) + int signo; +{ + + if (master != 0 && master != getpid()) + /* Signals master to call dumpabort */ + (void) kill(master, SIGTERM); + else { + killall(); + msg("The ENTIRE dump is aborted.\n"); + } +#ifdef RDUMP + rmtclose(); +#endif + Exit(X_ABORT); +} + +__dead void +Exit(status) + int status; +{ + +#ifdef TDEBUG + msg("pid = %d exits with status %d\n", getpid(), status); +#endif /* TDEBUG */ + exit(status); +} + +/* + * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. + */ +void +proceed(signo) + int signo; +{ + + if (ready) + longjmp(jmpbuf, 1); + caught++; +} + +void +enslave() +{ + int cmd[2]; + register int i, j; + + master = getpid(); + + signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ + signal(SIGPIPE, sigpipe); + signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ + signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ + + for (i = 0; i < SLAVES; i++) { + if (i == slp - &slaves[0]) { + caught = 1; + } else { + caught = 0; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || + (slaves[i].pid = fork()) < 0) + quit("too many slaves, %d (recompile smaller): %s\n", + i, strerror(errno)); + + slaves[i].fd = cmd[1]; + slaves[i].sent = 0; + if (slaves[i].pid == 0) { /* Slave starts up here */ + for (j = 0; j <= i; j++) + (void) close(slaves[j].fd); + signal(SIGINT, SIG_IGN); /* Master handles this */ + doslave(cmd[0], i); + Exit(X_FINOK); + } + } + + for (i = 0; i < SLAVES; i++) + (void) atomic(write, slaves[i].fd, + (char *) &slaves[(i + 1) % SLAVES].pid, + sizeof slaves[0].pid); + + master = 0; +} + +void +killall() +{ + register int i; + + for (i = 0; i < SLAVES; i++) + if (slaves[i].pid > 0) + (void) kill(slaves[i].pid, SIGKILL); +} + +/* + * Synchronization - each process has a lockfile, and shares file + * descriptors to the following process's lockfile. When our write + * completes, we release our lock on the following process's lock- + * file, allowing the following process to lock it and proceed. We + * get the lock back for the next cycle by swapping descriptors. + */ +static void +doslave(cmd, slave_number) + register int cmd; + int slave_number; +{ + register int nread; + int nextslave, size, wrote, eot_count; + + /* + * Need our own seek pointer. + */ + (void) close(diskfd); + if ((diskfd = open(disk, O_RDONLY)) < 0) + quit("slave couldn't reopen disk: %s\n", strerror(errno)); + + /* + * Need the pid of the next slave in the loop... + */ + if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) + != sizeof nextslave) { + quit("master/slave protocol botched - didn't get pid of next slave.\n"); + } + + /* + * Get list of blocks to dump, read the blocks into tape buffer + */ + while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { + register struct req *p = slp->req; + + for (trecno = 0; trecno < ntrec; + trecno += p->count, p += p->count) { + if (p->dblk) { + bread(p->dblk, slp->tblock[trecno], + p->count * TP_BSIZE); + } else { + if (p->count != 1 || atomic(read, cmd, + (char *)slp->tblock[trecno], + TP_BSIZE) != TP_BSIZE) + quit("master/slave protocol botched.\n"); + } + } + if (setjmp(jmpbuf) == 0) { + ready = 1; + if (!caught) + (void) pause(); + } + ready = 0; + caught = 0; + + /* Try to write the data... */ + eot_count = 0; + size = 0; + + while (eot_count < 10 && size < writesize) { +#ifdef RDUMP + if (host) + wrote = rmtwrite(slp->tblock[0]+size, + writesize-size); + else +#endif + wrote = write(tapefd, slp->tblock[0]+size, + writesize-size); +#ifdef WRITEDEBUG + printf("slave %d wrote %d\n", slave_number, wrote); +#endif + if (wrote < 0) + break; + if (wrote == 0) + eot_count++; + size += wrote; + } + +#ifdef WRITEDEBUG + if (size != writesize) + printf("slave %d only wrote %d out of %d bytes and gave up.\n", + slave_number, size, writesize); +#endif + + if (eot_count > 0) + size = 0; + + /* + * fixme: Pyramids running OSx return ENOSPC + * at EOT on 1/2 inch drives. + */ + if (size < 0) { + (void) kill(master, SIGUSR1); + for (;;) + (void) sigpause(0); + } else { + /* + * pass size of write back to master + * (for EOT handling) + */ + (void) atomic(write, cmd, (char *)&size, sizeof size); + } + + /* + * If partial write, don't want next slave to go. + * Also jolts him awake. + */ + (void) kill(nextslave, SIGUSR2); + } + if (nread != 0) + quit("error reading command pipe: %s\n", strerror(errno)); +} + +/* + * Since a read from a pipe may not return all we asked for, + * or a write may not write all we ask if we get a signal, + * loop until the count is satisfied (or error). + */ +static int +atomic(func, fd, buf, count) + int (*func)(), fd; + char *buf; + int count; +{ + int got, need = count; + + while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) + buf += got; + return (got < 0 ? got : count - need); +} diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c new file mode 100644 index 000000000000..ce70050ce073 --- /dev/null +++ b/sbin/dump/traverse.c @@ -0,0 +1,613 @@ +/*- + * Copyright (c) 1980, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)traverse.c 8.2 (Berkeley) 9/23/93"; +#endif /* not lint */ + +#include +#include +#include +#ifdef sunos +#include + +#include +#include +#include +#else +#include +#include +#include +#endif + +#include + +#include +#include +#ifdef __STDC__ +#include +#include +#endif + +#include "dump.h" + +#define HASDUMPEDFILE 0x1 +#define HASSUBDIRS 0x2 + +#ifdef FS_44INODEFMT +typedef quad_t fsizeT; +#else +typedef long fsizeT; +#endif + +static int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size)); +static void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size)); +static int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize)); + +/* + * This is an estimation of the number of TP_BSIZE blocks in the file. + * It estimates the number of blocks in files with holes by assuming + * that all of the blocks accounted for by di_blocks are data blocks + * (when some of the blocks are usually used for indirect pointers); + * hence the estimate may be high. + */ +long +blockest(dp) + register struct dinode *dp; +{ + long blkest, sizeest; + + /* + * dp->di_size is the size of the file in bytes. + * dp->di_blocks stores the number of sectors actually in the file. + * If there are more sectors than the size would indicate, this just + * means that there are indirect blocks in the file or unused + * sectors in the last file block; we can safely ignore these + * (blkest = sizeest below). + * If the file is bigger than the number of sectors would indicate, + * then the file has holes in it. In this case we must use the + * block count to estimate the number of data blocks used, but + * we use the actual size for estimating the number of indirect + * dump blocks (sizeest vs. blkest in the indirect block + * calculation). + */ + blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE); + sizeest = howmany(dp->di_size, TP_BSIZE); + if (blkest > sizeest) + blkest = sizeest; + if (dp->di_size > sblock->fs_bsize * NDADDR) { + /* calculate the number of indirect blocks on the dump tape */ + blkest += + howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE, + TP_NINDIR); + } + return (blkest + 1); +} + +/* Auxiliary macro to pick up files changed since previous dump. */ +#ifdef FS_44INODEFMT +#define CHANGEDSINCE(dp, t) \ + ((dp)->di_mtime.ts_sec >= (t) || (dp)->di_ctime.ts_sec >= (t)) +#else +#define CHANGEDSINCE(dp, t) \ + ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t)) +#endif + +/* The WANTTODUMP macro decides whether a file should be dumped. */ +#ifdef UF_NODUMP +#define WANTTODUMP(dp) \ + (CHANGEDSINCE(dp, spcl.c_ddate) && \ + (nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP)) +#else +#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) +#endif + +/* + * Dump pass 1. + * + * Walk the inode list for a filesystem to find all allocated inodes + * that have been modified since the previous dump time. Also, find all + * the directories in the filesystem. + */ +int +mapfiles(maxino, tapesize) + ino_t maxino; + long *tapesize; +{ + register int mode; + register ino_t ino; + register struct dinode *dp; + int anydirskipped = 0; + + for (ino = ROOTINO; ino < maxino; ino++) { + dp = getino(ino); + if ((mode = (dp->di_mode & IFMT)) == 0) + continue; + SETINO(ino, usedinomap); + if (mode == IFDIR) + SETINO(ino, dumpdirmap); + if (WANTTODUMP(dp)) { + SETINO(ino, dumpinomap); + if (mode != IFREG && mode != IFDIR && mode != IFLNK) + *tapesize += 1; + else + *tapesize += blockest(dp); + continue; + } + if (mode == IFDIR) + anydirskipped = 1; + } + /* + * Restore gets very upset if the root is not dumped, + * so ensure that it always is dumped. + */ + SETINO(ROOTINO, dumpinomap); + return (anydirskipped); +} + +/* + * Dump pass 2. + * + * Scan each directory on the filesystem to see if it has any modified + * files in it. If it does, and has not already been added to the dump + * list (because it was itself modified), then add it. If a directory + * has not been modified itself, contains no modified files and has no + * subdirectories, then it can be deleted from the dump list and from + * the list of directories. By deleting it from the list of directories, + * its parent may now qualify for the same treatment on this or a later + * pass using this algorithm. + */ +int +mapdirs(maxino, tapesize) + ino_t maxino; + long *tapesize; +{ + register struct dinode *dp; + register int i, isdir; + register char *map; + register ino_t ino; + long filesize; + int ret, change = 0; + + isdir = 0; /* XXX just to get gcc to shut up */ + for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { + if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ + isdir = *map++; + else + isdir >>= 1; + if ((isdir & 1) == 0 || TSTINO(ino, dumpinomap)) + continue; + dp = getino(ino); + filesize = dp->di_size; + for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) { + if (dp->di_db[i] != 0) + ret |= searchdir(ino, dp->di_db[i], + (long)dblksize(sblock, dp, i), + filesize); + if (ret & HASDUMPEDFILE) + filesize = 0; + else + filesize -= sblock->fs_bsize; + } + for (i = 0; filesize > 0 && i < NIADDR; i++) { + if (dp->di_ib[i] == 0) + continue; + ret |= dirindir(ino, dp->di_ib[i], i, &filesize); + } + if (ret & HASDUMPEDFILE) { + SETINO(ino, dumpinomap); + *tapesize += blockest(dp); + change = 1; + continue; + } + if ((ret & HASSUBDIRS) == 0) { + if (!TSTINO(ino, dumpinomap)) { + CLRINO(ino, dumpdirmap); + change = 1; + } + } + } + return (change); +} + +/* + * Read indirect blocks, and pass the data blocks to be searched + * as directories. Quit as soon as any entry is found that will + * require the directory to be dumped. + */ +static int +dirindir(ino, blkno, ind_level, filesize) + ino_t ino; + daddr_t blkno; + int ind_level; + long *filesize; +{ + int ret = 0; + register int i; + daddr_t idblk[MAXNINDIR]; + + bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize); + if (ind_level <= 0) { + for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { + blkno = idblk[i]; + if (blkno != 0) + ret |= searchdir(ino, blkno, sblock->fs_bsize, + *filesize); + if (ret & HASDUMPEDFILE) + *filesize = 0; + else + *filesize -= sblock->fs_bsize; + } + return (ret); + } + ind_level--; + for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { + blkno = idblk[i]; + if (blkno != 0) + ret |= dirindir(ino, blkno, ind_level, filesize); + } + return (ret); +} + +/* + * Scan a disk block containing directory information looking to see if + * any of the entries are on the dump list and to see if the directory + * contains any subdirectories. + */ +static int +searchdir(ino, blkno, size, filesize) + ino_t ino; + daddr_t blkno; + register long size; + long filesize; +{ + register struct direct *dp; + register long loc, ret = 0; + char dblk[MAXBSIZE]; + + bread(fsbtodb(sblock, blkno), dblk, (int)size); + if (filesize < size) + size = filesize; + for (loc = 0; loc < size; ) { + dp = (struct direct *)(dblk + loc); + if (dp->d_reclen == 0) { + msg("corrupted directory, inumber %d\n", ino); + break; + } + loc += dp->d_reclen; + if (dp->d_ino == 0) + continue; + if (dp->d_name[0] == '.') { + if (dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + } + if (TSTINO(dp->d_ino, dumpinomap)) { + ret |= HASDUMPEDFILE; + if (ret & HASSUBDIRS) + break; + } + if (TSTINO(dp->d_ino, dumpdirmap)) { + ret |= HASSUBDIRS; + if (ret & HASDUMPEDFILE) + break; + } + } + return (ret); +} + +/* + * Dump passes 3 and 4. + * + * Dump the contents of an inode to tape. + */ +void +dumpino(dp, ino) + register struct dinode *dp; + ino_t ino; +{ + int ind_level, cnt; + fsizeT size; + char buf[TP_BSIZE]; + + if (newtape) { + newtape = 0; + dumpmap(dumpinomap, TS_BITS, ino); + } + CLRINO(ino, dumpinomap); + spcl.c_dinode = *dp; + spcl.c_type = TS_INODE; + spcl.c_count = 0; + switch (dp->di_mode & S_IFMT) { + + case 0: + /* + * Freed inode. + */ + return; + + case S_IFLNK: + /* + * Check for short symbolic link. + */ +#ifdef FS_44INODEFMT + if (dp->di_size > 0 && + dp->di_size < sblock->fs_maxsymlinklen) { + spcl.c_addr[0] = 1; + spcl.c_count = 1; + writeheader(ino); + bcopy((caddr_t)dp->di_shortlink, buf, + (u_long)dp->di_size); + buf[dp->di_size] = '\0'; + writerec(buf, 0); + return; + } +#endif + /* fall through */ + + case S_IFDIR: + case S_IFREG: + if (dp->di_size > 0) + break; + /* fall through */ + + case S_IFIFO: + case S_IFSOCK: + case S_IFCHR: + case S_IFBLK: + writeheader(ino); + return; + + default: + msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT); + return; + } + if (dp->di_size > NDADDR * sblock->fs_bsize) + cnt = NDADDR * sblock->fs_frag; + else + cnt = howmany(dp->di_size, sblock->fs_fsize); + blksout(&dp->di_db[0], cnt, ino); + if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0) + return; + for (ind_level = 0; ind_level < NIADDR; ind_level++) { + dmpindir(ino, dp->di_ib[ind_level], ind_level, &size); + if (size <= 0) + return; + } +} + +/* + * Read indirect blocks, and pass the data blocks to be dumped. + */ +static void +dmpindir(ino, blk, ind_level, size) + ino_t ino; + daddr_t blk; + int ind_level; + fsizeT *size; +{ + int i, cnt; + daddr_t idblk[MAXNINDIR]; + + if (blk != 0) + bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize); + else + bzero((char *)idblk, (int)sblock->fs_bsize); + if (ind_level <= 0) { + if (*size < NINDIR(sblock) * sblock->fs_bsize) + cnt = howmany(*size, sblock->fs_fsize); + else + cnt = NINDIR(sblock) * sblock->fs_frag; + *size -= NINDIR(sblock) * sblock->fs_bsize; + blksout(&idblk[0], cnt, ino); + return; + } + ind_level--; + for (i = 0; i < NINDIR(sblock); i++) { + dmpindir(ino, idblk[i], ind_level, size); + if (*size <= 0) + return; + } +} + +/* + * Collect up the data into tape record sized buffers and output them. + */ +void +blksout(blkp, frags, ino) + daddr_t *blkp; + int frags; + ino_t ino; +{ + register daddr_t *bp; + int i, j, count, blks, tbperdb; + + blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); + tbperdb = sblock->fs_bsize >> tp_bshift; + for (i = 0; i < blks; i += TP_NINDIR) { + if (i + TP_NINDIR > blks) + count = blks; + else + count = i + TP_NINDIR; + for (j = i; j < count; j++) + if (blkp[j / tbperdb] != 0) + spcl.c_addr[j - i] = 1; + else + spcl.c_addr[j - i] = 0; + spcl.c_count = count - i; + writeheader(ino); + bp = &blkp[i / tbperdb]; + for (j = i; j < count; j += tbperdb, bp++) + if (*bp != 0) + if (j + tbperdb <= count) + dumpblock(*bp, (int)sblock->fs_bsize); + else + dumpblock(*bp, (count - j) * TP_BSIZE); + spcl.c_type = TS_ADDR; + } +} + +/* + * Dump a map to the tape. + */ +void +dumpmap(map, type, ino) + char *map; + int type; + ino_t ino; +{ + register int i; + char *cp; + + spcl.c_type = type; + spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); + writeheader(ino); + for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) + writerec(cp, 0); +} + +/* + * Write a header record to the dump tape. + */ +void +writeheader(ino) + ino_t ino; +{ + register long sum, cnt, *lp; + + spcl.c_inumber = ino; + spcl.c_magic = NFS_MAGIC; + spcl.c_checksum = 0; + lp = (long *)&spcl; + sum = 0; + cnt = sizeof(union u_spcl) / (4 * sizeof(long)); + while (--cnt >= 0) { + sum += *lp++; + sum += *lp++; + sum += *lp++; + sum += *lp++; + } + spcl.c_checksum = CHECKSUM - sum; + writerec((char *)&spcl, 1); +} + +struct dinode * +getino(inum) + ino_t inum; +{ + static daddr_t minino, maxino; + static struct dinode inoblock[MAXINOPB]; + + curino = inum; + if (inum >= minino && inum < maxino) + return (&inoblock[inum - minino]); + bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock, + (int)sblock->fs_bsize); + minino = inum - (inum % INOPB(sblock)); + maxino = minino + INOPB(sblock); + return (&inoblock[inum - minino]); +} + +/* + * Read a chunk of data from the disk. + * Try to recover from hard errors by reading in sector sized pieces. + * Error recovery is attempted at most BREADEMAX times before seeking + * consent from the operator to continue. + */ +int breaderrors = 0; +#define BREADEMAX 32 + +void +bread(blkno, buf, size) + daddr_t blkno; + char *buf; + int size; +{ + int cnt, i; + extern int errno; + +loop: + if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + msg("bread: lseek fails\n"); + if ((cnt = read(diskfd, buf, size)) == size) + return; + if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { + /* + * Trying to read the final fragment. + * + * NB - dump only works in TP_BSIZE blocks, hence + * rounds `dev_bsize' fragments up to TP_BSIZE pieces. + * It should be smarter about not actually trying to + * read more than it can get, but for the time being + * we punt and scale back the read only when it gets + * us into trouble. (mkm 9/25/83) + */ + size -= dev_bsize; + goto loop; + } + if (cnt == -1) + msg("read error from %s: %s: [block %d]: count=%d\n", + disk, strerror(errno), blkno, size); + else + msg("short read error from %s: [block %d]: count=%d, got=%d\n", + disk, blkno, size, cnt); + if (++breaderrors > BREADEMAX) { + msg("More than %d block read errors from %d\n", + BREADEMAX, disk); + broadcast("DUMP IS AILING!\n"); + msg("This is an unrecoverable error.\n"); + if (!query("Do you want to attempt to continue?")){ + dumpabort(0); + /*NOTREACHED*/ + } else + breaderrors = 0; + } + /* + * Zero buffer, then try to read each sector of buffer separately. + */ + bzero(buf, size); + for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { + if ((int)lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) + msg("bread: lseek2 fails!\n"); + if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize) + continue; + if (cnt == -1) { + msg("read error from %s: %s: [sector %d]: count=%d\n", + disk, strerror(errno), blkno, dev_bsize); + continue; + } + msg("short read error from %s: [sector %d]: count=%d, got=%d\n", + disk, blkno, dev_bsize, cnt); + } +} diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c new file mode 100644 index 000000000000..7b3fd4ea7cbd --- /dev/null +++ b/sbin/dump/unctime.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)unctime.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include + +#include +#include +#ifdef __STDC__ +#include +#include +#endif + +#ifndef __P +#include +#endif + +/* + * Convert a ctime(3) format string into a system format date. + * Return the date thus calculated. + * + * Return -1 if the string is not in ctime format. + */ + +/* + * Offsets into the ctime string to various parts. + */ + +#define E_MONTH 4 +#define E_DAY 8 +#define E_HOUR 11 +#define E_MINUTE 14 +#define E_SECOND 17 +#define E_YEAR 20 + +static int dcmp __P((struct tm *, struct tm *)); +static time_t emitl __P((struct tm *)); +static int lookup __P((char *)); + + +time_t +unctime(str) + char *str; +{ + struct tm then; + char dbuf[26]; + + (void) strncpy(dbuf, str, sizeof(dbuf) - 1); + dbuf[sizeof(dbuf) - 1] = '\0'; + dbuf[E_MONTH+3] = '\0'; + if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0) + return (-1); + then.tm_mday = atoi(&dbuf[E_DAY]); + then.tm_hour = atoi(&dbuf[E_HOUR]); + then.tm_min = atoi(&dbuf[E_MINUTE]); + then.tm_sec = atoi(&dbuf[E_SECOND]); + then.tm_year = atoi(&dbuf[E_YEAR]) - 1900; + return(emitl(&then)); +} + +static char months[] = + "JanFebMarAprMayJunJulAugSepOctNovDec"; + +static int +lookup(str) + char *str; +{ + register char *cp, *cp2; + + for (cp = months, cp2 = str; *cp != '\0'; cp += 3) + if (strncmp(cp, cp2, 3) == 0) + return((cp-months) / 3); + return(-1); +} +/* + * Routine to convert a localtime(3) format date back into + * a system format date. + * + * Use a binary search. + */ + +static time_t +emitl(dp) + struct tm *dp; +{ + time_t conv; + register int i, bit; + struct tm dcopy; + + dcopy = *dp; + dp = &dcopy; + conv = 0; + for (i = 30; i >= 0; i--) { + bit = 1 << i; + conv |= bit; + if (dcmp(localtime(&conv), dp) > 0) + conv &= ~bit; + } + return(conv); +} + +/* + * Compare two localtime dates, return result. + */ + +#define DECIDE(a) \ + if (dp->a > dp2->a) \ + return(1); \ + if (dp->a < dp2->a) \ + return(-1) + +static int +dcmp(dp, dp2) + register struct tm *dp, *dp2; +{ + + DECIDE(tm_year); + DECIDE(tm_mon); + DECIDE(tm_mday); + DECIDE(tm_hour); + DECIDE(tm_min); + DECIDE(tm_sec); + return(0); +} diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile new file mode 100644 index 000000000000..dd2a0c2830ca --- /dev/null +++ b/sbin/dumpfs/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= dumpfs +MAN8= dumpfs.0 + +.include diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8 new file mode 100644 index 000000000000..17263d0a96f0 --- /dev/null +++ b/sbin/dumpfs/dumpfs.8 @@ -0,0 +1,62 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dumpfs.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt DUMPFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm dumpfs +.Nd dump file system information +.Sh SYNOPSIS +.Nm dumpfs +.Op Ar filesys No \&| Ar device +.Sh DESCRIPTION +.Nm Dumpfs +prints out the super block and cylinder group information +for the file system or special device specified. +The listing is very long and detailed. This +command is useful mostly for finding out certain file system +information such as the file system block size and minimum +free space percentage. +.Sh SEE ALSO +.Xr fs 5 , +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr tunefs 8 , +.Xr newfs 8 , +.Xr fsck 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c new file mode 100644 index 000000000000..d453fab7512c --- /dev/null +++ b/sbin/dumpfs/dumpfs.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dumpfs.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +union { + struct fs fs; + char pad[MAXBSIZE]; +} fsun; +#define afs fsun.fs + +union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun; +#define acg cgun.cg + +long dev_bsize = 1; + +int dumpfs __P((char *)); +int dumpcg __P((char *, int, int)); +void pbits __P((void *, int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fstab *fs; + int ch, eval; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + for (eval = 0; *argv; ++argv) + if ((fs = getfsfile(*argv)) == NULL) + eval |= dumpfs(*argv); + else + eval |= dumpfs(fs->fs_spec); + exit(eval); +} + +int +dumpfs(name) + char *name; +{ + int fd, c, i, j, k, size; + + if ((fd = open(name, O_RDONLY, 0)) < 0) + goto err; + if (lseek(fd, (off_t)SBOFF, SEEK_SET) == (off_t)-1) + goto err; + if (read(fd, &afs, SBSIZE) != SBSIZE) + goto err; + + if (afs.fs_postblformat == FS_42POSTBLFMT) + afs.fs_nrpos = 8; + dev_bsize = afs.fs_fsize / fsbtodb(&afs, 1); + printf("magic\t%x\ttime\t%s", afs.fs_magic, + ctime(&afs.fs_time)); + printf("cylgrp\t%s\tinodes\t%s\n", + afs.fs_postblformat == FS_42POSTBLFMT ? "static" : "dynamic", + afs.fs_inodefmt < FS_44INODEFMT ? "4.2/4.3BSD" : "4.4BSD"); + printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", + afs.fs_cstotal.cs_nbfree, afs.fs_cstotal.cs_ndir, + afs.fs_cstotal.cs_nifree, afs.fs_cstotal.cs_nffree); + printf("ncg\t%d\tncyl\t%d\tsize\t%d\tblocks\t%d\n", + afs.fs_ncg, afs.fs_ncyl, afs.fs_size, afs.fs_dsize); + printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_bsize, afs.fs_bshift, afs.fs_bmask); + printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_fsize, afs.fs_fshift, afs.fs_fmask); + printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n", + afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb); + printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n", + afs.fs_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg); + printf("minfree\t%d%%\toptim\t%s\tmaxcontig %d\tmaxbpg\t%d\n", + afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time", + afs.fs_maxcontig, afs.fs_maxbpg); + printf("rotdelay %dms\theadswitch %dus\ttrackseek %dus\trps\t%d\n", + afs.fs_rotdelay, afs.fs_headswitch, afs.fs_trkseek, afs.fs_rps); + printf("ntrak\t%d\tnsect\t%d\tnpsect\t%d\tspc\t%d\n", + afs.fs_ntrak, afs.fs_nsect, afs.fs_npsect, afs.fs_spc); + printf("symlinklen %d\ttrackskew %d\tinterleave %d\tcontigsumsize %d\n", + afs.fs_maxsymlinklen, afs.fs_trackskew, afs.fs_interleave, + afs.fs_contigsumsize); + printf("nindir\t%d\tinopb\t%d\tnspf\t%d\n", + afs.fs_nindir, afs.fs_inopb, afs.fs_nspf); + printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n", + afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno); + printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n", + afs.fs_sbsize, afs.fs_cgsize, afs.fs_cgoffset, afs.fs_cgmask); + printf("csaddr\t%d\tcssize\t%d\tshift\t%d\tmask\t0x%08x\n", + afs.fs_csaddr, afs.fs_cssize, afs.fs_csshift, afs.fs_csmask); + printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\n", + afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly); + if (afs.fs_cpc != 0) + printf("blocks available in each of %d rotational positions", + afs.fs_nrpos); + else + printf("insufficient space to maintain rotational tables\n"); + for (c = 0; c < afs.fs_cpc; c++) { + printf("\ncylinder number %d:", c); + for (i = 0; i < afs.fs_nrpos; i++) { + if (fs_postbl(&afs, c)[i] == -1) + continue; + printf("\n position %d:\t", i); + for (j = fs_postbl(&afs, c)[i], k = 1; ; + j += fs_rotbl(&afs)[j], k++) { + printf("%5d", j); + if (k % 12 == 0) + printf("\n\t\t"); + if (fs_rotbl(&afs)[j] == 0) + break; + } + } + } + printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); + for (i = 0, j = 0; i < afs.fs_cssize; i += afs.fs_bsize, j++) { + size = afs.fs_cssize - i < afs.fs_bsize ? + afs.fs_cssize - i : afs.fs_bsize; + afs.fs_csp[j] = calloc(1, size); + if (lseek(fd, + (off_t)(fsbtodb(&afs, (afs.fs_csaddr + j * afs.fs_frag)) * + dev_bsize), SEEK_SET) == (off_t)-1) + goto err; + if (read(fd, afs.fs_csp[j], size) != size) + goto err; + } + for (i = 0; i < afs.fs_ncg; i++) { + struct csum *cs = &afs.fs_cs(&afs, i); + if (i && i % 4 == 0) + printf("\n\t"); + printf("(%d,%d,%d,%d) ", + cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree); + } + printf("\n"); + if (afs.fs_ncyl % afs.fs_cpg) { + printf("cylinders in last group %d\n", + i = afs.fs_ncyl % afs.fs_cpg); + printf("blocks in last group %d\n", + i * afs.fs_spc / NSPB(&afs)); + } + printf("\n"); + for (i = 0; i < afs.fs_ncg; i++) + if (dumpcg(name, fd, i)) + goto err; + (void)close(fd); + return (0); + +err: if (fd != -1) + (void)close(fd); + (void)fprintf(stderr, "dumpfs: %s: %s\n", name, strerror(errno)); + return (1); +}; + +int +dumpcg(name, fd, c) + char *name; + int fd, c; +{ + off_t cur; + int i, j; + + printf("\ncg %d:\n", c); + if ((cur = lseek(fd, (off_t)(fsbtodb(&afs, cgtod(&afs, c)) * dev_bsize), + SEEK_SET)) == (off_t)-1) + return (1); + if (read(fd, &acg, afs.fs_bsize) != afs.fs_bsize) { + (void)fprintf(stderr, "dumpfs: %s: error reading cg\n", name); + return (1); + } + printf("magic\t%x\ttell\t%qx\ttime\t%s", + afs.fs_postblformat == FS_42POSTBLFMT ? + ((struct ocg *)&acg)->cg_magic : acg.cg_magic, + cur, ctime(&acg.cg_time)); + printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n", + acg.cg_cgx, acg.cg_ncyl, acg.cg_niblk, acg.cg_ndblk); + printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", + acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir, + acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree); + printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum", + acg.cg_rotor, acg.cg_irotor, acg.cg_frotor); + for (i = 1, j = 0; i < afs.fs_frag; i++) { + printf("\t%d", acg.cg_frsum[i]); + j += i * acg.cg_frsum[i]; + } + printf("\nsum of frsum: %d", j); + if (afs.fs_contigsumsize > 0) { + for (i = 1; i < afs.fs_contigsumsize; i++) { + if ((i - 1) % 8 == 0) + printf("\nclusters %d-%d:", i, + afs.fs_contigsumsize - 1 < i + 7 ? + afs.fs_contigsumsize - 1 : i + 7); + printf("\t%d", cg_clustersum(&acg)[i]); + } + printf("\nclusters size %d and over: %d\n", + afs.fs_contigsumsize, + cg_clustersum(&acg)[afs.fs_contigsumsize]); + printf("clusters free:\t"); + pbits(cg_clustersfree(&acg), acg.cg_nclusterblks); + } else + printf("\n"); + printf("iused:\t"); + pbits(cg_inosused(&acg), afs.fs_ipg); + printf("free:\t"); + pbits(cg_blksfree(&acg), afs.fs_fpg); + printf("b:\n"); + for (i = 0; i < afs.fs_cpg; i++) { + if (cg_blktot(&acg)[i] == 0) + continue; + printf(" c%d:\t(%d)\t", i, cg_blktot(&acg)[i]); + for (j = 0; j < afs.fs_nrpos; j++) { + if (afs.fs_cpc > 0 && + fs_postbl(&afs, i % afs.fs_cpc)[j] == -1) + continue; + printf(" %d", cg_blks(&afs, &acg, i)[j]); + } + printf("\n"); + } + return (0); +}; + +void +pbits(vp, max) + register void *vp; + int max; +{ + register int i; + register char *p; + int count, j; + + for (count = i = 0, p = vp; i < max; i++) + if (isset(p, i)) { + if (count) + printf(",%s", count % 6 ? " " : "\n\t"); + count++; + printf("%d", i); + j = i; + while ((i+1) diff --git a/sbin/dumplfs/dumplfs.8 b/sbin/dumplfs/dumplfs.8 new file mode 100644 index 000000000000..de0c68055ac2 --- /dev/null +++ b/sbin/dumplfs/dumplfs.8 @@ -0,0 +1,59 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dumplfs.8 8.1 (Berkeley) 6/18/93 +.\" +.Dd June 18, 1993 +.Dt DUMPLFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm dumplfs +.Nd dump file system information +.Sh SYNOPSIS +.Nm dumplfs +.Op Ar filesys No \&| Ar device +.Sh DESCRIPTION +.Nm Dumplfs +prints out the file system layout information for the +LFS file system or special device specified. +The listing is very long and detailed. +This command is useful mostly for finding out certain file system +information such as the file system block size. +.Sh SEE ALSO +.Xr fs 5 , +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr newlfs 8 , +.Sh HISTORY +The +.Nm dumplfs +command appeared in +.Bx 4.4 . diff --git a/sbin/dumplfs/dumplfs.c b/sbin/dumplfs/dumplfs.c new file mode 100644 index 000000000000..943c6cd97701 --- /dev/null +++ b/sbin/dumplfs/dumplfs.c @@ -0,0 +1,612 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dumplfs.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "extern.h" + +static void addseg __P((char *)); +static void dump_cleaner_info __P((struct lfs *, void *)); +static void dump_dinode __P((struct dinode *)); +static void dump_ifile __P((int, struct lfs *, int)); +static int dump_ipage_ifile __P((int, IFILE *, int)); +static int dump_ipage_segusage __P((struct lfs *, int, IFILE *, int)); +static void dump_segment __P((int, int, daddr_t, struct lfs *, int)); +static int dump_sum __P((int, struct lfs *, SEGSUM *, int, daddr_t)); +static void dump_super __P((struct lfs *)); +static void usage __P((void)); + +typedef struct seglist SEGLIST; +struct seglist { + SEGLIST *next; + int num; +}; +SEGLIST *seglist; + +int daddr_shift; +char *special; + +/* Segment Usage formats */ +#define print_suheader \ + (void)printf("segnum\tflags\tnbytes\tninos\tnsums\tlastmod\n") + +#define print_suentry(i, sp) \ + (void)printf("%d\t%c%c%c\t%d\t%d\t%d\t%s", i, \ + (((sp)->su_flags & SEGUSE_ACTIVE) ? 'A' : ' '), \ + (((sp)->su_flags & SEGUSE_DIRTY) ? 'D' : 'C'), \ + (((sp)->su_flags & SEGUSE_SUPERBLOCK) ? 'S' : ' '), \ + (sp)->su_nbytes, (sp)->su_ninos, (sp)->su_nsums, \ + ctime((time_t *)&(sp)->su_lastmod)) + +/* Ifile formats */ +#define print_iheader \ + (void)printf("inum\tstatus\tversion\tdaddr\t\tfreeptr\n") +#define print_ientry(i, ip) \ + if (ip->if_daddr == LFS_UNUSED_DADDR) \ + (void)printf("%d\tFREE\t%d\t \t\t%d\n", \ + i, ip->if_version, ip->if_nextfree); \ + else \ + (void)printf("%d\tINUSE\t%d\t%8X \n", \ + i, ip->if_version, ip->if_daddr) +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct lfs lfs_sb1, lfs_sb2, *lfs_master; + daddr_t seg_addr; + int ch, do_allsb, do_ientries, fd, segnum; + + do_allsb = 0; + do_ientries = 0; + while ((ch = getopt(argc, argv, "ais:")) != EOF) + switch(ch) { + case 'a': /* Dump all superblocks */ + do_allsb = 1; + break; + case 'i': /* Dump ifile entries */ + do_ientries = 1; + break; + case 's': /* Dump out these segments */ + addseg(optarg); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + special = argv[0]; + if ((fd = open(special, O_RDONLY, 0)) < 0) + err("%s: %s", special, strerror(errno)); + + /* Read the first superblock */ + get(fd, LFS_LABELPAD, &lfs_sb1, sizeof(struct lfs)); + daddr_shift = lfs_sb1.lfs_bshift - lfs_sb1.lfs_fsbtodb; + + /* + * Read the second superblock and figure out which check point is + * most up to date. + */ + get(fd, + lfs_sb1.lfs_sboffs[1] << daddr_shift, &lfs_sb2, sizeof(struct lfs)); + + lfs_master = &lfs_sb1; + if (lfs_sb1.lfs_tstamp < lfs_sb2.lfs_tstamp) + lfs_master = &lfs_sb2; + + (void)printf("Master Superblock:\n"); + dump_super(lfs_master); + + dump_ifile(fd, lfs_master, do_ientries); + + if (seglist != NULL) + for (; seglist != NULL; seglist = seglist->next) { + seg_addr = lfs_master->lfs_sboffs[0] + seglist->num * + (lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb); + dump_segment(fd, + seglist->num, seg_addr, lfs_master, do_allsb); + } + else + for (segnum = 0, seg_addr = lfs_master->lfs_sboffs[0]; + segnum < lfs_master->lfs_nseg; segnum++, seg_addr += + lfs_master->lfs_ssize << lfs_master->lfs_fsbtodb) + dump_segment(fd, + segnum, seg_addr, lfs_master, do_allsb); + + (void)close(fd); + exit(0); +} + +/* + * We are reading all the blocks of an inode and dumping out the ifile table. + * This code could be tighter, but this is a first pass at getting the stuff + * printed out rather than making this code incredibly efficient. + */ +static void +dump_ifile(fd, lfsp, do_ientries) + int fd; + struct lfs *lfsp; + int do_ientries; +{ + IFILE *ipage; + struct dinode *dip, *dpage; + daddr_t addr, *addrp, *dindir, *iaddrp, *indir; + int block_limit, i, inum, j, nblocks, nsupb, psize; + + psize = lfsp->lfs_bsize; + addr = lfsp->lfs_idaddr; + + if (!(dpage = malloc(psize))) + err("%s", strerror(errno)); + get(fd, addr << daddr_shift, dpage, psize); + + for (dip = dpage + INOPB(lfsp) - 1; dip >= dpage; --dip) + if (dip->di_inumber == LFS_IFILE_INUM) + break; + + if (dip < dpage) + err("unable to locate ifile inode"); + + (void)printf("\nIFILE inode\n"); + dump_dinode(dip); + + (void)printf("\nIFILE contents\n"); + nblocks = dip->di_size >> lfsp->lfs_bshift; + block_limit = MIN(nblocks, NDADDR); + + /* Get the direct block */ + if ((ipage = malloc(psize)) == NULL) + err("%s", strerror(errno)); + for (inum = 0, addrp = dip->di_db, i = 0; i < block_limit; + i++, addrp++) { + get(fd, *addrp << daddr_shift, ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + print_suheader; + continue; + } + + if (i < (lfsp->lfs_segtabsz + lfsp->lfs_cleansz)) { + inum = dump_ipage_segusage(lfsp, inum, ipage, + lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e0; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb); + + } + + if (nblocks <= NDADDR) + goto e0; + + /* Dump out blocks off of single indirect block */ + if (!(indir = malloc(psize))) + err("%s", strerror(errno)); + get(fd, dip->di_ib[0] << daddr_shift, indir, psize); + block_limit = MIN(i + lfsp->lfs_nindir, nblocks); + for (addrp = indir; i < block_limit; i++, addrp++) { + if (*addrp == LFS_UNUSED_DADDR) + break; + get(fd, *addrp << daddr_shift,ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + continue; + } else + i -= lfsp->lfs_cleansz; + + if (i < lfsp->lfs_segtabsz) { + inum = dump_ipage_segusage(lfsp, inum, ipage, + lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e1; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, ipage, lfsp->lfs_ifpb); + } + + if (nblocks <= lfsp->lfs_nindir * lfsp->lfs_ifpb) + goto e1; + + /* Get the double indirect block */ + if (!(dindir = malloc(psize))) + err("%s", strerror(errno)); + get(fd, dip->di_ib[1] << daddr_shift, dindir, psize); + for (iaddrp = dindir, j = 0; j < lfsp->lfs_nindir; j++, iaddrp++) { + if (*iaddrp == LFS_UNUSED_DADDR) + break; + get(fd, *iaddrp << daddr_shift, indir, psize); + block_limit = MIN(i + lfsp->lfs_nindir, nblocks); + for (addrp = indir; i < block_limit; i++, addrp++) { + if (*addrp == LFS_UNUSED_DADDR) + break; + get(fd, *addrp << daddr_shift, ipage, psize); + if (i < lfsp->lfs_cleansz) { + dump_cleaner_info(lfsp, ipage); + continue; + } else + i -= lfsp->lfs_cleansz; + + if (i < lfsp->lfs_segtabsz) { + inum = dump_ipage_segusage(lfsp, + inum, ipage, lfsp->lfs_sepb); + if (!inum) + if(!do_ientries) + goto e2; + else + print_iheader; + } else + inum = dump_ipage_ifile(inum, + ipage, lfsp->lfs_ifpb); + } + } +e2: free(dindir); +e1: free(indir); +e0: free(dpage); + free(ipage); +} + +static int +dump_ipage_ifile(i, pp, tot) + int i; + IFILE *pp; + int tot; +{ + IFILE *ip; + int cnt, max; + + max = i + tot; + + for (ip = pp, cnt = i; cnt < max; cnt++, ip++) + print_ientry(cnt, ip); + return (max); +} + +static int +dump_ipage_segusage(lfsp, i, pp, tot) + struct lfs *lfsp; + int i; + IFILE *pp; + int tot; +{ + SEGUSE *sp; + int cnt, max; + + max = i + tot; + for (sp = (SEGUSE *)pp, cnt = i; + cnt < lfsp->lfs_nseg && cnt < max; cnt++, sp++) + print_suentry(cnt, sp); + if (max >= lfsp->lfs_nseg) + return (0); + else + return (max); +} + +static void +dump_dinode(dip) + struct dinode *dip; +{ + int i; + + (void)printf("%s%d\t%s%d\t%s%d\t%s%d\t%s%d\n", + "mode ", dip->di_mode, + "nlink ", dip->di_nlink, + "uid ", dip->di_uid, + "gid ", dip->di_gid, + "size ", dip->di_size); + (void)printf("%s%s%s%s%s%s", + "atime ", ctime(&dip->di_atime.ts_sec), + "mtime ", ctime(&dip->di_mtime.ts_sec), + "ctime ", ctime(&dip->di_ctime.ts_sec)); + (void)printf("inum %d\n", dip->di_inumber); + (void)printf("Direct Addresses\n"); + for (i = 0; i < NDADDR; i++) { + (void)printf("\t0x%X", dip->di_db[i]); + if ((i % 6) == 5) + (void)printf("\n"); + } + for (i = 0; i < NIADDR; i++) + (void)printf("\t0x%X", dip->di_ib[i]); + (void)printf("\n"); +} + +static int +dump_sum(fd, lfsp, sp, segnum, addr) + struct lfs *lfsp; + SEGSUM *sp; + int fd, segnum; + daddr_t addr; +{ + FINFO *fp; + long *dp; + int i, j; + int ck; + int numblocks; + struct dinode *inop; + + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) { + (void)printf("dumplfs: %s %d address 0x%lx\n", + "corrupt summary block; segment", segnum, addr); + return(0); + } + + (void)printf("Segment Summary Info at 0x%lx\n", addr); + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", + "next ", sp->ss_next, + "nfinfo ", sp->ss_nfinfo, + "ninos ", sp->ss_ninos, + "sumsum ", sp->ss_sumsum, + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); + + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + + /* Dump out inode disk addresses */ + dp = (daddr_t *)sp; + dp += LFS_SUMMARY_SIZE / sizeof(daddr_t); + inop = malloc(1 << lfsp->lfs_bshift); + printf(" Inode addresses:"); + for (dp--, i = 0; i < sp->ss_ninos; dp--) { + printf("\t0x%X {", *dp); + get(fd, *dp << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb), inop, + (1 << lfsp->lfs_bshift)); + for (j = 0; i < sp->ss_ninos && j < INOPB(lfsp); j++, i++) { + if (j > 0) + (void)printf(", "); + (void)printf("%d", inop[j].di_inumber); + } + (void)printf("}"); + if (((i/INOPB(lfsp)) % 4) == 3) + (void)printf("\n"); + } + free(inop); + + printf("\n"); + for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; i++) { + numblocks += fp->fi_nblocks; + (void)printf(" FINFO for inode: %d version %d nblocks %d\n", + fp->fi_ino, fp->fi_version, fp->fi_nblocks); + dp = &(fp->fi_blocks[0]); + for (j = 0; j < fp->fi_nblocks; j++, dp++) { + (void)printf("\t%d", *dp); + if ((j % 8) == 7) + (void)printf("\n"); + } + if ((j % 8) != 0) + (void)printf("\n"); + fp = (FINFO *)dp; + } + return (numblocks); +} + +static void +dump_segment(fd, segnum, addr, lfsp, dump_sb) + int fd, segnum; + daddr_t addr; + struct lfs *lfsp; + int dump_sb; +{ + struct lfs lfs_sb, *sbp; + SEGSUM *sump; + char sumblock[LFS_SUMMARY_SIZE]; + int did_one, nblocks, sb; + off_t sum_offset, super_off; + + (void)printf("\nSEGMENT %d (Disk Address 0x%X)\n", + addr >> (lfsp->lfs_segshift - daddr_shift), addr); + sum_offset = (addr << (lfsp->lfs_bshift - lfsp->lfs_fsbtodb)); + + sb = 0; + did_one = 0; + do { + get(fd, sum_offset, sumblock, LFS_SUMMARY_SIZE); + sump = (SEGSUM *)sumblock; + if (sump->ss_sumsum != cksum (&sump->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sump->ss_sumsum))) { + sbp = (struct lfs *)sump; + if (sb = (sbp->lfs_magic == LFS_MAGIC)) { + super_off = sum_offset; + sum_offset += LFS_SBPAD; + } else if (did_one) + break; + else { + printf("Segment at 0x%X corrupt\n", addr); + break; + } + } else { + nblocks = dump_sum(fd, lfsp, sump, segnum, sum_offset >> + (lfsp->lfs_bshift - lfsp->lfs_fsbtodb)); + if (nblocks) + sum_offset += LFS_SUMMARY_SIZE + + (nblocks << lfsp->lfs_bshift); + else + sum_offset = 0; + did_one = 1; + } + } while (sum_offset); + + if (dump_sb && sb) { + get(fd, super_off, &lfs_sb, sizeof(struct lfs)); + dump_super(&lfs_sb); + } + return; +} + +static void +dump_super(lfsp) + struct lfs *lfsp; +{ + int i; + + (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n", + "magic ", lfsp->lfs_magic, + "version ", lfsp->lfs_version, + "size ", lfsp->lfs_size, + "ssize ", lfsp->lfs_ssize); + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "dsize ", lfsp->lfs_dsize, + "bsize ", lfsp->lfs_bsize, + "fsize ", lfsp->lfs_fsize, + "frag ", lfsp->lfs_frag); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "minfree ", lfsp->lfs_minfree, + "inopb ", lfsp->lfs_inopb, + "ifpb ", lfsp->lfs_ifpb, + "nindir ", lfsp->lfs_nindir); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "nseg ", lfsp->lfs_nseg, + "nspf ", lfsp->lfs_nspf, + "cleansz ", lfsp->lfs_cleansz, + "segtabsz ", lfsp->lfs_segtabsz); + + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", + "segmask ", lfsp->lfs_segmask, + "segshift ", lfsp->lfs_segshift, + "bmask ", lfsp->lfs_bmask, + "bshift ", lfsp->lfs_bshift); + + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", + "ffmask ", lfsp->lfs_ffmask, + "ffshift ", lfsp->lfs_ffshift, + "fbmask ", lfsp->lfs_fbmask, + "fbshift ", lfsp->lfs_fbshift); + + (void)printf("%s%d\t%s%d\t%s0x%X\t%s0x%qx\n", + "sushift ", lfsp->lfs_sushift, + "fsbtodb ", lfsp->lfs_fsbtodb, + "cksum ", lfsp->lfs_cksum, + "maxfilesize ", lfsp->lfs_maxfilesize); + + (void)printf("Superblock disk addresses:\t"); + for (i = 0; i < LFS_MAXNUMSB; i++) { + (void)printf(" 0x%X", lfsp->lfs_sboffs[i]); + if ( i == (LFS_MAXNUMSB >> 1)) + (void)printf("\n\t\t\t\t"); + } + (void)printf("\n"); + + (void)printf("Checkpoint Info\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\n", + "free ", lfsp->lfs_free, + "idaddr ", lfsp->lfs_idaddr, + "ifile ", lfsp->lfs_ifile); + (void)printf("%s%d\t%s%d\t%s%d\n", + "bfree ", lfsp->lfs_bfree, + "avail ", lfsp->lfs_avail, + "uinodes ", lfsp->lfs_uinodes); + (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t", + "nfiles ", lfsp->lfs_nfiles, + "lastseg ", lfsp->lfs_lastseg, + "nextseg ", lfsp->lfs_nextseg, + "curseg ", lfsp->lfs_curseg, + "offset ", lfsp->lfs_offset); + (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp)); + (void)printf("\nIn-Memory Information\n"); + (void)printf("%s%d\t%s0x%X\t%s%d%s%d\t%s%d\n", + "seglock ", lfsp->lfs_seglock, + "iocount ", lfsp->lfs_iocount, + "writer ", lfsp->lfs_writer, + "dirops ", lfsp->lfs_dirops, + "doifile ", lfsp->lfs_doifile); + (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n", + "nactive ", lfsp->lfs_nactive, + "fmod ", lfsp->lfs_fmod, + "clean ", lfsp->lfs_clean, + "ronly ", lfsp->lfs_ronly); +} + +static void +addseg(arg) + char *arg; +{ + SEGLIST *p; + + if ((p = malloc(sizeof(SEGLIST))) == NULL) + err("%s", strerror(errno)); + p->next = seglist; + p->num = atoi(arg); + seglist = p; +} + +static void +dump_cleaner_info(lfsp, ipage) + struct lfs *lfsp; + void *ipage; +{ + CLEANERINFO *cip; + + cip = (CLEANERINFO *)ipage; + (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n", + cip->clean, cip->dirty); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: dumplfs [-ai] [-s segnum] file\n"); + exit(1); +} diff --git a/sbin/dumplfs/extern.h b/sbin/dumplfs/extern.h new file mode 100644 index 000000000000..82e87b24e0a5 --- /dev/null +++ b/sbin/dumplfs/extern.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/5/93 + */ + +void err __P((const char *, ...)); +void get __P((int, off_t, void *, size_t)); + +extern char *special; diff --git a/sbin/dumplfs/misc.c b/sbin/dumplfs/misc.c new file mode 100644 index 000000000000..861d0fd7f260 --- /dev/null +++ b/sbin/dumplfs/misc.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "extern.h" + +void +get(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int rbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + err("%s: %s", special, strerror(errno)); + if ((rbytes = read(fd, p, len)) < 0) + err("%s: %s", special, strerror(errno)); + if (rbytes != len) + err("%s: short read (%d, not %d)", special, rbytes, len); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dumplfs: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/sbin/fastboot/Makefile b/sbin/fastboot/Makefile new file mode 100644 index 000000000000..9504f792b9e0 --- /dev/null +++ b/sbin/fastboot/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +MAN8= fastboot.0 +MLINKS= fastboot.8 fasthalt.8 + +beforeinstall: + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/fastboot.sh ${DESTDIR}${BINDIR}/fastboot + install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/fasthalt.sh ${DESTDIR}${BINDIR}/fasthalt + +.include diff --git a/sbin/fastboot/fastboot.8 b/sbin/fastboot/fastboot.8 new file mode 100644 index 000000000000..2f6ac829da99 --- /dev/null +++ b/sbin/fastboot/fastboot.8 @@ -0,0 +1,69 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fastboot.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt FASTBOOT 8 +.Os BSD 4.2 +.Sh NAME +.Nm fastboot , +.Nm fasthalt +.Nd "reboot/halt the system without checking the disks" +.Sh SYNOPSIS +.Nm fastboot +.Op Ar boot-options +.Nm fasthalt +.Op Ar halt-options +.Sh DESCRIPTION +.Nm Fastboot +and +.Nm fasthalt +are shell scripts which reboot and halt the system without +checking the file systems. This is done by creating a +file +.Pa /fastboot , +then invoking the +.Xr reboot +program. The system startup script, +.Pa /etc/rc , +looks for this file and, if present, skips the normal +invocation of +.Xr fsck 8 . +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr rc 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/fastboot/fastboot.sh b/sbin/fastboot/fastboot.sh new file mode 100644 index 000000000000..852ec4a1b089 --- /dev/null +++ b/sbin/fastboot/fastboot.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# +# Copyright (c) 1985, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)fastboot.sh 8.1 (Berkeley) 6/5/93 +# + +cp /dev/null /fastboot +/sbin/reboot $* diff --git a/sbin/fastboot/fasthalt.sh b/sbin/fastboot/fasthalt.sh new file mode 100644 index 000000000000..c8e381f3e51e --- /dev/null +++ b/sbin/fastboot/fasthalt.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)fasthalt.sh 8.1 (Berkeley) 6/5/93 +# + +cp /dev/null /fastboot +/sbin/halt $* diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile new file mode 100644 index 000000000000..224f6b2200e7 --- /dev/null +++ b/sbin/fsck/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= fsck +MAN8= fsck.0 +SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ + pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c +.PATH: ${.CURDIR}/../../sys/ufs/ffs + +.include diff --git a/sbin/fsck/SMM.doc/0.t b/sbin/fsck/SMM.doc/0.t new file mode 100644 index 000000000000..9de391eec1ef --- /dev/null +++ b/sbin/fsck/SMM.doc/0.t @@ -0,0 +1,150 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)0.t 8.1 (Berkeley) 6/8/93 +.\" +.if n .ND +.TL +Fsck \- The UNIX\(dg File System Check Program +.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program' +.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%' +.AU +Marshall Kirk McKusick +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA 94720 +.AU +T. J. Kowalski +.AI +Bell Laboratories +Murray Hill, New Jersey 07974 +.AB +.FS +\(dgUNIX is a trademark of Bell Laboratories. +.FE +.FS +This work was done under grants from +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. +.FE +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +File System Check Program (\fIfsck\fR) +is an interactive file system check and repair program. +.I Fsck +uses the redundant structural information in the +UNIX file system to perform several consistency checks. +If an inconsistency is detected, it is reported +to the operator, who may elect to fix or ignore +each inconsistency. +These inconsistencies result from the permanent interruption +of the file system updates, which are performed every +time a file is modified. +Unless there has been a hardware failure, +.I fsck +is able to repair corrupted file systems +using procedures based upon the order in which UNIX honors +these file system update requests. +.PP +The purpose of this document is to describe the normal updating +of the file system, +to discuss the possible causes of file system corruption, +and to present the corrective actions implemented +by +.I fsck. +Both the program and the interaction between the +program and the operator are described. +.sp 2 +.LP +Revised July 16, 1985 +.AE +.LP +.bp +.ce +.B "TABLE OF CONTENTS" +.LP +.sp 1 +.nf +.B "1. Introduction" +.LP +.sp .5v +.nf +.B "2. Overview of the file system +2.1. Superblock +2.2. Summary Information +2.3. Cylinder groups +2.4. Fragments +2.5. Updates to the file system +.LP +.sp .5v +.nf +.B "3. Fixing corrupted file systems +3.1. Detecting and correcting corruption +3.2. Super block checking +3.3. Free block checking +3.4. Checking the inode state +3.5. Inode links +3.6. Inode data size +3.7. Checking the data associated with an inode +3.8. File system connectivity +.LP +.sp .5v +.nf +.B Acknowledgements +.LP +.sp .5v +.nf +.B References +.LP +.sp .5v +.nf +.B "4. Appendix A +4.1. Conventions +4.2. Initialization +4.3. Phase 1 - Check Blocks and Sizes +4.4. Phase 1b - Rescan for more Dups +4.5. Phase 2 - Check Pathnames +4.6. Phase 3 - Check Connectivity +4.7. Phase 4 - Check Reference Counts +4.8. Phase 5 - Check Cyl groups +4.9. Cleanup +.ds RH Introduction +.bp diff --git a/sbin/fsck/SMM.doc/1.t b/sbin/fsck/SMM.doc/1.t new file mode 100644 index 000000000000..4d2f5357131b --- /dev/null +++ b/sbin/fsck/SMM.doc/1.t @@ -0,0 +1,83 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)1.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Introduction +.NH +Introduction +.PP +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +When a UNIX +operating system is brought up, a consistency +check of the file systems should always be performed. +This precautionary measure helps to insure +a reliable environment for file storage on disk. +If an inconsistency is discovered, +corrective action must be taken. +.I Fsck +runs in two modes. +Normally it is run non-interactively by the system after +a normal boot. +When running in this mode, +it will only make changes to the file system that are known +to always be correct. +If an unexpected inconsistency is found +.I fsck +will exit with a non-zero exit status, +leaving the system running single-user. +Typically the operator then runs +.I fsck +interactively. +When running in this mode, +each problem is listed followed by a suggested corrective action. +The operator must decide whether or not the suggested correction +should be made. +.PP +The purpose of this memo is to dispel the +mystique surrounding +file system inconsistencies. +It first describes the updating of the file system +(the calm before the storm) and +then describes file system corruption (the storm). +Finally, +the set of deterministic corrective actions +used by +.I fsck +(the Coast Guard +to the rescue) is presented. +.ds RH Overview of the File System diff --git a/sbin/fsck/SMM.doc/2.t b/sbin/fsck/SMM.doc/2.t new file mode 100644 index 000000000000..7d00ceaa4efd --- /dev/null +++ b/sbin/fsck/SMM.doc/2.t @@ -0,0 +1,265 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)2.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Overview of the file system +.NH +Overview of the file system +.PP +The file system is discussed in detail in [Mckusick84]; +this section gives a brief overview. +.NH 2 +Superblock +.PP +A file system is described by its +.I "super-block" . +The super-block is built when the file system is created (\c +.I newfs (8)) +and never changes. +The super-block +contains the basic parameters of the file system, +such as the number of data blocks it contains +and a count of the maximum number of files. +Because the super-block contains critical data, +.I newfs +replicates it to protect against catastrophic loss. +The +.I "default super block" +always resides at a fixed offset from the beginning +of the file system's disk partition. +The +.I "redundant super blocks" +are not referenced unless a head crash +or other hard disk error causes the default super-block +to be unusable. +The redundant blocks are sprinkled throughout the disk partition. +.PP +Within the file system are files. +Certain files are distinguished as directories and contain collections +of pointers to files that may themselves be directories. +Every file has a descriptor associated with it called an +.I "inode". +The inode contains information describing ownership of the file, +time stamps indicating modification and access times for the file, +and an array of indices pointing to the data blocks for the file. +In this section, +we assume that the first 12 blocks +of the file are directly referenced by values stored +in the inode structure itself\(dg. +.FS +\(dgThe actual number may vary from system to system, but is usually in +the range 5-13. +.FE +The inode structure may also contain references to indirect blocks +containing further data block indices. +In a file system with a 4096 byte block size, a singly indirect +block contains 1024 further block addresses, +a doubly indirect block contains 1024 addresses of further single indirect +blocks, +and a triply indirect block contains 1024 addresses of further doubly indirect +blocks (the triple indirect block is never needed in practice). +.PP +In order to create files with up to +2\(ua32 bytes, +using only two levels of indirection, +the minimum size of a file system block is 4096 bytes. +The size of file system blocks can be any power of two +greater than or equal to 4096. +The block size of the file system is maintained in the super-block, +so it is possible for file systems of different block sizes +to be accessible simultaneously on the same system. +The block size must be decided when +.I newfs +creates the file system; +the block size cannot be subsequently +changed without rebuilding the file system. +.NH 2 +Summary information +.PP +Associated with the super block is non replicated +.I "summary information" . +The summary information changes +as the file system is modified. +The summary information contains +the number of blocks, fragments, inodes and directories in the file system. +.NH 2 +Cylinder groups +.PP +The file system partitions the disk into one or more areas called +.I "cylinder groups". +A cylinder group is comprised of one or more consecutive +cylinders on a disk. +Each cylinder group includes inode slots for files, a +.I "block map" +describing available blocks in the cylinder group, +and summary information describing the usage of data blocks +within the cylinder group. +A fixed number of inodes is allocated for each cylinder group +when the file system is created. +The current policy is to allocate one inode for each 2048 +bytes of disk space; +this is expected to be far more inodes than will ever be needed. +.PP +All the cylinder group bookkeeping information could be +placed at the beginning of each cylinder group. +However if this approach were used, +all the redundant information would be on the top platter. +A single hardware failure that destroyed the top platter +could cause the loss of all copies of the redundant super-blocks. +Thus the cylinder group bookkeeping information +begins at a floating offset from the beginning of the cylinder group. +The offset for +the +.I "i+1" st +cylinder group is about one track further +from the beginning of the cylinder group +than it was for the +.I "i" th +cylinder group. +In this way, +the redundant +information spirals down into the pack; +any single track, cylinder, +or platter can be lost without losing all copies of the super-blocks. +Except for the first cylinder group, +the space between the beginning of the cylinder group +and the beginning of the cylinder group information stores data. +.NH 2 +Fragments +.PP +To avoid waste in storing small files, +the file system space allocator divides a single +file system block into one or more +.I "fragments". +The fragmentation of the file system is specified +when the file system is created; +each file system block can be optionally broken into +2, 4, or 8 addressable fragments. +The lower bound on the size of these fragments is constrained +by the disk sector size; +typically 512 bytes is the lower bound on fragment size. +The block map associated with each cylinder group +records the space availability at the fragment level. +Aligned fragments are examined +to determine block availability. +.PP +On a file system with a block size of 4096 bytes +and a fragment size of 1024 bytes, +a file is represented by zero or more 4096 byte blocks of data, +and possibly a single fragmented block. +If a file system block must be fragmented to obtain +space for a small amount of data, +the remainder of the block is made available for allocation +to other files. +For example, +consider an 11000 byte file stored on +a 4096/1024 byte file system. +This file uses two full size blocks and a 3072 byte fragment. +If no fragments with at least 3072 bytes +are available when the file is created, +a full size block is split yielding the necessary 3072 byte +fragment and an unused 1024 byte fragment. +This remaining fragment can be allocated to another file, as needed. +.NH 2 +Updates to the file system +.PP +Every working day hundreds of files +are created, modified, and removed. +Every time a file is modified, +the operating system performs a +series of file system updates. +These updates, when written on disk, yield a consistent file system. +The file system stages +all modifications of critical information; +modification can +either be completed or cleanly backed out after a crash. +Knowing the information that is first written to the file system, +deterministic procedures can be developed to +repair a corrupted file system. +To understand this process, +the order that the update +requests were being honored must first be understood. +.PP +When a user program does an operation to change the file system, +such as a +.I write , +the data to be written is copied into an internal +.I "in-core" +buffer in the kernel. +Normally, the disk update is handled asynchronously; +the user process is allowed to proceed even though +the data has not yet been written to the disk. +The data, +along with the inode information reflecting the change, +is eventually written out to disk. +The real disk write may not happen until long after the +.I write +system call has returned. +Thus at any given time, the file system, +as it resides on the disk, +lags the state of the file system represented by the in-core information. +.PP +The disk information is updated to reflect the in-core information +when the buffer is required for another use, +when a +.I sync (2) +is done (at 30 second intervals) by +.I "/etc/update" "(8)," +or by manual operator intervention with the +.I sync (8) +command. +If the system is halted without writing out the in-core information, +the file system on the disk will be in an inconsistent state. +.PP +If all updates are done asynchronously, several serious +inconsistencies can arise. +One inconsistency is that a block may be claimed by two inodes. +Such an inconsistency can occur when the system is halted before +the pointer to the block in the old inode has been cleared +in the copy of the old inode on the disk, +and after the pointer to the block in the new inode has been written out +to the copy of the new inode on the disk. +Here, +there is no deterministic method for deciding +which inode should really claim the block. +A similar problem can arise with a multiply claimed inode. +.PP +The problem with asynchronous inode updates +can be avoided by doing all inode deallocations synchronously. +Consequently, +inodes and indirect blocks are written to the disk synchronously +(\fIi.e.\fP the process blocks until the information is +really written to disk) +when they are being deallocated. +Similarly inodes are kept consistent by synchronously +deleting, adding, or changing directory entries. +.ds RH Fixing corrupted file systems diff --git a/sbin/fsck/SMM.doc/3.t b/sbin/fsck/SMM.doc/3.t new file mode 100644 index 000000000000..07b5431f3e59 --- /dev/null +++ b/sbin/fsck/SMM.doc/3.t @@ -0,0 +1,439 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)3.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Fixing corrupted file systems +.NH +Fixing corrupted file systems +.PP +A file system +can become corrupted in several ways. +The most common of these ways are +improper shutdown procedures +and hardware failures. +.PP +File systems may become corrupted during an +.I "unclean halt" . +This happens when proper shutdown +procedures are not observed, +physically write-protecting a mounted file system, +or a mounted file system is taken off-line. +The most common operator procedural failure is forgetting to +.I sync +the system before halting the CPU. +.PP +File systems may become further corrupted if proper startup +procedures are not observed, e.g., +not checking a file system for inconsistencies, +and not repairing inconsistencies. +Allowing a corrupted file system to be used (and, thus, to be modified +further) can be disastrous. +.PP +Any piece of hardware can fail at any time. +Failures +can be as subtle as a bad block +on a disk pack, or as blatant as a non-functional disk-controller. +.NH 2 +Detecting and correcting corruption +.PP +Normally +.I fsck +is run non-interactively. +In this mode it will only fix +corruptions that are expected to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this paper we assume that +.I fsck +is being run interactively, +and all possible errors can be encountered. +When an inconsistency is discovered in this mode, +.I fsck +reports the inconsistency for the operator to +chose a corrective action. +.PP +A quiescent\(dd +.FS +\(dd I.e., unmounted and not being written on. +.FE +file system may be checked for structural integrity +by performing consistency checks on the +redundant data intrinsic to a file system. +The redundant data is either read from +the file system, +or computed from other known values. +The file system +.B must +be in a quiescent state when +.I fsck +is run, +since +.I fsck +is a multi-pass program. +.PP +In the following sections, +we discuss methods to discover inconsistencies +and possible corrective actions +for the cylinder group blocks, the inodes, the indirect blocks, and +the data blocks containing directory entries. +.NH 2 +Super-block checking +.PP +The most commonly corrupted item in a file system +is the summary information +associated with the super-block. +The summary information is prone to corruption +because it is modified with every change to the file +system's blocks or inodes, +and is usually corrupted +after an unclean halt. +.PP +The super-block is checked for inconsistencies +involving file-system size, number of inodes, +free-block count, and the free-inode count. +The file-system size must be larger than the +number of blocks used by the super-block +and the number of blocks used by the list of inodes. +The file-system size and layout information +are the most critical pieces of information for +.I fsck . +While there is no way to actually check these sizes, +since they are statically determined by +.I newfs , +.I fsck +can check that these sizes are within reasonable bounds. +All other file system checks require that these sizes be correct. +If +.I fsck +detects corruption in the static parameters of the default super-block, +.I fsck +requests the operator to specify the location of an alternate super-block. +.NH 2 +Free block checking +.PP +.I Fsck +checks that all the blocks +marked as free in the cylinder group block maps +are not claimed by any files. +When all the blocks have been initially accounted for, +.I fsck +checks that +the number of free blocks +plus the number of blocks claimed by the inodes +equals the total number of blocks in the file system. +.PP +If anything is wrong with the block allocation maps, +.I fsck +will rebuild them, +based on the list it has computed of allocated blocks. +.PP +The summary information associated with the super-block +counts the total number of free blocks within the file system. +.I Fsck +compares this count to the +number of free blocks it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the summary information +by the actual free-block count. +.PP +The summary information +counts the total number of free inodes within the file system. +.I Fsck +compares this count to the number +of free inodes it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the +summary information by the actual free-inode count. +.NH 2 +Checking the inode state +.PP +An individual inode is not as likely to be corrupted as +the allocation information. +However, because of the great number of active inodes, +a few of the inodes are usually corrupted. +.PP +The list of inodes in the file system +is checked sequentially starting with inode 2 +(inode 0 marks unused inodes; +inode 1 is saved for future generations) +and progressing through the last inode in the file system. +The state of each inode is checked for +inconsistencies involving format and type, +link count, +duplicate blocks, +bad blocks, +and inode size. +.PP +Each inode contains a mode word. +This mode word describes the type and state of the inode. +Inodes must be one of six types: +regular inode, directory inode, symbolic link inode, +special block inode, special character inode, or socket inode. +Inodes may be found in one of three allocation states: +unallocated, allocated, and neither unallocated nor allocated. +This last state suggests an incorrectly formated inode. +An inode can get in this state if +bad data is written into the inode list. +The only possible corrective action is for +.I fsck +is to clear the inode. +.NH 2 +Inode links +.PP +Each inode counts the +total number of directory entries +linked to the inode. +.I Fsck +verifies the link count of each inode +by starting at the root of the file system, +and descending through the directory structure. +The actual link count for each inode +is calculated during the descent. +.PP +If the stored link count is non-zero and the actual +link count is zero, +then no directory entry appears for the inode. +If this happens, +.I fsck +will place the disconnected file in the +.I lost+found +directory. +If the stored and actual link counts are non-zero and unequal, +a directory entry may have been added or removed without the inode being +updated. +If this happens, +.I fsck +replaces the incorrect stored link count by the actual link count. +.PP +Each inode contains a list, +or pointers to +lists (indirect blocks), +of all the blocks claimed by the inode. +Since indirect blocks are owned by an inode, +inconsistencies in indirect blocks directly +affect the inode that owns it. +.PP +.I Fsck +compares each block number claimed by an inode +against a list of already allocated blocks. +If another inode already claims a block number, +then the block number is added to a list of +.I "duplicate blocks" . +Otherwise, the list of allocated blocks +is updated to include the block number. +.PP +If there are any duplicate blocks, +.I fsck +will perform a partial second +pass over the inode list +to find the inode of the duplicated block. +The second pass is needed, +since without examining the files associated with +these inodes for correct content, +not enough information is available +to determine which inode is corrupted and should be cleared. +If this condition does arise +(only hardware failure will cause it), +then the inode with the earliest +modify time is usually incorrect, +and should be cleared. +If this happens, +.I fsck +prompts the operator to clear both inodes. +The operator must decide which one should be kept +and which one should be cleared. +.PP +.I Fsck +checks the range of each block number claimed by an inode. +If the block number is +lower than the first data block in the file system, +or greater than the last data block, +then the block number is a +.I "bad block number" . +Many bad blocks in an inode are usually caused by +an indirect block that was not written to the file system, +a condition which can only occur if there has been a hardware failure. +If an inode contains bad block numbers, +.I fsck +prompts the operator to clear it. +.NH 2 +Inode data size +.PP +Each inode contains a count of the number of data blocks +that it contains. +The number of actual data blocks +is the sum of the allocated data blocks +and the indirect blocks. +.I Fsck +computes the actual number of data blocks +and compares that block count against +the actual number of blocks the inode claims. +If an inode contains an incorrect count +.I fsck +prompts the operator to fix it. +.PP +Each inode contains a thirty-two bit size field. +The size is the number of data bytes +in the file associated with the inode. +The consistency of the byte size field is roughly checked +by computing from the size field the maximum number of blocks +that should be associated with the inode, +and comparing that expected block count against +the actual number of blocks the inode claims. +.NH 2 +Checking the data associated with an inode +.PP +An inode can directly or indirectly +reference three kinds of data blocks. +All referenced blocks must be the same kind. +The three types of data blocks are: +plain data blocks, symbolic link data blocks, and directory data blocks. +Plain data blocks +contain the information stored in a file; +symbolic link data blocks +contain the path name stored in a link. +Directory data blocks contain directory entries. +.I Fsck +can only check the validity of directory data blocks. +.PP +Each directory data block is checked for +several types of inconsistencies. +These inconsistencies include +directory inode numbers pointing to unallocated inodes, +directory inode numbers that are greater than +the number of inodes in the file system, +incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'', +and directories that are not attached to the file system. +If the inode number in a directory data block +references an unallocated inode, +then +.I fsck +will remove that directory entry. +Again, +this condition can only arise when there has been a hardware failure. +.PP +If a directory entry inode number references +outside the inode list, then +.I fsck +will remove that directory entry. +This condition occurs if bad data is written into a directory data block. +.PP +The directory inode number entry for ``\fB.\fP'' +must be the first entry in the directory data block. +The inode number for ``\fB.\fP'' +must reference itself; +e.g., it must equal the inode number +for the directory data block. +The directory inode number entry +for ``\fB..\fP'' must be +the second entry in the directory data block. +Its value must equal the inode number for the +parent of the directory entry +(or the inode number of the directory +data block if the directory is the +root directory). +If the directory inode numbers are +incorrect, +.I fsck +will replace them with the correct values. +If there are multiple hard links to a directory, +the first one encountered is considered the real parent +to which ``\fB..\fP'' should point; +\fIfsck\fP recommends deletion for the subsequently discovered names. +.NH 2 +File system connectivity +.PP +.I Fsck +checks the general connectivity of the file system. +If directories are not linked into the file system, then +.I fsck +links the directory back into the file system in the +.I lost+found +directory. +This condition only occurs when there has been a hardware failure. +.ds RH "References" +.SH +\s+2Acknowledgements\s0 +.PP +I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie +for their suggestions and help in implementing the new file system. +Thanks also to Robert Henry for his editorial input to +get this document together. +Finally we thank our sponsors, +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983) +.PP +I would like to thank Larry A. Wehr for advice that lead +to the first version of +.I fsck +and Rick B. Brandt for adapting +.I fsck +to +UNIX/TS. (T. Kowalski, July 1979) +.sp 2 +.SH +\s+2References\s0 +.LP +.IP [Dolotta78] 20 +Dolotta, T. A., and Olsson, S. B. eds., +.I "UNIX User's Manual, Edition 1.1\^" , +January 1978. +.IP [Joy83] 20 +Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D. +4.2BSD System Manual, +.I "University of California at Berkeley" , +.I "Computer Systems Research Group Technical Report" +#4, 1982. +.IP [McKusick84] 20 +McKusick, M., Joy, W., Leffler, S., and Fabry, R. +A Fast File System for UNIX, +\fIACM Transactions on Computer Systems 2\fP, 3. +pp. 181-197, August 1984. +.IP [Ritchie78] 20 +Ritchie, D. M., and Thompson, K., +The UNIX Time-Sharing System, +.I "The Bell System Technical Journal" +.B 57 , +6 (July-August 1978, Part 2), pp. 1905-29. +.IP [Thompson78] 20 +Thompson, K., +UNIX Implementation, +.I "The Bell System Technical Journal\^" +.B 57 , +6 (July-August 1978, Part 2), pp. 1931-46. +.ds RH Appendix A \- Fsck Error Conditions +.bp diff --git a/sbin/fsck/SMM.doc/4.t b/sbin/fsck/SMM.doc/4.t new file mode 100644 index 000000000000..5ea8179fa904 --- /dev/null +++ b/sbin/fsck/SMM.doc/4.t @@ -0,0 +1,1424 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)4.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Appendix A \- Fsck Error Conditions +.NH +Appendix A \- Fsck Error Conditions +.NH 2 +Conventions +.PP +.I Fsck +is +a multi-pass file system check program. +Each file system pass invokes a different Phase of the +.I fsck +program. +After the initial setup, +.I fsck +performs successive Phases over each file system, +checking blocks and sizes, +path-names, +connectivity, +reference counts, +and the map of free blocks, +(possibly rebuilding it), +and performs some cleanup. +.LP +Normally +.I fsck +is run non-interactively to +.I preen +the file systems after an unclean halt. +While preen'ing a file system, +it will only fix corruptions that are expected +to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this appendix many errors have several options +that the operator can take. +When an inconsistency is detected, +.I fsck +reports the error condition to the operator. +If a response is required, +.I fsck +prints a prompt message and +waits for a response. +When preen'ing most errors are fatal. +For those that are expected, +the response taken is noted. +This appendix explains the meaning of each error condition, +the possible responses, and the related error conditions. +.LP +The error conditions are organized by the +.I Phase +of the +.I fsck +program in which they can occur. +The error conditions that may occur +in more than one Phase +will be discussed in initialization. +.NH 2 +Initialization +.PP +Before a file system check can be performed, certain +tables have to be set up and certain files opened. +This section concerns itself with the opening of files and +the initialization of tables. +This section lists error conditions resulting from +command line options, +memory requests, +opening of files, +status of files, +file system size checks, +and creation of the scratch file. +All the initialization errors are fatal +when the file system is being preen'ed. +.sp +.LP +.B "\fIC\fP option?" +.br +\fIC\fP is not a legal option to +.I fsck ; +legal options are \-b, \-c, \-y, \-n, and \-p. +.I Fsck +terminates on this error condition. +See the +.I fsck (8) +manual entry for further detail. +.sp +.LP +.B "cannot alloc NNN bytes for blockmap" +.br +.B "cannot alloc NNN bytes for freemap" +.br +.B "cannot alloc NNN bytes for statemap" +.br +.B "cannot alloc NNN bytes for lncntp" +.br +.I Fsck 's +request for memory for its virtual +memory tables failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't open checklist file: \fIF\fP" +.br +The file system checklist file +\fIF\fP (usually +.I /etc/fstab ) +can not be opened for reading. +.I Fsck +terminates on this error condition. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't stat root" +.br +.I Fsck 's +request for statistics about the root directory ``/'' failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't stat \fIF\fP" +.br +.B "Can't make sense out of name \fIF\fP" +.br +.I Fsck 's +request for statistics about the file system \fIF\fP failed. +When running manually, +it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't open \fIF\fP" +.br +.I Fsck 's +request attempt to open the file system \fIF\fP failed. +When running manually, it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "\fIF\fP: (NO WRITE)" +.br +Either the \-n flag was specified or +.I fsck 's +attempt to open the file system \fIF\fP for writing failed. +When running manually, +all the diagnostics are printed out, +but no modifications are attempted to fix them. +.sp +.LP +.B "file is not a block or character device; OK" +.br +You have given +.I fsck +a regular file name by mistake. +Check the type of the file specified. +.LP +Possible responses to the OK prompt are: +.IP YES +ignore this error condition. +.IP NO +ignore this file system and continues checking +the next file system given. +.sp +.LP +.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock optimization parameter is neither OPT_TIME +nor OPT_SPACE. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The superblock is set to request optimization to minimize +running time of the system. +(If optimization to minimize disk space utilization is +desired, it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock minimum space percentage is greater than 99% +or less then 0%. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The minfree parameter is set to 10%. +(If some other percentage is desired, +it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The file system interleave is less than or equal to zero. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The interleave parameter is set to 1. +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The number of physical sectors per track is less than the number +of usable sectors per track. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The npsect parameter is set to the number of usable sectors per track. +.IP NO +ignore this error condition. +.sp +.LP +One of the following messages will appear: +.br +.B "MAGIC NUMBER WRONG" +.br +.B "NCG OUT OF RANGE" +.br +.B "CPG OUT OF RANGE" +.br +.B "NCYL DOES NOT JIVE WITH NCG*CPG" +.br +.B "SIZE PREPOSTEROUSLY LARGE" +.br +.B "TRASHED VALUES IN SUPER BLOCK" +.br +and will be followed by the message: +.br +.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP" +.br +.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE" +.br +.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)." +.br +The super block has been corrupted. +An alternative super block must be selected from among those +listed by +.I newfs +(8) when the file system was created. +For file systems with a blocksize less than 32K, +specifying \-b 32 is a good first choice. +.sp +.LP +.B "INTERNAL INCONSISTENCY: \fIM\fP" +.br +.I Fsck 's +has had an internal panic, whose message is specified as \fIM\fP. +This should never happen. +See a guru. +.sp +.LP +.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for moving to a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +Often, +however the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for reading a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +It will retry the read and print out the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be read. +If +.I fsck +ever tries to write back one of the blocks on which the read failed +it will print the message: +.br +.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK" +.br +where \fIN\fP indicates the sector that was written with zero's. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for writing a specified block number \fIB\fP +in the file system failed. +The disk is write-protected; +check the write protect lock on the drive. +If that is not the problem, see a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +The write operation will be retried with the failed blocks +indicated by the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be written. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "bad inode number DDD to ginode" +.br +An internal error has attempted to read non-existent inode \fIDDD\fP. +This error causes +.I fsck +to exit. +See a guru. +.NH 2 +Phase 1 \- Check Blocks and Sizes +.PP +This phase concerns itself with +the inode list. +This section lists error conditions resulting from +checking inode types, +setting up the zero-link-count table, +examining inode block numbers for bad or duplicate blocks, +checking inode size, +and checking inode format. +All errors in this phase except +.B "INCORRECT BLOCK COUNT" +and +.B "PARTIALLY TRUNCATED INODE" +are fatal if the file system is being preen'ed. +.sp +.LP +.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)" +.br +The mode word of the inode \fII\fP indicates that the inode is not a +special block inode, special character inode, socket inode, regular inode, +symbolic link, or directory inode. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +This will always invoke the UNALLOCATED error condition in Phase 2 +for each directory entry pointing to this inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)" +.br +.I Fsck +has found inode \fII\fP whose size is shorter than the number of +blocks allocated to it. +This condition should only occur if the system crashes while in the +midst of truncating a file. +When preen'ing the file system, +.I fsck +completes the truncation to the specified size. +.LP +Possible responses to SALVAGE are: +.IP YES +complete the truncation to the size specified in the inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "LINK COUNT TABLE OVERFLOW (CONTINUE)" +.br +An internal table for +.I fsck +containing allocated inodes with a link count of +zero cannot allocate more memory. +Increase the virtual memory for +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another allocated inode with a zero link count is found, +this error condition is repeated. +.IP NO +terminate the program. +.sp +.LP +.B "\fIB\fP BAD I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP with a number +lower than the number of the first data block in the file system or +greater than the number of the last block +in the file system. +This error condition may invoke the +.B "EXCESSIVE BAD BLKS" +error condition in Phase 1 (see next paragraph) if +inode \fII\fP has too many block numbers outside the file system range. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks with a number +lower than the number of the first data block in the file system or greater than +the number of last block in the file system associated with inode \fII\fP. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "BAD STATE DDD TO BLKERR" +.br +An internal error has scrambled +.I fsck 's +state map to have the impossible value \fIDDD\fP. +.I Fsck +exits immediately. +See a guru. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that is already claimed by +another inode. +This error condition may invoke the +.B "EXCESSIVE DUP BLKS" +error condition in Phase 1 if +inode \fII\fP has too many block numbers claimed by other inodes. +This error condition will always invoke Phase 1b and the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks claimed by other +inodes. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "DUP TABLE OVERFLOW (CONTINUE)" +.br +An internal table in +.I fsck +containing duplicate block numbers cannot allocate any more space. +Increase the amount of virtual memory available to +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another duplicate block is found, this error condition will repeat. +.IP NO +terminate the program. +.sp +.LP +.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)" +.br +Inode \fII\fP is neither allocated nor unallocated. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)" +.br +The block count for inode \fII\fP is \fIX\fP blocks, +but should be \fIY\fP blocks. +When preen'ing the count is corrected. +.LP +Possible responses to the CORRECT prompt are: +.IP YES +replace the block count of inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.NH 2 +Phase 1B: Rescan for More Dups +.PP +When a duplicate block is found in the file system, the file system is +rescanned to find the inode that previously claimed that block. +This section lists the error condition when the duplicate block is found. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that +is already claimed by another inode. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2. +You can determine which inodes have overlapping blocks by examining +this error condition and the DUP error condition in Phase 1. +.NH 2 +Phase 2 \- Check Pathnames +.PP +This phase concerns itself with removing directory entries +pointing to +error conditioned inodes +from Phase 1 and Phase 1b. +This section lists error conditions resulting from +root inode mode and status, +directory inode pointers in range, +and directory entries pointing to bad inodes, +and directory integrity checks. +All errors in this phase are fatal if the file system is being preen'ed, +except for directories not being a multiple of the blocks size +and extraneous hard links. +.sp +.LP +.B "ROOT INODE UNALLOCATED (ALLOCATE)" +.br +The root inode (usually inode number 2) has no allocate mode bits. +This should never happen. +.LP +Possible responses to the ALLOCATE prompt are: +.IP YES +allocate inode 2 as the root inode. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will exit. +.sp +.LP +.B "ROOT INODE NOT DIRECTORY (REALLOCATE)" +.br +The root inode (usually inode number 2) +is not directory inode type. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "FIX" +.LP +Possible responses to the FIX prompt are: +.IP YES +replace the root inode's type to be a directory. +If the root inode's data blocks are not directory blocks, +many error conditions will be produced. +.IP NO +terminate the program. +.sp +.LP +.B "DUPS/BAD IN ROOT INODE (REALLOCATE)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks in the root inode (usually inode number 2) for the file system. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "CONTINUE" . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the +.B "DUPS/BAD" +error condition in the root inode and +attempt to continue to run the file system check. +If the root inode is not correct, +then this may result in many other error conditions. +.IP NO +terminate the program. +.sp +.LP +.B "NAME TOO LONG \fIF\fP" +.br +An excessively long path name has been found. +This usually indicates loops in the file system name space. +This can occur if the super user has made circular links to directories. +The offending links must be removed (by a guru). +.sp +.LP +.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has an inode number \fII\fP that is greater than +the end of the inode list. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +A directory or file entry \fIF\fP points to an unallocated inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +Phase 1 or Phase 1b have found duplicate blocks or bad blocks +associated with directory or file entry \fIF\fP, inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has a size \fIS\fP that is zero. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed; +this will always invoke the BAD/DUP error condition in Phase 4. +.IP NO +ignore this error condition. +.sp +.LP +.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fIF\fP has been found whose size \fIS\fP +is less than the minimum size directory. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the FIX prompt are: +.IP YES +increase the size of the directory to the minimum directory size. +.IP NO +ignore this directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP. +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)" +.br +A directory with an inconsistent internal state has been found. +.LP +Possible responses to the FIX prompt are: +.IP YES +throw away all entries up to the next directory boundary (usually 512-byte) +boundary. +This drastic action can throw away up to 42 entries, +and should be taken only after other recovery efforts have failed. +.IP NO +skip up to the next directory boundary and resume reading, +but do not modify the directory. +.sp +.LP +.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `.' does +does not equal \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `.' to be equal to \fII\fP. +.IP NO +leave the inode number for `.' unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose first entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `.' with inode number equal to \fII\fP. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose first entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'" +.br +A directory \fII\fP has been found whose first entry is not `.'. +.I Fsck +cannot resolve this problem as it should never happen. +See a guru. +.sp +.LP +.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `.'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `.'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `..' does +does not equal the parent of \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `..' to be equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the inode number for `..' unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose second entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `..' with inode number equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose second entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'" +.br +A directory \fII\fP has been found whose second entry is not `..'. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the second entry in the directory +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `..'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `..'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE) +.br +.I Fsck +has found a hard link, \fIN\fP, to a directory, \fID\fP. +When preen'ing the extraneous links are ignored. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +delete the extraneous entry, \fIN\fP. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD RETURN STATE \fIS\fP FROM DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be returned +from the routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD STATE \fIS\fP FOR ROOT INODE" +.br +An internal error has caused an impossible state \fIS\fP to be assigned +to the root inode. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 3 \- Check Connectivity +.PP +This phase concerns itself with the directory connectivity seen in +Phase 2. +This section lists error conditions resulting from +unreferenced directories, +and missing or full +.I lost+found +directories. +.sp +.LP +.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +The directory inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of directory inode \fII\fP are printed. +When preen'ing, the directory is reconnected if its size is non-zero, +otherwise it is cleared. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect directory inode \fII\fP to the file system in the +directory for lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 3 +if there are problems connecting directory inode \fII\fP to \fIlost+found\fP. +This may also invoke the CONNECTED error condition in Phase 3 if the link +was successful. +.IP NO +ignore this error condition. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP" +.br +This is an advisory message indicating a directory inode \fII1\fP was +successfully connected to the +.I lost+found +directory. +The parent inode \fII2\fP of the directory inode \fII1\fP is +replaced by the inode number of the +.I lost+found +directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP +(this can reoccur in Phase 3 if it is not adjusted in Phase 2). +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 4 \- Check Reference Counts +.PP +This phase concerns itself with the link count information +seen in Phase 2 and Phase 3. +This section lists error conditions resulting from +unreferenced files, +missing or full +.I lost+found +directory, +incorrect link counts for files, directories, symbolic links, or special files, +unreferenced files, symbolic links, and directories, +and bad or duplicate blocks in files, symbolic links, and directories. +All errors in this phase are correctable if the file system is being preen'ed +except running out of space in the \fIlost+found\fP directory. +.sp +.LP +.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +Inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of inode \fII\fP are printed. +When preen'ing the file is cleared if either its size or its +link count is zero, +otherwise it is reconnected. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect inode \fII\fP to the file system in the directory for +lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 4 +if there are problems connecting inode \fII\fP to +.I lost+found . +.IP NO +ignore this error condition. +This will always invoke the CLEAR error condition in Phase 4. +.sp +.LP +.B "(CLEAR)" +.br +The inode mentioned in the immediately previous error condition can not be +reconnected. +This cannot occur if the file system is being preen'ed, +since lack of space to reconnect files is a fatal error. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)" +.br +The link count for inode \fII\fP, +is \fIX\fP but should be \fIY\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP +are printed. +When preen'ing the link count is adjusted unless the number of references +is increasing, a condition that should never occur unless precipitated +by a hardware failure. +When the number of references is increasing under preen mode, +.I fsck +exits with the message: +.br +.B "LINK COUNT INCREASING" +.LP +Possible responses to the ADJUST prompt are: +.IP YES +replace the link count of file inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Inode \fII\fP, was not connected to a directory entry when the +file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +When preen'ing, +this is a file that was not connected because its size or link count was zero, +hence it is cleared. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks associated with +inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +This error cannot arise when the file system is being preen'ed, +as it would have caused a fatal error earlier. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.NH 2 +Phase 5 - Check Cyl groups +.PP +This phase concerns itself with the free-block and used-inode maps. +This section lists error conditions resulting from +allocated blocks in the free-block maps, +free blocks missing from free-block maps, +and the total free-block count incorrect. +It also lists error conditions resulting from +free inodes in the used-inode maps, +allocated inodes missing from used-inode maps, +and the total used-inode count incorrect. +.sp +.LP +.B "CG \fIC\fP: BAD MAGIC NUMBER" +.br +The magic number of cylinder group \fIC\fP is wrong. +This usually indicates that the cylinder group maps have been destroyed. +When running manually the cylinder group is marked as needing +to be reconstructed. +This error is fatal if the file system is being preen'ed. +.sp +.LP +.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)" +.br +A cylinder group block map is missing some free blocks. +During preen'ing the maps are reconstructed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the free block map. +.IP NO +ignore this error condition. +.sp +.LP +.B "SUMMARY INFORMATION BAD (SALVAGE)" +.br +The summary information was found to be incorrect. +When preen'ing, +the summary information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the summary information. +.IP NO +ignore this error condition. +.sp +.LP +.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)" +.br +The superblock free block information was found to be incorrect. +When preen'ing, +the superblock free block information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the superblock free block information. +.IP NO +ignore this error condition. +.NH 2 +Cleanup +.PP +Once a file system has been checked, a few cleanup functions are performed. +This section lists advisory messages about +the file system +and modify status of the file system. +.sp +.LP +.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)" +.br +This is an advisory message indicating that +the file system checked contained +\fIV\fP files using +\fIW\fP fragment sized blocks leaving +\fIX\fP fragment sized blocks free in the file system. +The numbers in parenthesis breaks the free count down into +\fIY\fP free fragments and +\fIZ\fP free full sized blocks. +.sp +.LP +.B "***** REBOOT UNIX *****" +.br +This is an advisory message indicating that +the root file system has been modified by +.I fsck. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. +When preen'ing, +.I fsck +will exit with a code of 4. +The standard auto-reboot script distributed with 4.3BSD +interprets an exit code of 4 by issuing a reboot system call. +.sp +.LP +.B "***** FILE SYSTEM WAS MODIFIED *****" +.br +This is an advisory message indicating that +the current file system was modified by +.I fsck. +If this file system is mounted or is the current root file system, +.I fsck +should be halted and UNIX rebooted. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. diff --git a/sbin/fsck/SMM.doc/Makefile b/sbin/fsck/SMM.doc/Makefile new file mode 100644 index 000000000000..26823bcc94f2 --- /dev/null +++ b/sbin/fsck/SMM.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= smm/03.fsck +SRCS= 0.t 1.t 2.t 3.t 4.t +MACROS= -ms + +.include diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c new file mode 100644 index 000000000000..ee5d095e6c75 --- /dev/null +++ b/sbin/fsck/dir.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +char *lfname = "lost+found"; +int lfmode = 01777; +struct dirtemplate emptydir = { 0, DIRBLKSIZ }; +struct dirtemplate dirhead = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +struct odirtemplate odirhead = { + 0, 12, 1, ".", + 0, DIRBLKSIZ - 12, 2, ".." +}; + +struct direct *fsck_readdir(); +struct bufarea *getdirblk(); + +/* + * Propagate connected state through the tree. + */ +propagate() +{ + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + long change; + + inpend = &inpsort[inplast]; + do { + change = 0; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) { + statemap[inp->i_number] = DFOUND; + change++; + } + } + } while (change > 0); +} + +/* + * Scan each entry in a directory block. + */ +dirscan(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp; + register struct bufarea *bp; + int dsize, n; + long blksiz; + char dbuf[DIRBLKSIZ]; + + if (idesc->id_type != DATA) + errexit("wrong type to dirscan %d\n", idesc->id_type); + if (idesc->id_entryno == 0 && + (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) + idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); + blksiz = idesc->id_numfrags * sblock.fs_fsize; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { + idesc->id_filesize -= blksiz; + return (SKIP); + } + idesc->id_loc = 0; + for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { + dsize = dp->d_reclen; + bcopy((char *)dp, dbuf, (size_t)dsize); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + struct direct *tdp = (struct direct *)dbuf; + u_char tmp; + + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + idesc->id_dirp = (struct direct *)dbuf; + if ((n = (*idesc->id_func)(idesc)) & ALTERED) { +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt && !doinglevel2) { + struct direct *tdp; + u_char tmp; + + tdp = (struct direct *)dbuf; + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + bp = getdirblk(idesc->id_blkno, blksiz); + bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, + (size_t)dsize); + dirty(bp); + sbdirty(); + } + if (n & STOP) + return (n); + } + return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +struct direct * +fsck_readdir(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp, *ndp; + register struct bufarea *bp; + long size, blksiz, fix, dploc; + + blksiz = idesc->id_numfrags * sblock.fs_fsize; + bp = getdirblk(idesc->id_blkno, blksiz); + if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && + idesc->id_loc < blksiz) { + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (dircheck(idesc, dp)) + goto dpok; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + dp->d_reclen = DIRBLKSIZ; + dp->d_ino = 0; + dp->d_type = 0; + dp->d_namlen = 0; + dp->d_name[0] = '\0'; + if (fix) + dirty(bp); + idesc->id_loc += DIRBLKSIZ; + idesc->id_filesize -= DIRBLKSIZ; + return (dp); + } +dpok: + if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) + return NULL; + dploc = idesc->id_loc; + dp = (struct direct *)(bp->b_un.b_buf + dploc); + idesc->id_loc += dp->d_reclen; + idesc->id_filesize -= dp->d_reclen; + if ((idesc->id_loc % DIRBLKSIZ) == 0) + return (dp); + ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && + dircheck(idesc, ndp) == 0) { + size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + idesc->id_loc += size; + idesc->id_filesize -= size; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + dploc); + dp->d_reclen += size; + if (fix) + dirty(bp); + } + return (dp); +} + +/* + * Verify that a directory entry is valid. + * This is a superset of the checks made in the kernel. + */ +dircheck(idesc, dp) + struct inodesc *idesc; + register struct direct *dp; +{ + register int size; + register char *cp; + u_char namlen, type; + int spaceleft; + + size = DIRSIZ(!newinofmt, dp); + spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + type = dp->d_namlen; + namlen = dp->d_type; + } else { + namlen = dp->d_namlen; + type = dp->d_type; + } +# else + namlen = dp->d_namlen; + type = dp->d_type; +# endif + if (dp->d_ino < maxino && + dp->d_reclen != 0 && + dp->d_reclen <= spaceleft && + (dp->d_reclen & 0x3) == 0 && + dp->d_reclen >= size && + idesc->id_filesize >= size && + namlen <= MAXNAMLEN && + type <= 15) { + if (dp->d_ino == 0) + return (1); + for (cp = dp->d_name, size = 0; size < namlen; size++) + if (*cp == 0 || (*cp++ == '/')) + return (0); + if (*cp == 0) + return (1); + } + return (0); +} + +direrror(ino, errmesg) + ino_t ino; + char *errmesg; +{ + + fileerror(ino, ino, errmesg); +} + +fileerror(cwd, ino, errmesg) + ino_t cwd, ino; + char *errmesg; +{ + register struct dinode *dp; + char pathbuf[MAXPATHLEN + 1]; + + pwarn("%s ", errmesg); + pinode(ino); + printf("\n"); + getpathname(pathbuf, cwd, ino); + if (ino < ROOTINO || ino > maxino) { + pfatal("NAME=%s\n", pathbuf); + return; + } + dp = ginode(ino); + if (ftypeok(dp)) + pfatal("%s=%s\n", + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); + else + pfatal("NAME=%s\n", pathbuf); +} + +adjust(idesc, lcnt) + register struct inodesc *idesc; + short lcnt; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (dp->di_nlink == lcnt) { + if (linkup(idesc->id_number, (ino_t)0) == 0) + clri(idesc, "UNREF", 0); + } else { + pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : + ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); + pinode(idesc->id_number); + printf(" COUNT %d SHOULD BE %d", + dp->di_nlink, dp->di_nlink - lcnt); + if (preen) { + if (lcnt < 0) { + printf("\n"); + pfatal("LINK COUNT INCREASING"); + } + printf(" (ADJUSTED)\n"); + } + if (preen || reply("ADJUST") == 1) { + dp->di_nlink -= lcnt; + inodirty(); + } + } +} + +mkentry(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + struct direct newent; + int newlen, oldlen; + + newent.d_namlen = strlen(idesc->id_name); + newlen = DIRSIZ(0, &newent); + if (dirp->d_ino != 0) + oldlen = DIRSIZ(0, dirp); + else + oldlen = 0; + if (dirp->d_reclen - oldlen < newlen) + return (KEEPON); + newent.d_reclen = dirp->d_reclen - oldlen; + dirp->d_reclen = oldlen; + dirp = (struct direct *)(((char *)dirp) + oldlen); + dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + dirp->d_reclen = newent.d_reclen; + dirp->d_namlen = newent.d_namlen; + bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); + return (ALTERED|STOP); +} + +chgino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) + return (KEEPON); + dirp->d_ino = idesc->id_parent; + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + return (ALTERED|STOP); +} + +linkup(orphan, parentdir) + ino_t orphan; + ino_t parentdir; +{ + register struct dinode *dp; + int lostdir; + ino_t oldlfdir; + struct inodesc idesc; + char tempname[BUFSIZ]; + extern int pass4check(); + + bzero((char *)&idesc, sizeof(struct inodesc)); + dp = ginode(orphan); + lostdir = (dp->di_mode & IFMT) == IFDIR; + pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); + pinode(orphan); + if (preen && dp->di_size == 0) + return (0); + if (preen) + printf(" (RECONNECTED)\n"); + else + if (reply("RECONNECT") == 0) + return (0); + if (lfdir == 0) { + dp = ginode(ROOTINO); + idesc.id_name = lfname; + idesc.id_type = DATA; + idesc.id_func = findino; + idesc.id_number = ROOTINO; + if ((ckinode(dp, &idesc) & FOUND) != 0) { + lfdir = idesc.id_parent; + } else { + pwarn("NO lost+found DIRECTORY"); + if (preen || reply("CREATE")) { + lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); + if (lfdir != 0) { + if (makeentry(ROOTINO, lfdir, lfname) != 0) { + if (preen) + printf(" (CREATED)\n"); + } else { + freedir(lfdir, ROOTINO); + lfdir = 0; + if (preen) + printf("\n"); + } + } + } + } + if (lfdir == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + } + dp = ginode(lfdir); + if ((dp->di_mode & IFMT) != IFDIR) { + pfatal("lost+found IS NOT A DIRECTORY"); + if (reply("REALLOCATE") == 0) + return (0); + oldlfdir = lfdir; + if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + inodirty(); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = oldlfdir; + adjust(&idesc, lncntp[oldlfdir] + 1); + lncntp[oldlfdir] = 0; + dp = ginode(lfdir); + } + if (statemap[lfdir] != DFOUND) { + pfatal("SORRY. NO lost+found DIRECTORY\n\n"); + return (0); + } + (void)lftempname(tempname, orphan); + if (makeentry(lfdir, orphan, tempname) == 0) { + pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + lncntp[orphan]--; + if (lostdir) { + if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + parentdir != (ino_t)-1) + (void)makeentry(orphan, lfdir, ".."); + dp = ginode(lfdir); + dp->di_nlink++; + inodirty(); + lncntp[lfdir]++; + pwarn("DIR I=%lu CONNECTED. ", orphan); + if (parentdir != (ino_t)-1) + printf("PARENT WAS I=%lu\n", parentdir); + if (preen == 0) + printf("\n"); + } + return (1); +} + +/* + * fix an entry in a directory. + */ +changeino(dir, name, newnum) + ino_t dir; + char *name; + ino_t newnum; +{ + struct inodesc idesc; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = chgino; + idesc.id_number = dir; + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + idesc.id_parent = newnum; /* new value for name */ + return (ckinode(ginode(dir), &idesc)); +} + +/* + * make an entry in a directory + */ +makeentry(parent, ino, name) + ino_t parent, ino; + char *name; +{ + struct dinode *dp; + struct inodesc idesc; + char pathbuf[MAXPATHLEN + 1]; + + if (parent < ROOTINO || parent >= maxino || + ino < ROOTINO || ino >= maxino) + return (0); + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = mkentry; + idesc.id_number = parent; + idesc.id_parent = ino; /* this is the inode to enter */ + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + dp = ginode(parent); + if (dp->di_size % DIRBLKSIZ) { + dp->di_size = roundup(dp->di_size, DIRBLKSIZ); + inodirty(); + } + if ((ckinode(dp, &idesc) & ALTERED) != 0) + return (1); + getpathname(pathbuf, parent, parent); + dp = ginode(parent); + if (expanddir(dp, pathbuf) == 0) + return (0); + return (ckinode(dp, &idesc) & ALTERED); +} + +/* + * Attempt to expand the size of a directory + */ +expanddir(dp, name) + register struct dinode *dp; + char *name; +{ + daddr_t lastbn, newblk; + register struct bufarea *bp; + char *cp, firstblk[DIRBLKSIZ]; + + lastbn = lblkno(&sblock, dp->di_size); + if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) + return (0); + if ((newblk = allocblk(sblock.fs_frag)) == 0) + return (0); + dp->di_db[lastbn + 1] = dp->di_db[lastbn]; + dp->di_db[lastbn] = newblk; + dp->di_size += sblock.fs_bsize; + dp->di_blocks += btodb(sblock.fs_bsize); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); + bp = getdirblk(newblk, sblock.fs_bsize); + if (bp->b_errs) + goto bad; + bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_bsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); + pwarn("NO SPACE LEFT IN %s", name); + if (preen) + printf(" (EXPANDED)\n"); + else if (reply("EXPAND") == 0) + goto bad; + dirty(bp); + inodirty(); + return (1); +bad: + dp->di_db[lastbn] = dp->di_db[lastbn + 1]; + dp->di_db[lastbn + 1] = 0; + dp->di_size -= sblock.fs_bsize; + dp->di_blocks -= btodb(sblock.fs_bsize); + freeblk(newblk, sblock.fs_frag); + return (0); +} + +/* + * allocate a new directory + */ +allocdir(parent, request, mode) + ino_t parent, request; + int mode; +{ + ino_t ino; + char *cp; + struct dinode *dp; + register struct bufarea *bp; + struct dirtemplate *dirp; + + ino = allocino(request, IFDIR|mode); + if (newinofmt) + dirp = &dirhead; + else + dirp = (struct dirtemplate *)&odirhead; + dirp->dot_ino = ino; + dirp->dotdot_ino = parent; + dp = ginode(ino); + bp = getdirblk(dp->di_db[0], sblock.fs_fsize); + if (bp->b_errs) { + freeino(ino); + return (0); + } + bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_fsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + dp->di_nlink = 2; + inodirty(); + if (ino == ROOTINO) { + lncntp[ino] = dp->di_nlink; + cacheino(dp, ino); + return(ino); + } + if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { + freeino(ino); + return (0); + } + cacheino(dp, ino); + statemap[ino] = statemap[parent]; + if (statemap[ino] == DSTATE) { + lncntp[ino] = dp->di_nlink; + lncntp[parent]++; + } + dp = ginode(parent); + dp->di_nlink++; + inodirty(); + return (ino); +} + +/* + * free a directory inode + */ +freedir(ino, parent) + ino_t ino, parent; +{ + struct dinode *dp; + + if (ino != parent) { + dp = ginode(parent); + dp->di_nlink--; + inodirty(); + } + freeino(ino); +} + +/* + * generate a temporary name for the lost+found directory. + */ +lftempname(bufp, ino) + char *bufp; + ino_t ino; +{ + register ino_t in; + register char *cp; + int namlen; + + cp = bufp + 2; + for (in = maxino; in > 0; in /= 10) + cp++; + *--cp = 0; + namlen = cp - bufp; + in = ino; + while (cp > bufp) { + *--cp = (in % 10) + '0'; + in /= 10; + } + *cp = '#'; + return (namlen); +} + +/* + * Get a directory block. + * Insure that it is held until another is requested. + */ +struct bufarea * +getdirblk(blkno, size) + daddr_t blkno; + long size; +{ + + if (pdirbp != 0) + pdirbp->b_flags &= ~B_INUSE; + pdirbp = getdatablk(blkno, size); + return (pdirbp); +} diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8 new file mode 100644 index 000000000000..cf8c499a35e9 --- /dev/null +++ b/sbin/fsck/fsck.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt FSCK 8 +.Os BSD 4 +.Sh NAME +.Nm fsck +.Nd filesystem consistency check and interactive repair +.Sh SYNOPSIS +.Nm fsck +.Fl p +.Op Fl m Ar mode +.Nm fsck +.Op Fl b Ar block# +.Op Fl c Ar level +.Op Fl l Ar maxparallel +.Op Fl y +.Op Fl n +.Op Fl m Ar mode +.Op Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm fsck +preens a standard set of filesystems or the specified filesystems. +It is normally used in the script +.Pa /etc/rc +during automatic reboot. +Here +.Nm fsck +reads the table +.Pa /etc/fstab +to determine which filesystems to check. +Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro'' +and that have non-zero pass number are checked. +Filesystems with pass number 1 (normally just the root filesystem) +are checked one at a time. +When pass 1 completes, all remaining filesystems are checked, +running one process per disk drive. +The disk drive containing each filesystem is inferred from the longest prefix +of the device name that ends in a digit; the remaining characters are assumed +to be the partition designator. +.Pp +The kernel takes care that only a restricted class of innocuous filesystem +inconsistencies can happen unless hardware or software failures intervene. +These are limited to the following: +.Bl -item -compact +.It +Unreferenced inodes +.It +Link counts in inodes too large +.It +Missing blocks in the free map +.It +Blocks in the free map also in files +.It +Counts in the super-block wrong +.El +.Pp +These are the only inconsistencies that +.Nm fsck +with the +.Fl p +option will correct; if it encounters other inconsistencies, it exits +with an abnormal return status and an automatic reboot will then fail. +For each corrected inconsistency one or more lines will be printed +identifying the filesystem on which the correction will take place, +and the nature of the correction. After successfully correcting a filesystem, +.Nm fsck +will print the number of files on that filesystem, +the number of used and free blocks, +and the percentage of fragmentation. +.Pp +If sent a +.Dv QUIT +signal, +.Nm fsck +will finish the filesystem checks, then exit with an abnormal +return status that causes an automatic reboot to fail. +This is useful when you want to finish the filesystem checks during an +automatic reboot, +but do not want the machine to come up multiuser after the checks complete. +.Pp +Without the +.Fl p +option, +.Nm fsck +audits and interactively repairs inconsistent conditions for filesystems. +If the filesystem is inconsistent the operator is prompted for concurrence +before each correction is attempted. +It should be noted that some of the corrective actions which are not +correctable under the +.Fl p +option will result in some loss of data. +The amount and severity of data lost may be determined from the diagnostic +output. +The default action for each consistency correction +is to wait for the operator to respond +.Li yes +or +.Li no . +If the operator does not have write permission on the filesystem +.Nm fsck +will default to a +.Fl n +action. +.Pp +.Nm Fsck +has more consistency checks than +its predecessors +.Em check , dcheck , fcheck , +and +.Em icheck +combined. +.Pp +The following flags are interpreted by +.Nm fsck . +.Bl -tag -width indent +.It Fl b +Use the block specified immediately after the flag as +the super block for the filesystem. Block 32 is usually +an alternate super block. +.It Fl l +Limit the number of parallel checks to the number specified in the following +argument. +By default, the limit is the number of disks, running one process per disk. +If a smaller limit is given, the disks are checked round-robin, one filesystem +at a time. +.It Fl m +Use the mode specified in octal immediately after the flag as the +permission bits to use when creating the +.Pa lost+found +directory rather than the default 1777. +In particular, systems that do not wish to have lost files accessible +by all users on the system should use a more restrictive +set of permissions such as 700. +.It Fl y +Assume a yes response to all questions asked by +.Nm fsck ; +this should be used with great caution as this is a free license +to continue after essentially unlimited trouble has been encountered. +.It Fl n +Assume a no response to all questions asked by +.Nm fsck +except for +.Ql CONTINUE? , +which is assumed to be affirmative; +do not open the filesystem for writing. +.It Fl c +Convert the filesystem to the specified level. +Note that the level of a filesystem can only be raised. +.Bl -tag -width indent +There are currently three levels defined: +.It 0 +The filesystem is in the old (static table) format. +.It 1 +The filesystem is in the new (dynamic table) format. +.It 2 +The filesystem supports 32-bit uid's and gid's, +short symbolic links are stored in the inode, +and directories have an added field showing the file type. +.El +.Pp +In interactive mode, +.Nm fsck +will list the conversion to be made +and ask whether the conversion should be done. +If a negative answer is given, +no further operations are done on the filesystem. +In preen mode, +the conversion is listed and done if +possible without user interaction. +Conversion in preen mode is best used when all the filesystems +are being converted at once. +The format of a filesystem can be determined from the +first line of output from +.Xr dumpfs 8 . +.El +.Pp +If no filesystems are given to +.Nm fsck +then a default list of filesystems is read from +the file +.Pa /etc/fstab . +.Pp +.Bl -enum -indent indent -compact +Inconsistencies checked are as follows: +.It +Blocks claimed by more than one inode or the free map. +.It +Blocks claimed by an inode outside the range of the filesystem. +.It +Incorrect link counts. +.It +Size checks: +.Bl -item -indent indent -compact +.It +Directory size not a multiple of DIRBLKSIZ. +.It +Partially truncated file. +.El +.It +Bad inode format. +.It +Blocks not accounted for anywhere. +.It +Directory checks: +.Bl -item -indent indent -compact +.It +File pointing to unallocated inode. +.It +Inode number out of range. +.It +Dot or dot-dot not the first two entries of a directory +or having the wrong inode number. +.El +.It +Super Block checks: +.Bl -item -indent indent -compact +.It +More blocks for inodes than there are in the filesystem. +.It +Bad free block map format. +.It +Total free block and/or free inode count incorrect. +.El +.El +.Pp +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the +.Pa lost+found +directory. +The name assigned is the inode number. +If the +.Pa lost+found +directory does not exist, it is created. +If there is insufficient space its size is increased. +.Pp +Because of inconsistencies between the block device and the buffer cache, +the raw device should always be used. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +contains default list of filesystems to check. +.El +.Sh DIAGNOSTICS +The diagnostics produced by +.Nm fsck +are fully enumerated and explained in Appendix A of +.Rs +.%T "Fsck \- The UNIX File System Check Program" +.Re +.Sh SEE ALSO +.Xr fstab 5 , +.Xr fs 5 , +.Xr fsdb 8 , +.Xr newfs 8 , +.Xr mkfs 8 , +.Xr reboot 8 diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h new file mode 100644 index 000000000000..7fa831f6c52e --- /dev/null +++ b/sbin/fsck/fsck.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fsck.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXDUP 10 /* limit on dup blks (per inode) */ +#define MAXBAD 10 /* limit on bad blks (per inode) */ +#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */ +#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */ + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +#define USTATE 01 /* inode not allocated */ +#define FSTATE 02 /* inode is file */ +#define DSTATE 03 /* inode is directory */ +#define DFOUND 04 /* directory found during descent */ +#define DCLEAR 05 /* directory is to be cleared */ +#define FCLEAR 06 /* file is to be cleared */ + +/* + * buffer cache structure. + */ +struct bufarea { + struct bufarea *b_next; /* free list queue */ + struct bufarea *b_prev; /* free list queue */ + daddr_t b_bno; + int b_size; + int b_errs; + int b_flags; + union { + char *b_buf; /* buffer space */ + daddr_t *b_indir; /* indirect block */ + struct fs *b_fs; /* super block */ + struct cg *b_cg; /* cylinder group */ + struct dinode *b_dinode; /* inode block */ + } b_un; + char b_dirty; +}; + +#define B_INUSE 1 + +#define MINBUFS 5 /* minimum number of buffers required */ +struct bufarea bufhead; /* head of list of other blks in filesys */ +struct bufarea sblk; /* file system superblock */ +struct bufarea cgblk; /* cylinder group blocks */ +struct bufarea *pdirbp; /* current directory contents */ +struct bufarea *pbp; /* current inode block */ +struct bufarea *getdatablk(); + +#define dirty(bp) (bp)->b_dirty = 1 +#define initbarea(bp) \ + (bp)->b_dirty = 0; \ + (bp)->b_bno = (daddr_t)-1; \ + (bp)->b_flags = 0; + +#define sbdirty() sblk.b_dirty = 1 +#define cgdirty() cgblk.b_dirty = 1 +#define sblock (*sblk.b_un.b_fs) +#define cgrp (*cgblk.b_un.b_cg) + +enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; + +struct inodesc { + enum fixstate id_fix; /* policy on fixing errors */ + int (*id_func)(); /* function to be applied to blocks of inode */ + ino_t id_number; /* inode number described */ + ino_t id_parent; /* for DATA nodes, their parent */ + daddr_t id_blkno; /* current block number being examined */ + int id_numfrags; /* number of frags contained in block */ + quad_t id_filesize; /* for DATA nodes, the size of the directory */ + int id_loc; /* for DATA nodes, current location in dir */ + int id_entryno; /* for DATA nodes, current entry number */ + struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ + char *id_name; /* for DATA nodes, name to find or enter */ + char id_type; /* type of descriptor, DATA or ADDR */ +}; +/* file types */ +#define DATA 1 +#define ADDR 2 + +/* + * Linked list of duplicate blocks. + * + * The list is composed of two parts. The first part of the + * list (from duplist through the node pointed to by muldup) + * contains a single copy of each duplicate block that has been + * found. The second part of the list (from muldup to the end) + * contains duplicate blocks that have been found more than once. + * To check if a block has been found as a duplicate it is only + * necessary to search from duplist through muldup. To find the + * total number of times that a block has been found as a duplicate + * the entire list must be searched for occurences of the block + * in question. The following diagram shows a sample list where + * w (found twice), x (found once), y (found three times), and z + * (found once) are duplicate block numbers: + * + * w -> y -> x -> z -> y -> w -> y + * ^ ^ + * | | + * duplist muldup + */ +struct dups { + struct dups *next; + daddr_t dup; +}; +struct dups *duplist; /* head of dup list */ +struct dups *muldup; /* end of unique duplicate dup block numbers */ + +/* + * Linked list of inodes with zero link counts. + */ +struct zlncnt { + struct zlncnt *next; + ino_t zlncnt; +}; +struct zlncnt *zlnhead; /* head of zero link count list */ + +/* + * Inode cache data structures. + */ +struct inoinfo { + struct inoinfo *i_nexthash; /* next entry in hash chain */ + ino_t i_number; /* inode number of this entry */ + ino_t i_parent; /* inode number of parent */ + ino_t i_dotdot; /* inode number of `..' */ + size_t i_isize; /* size of inode */ + u_int i_numblks; /* size of block array in bytes */ + daddr_t i_blks[1]; /* actually longer */ +} **inphead, **inpsort; +long numdirs, listmax, inplast; + +char *cdevname; /* name of device being checked */ +long dev_bsize; /* computed value of DEV_BSIZE */ +long secsize; /* actual disk sector size */ +char nflag; /* assume a no response */ +char yflag; /* assume a yes response */ +int bflag; /* location of alternate super block */ +int debug; /* output debugging info */ +int cvtlevel; /* convert to newer file system format */ +int doinglevel1; /* converting to new cylinder group format */ +int doinglevel2; /* converting to new inode format */ +int newinofmt; /* filesystem has new inode format */ +char preen; /* just fix normal inconsistencies */ +char hotroot; /* checking root device */ +char havesb; /* superblock has been read */ +int fsmodified; /* 1 => write done to file system */ +int fsreadfd; /* file descriptor for reading file system */ +int fswritefd; /* file descriptor for writing file system */ + +daddr_t maxfsblock; /* number of blocks in the file system */ +char *blockmap; /* ptr to primary blk allocation map */ +ino_t maxino; /* number of inodes in file system */ +ino_t lastino; /* last inode in use */ +char *statemap; /* ptr to inode state table */ +char *typemap; /* ptr to inode type table */ +short *lncntp; /* ptr to link count table */ + +ino_t lfdir; /* lost & found directory inode number */ +char *lfname; /* lost & found directory name */ +int lfmode; /* lost & found directory creation mode */ + +daddr_t n_blks; /* number of blocks in use */ +daddr_t n_files; /* number of files in use */ + +#define clearinode(dp) (*(dp) = zino) +struct dinode zino; + +#define setbmap(blkno) setbit(blockmap, blkno) +#define testbmap(blkno) isset(blockmap, blkno) +#define clrbmap(blkno) clrbit(blockmap, blkno) + +#define STOP 0x01 +#define SKIP 0x02 +#define KEEPON 0x04 +#define ALTERED 0x08 +#define FOUND 0x10 + +time_t time(); +struct dinode *ginode(); +struct inoinfo *getinoinfo(); +void getblk(); +ino_t allocino(); +int findino(); diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c new file mode 100644 index 000000000000..f1c1758f746a --- /dev/null +++ b/sbin/fsck/inode.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static ino_t startinum; + +ckinode(dp, idesc) + struct dinode *dp; + register struct inodesc *idesc; +{ + register daddr_t *ap; + long ret, n, ndb, offset; + struct dinode dino; + quad_t remsize, sizepb; + mode_t mode; + + if (idesc->id_fix != IGNORE) + idesc->id_fix = DONTKNOW; + idesc->id_entryno = 0; + idesc->id_filesize = dp->di_size; + mode = dp->di_mode & IFMT; + if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && + dp->di_size < sblock.fs_maxsymlinklen)) + return (KEEPON); + dino = *dp; + ndb = howmany(dino.di_size, sblock.fs_bsize); + for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) + idesc->id_numfrags = + numfrags(&sblock, fragroundup(&sblock, offset)); + else + idesc->id_numfrags = sblock.fs_frag; + if (*ap == 0) + continue; + idesc->id_blkno = *ap; + if (idesc->id_type == ADDR) + ret = (*idesc->id_func)(idesc); + else + ret = dirscan(idesc); + if (ret & STOP) + return (ret); + } + idesc->id_numfrags = sblock.fs_frag; + remsize = dino.di_size - sblock.fs_bsize * NDADDR; + sizepb = sblock.fs_bsize; + for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + if (*ap) { + idesc->id_blkno = *ap; + ret = iblock(idesc, n, remsize); + if (ret & STOP) + return (ret); + } + sizepb *= NINDIR(&sblock); + remsize -= sizepb; + } + return (KEEPON); +} + +iblock(idesc, ilevel, isize) + struct inodesc *idesc; + long ilevel; + quad_t isize; +{ + register daddr_t *ap; + register daddr_t *aplim; + register struct bufarea *bp; + int i, n, (*func)(), nif; + quad_t sizepb; + char buf[BUFSIZ]; + extern int dirscan(), pass1check(); + + if (idesc->id_type == ADDR) { + func = idesc->id_func; + if (((n = (*func)(idesc)) & KEEPON) == 0) + return (n); + } else + func = dirscan; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) + return (SKIP); + bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); + ilevel--; + for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) + sizepb *= NINDIR(&sblock); + nif = howmany(isize , sizepb); + if (nif > NINDIR(&sblock)) + nif = NINDIR(&sblock); + if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { + aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; + for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { + if (*ap == 0) + continue; + (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", + idesc->id_number); + if (dofix(idesc, buf)) { + *ap = 0; + dirty(bp); + } + } + flush(fswritefd, bp); + } + aplim = &bp->b_un.b_indir[nif]; + for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (*ap) { + idesc->id_blkno = *ap; + if (ilevel == 0) + n = (*func)(idesc); + else + n = iblock(idesc, ilevel, isize); + if (n & STOP) { + bp->b_flags &= ~B_INUSE; + return (n); + } + } + isize -= sizepb; + } + bp->b_flags &= ~B_INUSE; + return (KEEPON); +} + +/* + * Check that a block in a legal block number. + * Return 0 if in range, 1 if out of range. + */ +chkrange(blk, cnt) + daddr_t blk; + int cnt; +{ + register int c; + + if ((unsigned)(blk + cnt) > maxfsblock) + return (1); + c = dtog(&sblock, blk); + if (blk < cgdmin(&sblock, c)) { + if ((blk + cnt) > cgsblock(&sblock, c)) { + if (debug) { + printf("blk %ld < cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > cgsbase %ld\n", + blk + cnt, cgsblock(&sblock, c)); + } + return (1); + } + } else { + if ((blk + cnt) > cgbase(&sblock, c+1)) { + if (debug) { + printf("blk %ld >= cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", + blk+cnt, sblock.fs_fpg); + } + return (1); + } + } + return (0); +} + +/* + * General purpose interface for reading inodes. + */ +struct dinode * +ginode(inumber) + ino_t inumber; +{ + daddr_t iblk; + + if (inumber < ROOTINO || inumber > maxino) + errexit("bad inode number %d to ginode\n", inumber); + if (startinum == 0 || + inumber < startinum || inumber >= startinum + INOPB(&sblock)) { + iblk = ino_to_fsba(&sblock, inumber); + if (pbp != 0) + pbp->b_flags &= ~B_INUSE; + pbp = getdatablk(iblk, sblock.fs_bsize); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]); +} + +/* + * Special purpose version of ginode used to optimize first pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; + +struct dinode * +getnextinode(inumber) + ino_t inumber; +{ + long size; + daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + errexit("bad inode number %d to nextinode\n", inumber); + if (inumber >= lastinum) { + readcnt++; + dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); + if (readcnt % readpercg == 0) { + size = partialsize; + lastinum += partialcnt; + } else { + size = inobufsize; + lastinum += fullcnt; + } + (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ + dp = inodebuf; + } + return (dp++); +} + +resetinodebuf() +{ + + startinum = 0; + nextino = 0; + lastinum = 0; + readcnt = 0; + inobufsize = blkroundup(&sblock, INOBUFSIZE); + fullcnt = inobufsize / sizeof(struct dinode); + readpercg = sblock.fs_ipg / fullcnt; + partialcnt = sblock.fs_ipg % fullcnt; + partialsize = partialcnt * sizeof(struct dinode); + if (partialcnt != 0) { + readpercg++; + } else { + partialcnt = fullcnt; + partialsize = inobufsize; + } + if (inodebuf == NULL && + (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) + errexit("Cannot allocate space for inode buffer\n"); + while (nextino < ROOTINO) + (void)getnextinode(nextino); +} + +freeinodebuf() +{ + + if (inodebuf != NULL) + free((char *)inodebuf); + inodebuf = NULL; +} + +/* + * Routines to maintain information about directory inodes. + * This is built during the first pass and used during the + * second and third passes. + * + * Enter inodes into the cache. + */ +cacheino(dp, inumber) + register struct dinode *dp; + ino_t inumber; +{ + register struct inoinfo *inp; + struct inoinfo **inpp; + unsigned int blks; + + blks = howmany(dp->di_size, sblock.fs_bsize); + if (blks > NDADDR) + blks = NDADDR + NIADDR; + inp = (struct inoinfo *) + malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); + if (inp == NULL) + return; + inpp = &inphead[inumber % numdirs]; + inp->i_nexthash = *inpp; + *inpp = inp; + inp->i_parent = (ino_t)0; + inp->i_dotdot = (ino_t)0; + inp->i_number = inumber; + inp->i_isize = dp->di_size; + inp->i_numblks = blks * sizeof(daddr_t); + bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], + (size_t)inp->i_numblks); + if (inplast == listmax) { + listmax += 100; + inpsort = (struct inoinfo **)realloc((char *)inpsort, + (unsigned)listmax * sizeof(struct inoinfo *)); + if (inpsort == NULL) + errexit("cannot increase directory list"); + } + inpsort[inplast++] = inp; +} + +/* + * Look up an inode cache structure. + */ +struct inoinfo * +getinoinfo(inumber) + ino_t inumber; +{ + register struct inoinfo *inp; + + for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { + if (inp->i_number != inumber) + continue; + return (inp); + } + errexit("cannot find inode %d\n", inumber); + return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +inocleanup() +{ + register struct inoinfo **inpp; + + if (inphead == NULL) + return; + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) + free((char *)(*inpp)); + free((char *)inphead); + free((char *)inpsort); + inphead = inpsort = NULL; +} + +inodirty() +{ + + dirty(pbp); +} + +clri(idesc, type, flag) + register struct inodesc *idesc; + char *type; + int flag; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (flag == 1) { + pwarn("%s %s", type, + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); + pinode(idesc->id_number); + } + if (preen || reply("CLEAR") == 1) { + if (preen) + printf(" (CLEARED)\n"); + n_files--; + (void)ckinode(dp, idesc); + clearinode(dp); + statemap[idesc->id_number] = USTATE; + inodirty(); + } +} + +findname(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino != idesc->id_parent) + return (KEEPON); + bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1); + return (STOP|FOUND); +} + +findino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino == 0) + return (KEEPON); + if (strcmp(dirp->d_name, idesc->id_name) == 0 && + dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { + idesc->id_parent = dirp->d_ino; + return (STOP|FOUND); + } + return (KEEPON); +} + +pinode(ino) + ino_t ino; +{ + register struct dinode *dp; + register char *p; + struct passwd *pw; + char *ctime(); + + printf(" I=%lu ", ino); + if (ino < ROOTINO || ino > maxino) + return; + dp = ginode(ino); + printf(" OWNER="); + if ((pw = getpwuid((int)dp->di_uid)) != 0) + printf("%s ", pw->pw_name); + else + printf("%u ", (unsigned)dp->di_uid); + printf("MODE=%o\n", dp->di_mode); + if (preen) + printf("%s: ", cdevname); + printf("SIZE=%qu ", dp->di_size); + p = ctime(&dp->di_mtime.ts_sec); + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +blkerror(ino, type, blk) + ino_t ino; + char *type; + daddr_t blk; +{ + + pfatal("%ld %s I=%lu", blk, type, ino); + printf("\n"); + switch (statemap[ino]) { + + case FSTATE: + statemap[ino] = FCLEAR; + return; + + case DSTATE: + statemap[ino] = DCLEAR; + return; + + case FCLEAR: + case DCLEAR: + return; + + default: + errexit("BAD STATE %d TO BLKERR", statemap[ino]); + /* NOTREACHED */ + } +} + +/* + * allocate an unused inode + */ +ino_t +allocino(request, type) + ino_t request; + int type; +{ + register ino_t ino; + register struct dinode *dp; + + if (request == 0) + request = ROOTINO; + else if (statemap[request] != USTATE) + return (0); + for (ino = request; ino < maxino; ino++) + if (statemap[ino] == USTATE) + break; + if (ino == maxino) + return (0); + switch (type & IFMT) { + case IFDIR: + statemap[ino] = DSTATE; + break; + case IFREG: + case IFLNK: + statemap[ino] = FSTATE; + break; + default: + return (0); + } + dp = ginode(ino); + dp->di_db[0] = allocblk((long)1); + if (dp->di_db[0] == 0) { + statemap[ino] = USTATE; + return (0); + } + dp->di_mode = type; + (void)time(&dp->di_atime.ts_sec); + dp->di_mtime = dp->di_ctime = dp->di_atime; + dp->di_size = sblock.fs_fsize; + dp->di_blocks = btodb(sblock.fs_fsize); + n_files++; + inodirty(); + if (newinofmt) + typemap[ino] = IFTODT(type); + return (ino); +} + +/* + * deallocate an inode + */ +freeino(ino) + ino_t ino; +{ + struct inodesc idesc; + extern int pass4check(); + struct dinode *dp; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = ino; + dp = ginode(ino); + (void)ckinode(dp, &idesc); + clearinode(dp); + inodirty(); + statemap[ino] = USTATE; + n_files--; +} diff --git a/sbin/fsck/main.c b/sbin/fsck/main.c new file mode 100644 index 000000000000..2e69715fecf0 --- /dev/null +++ b/sbin/fsck/main.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +void catch(), catchquit(), voidquit(); +int returntosingle; + +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int ret, maxrun = 0; + extern int docheck(), checkfilesys(); + extern char *optarg, *blockcheck(); + extern int optind; + + sync(); + while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) { + switch (ch) { + case 'p': + preen++; + break; + + case 'b': + bflag = argtoi('b', "number", optarg, 10); + printf("Alternate super block location: %d\n", bflag); + break; + + case 'c': + cvtlevel = argtoi('c', "conversion level", optarg, 10); + break; + + case 'd': + debug++; + break; + + case 'l': + maxrun = argtoi('l', "number", optarg, 10); + break; + + case 'm': + lfmode = argtoi('m', "mode", optarg, 8); + if (lfmode &~ 07777) + errexit("bad mode to -m: %o\n", lfmode); + printf("** lost+found creation mode %o\n", lfmode); + break; + + case 'n': + case 'N': + nflag++; + yflag = 0; + break; + + case 'y': + case 'Y': + yflag++; + nflag = 0; + break; + + default: + errexit("%c option?\n", ch); + } + } + argc -= optind; + argv += optind; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, catch); + if (preen) + (void)signal(SIGQUIT, catchquit); + if (argc) { + while (argc-- > 0) + (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0); + exit(0); + } + ret = checkfstab(preen, maxrun, docheck, checkfilesys); + if (returntosingle) + exit(2); + exit(ret); +} + +argtoi(flag, req, str, base) + int flag; + char *req, *str; + int base; +{ + char *cp; + int ret; + + ret = (int)strtol(str, &cp, base); + if (cp == str || *cp) + errexit("-%c flag requires a %s\n", flag, req); + return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +docheck(fsp) + register struct fstab *fsp; +{ + + if (strcmp(fsp->fs_vfstype, "ufs") || + (strcmp(fsp->fs_type, FSTAB_RW) && + strcmp(fsp->fs_type, FSTAB_RO)) || + fsp->fs_passno == 0) + return (0); + return (1); +} + +/* + * Check the specified filesystem. + */ +/* ARGSUSED */ +checkfilesys(filesys, mntpt, auxdata, child) + char *filesys, *mntpt; + long auxdata; +{ + daddr_t n_ffree, n_bfree; + struct dups *dp; + struct zlncnt *zlnp; + int cylno; + + if (preen && child) + (void)signal(SIGQUIT, voidquit); + cdevname = filesys; + if (debug && preen) + pwarn("starting\n"); + if (setup(filesys) == 0) { + if (preen) + pfatal("CAN'T CHECK FILE SYSTEM."); + return (0); + } + /* + * 1: scan inodes tallying blocks used + */ + if (preen == 0) { + printf("** Last Mounted on %s\n", sblock.fs_fsmnt); + if (hotroot) + printf("** Root file system\n"); + printf("** Phase 1 - Check Blocks and Sizes\n"); + } + pass1(); + + /* + * 1b: locate first references to duplicates, if any + */ + if (duplist) { + if (preen) + pfatal("INTERNAL ERROR: dups with -p"); + printf("** Phase 1b - Rescan For More DUPS\n"); + pass1b(); + } + + /* + * 2: traverse directories from root to mark all connected directories + */ + if (preen == 0) + printf("** Phase 2 - Check Pathnames\n"); + pass2(); + + /* + * 3: scan inodes looking for disconnected directories + */ + if (preen == 0) + printf("** Phase 3 - Check Connectivity\n"); + pass3(); + + /* + * 4: scan inodes looking for disconnected files; check reference counts + */ + if (preen == 0) + printf("** Phase 4 - Check Reference Counts\n"); + pass4(); + + /* + * 5: check and repair resource counts in cylinder groups + */ + if (preen == 0) + printf("** Phase 5 - Check Cyl groups\n"); + pass5(); + + /* + * print out summary statistics + */ + n_ffree = sblock.fs_cstotal.cs_nffree; + n_bfree = sblock.fs_cstotal.cs_nbfree; + pwarn("%ld files, %ld used, %ld free ", + n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); + printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n", + n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize, + ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10); + if (debug && + (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) + printf("%ld files missing\n", n_files); + if (debug) { + n_blks += sblock.fs_ncg * + (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); + n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); + n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); + if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) + printf("%ld blocks missing\n", n_blks); + if (duplist != NULL) { + printf("The following duplicate blocks remain:"); + for (dp = duplist; dp; dp = dp->next) + printf(" %ld,", dp->dup); + printf("\n"); + } + if (zlnhead != NULL) { + printf("The following zero link count inodes remain:"); + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + printf(" %lu,", zlnp->zlncnt); + printf("\n"); + } + } + zlnhead = (struct zlncnt *)0; + duplist = (struct dups *)0; + muldup = (struct dups *)0; + inocleanup(); + if (fsmodified) { + (void)time(&sblock.fs_time); + sbdirty(); + } + if (cvtlevel && sblk.b_dirty) { + /* + * Write out the duplicate super blocks + */ + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) + bwrite(fswritefd, (char *)&sblock, + fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); + } + ckfini(); + free(blockmap); + free(statemap); + free((char *)lncntp); + if (!fsmodified) + return (0); + if (!preen) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + if (hotroot) { + struct statfs stfs_buf; + /* + * We modified the root. Do a mount update on + * it, unless it is read-write, so we can continue. + */ + if (statfs("/", &stfs_buf) == 0) { + long flags = stfs_buf.f_flags; + struct ufs_args args; + int ret; + + if (flags & MNT_RDONLY) { + args.fspec = 0; + args.export.ex_flags = 0; + args.export.ex_root = 0; + flags |= MNT_UPDATE | MNT_RELOAD; + ret = mount(MOUNT_UFS, "/", flags, &args); + if (ret == 0) + return(0); + } + } + if (!preen) + printf("\n***** REBOOT NOW *****\n"); + sync(); + return (4); + } + return (0); +} diff --git a/sbin/fsck/pass1.c b/sbin/fsck/pass1.c new file mode 100644 index 000000000000..ca255fe78579 --- /dev/null +++ b/sbin/fsck/pass1.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static daddr_t badblk; +static daddr_t dupblk; +int pass1check(); +struct dinode *getnextinode(); + +pass1() +{ + ino_t inumber; + int c, i, cgd; + struct inodesc idesc; + + /* + * Set file system reserved blocks in used block map. + */ + for (c = 0; c < sblock.fs_ncg; c++) { + cgd = cgdmin(&sblock, c); + if (c == 0) { + i = cgbase(&sblock, c); + cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); + } else + i = cgsblock(&sblock, c); + for (; i < cgd; i++) + setbmap(i); + } + /* + * Find all allocated blocks. + */ + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1check; + inumber = 0; + n_files = n_blks = 0; + resetinodebuf(); + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + checkinode(inumber, &idesc); + } + } + freeinodebuf(); +} + +checkinode(inumber, idesc) + ino_t inumber; + register struct inodesc *idesc; +{ + register struct dinode *dp; + struct zlncnt *zlnp; + int ndb, j; + mode_t mode; + char symbuf[MAXSYMLINKLEN]; + + dp = getnextinode(inumber); + mode = dp->di_mode & IFMT; + if (mode == 0) { + if (bcmp((char *)dp->di_db, (char *)zino.di_db, + NDADDR * sizeof(daddr_t)) || + bcmp((char *)dp->di_ib, (char *)zino.di_ib, + NIADDR * sizeof(daddr_t)) || + dp->di_mode || dp->di_size) { + pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber); + if (reply("CLEAR") == 1) { + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } + } + statemap[inumber] = USTATE; + return; + } + lastino = inumber; + if (/* dp->di_size < 0 || */ + dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { + if (debug) + printf("bad size %qu:", dp->di_size); + goto unknown; + } + if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { + dp = ginode(inumber); + dp->di_size = sblock.fs_fsize; + dp->di_mode = IFREG|0600; + inodirty(); + } + ndb = howmany(dp->di_size, sblock.fs_bsize); + if (ndb < 0) { + if (debug) + printf("bad size %qu ndb %d:", + dp->di_size, ndb); + goto unknown; + } + if (mode == IFBLK || mode == IFCHR) + ndb++; + if (mode == IFLNK) { + if (doinglevel2 && + dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN && + dp->di_blocks != 0) { + if (bread(fsreadfd, symbuf, + fsbtodb(&sblock, dp->di_db[0]), + (long)dp->di_size) != 0) + errexit("cannot read symlink"); + if (debug) { + symbuf[dp->di_size] = 0; + printf("convert symlink %d(%s) of size %d\n", + inumber, symbuf, (long)dp->di_size); + } + dp = ginode(inumber); + bcopy(symbuf, (caddr_t)dp->di_shortlink, + (long)dp->di_size); + dp->di_blocks = 0; + inodirty(); + } + /* + * Fake ndb value so direct/indirect block checks below + * will detect any garbage after symlink string. + */ + if (dp->di_size < sblock.fs_maxsymlinklen) { + ndb = howmany(dp->di_size, sizeof(daddr_t)); + if (ndb > NDADDR) { + j = ndb - NDADDR; + for (ndb = 1; j > 1; j--) + ndb *= NINDIR(&sblock); + ndb += NDADDR; + } + } + } + for (j = ndb; j < NDADDR; j++) + if (dp->di_db[j] != 0) { + if (debug) + printf("bad direct addr: %ld\n", dp->di_db[j]); + goto unknown; + } + for (j = 0, ndb -= NDADDR; ndb > 0; j++) + ndb /= NINDIR(&sblock); + for (; j < NIADDR; j++) + if (dp->di_ib[j] != 0) { + if (debug) + printf("bad indirect addr: %ld\n", + dp->di_ib[j]); + goto unknown; + } + if (ftypeok(dp) == 0) + goto unknown; + n_files++; + lncntp[inumber] = dp->di_nlink; + if (dp->di_nlink <= 0) { + zlnp = (struct zlncnt *)malloc(sizeof *zlnp); + if (zlnp == NULL) { + pfatal("LINK COUNT TABLE OVERFLOW"); + if (reply("CONTINUE") == 0) + errexit(""); + } else { + zlnp->zlncnt = inumber; + zlnp->next = zlnhead; + zlnhead = zlnp; + } + } + if (mode == IFDIR) { + if (dp->di_size == 0) + statemap[inumber] = DCLEAR; + else + statemap[inumber] = DSTATE; + cacheino(dp, inumber); + } else + statemap[inumber] = FSTATE; + typemap[inumber] = IFTODT(mode); + if (doinglevel2 && + (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) { + dp = ginode(inumber); + dp->di_uid = dp->di_ouid; + dp->di_ouid = -1; + dp->di_gid = dp->di_ogid; + dp->di_ogid = -1; + inodirty(); + } + badblk = dupblk = 0; + idesc->id_number = inumber; + (void)ckinode(dp, idesc); + idesc->id_entryno *= btodb(sblock.fs_fsize); + if (dp->di_blocks != idesc->id_entryno) { + pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", + inumber, dp->di_blocks, idesc->id_entryno); + if (preen) + printf(" (CORRECTED)\n"); + else if (reply("CORRECT") == 0) + return; + dp = ginode(inumber); + dp->di_blocks = idesc->id_entryno; + inodirty(); + } + return; +unknown: + pfatal("UNKNOWN FILE TYPE I=%lu", inumber); + statemap[inumber] = FCLEAR; + if (reply("CLEAR") == 1) { + statemap[inumber] = USTATE; + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } +} + +pass1check(idesc) + register struct inodesc *idesc; +{ + int res = KEEPON; + int anyout, nfrags; + daddr_t blkno = idesc->id_blkno; + register struct dups *dlp; + struct dups *new; + + if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { + blkerror(idesc->id_number, "BAD", blkno); + if (badblk++ >= MAXBAD) { + pwarn("EXCESSIVE BAD BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + } + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (anyout && chkrange(blkno, 1)) { + res = SKIP; + } else if (!testbmap(blkno)) { + n_blks++; + setbmap(blkno); + } else { + blkerror(idesc->id_number, "DUP", blkno); + if (dupblk++ >= MAXDUP) { + pwarn("EXCESSIVE DUP BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new = (struct dups *)malloc(sizeof(struct dups)); + if (new == NULL) { + pfatal("DUP TABLE OVERFLOW."); + if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new->dup = blkno; + if (muldup == 0) { + duplist = muldup = new; + new->next = 0; + } else { + new->next = muldup->next; + muldup->next = new; + } + for (dlp = duplist; dlp != muldup; dlp = dlp->next) + if (dlp->dup == blkno) + break; + if (dlp == muldup && dlp->dup != blkno) + muldup = new; + } + /* + * count the number of blocks found in id_entryno + */ + idesc->id_entryno++; + } + return (res); +} diff --git a/sbin/fsck/pass1b.c b/sbin/fsck/pass1b.c new file mode 100644 index 000000000000..27b2dfd6238c --- /dev/null +++ b/sbin/fsck/pass1b.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass1bcheck(); +static struct dups *duphead; + +pass1b() +{ + register int c, i; + register struct dinode *dp; + struct inodesc idesc; + ino_t inumber; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1bcheck; + duphead = duplist; + inumber = 0; + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + dp = ginode(inumber); + if (dp == NULL) + continue; + idesc.id_number = inumber; + if (statemap[inumber] != USTATE && + (ckinode(dp, &idesc) & STOP)) + return; + } + } +} + +pass1bcheck(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) + res = SKIP; + for (dlp = duphead; dlp; dlp = dlp->next) { + if (dlp->dup == blkno) { + blkerror(idesc->id_number, "DUP", blkno); + dlp->dup = duphead->dup; + duphead->dup = blkno; + duphead = duphead->next; + } + if (dlp == muldup) + break; + } + if (muldup == 0 || duphead == muldup->next) + return (STOP); + } + return (res); +} diff --git a/sbin/fsck/pass2.c b/sbin/fsck/pass2.c new file mode 100644 index 000000000000..631475174372 --- /dev/null +++ b/sbin/fsck/pass2.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +int pass2check(), blksort(); + +pass2() +{ + register struct dinode *dp; + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + struct inodesc curino; + struct dinode dino; + char pathbuf[MAXPATHLEN + 1]; + + switch (statemap[ROOTINO]) { + + case USTATE: + pfatal("ROOT INODE UNALLOCATED"); + if (reply("ALLOCATE") == 0) + errexit(""); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("CONTINUE") == 0) + errexit(""); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("FIX") == 0) + errexit(""); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + break; + + default: + errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); + } + statemap[ROOTINO] = DFOUND; + /* + * Sort the directory list into disk block order. + */ + qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); + /* + * Check the integrity of each directory. + */ + bzero((char *)&curino, sizeof(struct inodesc)); + curino.id_type = DATA; + curino.id_func = pass2check; + dp = &dino; + inpend = &inpsort[inplast]; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_isize == 0) + continue; + if (inp->i_isize < MINDIRSIZE) { + direrror(inp->i_number, "DIRECTORY TOO SHORT"); + inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); + if (reply("FIX") == 1) { + dp = ginode(inp->i_number); + dp->di_size = inp->i_isize; + inodirty(); + dp = &dino; + } + } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { + getpathname(pathbuf, inp->i_number, inp->i_number); + pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", + pathbuf, inp->i_isize, DIRBLKSIZ); + if (preen) + printf(" (ADJUSTED)\n"); + inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); + if (preen || reply("ADJUST") == 1) { + dp = ginode(inp->i_number); + dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); + inodirty(); + dp = &dino; + } + } + bzero((char *)&dino, sizeof(struct dinode)); + dino.di_mode = IFDIR; + dp->di_size = inp->i_isize; + bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], + (size_t)inp->i_numblks); + curino.id_number = inp->i_number; + curino.id_parent = inp->i_parent; + (void)ckinode(dp, &curino); + } + /* + * Now that the parents of all directories have been found, + * make another pass to verify the value of `..' + */ + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0 || inp->i_isize == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) + statemap[inp->i_number] = DFOUND; + if (inp->i_dotdot == inp->i_parent || + inp->i_dotdot == (ino_t)-1) + continue; + if (inp->i_dotdot == 0) { + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); + if (reply("FIX") == 0) + continue; + (void)makeentry(inp->i_number, inp->i_parent, ".."); + lncntp[inp->i_parent]--; + continue; + } + fileerror(inp->i_parent, inp->i_number, + "BAD INODE NUMBER FOR '..'"); + if (reply("FIX") == 0) + continue; + lncntp[inp->i_dotdot]++; + lncntp[inp->i_parent]--; + inp->i_dotdot = inp->i_parent; + (void)changeino(inp->i_number, "..", inp->i_parent); + } + /* + * Mark all the directories that can be found from the root. + */ + propagate(); +} + +pass2check(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + register struct inoinfo *inp; + int n, entrysize, ret = 0; + struct dinode *dp; + char *errmsg; + struct direct proto; + char namebuf[MAXPATHLEN + 1]; + char pathbuf[MAXPATHLEN + 1]; + + /* + * If converting, set directory entry type. + */ + if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { + dirp->d_type = typemap[dirp->d_ino]; + ret |= ALTERED; + } + /* + * check for "." + */ + if (idesc->id_entryno != 0) + goto chk1; + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { + if (dirp->d_ino != idesc->id_number) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); + dirp->d_ino = idesc->id_number; + if (reply("FIX") == 1) + ret |= ALTERED; + } + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk1; + } + direrror(idesc->id_number, "MISSING '.'"); + proto.d_ino = idesc->id_number; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 1; + (void)strcpy(proto.d_name, "."); + entrysize = DIRSIZ(0, &proto); + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { + pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + } else if (dirp->d_reclen < entrysize) { + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); + } else if (dirp->d_reclen < 2 * entrysize) { + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + bzero((char *)dirp, (size_t)n); + dirp->d_reclen = n; + if (reply("FIX") == 1) + ret |= ALTERED; + } +chk1: + if (idesc->id_entryno > 1) + goto chk2; + inp = getinoinfo(idesc->id_number); + proto.d_ino = inp->i_parent; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 2; + (void)strcpy(proto.d_name, ".."); + entrysize = DIRSIZ(0, &proto); + if (idesc->id_entryno == 0) { + n = DIRSIZ(0, dirp); + if (dirp->d_reclen < n + entrysize) + goto chk2; + proto.d_reclen = dirp->d_reclen - n; + dirp->d_reclen = n; + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + n); + bzero((char *)dirp, (size_t)proto.d_reclen); + dirp->d_reclen = proto.d_reclen; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { + inp->i_dotdot = dirp->d_ino; + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk2; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + inp->i_dotdot = (ino_t)-1; + } else if (dirp->d_reclen < entrysize) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); + inp->i_dotdot = (ino_t)-1; + } else if (inp->i_parent != 0) { + /* + * We know the parent, so fix now. + */ + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } + idesc->id_entryno++; + if (dirp->d_ino != 0) + lncntp[dirp->d_ino]--; + return (ret|KEEPON); +chk2: + if (dirp->d_ino == 0) + return (ret|KEEPON); + if (dirp->d_namlen <= 2 && + dirp->d_name[0] == '.' && + idesc->id_entryno >= 2) { + if (dirp->d_namlen == 1) { + direrror(idesc->id_number, "EXTRA '.' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + if (dirp->d_name[1] == '.') { + direrror(idesc->id_number, "EXTRA '..' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + } + idesc->id_entryno++; + n = 0; + if (dirp->d_ino > maxino) { + fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); + n = reply("REMOVE"); + } else { +again: + switch (statemap[dirp->d_ino]) { + case USTATE: + if (idesc->id_entryno <= 2) + break; + fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); + n = reply("REMOVE"); + break; + + case DCLEAR: + case FCLEAR: + if (idesc->id_entryno <= 2) + break; + if (statemap[dirp->d_ino] == FCLEAR) + errmsg = "DUP/BAD"; + else if (!preen) + errmsg = "ZERO LENGTH DIRECTORY"; + else { + n = 1; + break; + } + fileerror(idesc->id_number, dirp->d_ino, errmsg); + if ((n = reply("REMOVE")) == 1) + break; + dp = ginode(dirp->d_ino); + statemap[dirp->d_ino] = + (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; + lncntp[dirp->d_ino] = dp->di_nlink; + goto again; + + case DSTATE: + if (statemap[idesc->id_number] == DFOUND) + statemap[dirp->d_ino] = DFOUND; + /* fall through */ + + case DFOUND: + inp = getinoinfo(dirp->d_ino); + if (inp->i_parent != 0 && idesc->id_entryno > 2) { + getpathname(pathbuf, idesc->id_number, + idesc->id_number); + getpathname(namebuf, dirp->d_ino, dirp->d_ino); + pwarn("%s %s %s\n", pathbuf, + "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", + namebuf); + if (preen) + printf(" (IGNORED)\n"); + else if ((n = reply("REMOVE")) == 1) + break; + } + if (idesc->id_entryno > 2) + inp->i_parent = idesc->id_number; + /* fall through */ + + case FSTATE: + if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { + fileerror(idesc->id_number, dirp->d_ino, + "BAD TYPE VALUE"); + dirp->d_type = typemap[dirp->d_ino]; + if (reply("FIX") == 1) + ret |= ALTERED; + } + lncntp[dirp->d_ino]--; + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[dirp->d_ino], dirp->d_ino); + } + } + if (n == 0) + return (ret|KEEPON); + dirp->d_ino = 0; + return (ret|KEEPON|ALTERED); +} + +/* + * Routine to sort disk blocks. + */ +blksort(inpp1, inpp2) + struct inoinfo **inpp1, **inpp2; +{ + + return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); +} diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c new file mode 100644 index 000000000000..5c6f09d041de --- /dev/null +++ b/sbin/fsck/pass3.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include "fsck.h" + +pass3() +{ + register struct inoinfo **inpp, *inp; + ino_t orphan; + int loopcnt; + + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { + inp = *inpp; + if (inp->i_number == ROOTINO || + !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE)) + continue; + if (statemap[inp->i_number] == DCLEAR) + continue; + for (loopcnt = 0; ; loopcnt++) { + orphan = inp->i_number; + if (inp->i_parent == 0 || + statemap[inp->i_parent] != DSTATE || + loopcnt > numdirs) + break; + inp = getinoinfo(inp->i_parent); + } + (void)linkup(orphan, inp->i_dotdot); + inp->i_parent = inp->i_dotdot = lfdir; + lncntp[lfdir]--; + statemap[orphan] = DFOUND; + propagate(); + } +} diff --git a/sbin/fsck/pass4.c b/sbin/fsck/pass4.c new file mode 100644 index 000000000000..7450530e8a4e --- /dev/null +++ b/sbin/fsck/pass4.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass4check(); + +pass4() +{ + register ino_t inumber; + register struct zlncnt *zlnp; + struct dinode *dp; + struct inodesc idesc; + int n; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + for (inumber = ROOTINO; inumber <= lastino; inumber++) { + idesc.id_number = inumber; + switch (statemap[inumber]) { + + case FSTATE: + case DFOUND: + n = lncntp[inumber]; + if (n) + adjust(&idesc, (short)n); + else { + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + if (zlnp->zlncnt == inumber) { + zlnp->zlncnt = zlnhead->zlncnt; + zlnp = zlnhead; + zlnhead = zlnhead->next; + free((char *)zlnp); + clri(&idesc, "UNREF", 1); + break; + } + } + break; + + case DSTATE: + clri(&idesc, "UNREF", 1); + break; + + case DCLEAR: + dp = ginode(inumber); + if (dp->di_size == 0) { + clri(&idesc, "ZERO LENGTH", 1); + break; + } + /* fall through */ + case FCLEAR: + clri(&idesc, "BAD/DUP", 1); + break; + + case USTATE: + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[inumber], inumber); + } + } +} + +pass4check(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) { + res = SKIP; + } else if (testbmap(blkno)) { + for (dlp = duplist; dlp; dlp = dlp->next) { + if (dlp->dup != blkno) + continue; + dlp->dup = duplist->dup; + dlp = duplist; + duplist = duplist->next; + free((char *)dlp); + break; + } + if (dlp == 0) { + clrbmap(blkno); + n_blks--; + } + } + } + return (res); +} diff --git a/sbin/fsck/pass5.c b/sbin/fsck/pass5.c new file mode 100644 index 000000000000..2e98f96b5ca8 --- /dev/null +++ b/sbin/fsck/pass5.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +pass5() +{ + int c, blk, frags, basesize, sumsize, mapsize, savednrpos; + register struct fs *fs = &sblock; + register struct cg *cg = &cgrp; + daddr_t dbase, dmax; + register daddr_t d; + register long i, j; + struct csum *cs; + struct csum cstotal; + struct inodesc idesc[3]; + char buf[MAXBSIZE]; + register struct cg *newcg = (struct cg *)buf; + struct ocg *ocg = (struct ocg *)buf; + + bzero((char *)newcg, (size_t)fs->fs_cgsize); + newcg->cg_niblk = fs->fs_ipg; + if (cvtlevel > 3) { + if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) { + if (preen) + pwarn("DELETING CLUSTERING MAPS\n"); + if (preen || reply("DELETE CLUSTERING MAPS")) { + fs->fs_contigsumsize = 0; + doinglevel1 = 1; + sbdirty(); + } + } + if (fs->fs_maxcontig > 1) { + char *doit = 0; + + if (fs->fs_contigsumsize < 1) { + doit = "CREAT"; + } else if (fs->fs_contigsumsize < fs->fs_maxcontig && + fs->fs_contigsumsize < FS_MAXCONTIG) { + doit = "EXPAND"; + } + if (doit) { + i = fs->fs_contigsumsize; + fs->fs_contigsumsize = + MIN(fs->fs_maxcontig, FS_MAXCONTIG); + if (CGSIZE(fs) > fs->fs_bsize) { + pwarn("CANNOT %s CLUSTER MAPS\n", doit); + fs->fs_contigsumsize = i; + } else if (preen || + reply("CREATE CLUSTER MAPS")) { + if (preen) + pwarn("%sING CLUSTER MAPS\n", + doit); + fs->fs_cgsize = + fragroundup(fs, CGSIZE(fs)); + doinglevel1 = 1; + sbdirty(); + } + } + } + } + switch ((int)fs->fs_postblformat) { + + case FS_42POSTBLFMT: + basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link); + sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]); + mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] - + (u_char *)&ocg->cg_iused[0]; + ocg->cg_magic = CG_MAGIC; + savednrpos = fs->fs_nrpos; + fs->fs_nrpos = 8; + break; + + case FS_DYNAMICPOSTBLFMT: + newcg->cg_btotoff = + &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + newcg->cg_boff = + newcg->cg_btotoff + fs->fs_cpg * sizeof(long); + newcg->cg_iusedoff = newcg->cg_boff + + fs->fs_cpg * fs->fs_nrpos * sizeof(short); + newcg->cg_freeoff = + newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY); + if (fs->fs_contigsumsize <= 0) { + newcg->cg_nextfreeoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY); + } else { + newcg->cg_clustersumoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) - + sizeof(long); + newcg->cg_clustersumoff = + roundup(newcg->cg_clustersumoff, sizeof(long)); + newcg->cg_clusteroff = newcg->cg_clustersumoff + + (fs->fs_contigsumsize + 1) * sizeof(long); + newcg->cg_nextfreeoff = newcg->cg_clusteroff + + howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY); + } + newcg->cg_magic = CG_MAGIC; + basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; + mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; + break; + + default: + errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n", + fs->fs_postblformat); + } + bzero((char *)&idesc[0], sizeof idesc); + for (i = 0; i < 3; i++) { + idesc[i].id_type = ADDR; + if (doinglevel2) + idesc[i].id_fix = FIX; + } + bzero((char *)&cstotal, sizeof(struct csum)); + j = blknum(fs, fs->fs_size + fs->fs_frag - 1); + for (i = fs->fs_size; i < j; i++) + setbmap(i); + for (c = 0; c < fs->fs_ncg; c++) { + getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize); + if (!cg_chkmagic(cg)) + pfatal("CG %d: BAD MAGIC NUMBER\n", c); + dbase = cgbase(fs, c); + dmax = dbase + fs->fs_fpg; + if (dmax > fs->fs_size) + dmax = fs->fs_size; + newcg->cg_time = cg->cg_time; + newcg->cg_cgx = c; + if (c == fs->fs_ncg - 1) + newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg; + else + newcg->cg_ncyl = fs->fs_cpg; + newcg->cg_ndblk = dmax - dbase; + if (fs->fs_contigsumsize > 0) + newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag; + newcg->cg_cs.cs_ndir = 0; + newcg->cg_cs.cs_nffree = 0; + newcg->cg_cs.cs_nbfree = 0; + newcg->cg_cs.cs_nifree = fs->fs_ipg; + if (cg->cg_rotor < newcg->cg_ndblk) + newcg->cg_rotor = cg->cg_rotor; + else + newcg->cg_rotor = 0; + if (cg->cg_frotor < newcg->cg_ndblk) + newcg->cg_frotor = cg->cg_frotor; + else + newcg->cg_frotor = 0; + if (cg->cg_irotor < newcg->cg_niblk) + newcg->cg_irotor = cg->cg_irotor; + else + newcg->cg_irotor = 0; + bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum); + bzero((char *)&cg_blktot(newcg)[0], + (size_t)(sumsize + mapsize)); + if (fs->fs_postblformat == FS_42POSTBLFMT) + ocg->cg_magic = CG_MAGIC; + j = fs->fs_ipg * c; + for (i = 0; i < fs->fs_ipg; j++, i++) { + switch (statemap[j]) { + + case USTATE: + break; + + case DSTATE: + case DCLEAR: + case DFOUND: + newcg->cg_cs.cs_ndir++; + /* fall through */ + + case FSTATE: + case FCLEAR: + newcg->cg_cs.cs_nifree--; + setbit(cg_inosused(newcg), i); + break; + + default: + if (j < ROOTINO) + break; + errexit("BAD STATE %d FOR INODE I=%d", + statemap[j], j); + } + } + if (c == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(newcg), i); + newcg->cg_cs.cs_nifree--; + } + for (i = 0, d = dbase; + d < dmax; + d += fs->fs_frag, i += fs->fs_frag) { + frags = 0; + for (j = 0; j < fs->fs_frag; j++) { + if (testbmap(d + j)) + continue; + setbit(cg_blksfree(newcg), i + j); + frags++; + } + if (frags == fs->fs_frag) { + newcg->cg_cs.cs_nbfree++; + j = cbtocylno(fs, i); + cg_blktot(newcg)[j]++; + cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++; + if (fs->fs_contigsumsize > 0) + setbit(cg_clustersfree(newcg), + i / fs->fs_frag); + } else if (frags > 0) { + newcg->cg_cs.cs_nffree += frags; + blk = blkmap(fs, cg_blksfree(newcg), i); + ffs_fragacct(fs, blk, newcg->cg_frsum, 1); + } + } + if (fs->fs_contigsumsize > 0) { + long *sump = cg_clustersum(newcg); + u_char *mapp = cg_clustersfree(newcg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < newcg->cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + } + } + cstotal.cs_nffree += newcg->cg_cs.cs_nffree; + cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree; + cstotal.cs_nifree += newcg->cg_cs.cs_nifree; + cstotal.cs_ndir += newcg->cg_cs.cs_ndir; + cs = &fs->fs_cs(fs, c); + if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 && + dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs); + sbdirty(); + } + if (doinglevel1) { + bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize); + cgdirty(); + continue; + } + if (bcmp(cg_inosused(newcg), + cg_inosused(cg), mapsize) != 0 && + dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { + bcopy(cg_inosused(newcg), cg_inosused(cg), + (size_t)mapsize); + cgdirty(); + } + if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 || + bcmp((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], sumsize) != 0) && + dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { + bcopy((char *)newcg, (char *)cg, (size_t)basesize); + bcopy((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], (size_t)sumsize); + cgdirty(); + } + } + if (fs->fs_postblformat == FS_42POSTBLFMT) + fs->fs_nrpos = savednrpos; + if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0 + && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs); + fs->fs_ronly = 0; + fs->fs_fmod = 0; + sbdirty(); + } +} diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c new file mode 100644 index 000000000000..005a65d97989 --- /dev/null +++ b/sbin/fsck/preen.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char *rawname(), *unrawname(), *blockcheck(); + +struct part { + struct part *next; /* forward link of partitions on disk */ + char *name; /* device name */ + char *fsname; /* mounted filesystem name */ + long auxdata; /* auxillary data for application */ +} *badlist, **badnext = &badlist; + +struct disk { + char *name; /* disk base name */ + struct disk *next; /* forward link for list of disks */ + struct part *part; /* head of list of partitions on disk */ + int pid; /* If != 0, pid of proc working on */ +} *disks; + +int nrun, ndisks; +char hotroot; + +checkfstab(preen, maxrun, docheck, chkit) + int preen, maxrun; + int (*docheck)(), (*chkit)(); +{ + register struct fstab *fsp; + register struct disk *dk, *nextdisk; + register struct part *pt; + int ret, pid, retcode, passno, sumstatus, status; + long auxdata; + char *name; + + sumstatus = 0; + for (passno = 1; passno <= 2; passno++) { + if (setfsent() == 0) { + fprintf(stderr, "Can't open checklist file: %s\n", + _PATH_FSTAB); + return (8); + } + while ((fsp = getfsent()) != 0) { + if ((auxdata = (*docheck)(fsp)) == 0) + continue; + if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { + if (name = blockcheck(fsp->fs_spec)) { + if (sumstatus = (*chkit)(name, + fsp->fs_file, auxdata, 0)) + return (sumstatus); + } else if (preen) + return (8); + } else if (passno == 2 && fsp->fs_passno > 1) { + if ((name = blockcheck(fsp->fs_spec)) == NULL) { + fprintf(stderr, "BAD DISK NAME %s\n", + fsp->fs_spec); + sumstatus |= 8; + continue; + } + addpart(name, fsp->fs_file, auxdata); + } + } + if (preen == 0) + return (0); + } + if (preen) { + if (maxrun == 0) + maxrun = ndisks; + if (maxrun > ndisks) + maxrun = ndisks; + nextdisk = disks; + for (passno = 0; passno < maxrun; ++passno) { + while (ret = startdisk(nextdisk, chkit) && nrun > 0) + sleep(10); + if (ret) + return (ret); + nextdisk = nextdisk->next; + } + while ((pid = wait(&status)) != -1) { + for (dk = disks; dk; dk = dk->next) + if (dk->pid == pid) + break; + if (dk == 0) { + printf("Unknown pid %d\n", pid); + continue; + } + if (WIFEXITED(status)) + retcode = WEXITSTATUS(status); + else + retcode = 0; + if (WIFSIGNALED(status)) { + printf("%s (%s): EXITED WITH SIGNAL %d\n", + dk->part->name, dk->part->fsname, + WTERMSIG(status)); + retcode = 8; + } + if (retcode != 0) { + sumstatus |= retcode; + *badnext = dk->part; + badnext = &dk->part->next; + dk->part = dk->part->next; + *badnext = NULL; + } else + dk->part = dk->part->next; + dk->pid = 0; + nrun--; + if (dk->part == NULL) + ndisks--; + + if (nextdisk == NULL) { + if (dk->part) { + while (ret = startdisk(dk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } else if (nrun < maxrun && nrun < ndisks) { + for ( ;; ) { + if ((nextdisk = nextdisk->next) == NULL) + nextdisk = disks; + if (nextdisk->part != NULL && + nextdisk->pid == 0) + break; + } + while (ret = startdisk(nextdisk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } + } + if (sumstatus) { + if (badlist == 0) + return (sumstatus); + fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", + badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); + for (pt = badlist; pt; pt = pt->next) + fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname, + pt->next ? ", " : "\n"); + return (sumstatus); + } + (void)endfsent(); + return (0); +} + +struct disk * +finddisk(name) + char *name; +{ + register struct disk *dk, **dkp; + register char *p; + size_t len; + + for (p = name + strlen(name) - 1; p >= name; --p) + if (isdigit(*p)) { + len = p - name + 1; + break; + } + if (p < name) + len = strlen(name); + + for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { + if (strncmp(dk->name, name, len) == 0 && + dk->name[len] == 0) + return (dk); + } + if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + dk = *dkp; + if ((dk->name = malloc(len + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strncpy(dk->name, name, len); + dk->name[len] = '\0'; + dk->part = NULL; + dk->next = NULL; + dk->pid = 0; + ndisks++; + return (dk); +} + +addpart(name, fsname, auxdata) + char *name, *fsname; + long auxdata; +{ + struct disk *dk = finddisk(name); + register struct part *pt, **ppt = &dk->part; + + for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) + if (strcmp(pt->name, name) == 0) { + printf("%s in fstab more than once!\n", name); + return; + } + if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + pt = *ppt; + if ((pt->name = malloc(strlen(name) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->name, name); + if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->fsname, fsname); + pt->next = NULL; + pt->auxdata = auxdata; +} + +startdisk(dk, checkit) + register struct disk *dk; + int (*checkit)(); +{ + register struct part *pt = dk->part; + + dk->pid = fork(); + if (dk->pid < 0) { + perror("fork"); + return (8); + } + if (dk->pid == 0) + exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1)); + nrun++; + return (0); +} + +char * +blockcheck(name) + char *name; +{ + struct stat stslash, stblock, stchar; + char *raw; + int retried = 0; + + hotroot = 0; + if (stat("/", &stslash) < 0) { + perror("/"); + printf("Can't stat root\n"); + return (0); + } +retry: + if (stat(name, &stblock) < 0) { + perror(name); + printf("Can't stat %s\n", name); + return (0); + } + if ((stblock.st_mode & S_IFMT) == S_IFBLK) { + if (stslash.st_dev == stblock.st_rdev) + hotroot++; + raw = rawname(name); + if (stat(raw, &stchar) < 0) { + perror(raw); + printf("Can't stat %s\n", raw); + return (name); + } + if ((stchar.st_mode & S_IFMT) == S_IFCHR) { + return (raw); + } else { + printf("%s is not a character device\n", raw); + return (name); + } + } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { + name = unrawname(name); + retried++; + goto retry; + } + printf("Can't make sense out of name %s\n", name); + return (0); +} + +char * +unrawname(name) + char *name; +{ + char *dp; + struct stat stb; + + if ((dp = rindex(name, '/')) == 0) + return (name); + if (stat(name, &stb) < 0) + return (name); + if ((stb.st_mode & S_IFMT) != S_IFCHR) + return (name); + if (dp[1] != 'r') + return (name); + (void)strcpy(&dp[1], &dp[2]); + return (name); +} + +char * +rawname(name) + char *name; +{ + static char rawbuf[32]; + char *dp; + + if ((dp = rindex(name, '/')) == 0) + return (0); + *dp = 0; + (void)strcpy(rawbuf, name); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, &dp[1]); + return (rawbuf); +} diff --git a/sbin/fsck/setup.c b/sbin/fsck/setup.c new file mode 100644 index 000000000000..a32b23cc294d --- /dev/null +++ b/sbin/fsck/setup.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94"; +#endif /* not lint */ + +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +struct disklabel *getdisklabel(); + +setup(dev) + char *dev; +{ + long cg, size, asked, i, j; + long bmapsize; + struct disklabel *lp; + off_t sizepb; + struct stat statb; + struct fs proto; + + havesb = 0; + fswritefd = -1; + if (stat(dev, &statb) < 0) { + printf("Can't stat %s: %s\n", dev, strerror(errno)); + return (0); + } + if ((statb.st_mode & S_IFMT) != S_IFCHR) { + pfatal("%s is not a character device", dev); + if (reply("CONTINUE") == 0) + return (0); + } + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + printf("Can't open %s: %s\n", dev, strerror(errno)); + return (0); + } + if (preen == 0) + printf("** %s", dev); + if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) { + fswritefd = -1; + if (preen) + pfatal("NO WRITE ACCESS"); + printf(" (NO WRITE)"); + } + if (preen == 0) + printf("\n"); + fsmodified = 0; + lfdir = 0; + initbarea(&sblk); + initbarea(&asblk); + sblk.b_un.b_buf = malloc(SBSIZE); + asblk.b_un.b_buf = malloc(SBSIZE); + if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) + errexit("cannot allocate space for superblock\n"); + if (lp = getdisklabel((char *)NULL, fsreadfd)) + dev_bsize = secsize = lp->d_secsize; + else + dev_bsize = secsize = DEV_BSIZE; + /* + * Read in the superblock, looking for alternates if necessary + */ + if (readsb(1) == 0) { + if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) + return(0); + if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) + return (0); + for (cg = 0; cg < proto.fs_ncg; cg++) { + bflag = fsbtodb(&proto, cgsblock(&proto, cg)); + if (readsb(0) != 0) + break; + } + if (cg >= proto.fs_ncg) { + printf("%s %s\n%s %s\n%s %s\n", + "SEARCH FOR ALTERNATE SUPER-BLOCK", + "FAILED. YOU MUST USE THE", + "-b OPTION TO FSCK TO SPECIFY THE", + "LOCATION OF AN ALTERNATE", + "SUPER-BLOCK TO SUPPLY NEEDED", + "INFORMATION; SEE fsck(8)."); + return(0); + } + pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); + } + maxfsblock = sblock.fs_size; + maxino = sblock.fs_ncg * sblock.fs_ipg; + /* + * Check and potentially fix certain fields in the super block. + */ + if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { + pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_optim = FS_OPTTIME; + sbdirty(); + } + } + if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { + pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", + sblock.fs_minfree); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_minfree = 10; + sbdirty(); + } + } + if (sblock.fs_interleave < 1 || + sblock.fs_interleave > sblock.fs_nsect) { + pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK", + sblock.fs_interleave); + sblock.fs_interleave = 1; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_npsect < sblock.fs_nsect || + sblock.fs_npsect > sblock.fs_nsect*2) { + pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK", + sblock.fs_npsect); + sblock.fs_npsect = sblock.fs_nsect; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_inodefmt >= FS_44INODEFMT) { + newinofmt = 1; + } else { + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + newinofmt = 0; + } + /* + * Convert to new inode format. + */ + if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) { + if (preen) + pwarn("CONVERTING TO NEW INODE FORMAT\n"); + else if (!reply("CONVERT TO NEW INODE FORMAT")) + return(0); + doinglevel2++; + sblock.fs_inodefmt = FS_44INODEFMT; + sizepb = sblock.fs_bsize; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + sblock.fs_maxsymlinklen = MAXSYMLINKLEN; + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + sbdirty(); + dirty(&asblk); + } + /* + * Convert to new cylinder group format. + */ + if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) { + if (preen) + pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n"); + else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT")) + return(0); + doinglevel1++; + sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; + sblock.fs_nrpos = 8; + sblock.fs_postbloff = + (char *)(&sblock.fs_opostbl[0][0]) - + (char *)(&sblock.fs_link); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + sblock.fs_cgsize = + fragroundup(&sblock, CGSIZE(&sblock)); + sbdirty(); + dirty(&asblk); + } + if (asblk.b_dirty) { + bcopy((char *)&sblock, (char *)&altsblock, + (size_t)sblock.fs_sbsize); + flush(fswritefd, &asblk); + } + /* + * read in the summary info. + */ + asked = 0; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + size = sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize; + sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); + if (bread(fsreadfd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + size) != 0 && !asked) { + pfatal("BAD SUMMARY INFORMATION"); + if (reply("CONTINUE") == 0) + errexit(""); + asked++; + } + } + /* + * allocate and initialize the necessary maps + */ + bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short)); + blockmap = calloc((unsigned)bmapsize, sizeof (char)); + if (blockmap == NULL) { + printf("cannot alloc %u bytes for blockmap\n", + (unsigned)bmapsize); + goto badsb; + } + statemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (statemap == NULL) { + printf("cannot alloc %u bytes for statemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + typemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (typemap == NULL) { + printf("cannot alloc %u bytes for typemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short)); + if (lncntp == NULL) { + printf("cannot alloc %u bytes for lncntp\n", + (unsigned)(maxino + 1) * sizeof(short)); + goto badsb; + } + numdirs = sblock.fs_cstotal.cs_ndir; + inplast = 0; + listmax = numdirs + 10; + inpsort = (struct inoinfo **)calloc((unsigned)listmax, + sizeof(struct inoinfo *)); + inphead = (struct inoinfo **)calloc((unsigned)numdirs, + sizeof(struct inoinfo *)); + if (inpsort == NULL || inphead == NULL) { + printf("cannot alloc %u bytes for inphead\n", + (unsigned)numdirs * sizeof(struct inoinfo *)); + goto badsb; + } + bufinit(); + return (1); + +badsb: + ckfini(); + return (0); +} + +/* + * Read in the super block and its summary info. + */ +readsb(listerr) + int listerr; +{ + daddr_t super = bflag ? bflag : SBOFF / dev_bsize; + + if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0) + return (0); + sblk.b_bno = super; + sblk.b_size = SBSIZE; + /* + * run a few consistency checks of the super block + */ + if (sblock.fs_magic != FS_MAGIC) + { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); } + if (sblock.fs_ncg < 1) + { badsb(listerr, "NCG OUT OF RANGE"); return (0); } + if (sblock.fs_cpg < 1) + { badsb(listerr, "CPG OUT OF RANGE"); return (0); } + if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || + (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) + { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); } + if (sblock.fs_sbsize > SBSIZE) + { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); } + /* + * Compute block size that the filesystem is based on, + * according to fsbtodb, and adjust superblock block number + * so we can tell if this is an alternate later. + */ + super *= dev_bsize; + dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); + sblk.b_bno = super / dev_bsize; + if (bflag) { + havesb = 1; + return (1); + } + /* + * Set all possible fields that could differ, then do check + * of whole super block against an alternate super block. + * When an alternate super-block is specified this check is skipped. + */ + getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); + if (asblk.b_errs) + return (0); + altsblock.fs_link = sblock.fs_link; + altsblock.fs_rlink = sblock.fs_rlink; + altsblock.fs_time = sblock.fs_time; + altsblock.fs_cstotal = sblock.fs_cstotal; + altsblock.fs_cgrotor = sblock.fs_cgrotor; + altsblock.fs_fmod = sblock.fs_fmod; + altsblock.fs_clean = sblock.fs_clean; + altsblock.fs_ronly = sblock.fs_ronly; + altsblock.fs_flags = sblock.fs_flags; + altsblock.fs_maxcontig = sblock.fs_maxcontig; + altsblock.fs_minfree = sblock.fs_minfree; + altsblock.fs_optim = sblock.fs_optim; + altsblock.fs_rotdelay = sblock.fs_rotdelay; + altsblock.fs_maxbpg = sblock.fs_maxbpg; + bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp, + sizeof sblock.fs_csp); + bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt, + sizeof sblock.fs_fsmnt); + bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon, + sizeof sblock.fs_sparecon); + /* + * The following should not have to be copied. + */ + altsblock.fs_fsbtodb = sblock.fs_fsbtodb; + altsblock.fs_interleave = sblock.fs_interleave; + altsblock.fs_npsect = sblock.fs_npsect; + altsblock.fs_nrpos = sblock.fs_nrpos; + altsblock.fs_qbmask = sblock.fs_qbmask; + altsblock.fs_qfmask = sblock.fs_qfmask; + altsblock.fs_state = sblock.fs_state; + altsblock.fs_maxfilesize = sblock.fs_maxfilesize; + if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) { + badsb(listerr, + "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); + return (0); + } + havesb = 1; + return (1); +} + +badsb(listerr, s) + int listerr; + char *s; +{ + + if (!listerr) + return; + if (preen) + printf("%s: ", cdevname); + pfatal("BAD SUPER BLOCK: %s\n", s); +} + +/* + * Calculate a prototype superblock based on information in the disk label. + * When done the cgsblock macro can be calculated and the fs_ncg field + * can be used. Do NOT attempt to use other macros without verifying that + * their needed information is available! + */ +calcsb(dev, devfd, fs) + char *dev; + int devfd; + register struct fs *fs; +{ + register struct disklabel *lp; + register struct partition *pp; + register char *cp; + int i; + + cp = index(dev, '\0') - 1; + if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) { + pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev); + return (0); + } + lp = getdisklabel(dev, devfd); + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_fstype != FS_BSDFFS) { + pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n", + dev, pp->p_fstype < FSMAXTYPES ? + fstypenames[pp->p_fstype] : "unknown"); + return (0); + } + bzero((char *)fs, sizeof(struct fs)); + fs->fs_fsize = pp->p_fsize; + fs->fs_frag = pp->p_frag; + fs->fs_cpg = pp->p_cpg; + fs->fs_size = pp->p_size; + fs->fs_ntrak = lp->d_ntracks; + fs->fs_nsect = lp->d_nsectors; + fs->fs_spc = lp->d_secpercyl; + fs->fs_nspf = fs->fs_fsize / lp->d_secsize; + fs->fs_sblkno = roundup( + howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize), + fs->fs_frag); + fs->fs_cgmask = 0xffffffff; + for (i = fs->fs_ntrak; i > 1; i >>= 1) + fs->fs_cgmask <<= 1; + if (!POWEROF2(fs->fs_ntrak)) + fs->fs_cgmask <<= 1; + fs->fs_cgoffset = roundup( + howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag); + fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs); + fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg); + for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1) + fs->fs_fsbtodb++; + dev_bsize = lp->d_secsize; + return (1); +} + +struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { + if (s == NULL) + return ((struct disklabel *)NULL); + pwarn("ioctl (GCINFO): %s\n", strerror(errno)); + errexit("%s: can't read disk label\n", s); + } + return (&lab); +} diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c new file mode 100644 index 000000000000..64a4cac19ab3 --- /dev/null +++ b/sbin/fsck/utilities.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +long diskreads, totalreads; /* Disk cache statistics */ + +ftypeok(dp) + struct dinode *dp; +{ + switch (dp->di_mode & IFMT) { + + case IFDIR: + case IFREG: + case IFBLK: + case IFCHR: + case IFLNK: + case IFSOCK: + case IFIFO: + return (1); + + default: + if (debug) + printf("bad file type 0%o\n", dp->di_mode); + return (0); + } +} + +reply(question) + char *question; +{ + int persevere; + char c; + + if (preen) + pfatal("INTERNAL ERROR: GOT TO reply()"); + persevere = !strcmp(question, "CONTINUE"); + printf("\n"); + if (!persevere && (nflag || fswritefd < 0)) { + printf("%s? no\n\n", question); + return (0); + } + if (yflag || (persevere && nflag)) { + printf("%s? yes\n\n", question); + return (1); + } + do { + printf("%s? [yn] ", question); + (void) fflush(stdout); + c = getc(stdin); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return (0); + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + printf("\n"); + if (c == 'y' || c == 'Y') + return (1); + return (0); +} + +/* + * Malloc buffers and set up cache. + */ +bufinit() +{ + register struct bufarea *bp; + long bufcnt, i; + char *bufp; + + pbp = pdirbp = (struct bufarea *)0; + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bufp == 0) + errexit("cannot allocate buffer pool\n"); + cgblk.b_un.b_buf = bufp; + initbarea(&cgblk); + bufhead.b_next = bufhead.b_prev = &bufhead; + bufcnt = MAXBUFSPACE / sblock.fs_bsize; + if (bufcnt < MINBUFS) + bufcnt = MINBUFS; + for (i = 0; i < bufcnt; i++) { + bp = (struct bufarea *)malloc(sizeof(struct bufarea)); + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bp == NULL || bufp == NULL) { + if (i >= MINBUFS) + break; + errexit("cannot allocate buffer pool\n"); + } + bp->b_un.b_buf = bufp; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + initbarea(bp); + } + bufhead.b_size = i; /* save number of buffers */ +} + +/* + * Manage a cache of directory blocks. + */ +struct bufarea * +getdatablk(blkno, size) + daddr_t blkno; + long size; +{ + register struct bufarea *bp; + + for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) + if (bp->b_bno == fsbtodb(&sblock, blkno)) + goto foundit; + for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) + if ((bp->b_flags & B_INUSE) == 0) + break; + if (bp == &bufhead) + errexit("deadlocked buffer pool\n"); + getblk(bp, blkno, size); + /* fall through */ +foundit: + totalreads++; + bp->b_prev->b_next = bp->b_next; + bp->b_next->b_prev = bp->b_prev; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + bp->b_flags |= B_INUSE; + return (bp); +} + +void +getblk(bp, blk, size) + register struct bufarea *bp; + daddr_t blk; + long size; +{ + daddr_t dblk; + + dblk = fsbtodb(&sblock, blk); + if (bp->b_bno != dblk) { + flush(fswritefd, bp); + diskreads++; + bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); + bp->b_bno = dblk; + bp->b_size = size; + } +} + +flush(fd, bp) + int fd; + register struct bufarea *bp; +{ + register int i, j; + + if (!bp->b_dirty) + return; + if (bp->b_errs != 0) + pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", + (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", + bp->b_bno); + bp->b_dirty = 0; + bp->b_errs = 0; + bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); + if (bp != &sblk) + return; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + bwrite(fswritefd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize); + } +} + +rwerror(mesg, blk) + char *mesg; + daddr_t blk; +{ + + if (preen == 0) + printf("\n"); + pfatal("CANNOT %s: BLK %ld", mesg, blk); + if (reply("CONTINUE") == 0) + errexit("Program terminated\n"); +} + +ckfini() +{ + register struct bufarea *bp, *nbp; + int cnt = 0; + + if (fswritefd < 0) { + (void)close(fsreadfd); + return; + } + flush(fswritefd, &sblk); + if (havesb && sblk.b_bno != SBOFF / dev_bsize && + !preen && reply("UPDATE STANDARD SUPERBLOCK")) { + sblk.b_bno = SBOFF / dev_bsize; + sbdirty(); + flush(fswritefd, &sblk); + } + flush(fswritefd, &cgblk); + free(cgblk.b_un.b_buf); + for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { + cnt++; + flush(fswritefd, bp); + nbp = bp->b_prev; + free(bp->b_un.b_buf); + free((char *)bp); + } + if (bufhead.b_size != cnt) + errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); + pbp = pdirbp = (struct bufarea *)0; + if (debug) + printf("cache missed %ld of %ld (%d%%)\n", diskreads, + totalreads, (int)(diskreads * 100 / totalreads)); + (void)close(fsreadfd); + (void)close(fswritefd); +} + +bread(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + char *cp; + int i, errs; + off_t offset; + + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (read(fd, buf, (int)size) == size) + return (0); + rwerror("READ", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + errs = 0; + bzero(buf, (size_t)size); + printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); + for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { + if (read(fd, cp, (int)secsize) != secsize) { + (void)lseek(fd, offset + i + secsize, 0); + if (secsize != dev_bsize && dev_bsize != 1) + printf(" %ld (%ld),", + (blk * dev_bsize + i) / secsize, + blk + i / dev_bsize); + else + printf(" %ld,", blk + i / dev_bsize); + errs++; + } + } + printf("\n"); + return (errs); +} + +bwrite(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + int i; + char *cp; + off_t offset; + + if (fd < 0) + return; + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (write(fd, buf, (int)size) == size) { + fsmodified = 1; + return; + } + rwerror("WRITE", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); + for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) + if (write(fd, cp, (int)dev_bsize) != dev_bsize) { + (void)lseek(fd, offset + i + dev_bsize, 0); + printf(" %ld,", blk + i / dev_bsize); + } + printf("\n"); + return; +} + +/* + * allocate a data block with the specified number of fragments + */ +allocblk(frags) + long frags; +{ + register int i, j, k; + + if (frags <= 0 || frags > sblock.fs_frag) + return (0); + for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (testbmap(i + j)) + continue; + for (k = 1; k < frags; k++) + if (testbmap(i + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + for (k = 0; k < frags; k++) + setbmap(i + j + k); + n_blks += frags; + return (i + j); + } + } + return (0); +} + +/* + * Free a previously allocated block + */ +freeblk(blkno, frags) + daddr_t blkno; + long frags; +{ + struct inodesc idesc; + + idesc.id_blkno = blkno; + idesc.id_numfrags = frags; + (void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +getpathname(namebuf, curdir, ino) + char *namebuf; + ino_t curdir, ino; +{ + int len; + register char *cp; + struct inodesc idesc; + static int busy = 0; + extern int findname(); + + if (curdir == ino && ino == ROOTINO) { + (void)strcpy(namebuf, "/"); + return; + } + if (busy || + (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { + (void)strcpy(namebuf, "?"); + return; + } + busy = 1; + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_fix = IGNORE; + cp = &namebuf[MAXPATHLEN - 1]; + *cp = '\0'; + if (curdir != ino) { + idesc.id_parent = curdir; + goto namelookup; + } + while (ino != ROOTINO) { + idesc.id_number = ino; + idesc.id_func = findino; + idesc.id_name = ".."; + if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) + break; + namelookup: + idesc.id_number = idesc.id_parent; + idesc.id_parent = ino; + idesc.id_func = findname; + idesc.id_name = namebuf; + if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) + break; + len = strlen(namebuf); + cp -= len; + bcopy(namebuf, cp, (size_t)len); + *--cp = '/'; + if (cp < &namebuf[MAXNAMLEN]) + break; + ino = idesc.id_number; + } + busy = 0; + if (ino != ROOTINO) + *--cp = '?'; + bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch() +{ + if (!doinglevel2) + ckfini(); + exit(12); +} + +/* + * When preening, allow a single quit to signal + * a special exit after filesystem checks complete + * so that reboot sequence may be interrupted. + */ +void +catchquit() +{ + extern returntosingle; + + printf("returning to single-user after filesystem check\n"); + returntosingle = 1; + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * Ignore a single quit signal; wait and flush just in case. + * Used by child processes in preen. + */ +void +voidquit() +{ + + sleep(1); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +dofix(idesc, msg) + register struct inodesc *idesc; + char *msg; +{ + + switch (idesc->id_fix) { + + case DONTKNOW: + if (idesc->id_type == DATA) + direrror(idesc->id_number, msg); + else + pwarn(msg); + if (preen) { + printf(" (SALVAGED)\n"); + idesc->id_fix = FIX; + return (ALTERED); + } + if (reply("SALVAGE") == 0) { + idesc->id_fix = NOFIX; + return (0); + } + idesc->id_fix = FIX; + return (ALTERED); + + case FIX: + return (ALTERED); + + case NOFIX: + case IGNORE: + return (0); + + default: + errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); + } + /* NOTREACHED */ +} + +/* VARARGS1 */ +errexit(s1, s2, s3, s4) + char *s1; +{ + printf(s1, s2, s3, s4); + exit(8); +} + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +/* VARARGS1 */ +pfatal(s, a1, a2, a3) + char *s; +{ + + if (preen) { + printf("%s: ", cdevname); + printf(s, a1, a2, a3); + printf("\n"); + printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", + cdevname); + exit(8); + } + printf(s, a1, a2, a3); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +/* VARARGS1 */ +pwarn(s, a1, a2, a3, a4, a5, a6) + char *s; +{ + + if (preen) + printf("%s: ", cdevname); + printf(s, a1, a2, a3, a4, a5, a6); +} + +#ifndef lint +/* + * Stub for routines from kernel. + */ +panic(s) + char *s; +{ + + pfatal("INTERNAL INCONSISTENCY:"); + errexit(s); +} +#endif diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile new file mode 100644 index 000000000000..224f6b2200e7 --- /dev/null +++ b/sbin/fsck_ffs/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= fsck +MAN8= fsck.0 +SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ + pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c +.PATH: ${.CURDIR}/../../sys/ufs/ffs + +.include diff --git a/sbin/fsck_ffs/SMM.doc/0.t b/sbin/fsck_ffs/SMM.doc/0.t new file mode 100644 index 000000000000..9de391eec1ef --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/0.t @@ -0,0 +1,150 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)0.t 8.1 (Berkeley) 6/8/93 +.\" +.if n .ND +.TL +Fsck \- The UNIX\(dg File System Check Program +.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program' +.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%' +.AU +Marshall Kirk McKusick +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA 94720 +.AU +T. J. Kowalski +.AI +Bell Laboratories +Murray Hill, New Jersey 07974 +.AB +.FS +\(dgUNIX is a trademark of Bell Laboratories. +.FE +.FS +This work was done under grants from +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. +.FE +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +File System Check Program (\fIfsck\fR) +is an interactive file system check and repair program. +.I Fsck +uses the redundant structural information in the +UNIX file system to perform several consistency checks. +If an inconsistency is detected, it is reported +to the operator, who may elect to fix or ignore +each inconsistency. +These inconsistencies result from the permanent interruption +of the file system updates, which are performed every +time a file is modified. +Unless there has been a hardware failure, +.I fsck +is able to repair corrupted file systems +using procedures based upon the order in which UNIX honors +these file system update requests. +.PP +The purpose of this document is to describe the normal updating +of the file system, +to discuss the possible causes of file system corruption, +and to present the corrective actions implemented +by +.I fsck. +Both the program and the interaction between the +program and the operator are described. +.sp 2 +.LP +Revised July 16, 1985 +.AE +.LP +.bp +.ce +.B "TABLE OF CONTENTS" +.LP +.sp 1 +.nf +.B "1. Introduction" +.LP +.sp .5v +.nf +.B "2. Overview of the file system +2.1. Superblock +2.2. Summary Information +2.3. Cylinder groups +2.4. Fragments +2.5. Updates to the file system +.LP +.sp .5v +.nf +.B "3. Fixing corrupted file systems +3.1. Detecting and correcting corruption +3.2. Super block checking +3.3. Free block checking +3.4. Checking the inode state +3.5. Inode links +3.6. Inode data size +3.7. Checking the data associated with an inode +3.8. File system connectivity +.LP +.sp .5v +.nf +.B Acknowledgements +.LP +.sp .5v +.nf +.B References +.LP +.sp .5v +.nf +.B "4. Appendix A +4.1. Conventions +4.2. Initialization +4.3. Phase 1 - Check Blocks and Sizes +4.4. Phase 1b - Rescan for more Dups +4.5. Phase 2 - Check Pathnames +4.6. Phase 3 - Check Connectivity +4.7. Phase 4 - Check Reference Counts +4.8. Phase 5 - Check Cyl groups +4.9. Cleanup +.ds RH Introduction +.bp diff --git a/sbin/fsck_ffs/SMM.doc/1.t b/sbin/fsck_ffs/SMM.doc/1.t new file mode 100644 index 000000000000..4d2f5357131b --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/1.t @@ -0,0 +1,83 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)1.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Introduction +.NH +Introduction +.PP +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization. This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +When a UNIX +operating system is brought up, a consistency +check of the file systems should always be performed. +This precautionary measure helps to insure +a reliable environment for file storage on disk. +If an inconsistency is discovered, +corrective action must be taken. +.I Fsck +runs in two modes. +Normally it is run non-interactively by the system after +a normal boot. +When running in this mode, +it will only make changes to the file system that are known +to always be correct. +If an unexpected inconsistency is found +.I fsck +will exit with a non-zero exit status, +leaving the system running single-user. +Typically the operator then runs +.I fsck +interactively. +When running in this mode, +each problem is listed followed by a suggested corrective action. +The operator must decide whether or not the suggested correction +should be made. +.PP +The purpose of this memo is to dispel the +mystique surrounding +file system inconsistencies. +It first describes the updating of the file system +(the calm before the storm) and +then describes file system corruption (the storm). +Finally, +the set of deterministic corrective actions +used by +.I fsck +(the Coast Guard +to the rescue) is presented. +.ds RH Overview of the File System diff --git a/sbin/fsck_ffs/SMM.doc/2.t b/sbin/fsck_ffs/SMM.doc/2.t new file mode 100644 index 000000000000..7d00ceaa4efd --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/2.t @@ -0,0 +1,265 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)2.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Overview of the file system +.NH +Overview of the file system +.PP +The file system is discussed in detail in [Mckusick84]; +this section gives a brief overview. +.NH 2 +Superblock +.PP +A file system is described by its +.I "super-block" . +The super-block is built when the file system is created (\c +.I newfs (8)) +and never changes. +The super-block +contains the basic parameters of the file system, +such as the number of data blocks it contains +and a count of the maximum number of files. +Because the super-block contains critical data, +.I newfs +replicates it to protect against catastrophic loss. +The +.I "default super block" +always resides at a fixed offset from the beginning +of the file system's disk partition. +The +.I "redundant super blocks" +are not referenced unless a head crash +or other hard disk error causes the default super-block +to be unusable. +The redundant blocks are sprinkled throughout the disk partition. +.PP +Within the file system are files. +Certain files are distinguished as directories and contain collections +of pointers to files that may themselves be directories. +Every file has a descriptor associated with it called an +.I "inode". +The inode contains information describing ownership of the file, +time stamps indicating modification and access times for the file, +and an array of indices pointing to the data blocks for the file. +In this section, +we assume that the first 12 blocks +of the file are directly referenced by values stored +in the inode structure itself\(dg. +.FS +\(dgThe actual number may vary from system to system, but is usually in +the range 5-13. +.FE +The inode structure may also contain references to indirect blocks +containing further data block indices. +In a file system with a 4096 byte block size, a singly indirect +block contains 1024 further block addresses, +a doubly indirect block contains 1024 addresses of further single indirect +blocks, +and a triply indirect block contains 1024 addresses of further doubly indirect +blocks (the triple indirect block is never needed in practice). +.PP +In order to create files with up to +2\(ua32 bytes, +using only two levels of indirection, +the minimum size of a file system block is 4096 bytes. +The size of file system blocks can be any power of two +greater than or equal to 4096. +The block size of the file system is maintained in the super-block, +so it is possible for file systems of different block sizes +to be accessible simultaneously on the same system. +The block size must be decided when +.I newfs +creates the file system; +the block size cannot be subsequently +changed without rebuilding the file system. +.NH 2 +Summary information +.PP +Associated with the super block is non replicated +.I "summary information" . +The summary information changes +as the file system is modified. +The summary information contains +the number of blocks, fragments, inodes and directories in the file system. +.NH 2 +Cylinder groups +.PP +The file system partitions the disk into one or more areas called +.I "cylinder groups". +A cylinder group is comprised of one or more consecutive +cylinders on a disk. +Each cylinder group includes inode slots for files, a +.I "block map" +describing available blocks in the cylinder group, +and summary information describing the usage of data blocks +within the cylinder group. +A fixed number of inodes is allocated for each cylinder group +when the file system is created. +The current policy is to allocate one inode for each 2048 +bytes of disk space; +this is expected to be far more inodes than will ever be needed. +.PP +All the cylinder group bookkeeping information could be +placed at the beginning of each cylinder group. +However if this approach were used, +all the redundant information would be on the top platter. +A single hardware failure that destroyed the top platter +could cause the loss of all copies of the redundant super-blocks. +Thus the cylinder group bookkeeping information +begins at a floating offset from the beginning of the cylinder group. +The offset for +the +.I "i+1" st +cylinder group is about one track further +from the beginning of the cylinder group +than it was for the +.I "i" th +cylinder group. +In this way, +the redundant +information spirals down into the pack; +any single track, cylinder, +or platter can be lost without losing all copies of the super-blocks. +Except for the first cylinder group, +the space between the beginning of the cylinder group +and the beginning of the cylinder group information stores data. +.NH 2 +Fragments +.PP +To avoid waste in storing small files, +the file system space allocator divides a single +file system block into one or more +.I "fragments". +The fragmentation of the file system is specified +when the file system is created; +each file system block can be optionally broken into +2, 4, or 8 addressable fragments. +The lower bound on the size of these fragments is constrained +by the disk sector size; +typically 512 bytes is the lower bound on fragment size. +The block map associated with each cylinder group +records the space availability at the fragment level. +Aligned fragments are examined +to determine block availability. +.PP +On a file system with a block size of 4096 bytes +and a fragment size of 1024 bytes, +a file is represented by zero or more 4096 byte blocks of data, +and possibly a single fragmented block. +If a file system block must be fragmented to obtain +space for a small amount of data, +the remainder of the block is made available for allocation +to other files. +For example, +consider an 11000 byte file stored on +a 4096/1024 byte file system. +This file uses two full size blocks and a 3072 byte fragment. +If no fragments with at least 3072 bytes +are available when the file is created, +a full size block is split yielding the necessary 3072 byte +fragment and an unused 1024 byte fragment. +This remaining fragment can be allocated to another file, as needed. +.NH 2 +Updates to the file system +.PP +Every working day hundreds of files +are created, modified, and removed. +Every time a file is modified, +the operating system performs a +series of file system updates. +These updates, when written on disk, yield a consistent file system. +The file system stages +all modifications of critical information; +modification can +either be completed or cleanly backed out after a crash. +Knowing the information that is first written to the file system, +deterministic procedures can be developed to +repair a corrupted file system. +To understand this process, +the order that the update +requests were being honored must first be understood. +.PP +When a user program does an operation to change the file system, +such as a +.I write , +the data to be written is copied into an internal +.I "in-core" +buffer in the kernel. +Normally, the disk update is handled asynchronously; +the user process is allowed to proceed even though +the data has not yet been written to the disk. +The data, +along with the inode information reflecting the change, +is eventually written out to disk. +The real disk write may not happen until long after the +.I write +system call has returned. +Thus at any given time, the file system, +as it resides on the disk, +lags the state of the file system represented by the in-core information. +.PP +The disk information is updated to reflect the in-core information +when the buffer is required for another use, +when a +.I sync (2) +is done (at 30 second intervals) by +.I "/etc/update" "(8)," +or by manual operator intervention with the +.I sync (8) +command. +If the system is halted without writing out the in-core information, +the file system on the disk will be in an inconsistent state. +.PP +If all updates are done asynchronously, several serious +inconsistencies can arise. +One inconsistency is that a block may be claimed by two inodes. +Such an inconsistency can occur when the system is halted before +the pointer to the block in the old inode has been cleared +in the copy of the old inode on the disk, +and after the pointer to the block in the new inode has been written out +to the copy of the new inode on the disk. +Here, +there is no deterministic method for deciding +which inode should really claim the block. +A similar problem can arise with a multiply claimed inode. +.PP +The problem with asynchronous inode updates +can be avoided by doing all inode deallocations synchronously. +Consequently, +inodes and indirect blocks are written to the disk synchronously +(\fIi.e.\fP the process blocks until the information is +really written to disk) +when they are being deallocated. +Similarly inodes are kept consistent by synchronously +deleting, adding, or changing directory entries. +.ds RH Fixing corrupted file systems diff --git a/sbin/fsck_ffs/SMM.doc/3.t b/sbin/fsck_ffs/SMM.doc/3.t new file mode 100644 index 000000000000..07b5431f3e59 --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/3.t @@ -0,0 +1,439 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)3.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Fixing corrupted file systems +.NH +Fixing corrupted file systems +.PP +A file system +can become corrupted in several ways. +The most common of these ways are +improper shutdown procedures +and hardware failures. +.PP +File systems may become corrupted during an +.I "unclean halt" . +This happens when proper shutdown +procedures are not observed, +physically write-protecting a mounted file system, +or a mounted file system is taken off-line. +The most common operator procedural failure is forgetting to +.I sync +the system before halting the CPU. +.PP +File systems may become further corrupted if proper startup +procedures are not observed, e.g., +not checking a file system for inconsistencies, +and not repairing inconsistencies. +Allowing a corrupted file system to be used (and, thus, to be modified +further) can be disastrous. +.PP +Any piece of hardware can fail at any time. +Failures +can be as subtle as a bad block +on a disk pack, or as blatant as a non-functional disk-controller. +.NH 2 +Detecting and correcting corruption +.PP +Normally +.I fsck +is run non-interactively. +In this mode it will only fix +corruptions that are expected to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this paper we assume that +.I fsck +is being run interactively, +and all possible errors can be encountered. +When an inconsistency is discovered in this mode, +.I fsck +reports the inconsistency for the operator to +chose a corrective action. +.PP +A quiescent\(dd +.FS +\(dd I.e., unmounted and not being written on. +.FE +file system may be checked for structural integrity +by performing consistency checks on the +redundant data intrinsic to a file system. +The redundant data is either read from +the file system, +or computed from other known values. +The file system +.B must +be in a quiescent state when +.I fsck +is run, +since +.I fsck +is a multi-pass program. +.PP +In the following sections, +we discuss methods to discover inconsistencies +and possible corrective actions +for the cylinder group blocks, the inodes, the indirect blocks, and +the data blocks containing directory entries. +.NH 2 +Super-block checking +.PP +The most commonly corrupted item in a file system +is the summary information +associated with the super-block. +The summary information is prone to corruption +because it is modified with every change to the file +system's blocks or inodes, +and is usually corrupted +after an unclean halt. +.PP +The super-block is checked for inconsistencies +involving file-system size, number of inodes, +free-block count, and the free-inode count. +The file-system size must be larger than the +number of blocks used by the super-block +and the number of blocks used by the list of inodes. +The file-system size and layout information +are the most critical pieces of information for +.I fsck . +While there is no way to actually check these sizes, +since they are statically determined by +.I newfs , +.I fsck +can check that these sizes are within reasonable bounds. +All other file system checks require that these sizes be correct. +If +.I fsck +detects corruption in the static parameters of the default super-block, +.I fsck +requests the operator to specify the location of an alternate super-block. +.NH 2 +Free block checking +.PP +.I Fsck +checks that all the blocks +marked as free in the cylinder group block maps +are not claimed by any files. +When all the blocks have been initially accounted for, +.I fsck +checks that +the number of free blocks +plus the number of blocks claimed by the inodes +equals the total number of blocks in the file system. +.PP +If anything is wrong with the block allocation maps, +.I fsck +will rebuild them, +based on the list it has computed of allocated blocks. +.PP +The summary information associated with the super-block +counts the total number of free blocks within the file system. +.I Fsck +compares this count to the +number of free blocks it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the summary information +by the actual free-block count. +.PP +The summary information +counts the total number of free inodes within the file system. +.I Fsck +compares this count to the number +of free inodes it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the +summary information by the actual free-inode count. +.NH 2 +Checking the inode state +.PP +An individual inode is not as likely to be corrupted as +the allocation information. +However, because of the great number of active inodes, +a few of the inodes are usually corrupted. +.PP +The list of inodes in the file system +is checked sequentially starting with inode 2 +(inode 0 marks unused inodes; +inode 1 is saved for future generations) +and progressing through the last inode in the file system. +The state of each inode is checked for +inconsistencies involving format and type, +link count, +duplicate blocks, +bad blocks, +and inode size. +.PP +Each inode contains a mode word. +This mode word describes the type and state of the inode. +Inodes must be one of six types: +regular inode, directory inode, symbolic link inode, +special block inode, special character inode, or socket inode. +Inodes may be found in one of three allocation states: +unallocated, allocated, and neither unallocated nor allocated. +This last state suggests an incorrectly formated inode. +An inode can get in this state if +bad data is written into the inode list. +The only possible corrective action is for +.I fsck +is to clear the inode. +.NH 2 +Inode links +.PP +Each inode counts the +total number of directory entries +linked to the inode. +.I Fsck +verifies the link count of each inode +by starting at the root of the file system, +and descending through the directory structure. +The actual link count for each inode +is calculated during the descent. +.PP +If the stored link count is non-zero and the actual +link count is zero, +then no directory entry appears for the inode. +If this happens, +.I fsck +will place the disconnected file in the +.I lost+found +directory. +If the stored and actual link counts are non-zero and unequal, +a directory entry may have been added or removed without the inode being +updated. +If this happens, +.I fsck +replaces the incorrect stored link count by the actual link count. +.PP +Each inode contains a list, +or pointers to +lists (indirect blocks), +of all the blocks claimed by the inode. +Since indirect blocks are owned by an inode, +inconsistencies in indirect blocks directly +affect the inode that owns it. +.PP +.I Fsck +compares each block number claimed by an inode +against a list of already allocated blocks. +If another inode already claims a block number, +then the block number is added to a list of +.I "duplicate blocks" . +Otherwise, the list of allocated blocks +is updated to include the block number. +.PP +If there are any duplicate blocks, +.I fsck +will perform a partial second +pass over the inode list +to find the inode of the duplicated block. +The second pass is needed, +since without examining the files associated with +these inodes for correct content, +not enough information is available +to determine which inode is corrupted and should be cleared. +If this condition does arise +(only hardware failure will cause it), +then the inode with the earliest +modify time is usually incorrect, +and should be cleared. +If this happens, +.I fsck +prompts the operator to clear both inodes. +The operator must decide which one should be kept +and which one should be cleared. +.PP +.I Fsck +checks the range of each block number claimed by an inode. +If the block number is +lower than the first data block in the file system, +or greater than the last data block, +then the block number is a +.I "bad block number" . +Many bad blocks in an inode are usually caused by +an indirect block that was not written to the file system, +a condition which can only occur if there has been a hardware failure. +If an inode contains bad block numbers, +.I fsck +prompts the operator to clear it. +.NH 2 +Inode data size +.PP +Each inode contains a count of the number of data blocks +that it contains. +The number of actual data blocks +is the sum of the allocated data blocks +and the indirect blocks. +.I Fsck +computes the actual number of data blocks +and compares that block count against +the actual number of blocks the inode claims. +If an inode contains an incorrect count +.I fsck +prompts the operator to fix it. +.PP +Each inode contains a thirty-two bit size field. +The size is the number of data bytes +in the file associated with the inode. +The consistency of the byte size field is roughly checked +by computing from the size field the maximum number of blocks +that should be associated with the inode, +and comparing that expected block count against +the actual number of blocks the inode claims. +.NH 2 +Checking the data associated with an inode +.PP +An inode can directly or indirectly +reference three kinds of data blocks. +All referenced blocks must be the same kind. +The three types of data blocks are: +plain data blocks, symbolic link data blocks, and directory data blocks. +Plain data blocks +contain the information stored in a file; +symbolic link data blocks +contain the path name stored in a link. +Directory data blocks contain directory entries. +.I Fsck +can only check the validity of directory data blocks. +.PP +Each directory data block is checked for +several types of inconsistencies. +These inconsistencies include +directory inode numbers pointing to unallocated inodes, +directory inode numbers that are greater than +the number of inodes in the file system, +incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'', +and directories that are not attached to the file system. +If the inode number in a directory data block +references an unallocated inode, +then +.I fsck +will remove that directory entry. +Again, +this condition can only arise when there has been a hardware failure. +.PP +If a directory entry inode number references +outside the inode list, then +.I fsck +will remove that directory entry. +This condition occurs if bad data is written into a directory data block. +.PP +The directory inode number entry for ``\fB.\fP'' +must be the first entry in the directory data block. +The inode number for ``\fB.\fP'' +must reference itself; +e.g., it must equal the inode number +for the directory data block. +The directory inode number entry +for ``\fB..\fP'' must be +the second entry in the directory data block. +Its value must equal the inode number for the +parent of the directory entry +(or the inode number of the directory +data block if the directory is the +root directory). +If the directory inode numbers are +incorrect, +.I fsck +will replace them with the correct values. +If there are multiple hard links to a directory, +the first one encountered is considered the real parent +to which ``\fB..\fP'' should point; +\fIfsck\fP recommends deletion for the subsequently discovered names. +.NH 2 +File system connectivity +.PP +.I Fsck +checks the general connectivity of the file system. +If directories are not linked into the file system, then +.I fsck +links the directory back into the file system in the +.I lost+found +directory. +This condition only occurs when there has been a hardware failure. +.ds RH "References" +.SH +\s+2Acknowledgements\s0 +.PP +I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie +for their suggestions and help in implementing the new file system. +Thanks also to Robert Henry for his editorial input to +get this document together. +Finally we thank our sponsors, +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983) +.PP +I would like to thank Larry A. Wehr for advice that lead +to the first version of +.I fsck +and Rick B. Brandt for adapting +.I fsck +to +UNIX/TS. (T. Kowalski, July 1979) +.sp 2 +.SH +\s+2References\s0 +.LP +.IP [Dolotta78] 20 +Dolotta, T. A., and Olsson, S. B. eds., +.I "UNIX User's Manual, Edition 1.1\^" , +January 1978. +.IP [Joy83] 20 +Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D. +4.2BSD System Manual, +.I "University of California at Berkeley" , +.I "Computer Systems Research Group Technical Report" +#4, 1982. +.IP [McKusick84] 20 +McKusick, M., Joy, W., Leffler, S., and Fabry, R. +A Fast File System for UNIX, +\fIACM Transactions on Computer Systems 2\fP, 3. +pp. 181-197, August 1984. +.IP [Ritchie78] 20 +Ritchie, D. M., and Thompson, K., +The UNIX Time-Sharing System, +.I "The Bell System Technical Journal" +.B 57 , +6 (July-August 1978, Part 2), pp. 1905-29. +.IP [Thompson78] 20 +Thompson, K., +UNIX Implementation, +.I "The Bell System Technical Journal\^" +.B 57 , +6 (July-August 1978, Part 2), pp. 1931-46. +.ds RH Appendix A \- Fsck Error Conditions +.bp diff --git a/sbin/fsck_ffs/SMM.doc/4.t b/sbin/fsck_ffs/SMM.doc/4.t new file mode 100644 index 000000000000..5ea8179fa904 --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/4.t @@ -0,0 +1,1424 @@ +.\" Copyright (c) 1982, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)4.t 8.1 (Berkeley) 6/5/93 +.\" +.ds RH Appendix A \- Fsck Error Conditions +.NH +Appendix A \- Fsck Error Conditions +.NH 2 +Conventions +.PP +.I Fsck +is +a multi-pass file system check program. +Each file system pass invokes a different Phase of the +.I fsck +program. +After the initial setup, +.I fsck +performs successive Phases over each file system, +checking blocks and sizes, +path-names, +connectivity, +reference counts, +and the map of free blocks, +(possibly rebuilding it), +and performs some cleanup. +.LP +Normally +.I fsck +is run non-interactively to +.I preen +the file systems after an unclean halt. +While preen'ing a file system, +it will only fix corruptions that are expected +to occur from an unclean halt. +These actions are a proper subset of the actions that +.I fsck +will take when it is running interactively. +Throughout this appendix many errors have several options +that the operator can take. +When an inconsistency is detected, +.I fsck +reports the error condition to the operator. +If a response is required, +.I fsck +prints a prompt message and +waits for a response. +When preen'ing most errors are fatal. +For those that are expected, +the response taken is noted. +This appendix explains the meaning of each error condition, +the possible responses, and the related error conditions. +.LP +The error conditions are organized by the +.I Phase +of the +.I fsck +program in which they can occur. +The error conditions that may occur +in more than one Phase +will be discussed in initialization. +.NH 2 +Initialization +.PP +Before a file system check can be performed, certain +tables have to be set up and certain files opened. +This section concerns itself with the opening of files and +the initialization of tables. +This section lists error conditions resulting from +command line options, +memory requests, +opening of files, +status of files, +file system size checks, +and creation of the scratch file. +All the initialization errors are fatal +when the file system is being preen'ed. +.sp +.LP +.B "\fIC\fP option?" +.br +\fIC\fP is not a legal option to +.I fsck ; +legal options are \-b, \-c, \-y, \-n, and \-p. +.I Fsck +terminates on this error condition. +See the +.I fsck (8) +manual entry for further detail. +.sp +.LP +.B "cannot alloc NNN bytes for blockmap" +.br +.B "cannot alloc NNN bytes for freemap" +.br +.B "cannot alloc NNN bytes for statemap" +.br +.B "cannot alloc NNN bytes for lncntp" +.br +.I Fsck 's +request for memory for its virtual +memory tables failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't open checklist file: \fIF\fP" +.br +The file system checklist file +\fIF\fP (usually +.I /etc/fstab ) +can not be opened for reading. +.I Fsck +terminates on this error condition. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't stat root" +.br +.I Fsck 's +request for statistics about the root directory ``/'' failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't stat \fIF\fP" +.br +.B "Can't make sense out of name \fIF\fP" +.br +.I Fsck 's +request for statistics about the file system \fIF\fP failed. +When running manually, +it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't open \fIF\fP" +.br +.I Fsck 's +request attempt to open the file system \fIF\fP failed. +When running manually, it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "\fIF\fP: (NO WRITE)" +.br +Either the \-n flag was specified or +.I fsck 's +attempt to open the file system \fIF\fP for writing failed. +When running manually, +all the diagnostics are printed out, +but no modifications are attempted to fix them. +.sp +.LP +.B "file is not a block or character device; OK" +.br +You have given +.I fsck +a regular file name by mistake. +Check the type of the file specified. +.LP +Possible responses to the OK prompt are: +.IP YES +ignore this error condition. +.IP NO +ignore this file system and continues checking +the next file system given. +.sp +.LP +.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock optimization parameter is neither OPT_TIME +nor OPT_SPACE. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The superblock is set to request optimization to minimize +running time of the system. +(If optimization to minimize disk space utilization is +desired, it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock minimum space percentage is greater than 99% +or less then 0%. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The minfree parameter is set to 10%. +(If some other percentage is desired, +it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The file system interleave is less than or equal to zero. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The interleave parameter is set to 1. +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The number of physical sectors per track is less than the number +of usable sectors per track. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The npsect parameter is set to the number of usable sectors per track. +.IP NO +ignore this error condition. +.sp +.LP +One of the following messages will appear: +.br +.B "MAGIC NUMBER WRONG" +.br +.B "NCG OUT OF RANGE" +.br +.B "CPG OUT OF RANGE" +.br +.B "NCYL DOES NOT JIVE WITH NCG*CPG" +.br +.B "SIZE PREPOSTEROUSLY LARGE" +.br +.B "TRASHED VALUES IN SUPER BLOCK" +.br +and will be followed by the message: +.br +.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP" +.br +.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE" +.br +.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)." +.br +The super block has been corrupted. +An alternative super block must be selected from among those +listed by +.I newfs +(8) when the file system was created. +For file systems with a blocksize less than 32K, +specifying \-b 32 is a good first choice. +.sp +.LP +.B "INTERNAL INCONSISTENCY: \fIM\fP" +.br +.I Fsck 's +has had an internal panic, whose message is specified as \fIM\fP. +This should never happen. +See a guru. +.sp +.LP +.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for moving to a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +Often, +however the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for reading a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +It will retry the read and print out the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be read. +If +.I fsck +ever tries to write back one of the blocks on which the read failed +it will print the message: +.br +.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK" +.br +where \fIN\fP indicates the sector that was written with zero's. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for writing a specified block number \fIB\fP +in the file system failed. +The disk is write-protected; +check the write protect lock on the drive. +If that is not the problem, see a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +The write operation will be retried with the failed blocks +indicated by the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be written. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "bad inode number DDD to ginode" +.br +An internal error has attempted to read non-existent inode \fIDDD\fP. +This error causes +.I fsck +to exit. +See a guru. +.NH 2 +Phase 1 \- Check Blocks and Sizes +.PP +This phase concerns itself with +the inode list. +This section lists error conditions resulting from +checking inode types, +setting up the zero-link-count table, +examining inode block numbers for bad or duplicate blocks, +checking inode size, +and checking inode format. +All errors in this phase except +.B "INCORRECT BLOCK COUNT" +and +.B "PARTIALLY TRUNCATED INODE" +are fatal if the file system is being preen'ed. +.sp +.LP +.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)" +.br +The mode word of the inode \fII\fP indicates that the inode is not a +special block inode, special character inode, socket inode, regular inode, +symbolic link, or directory inode. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +This will always invoke the UNALLOCATED error condition in Phase 2 +for each directory entry pointing to this inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)" +.br +.I Fsck +has found inode \fII\fP whose size is shorter than the number of +blocks allocated to it. +This condition should only occur if the system crashes while in the +midst of truncating a file. +When preen'ing the file system, +.I fsck +completes the truncation to the specified size. +.LP +Possible responses to SALVAGE are: +.IP YES +complete the truncation to the size specified in the inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "LINK COUNT TABLE OVERFLOW (CONTINUE)" +.br +An internal table for +.I fsck +containing allocated inodes with a link count of +zero cannot allocate more memory. +Increase the virtual memory for +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another allocated inode with a zero link count is found, +this error condition is repeated. +.IP NO +terminate the program. +.sp +.LP +.B "\fIB\fP BAD I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP with a number +lower than the number of the first data block in the file system or +greater than the number of the last block +in the file system. +This error condition may invoke the +.B "EXCESSIVE BAD BLKS" +error condition in Phase 1 (see next paragraph) if +inode \fII\fP has too many block numbers outside the file system range. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks with a number +lower than the number of the first data block in the file system or greater than +the number of last block in the file system associated with inode \fII\fP. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "BAD STATE DDD TO BLKERR" +.br +An internal error has scrambled +.I fsck 's +state map to have the impossible value \fIDDD\fP. +.I Fsck +exits immediately. +See a guru. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that is already claimed by +another inode. +This error condition may invoke the +.B "EXCESSIVE DUP BLKS" +error condition in Phase 1 if +inode \fII\fP has too many block numbers claimed by other inodes. +This error condition will always invoke Phase 1b and the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks claimed by other +inodes. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "DUP TABLE OVERFLOW (CONTINUE)" +.br +An internal table in +.I fsck +containing duplicate block numbers cannot allocate any more space. +Increase the amount of virtual memory available to +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another duplicate block is found, this error condition will repeat. +.IP NO +terminate the program. +.sp +.LP +.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)" +.br +Inode \fII\fP is neither allocated nor unallocated. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)" +.br +The block count for inode \fII\fP is \fIX\fP blocks, +but should be \fIY\fP blocks. +When preen'ing the count is corrected. +.LP +Possible responses to the CORRECT prompt are: +.IP YES +replace the block count of inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.NH 2 +Phase 1B: Rescan for More Dups +.PP +When a duplicate block is found in the file system, the file system is +rescanned to find the inode that previously claimed that block. +This section lists the error condition when the duplicate block is found. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that +is already claimed by another inode. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2. +You can determine which inodes have overlapping blocks by examining +this error condition and the DUP error condition in Phase 1. +.NH 2 +Phase 2 \- Check Pathnames +.PP +This phase concerns itself with removing directory entries +pointing to +error conditioned inodes +from Phase 1 and Phase 1b. +This section lists error conditions resulting from +root inode mode and status, +directory inode pointers in range, +and directory entries pointing to bad inodes, +and directory integrity checks. +All errors in this phase are fatal if the file system is being preen'ed, +except for directories not being a multiple of the blocks size +and extraneous hard links. +.sp +.LP +.B "ROOT INODE UNALLOCATED (ALLOCATE)" +.br +The root inode (usually inode number 2) has no allocate mode bits. +This should never happen. +.LP +Possible responses to the ALLOCATE prompt are: +.IP YES +allocate inode 2 as the root inode. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will exit. +.sp +.LP +.B "ROOT INODE NOT DIRECTORY (REALLOCATE)" +.br +The root inode (usually inode number 2) +is not directory inode type. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "FIX" +.LP +Possible responses to the FIX prompt are: +.IP YES +replace the root inode's type to be a directory. +If the root inode's data blocks are not directory blocks, +many error conditions will be produced. +.IP NO +terminate the program. +.sp +.LP +.B "DUPS/BAD IN ROOT INODE (REALLOCATE)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks in the root inode (usually inode number 2) for the file system. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "CONTINUE" . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the +.B "DUPS/BAD" +error condition in the root inode and +attempt to continue to run the file system check. +If the root inode is not correct, +then this may result in many other error conditions. +.IP NO +terminate the program. +.sp +.LP +.B "NAME TOO LONG \fIF\fP" +.br +An excessively long path name has been found. +This usually indicates loops in the file system name space. +This can occur if the super user has made circular links to directories. +The offending links must be removed (by a guru). +.sp +.LP +.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has an inode number \fII\fP that is greater than +the end of the inode list. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +A directory or file entry \fIF\fP points to an unallocated inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +Phase 1 or Phase 1b have found duplicate blocks or bad blocks +associated with directory or file entry \fIF\fP, inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has a size \fIS\fP that is zero. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed; +this will always invoke the BAD/DUP error condition in Phase 4. +.IP NO +ignore this error condition. +.sp +.LP +.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fIF\fP has been found whose size \fIS\fP +is less than the minimum size directory. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the FIX prompt are: +.IP YES +increase the size of the directory to the minimum directory size. +.IP NO +ignore this directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP. +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)" +.br +A directory with an inconsistent internal state has been found. +.LP +Possible responses to the FIX prompt are: +.IP YES +throw away all entries up to the next directory boundary (usually 512-byte) +boundary. +This drastic action can throw away up to 42 entries, +and should be taken only after other recovery efforts have failed. +.IP NO +skip up to the next directory boundary and resume reading, +but do not modify the directory. +.sp +.LP +.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `.' does +does not equal \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `.' to be equal to \fII\fP. +.IP NO +leave the inode number for `.' unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose first entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `.' with inode number equal to \fII\fP. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose first entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'" +.br +A directory \fII\fP has been found whose first entry is not `.'. +.I Fsck +cannot resolve this problem as it should never happen. +See a guru. +.sp +.LP +.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `.'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `.'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `..' does +does not equal the parent of \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `..' to be equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the inode number for `..' unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose second entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `..' with inode number equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose second entry is \fIF\fP. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'" +.br +A directory \fII\fP has been found whose second entry is not `..'. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the second entry in the directory +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `..'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `..'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE) +.br +.I Fsck +has found a hard link, \fIN\fP, to a directory, \fID\fP. +When preen'ing the extraneous links are ignored. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +delete the extraneous entry, \fIN\fP. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD RETURN STATE \fIS\fP FROM DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be returned +from the routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD STATE \fIS\fP FOR ROOT INODE" +.br +An internal error has caused an impossible state \fIS\fP to be assigned +to the root inode. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 3 \- Check Connectivity +.PP +This phase concerns itself with the directory connectivity seen in +Phase 2. +This section lists error conditions resulting from +unreferenced directories, +and missing or full +.I lost+found +directories. +.sp +.LP +.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +The directory inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of directory inode \fII\fP are printed. +When preen'ing, the directory is reconnected if its size is non-zero, +otherwise it is cleared. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect directory inode \fII\fP to the file system in the +directory for lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 3 +if there are problems connecting directory inode \fII\fP to \fIlost+found\fP. +This may also invoke the CONNECTED error condition in Phase 3 if the link +was successful. +.IP NO +ignore this error condition. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP" +.br +This is an advisory message indicating a directory inode \fII1\fP was +successfully connected to the +.I lost+found +directory. +The parent inode \fII2\fP of the directory inode \fII1\fP is +replaced by the inode number of the +.I lost+found +directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP +(this can reoccur in Phase 3 if it is not adjusted in Phase 2). +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.NH 2 +Phase 4 \- Check Reference Counts +.PP +This phase concerns itself with the link count information +seen in Phase 2 and Phase 3. +This section lists error conditions resulting from +unreferenced files, +missing or full +.I lost+found +directory, +incorrect link counts for files, directories, symbolic links, or special files, +unreferenced files, symbolic links, and directories, +and bad or duplicate blocks in files, symbolic links, and directories. +All errors in this phase are correctable if the file system is being preen'ed +except running out of space in the \fIlost+found\fP directory. +.sp +.LP +.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +Inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of inode \fII\fP are printed. +When preen'ing the file is cleared if either its size or its +link count is zero, +otherwise it is reconnected. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect inode \fII\fP to the file system in the directory for +lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 4 +if there are problems connecting inode \fII\fP to +.I lost+found . +.IP NO +ignore this error condition. +This will always invoke the CLEAR error condition in Phase 4. +.sp +.LP +.B "(CLEAR)" +.br +The inode mentioned in the immediately previous error condition can not be +reconnected. +This cannot occur if the file system is being preen'ed, +since lack of space to reconnect files is a fatal error. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)" +.br +The link count for inode \fII\fP, +is \fIX\fP but should be \fIY\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP +are printed. +When preen'ing the link count is adjusted unless the number of references +is increasing, a condition that should never occur unless precipitated +by a hardware failure. +When the number of references is increasing under preen mode, +.I fsck +exits with the message: +.br +.B "LINK COUNT INCREASING" +.LP +Possible responses to the ADJUST prompt are: +.IP YES +replace the link count of file inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Inode \fII\fP, was not connected to a directory entry when the +file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +When preen'ing, +this is a file that was not connected because its size or link count was zero, +hence it is cleared. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks associated with +inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +This error cannot arise when the file system is being preen'ed, +as it would have caused a fatal error earlier. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.NH 2 +Phase 5 - Check Cyl groups +.PP +This phase concerns itself with the free-block and used-inode maps. +This section lists error conditions resulting from +allocated blocks in the free-block maps, +free blocks missing from free-block maps, +and the total free-block count incorrect. +It also lists error conditions resulting from +free inodes in the used-inode maps, +allocated inodes missing from used-inode maps, +and the total used-inode count incorrect. +.sp +.LP +.B "CG \fIC\fP: BAD MAGIC NUMBER" +.br +The magic number of cylinder group \fIC\fP is wrong. +This usually indicates that the cylinder group maps have been destroyed. +When running manually the cylinder group is marked as needing +to be reconstructed. +This error is fatal if the file system is being preen'ed. +.sp +.LP +.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)" +.br +A cylinder group block map is missing some free blocks. +During preen'ing the maps are reconstructed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the free block map. +.IP NO +ignore this error condition. +.sp +.LP +.B "SUMMARY INFORMATION BAD (SALVAGE)" +.br +The summary information was found to be incorrect. +When preen'ing, +the summary information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the summary information. +.IP NO +ignore this error condition. +.sp +.LP +.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)" +.br +The superblock free block information was found to be incorrect. +When preen'ing, +the superblock free block information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the superblock free block information. +.IP NO +ignore this error condition. +.NH 2 +Cleanup +.PP +Once a file system has been checked, a few cleanup functions are performed. +This section lists advisory messages about +the file system +and modify status of the file system. +.sp +.LP +.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)" +.br +This is an advisory message indicating that +the file system checked contained +\fIV\fP files using +\fIW\fP fragment sized blocks leaving +\fIX\fP fragment sized blocks free in the file system. +The numbers in parenthesis breaks the free count down into +\fIY\fP free fragments and +\fIZ\fP free full sized blocks. +.sp +.LP +.B "***** REBOOT UNIX *****" +.br +This is an advisory message indicating that +the root file system has been modified by +.I fsck. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. +When preen'ing, +.I fsck +will exit with a code of 4. +The standard auto-reboot script distributed with 4.3BSD +interprets an exit code of 4 by issuing a reboot system call. +.sp +.LP +.B "***** FILE SYSTEM WAS MODIFIED *****" +.br +This is an advisory message indicating that +the current file system was modified by +.I fsck. +If this file system is mounted or is the current root file system, +.I fsck +should be halted and UNIX rebooted. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. diff --git a/sbin/fsck_ffs/SMM.doc/Makefile b/sbin/fsck_ffs/SMM.doc/Makefile new file mode 100644 index 000000000000..26823bcc94f2 --- /dev/null +++ b/sbin/fsck_ffs/SMM.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= smm/03.fsck +SRCS= 0.t 1.t 2.t 3.t 4.t +MACROS= -ms + +.include diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c new file mode 100644 index 000000000000..ee5d095e6c75 --- /dev/null +++ b/sbin/fsck_ffs/dir.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +char *lfname = "lost+found"; +int lfmode = 01777; +struct dirtemplate emptydir = { 0, DIRBLKSIZ }; +struct dirtemplate dirhead = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +struct odirtemplate odirhead = { + 0, 12, 1, ".", + 0, DIRBLKSIZ - 12, 2, ".." +}; + +struct direct *fsck_readdir(); +struct bufarea *getdirblk(); + +/* + * Propagate connected state through the tree. + */ +propagate() +{ + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + long change; + + inpend = &inpsort[inplast]; + do { + change = 0; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) { + statemap[inp->i_number] = DFOUND; + change++; + } + } + } while (change > 0); +} + +/* + * Scan each entry in a directory block. + */ +dirscan(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp; + register struct bufarea *bp; + int dsize, n; + long blksiz; + char dbuf[DIRBLKSIZ]; + + if (idesc->id_type != DATA) + errexit("wrong type to dirscan %d\n", idesc->id_type); + if (idesc->id_entryno == 0 && + (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) + idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); + blksiz = idesc->id_numfrags * sblock.fs_fsize; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { + idesc->id_filesize -= blksiz; + return (SKIP); + } + idesc->id_loc = 0; + for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { + dsize = dp->d_reclen; + bcopy((char *)dp, dbuf, (size_t)dsize); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + struct direct *tdp = (struct direct *)dbuf; + u_char tmp; + + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + idesc->id_dirp = (struct direct *)dbuf; + if ((n = (*idesc->id_func)(idesc)) & ALTERED) { +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt && !doinglevel2) { + struct direct *tdp; + u_char tmp; + + tdp = (struct direct *)dbuf; + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + bp = getdirblk(idesc->id_blkno, blksiz); + bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, + (size_t)dsize); + dirty(bp); + sbdirty(); + } + if (n & STOP) + return (n); + } + return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +struct direct * +fsck_readdir(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp, *ndp; + register struct bufarea *bp; + long size, blksiz, fix, dploc; + + blksiz = idesc->id_numfrags * sblock.fs_fsize; + bp = getdirblk(idesc->id_blkno, blksiz); + if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && + idesc->id_loc < blksiz) { + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (dircheck(idesc, dp)) + goto dpok; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + dp->d_reclen = DIRBLKSIZ; + dp->d_ino = 0; + dp->d_type = 0; + dp->d_namlen = 0; + dp->d_name[0] = '\0'; + if (fix) + dirty(bp); + idesc->id_loc += DIRBLKSIZ; + idesc->id_filesize -= DIRBLKSIZ; + return (dp); + } +dpok: + if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) + return NULL; + dploc = idesc->id_loc; + dp = (struct direct *)(bp->b_un.b_buf + dploc); + idesc->id_loc += dp->d_reclen; + idesc->id_filesize -= dp->d_reclen; + if ((idesc->id_loc % DIRBLKSIZ) == 0) + return (dp); + ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && + dircheck(idesc, ndp) == 0) { + size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + idesc->id_loc += size; + idesc->id_filesize -= size; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + dploc); + dp->d_reclen += size; + if (fix) + dirty(bp); + } + return (dp); +} + +/* + * Verify that a directory entry is valid. + * This is a superset of the checks made in the kernel. + */ +dircheck(idesc, dp) + struct inodesc *idesc; + register struct direct *dp; +{ + register int size; + register char *cp; + u_char namlen, type; + int spaceleft; + + size = DIRSIZ(!newinofmt, dp); + spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + type = dp->d_namlen; + namlen = dp->d_type; + } else { + namlen = dp->d_namlen; + type = dp->d_type; + } +# else + namlen = dp->d_namlen; + type = dp->d_type; +# endif + if (dp->d_ino < maxino && + dp->d_reclen != 0 && + dp->d_reclen <= spaceleft && + (dp->d_reclen & 0x3) == 0 && + dp->d_reclen >= size && + idesc->id_filesize >= size && + namlen <= MAXNAMLEN && + type <= 15) { + if (dp->d_ino == 0) + return (1); + for (cp = dp->d_name, size = 0; size < namlen; size++) + if (*cp == 0 || (*cp++ == '/')) + return (0); + if (*cp == 0) + return (1); + } + return (0); +} + +direrror(ino, errmesg) + ino_t ino; + char *errmesg; +{ + + fileerror(ino, ino, errmesg); +} + +fileerror(cwd, ino, errmesg) + ino_t cwd, ino; + char *errmesg; +{ + register struct dinode *dp; + char pathbuf[MAXPATHLEN + 1]; + + pwarn("%s ", errmesg); + pinode(ino); + printf("\n"); + getpathname(pathbuf, cwd, ino); + if (ino < ROOTINO || ino > maxino) { + pfatal("NAME=%s\n", pathbuf); + return; + } + dp = ginode(ino); + if (ftypeok(dp)) + pfatal("%s=%s\n", + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); + else + pfatal("NAME=%s\n", pathbuf); +} + +adjust(idesc, lcnt) + register struct inodesc *idesc; + short lcnt; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (dp->di_nlink == lcnt) { + if (linkup(idesc->id_number, (ino_t)0) == 0) + clri(idesc, "UNREF", 0); + } else { + pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : + ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); + pinode(idesc->id_number); + printf(" COUNT %d SHOULD BE %d", + dp->di_nlink, dp->di_nlink - lcnt); + if (preen) { + if (lcnt < 0) { + printf("\n"); + pfatal("LINK COUNT INCREASING"); + } + printf(" (ADJUSTED)\n"); + } + if (preen || reply("ADJUST") == 1) { + dp->di_nlink -= lcnt; + inodirty(); + } + } +} + +mkentry(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + struct direct newent; + int newlen, oldlen; + + newent.d_namlen = strlen(idesc->id_name); + newlen = DIRSIZ(0, &newent); + if (dirp->d_ino != 0) + oldlen = DIRSIZ(0, dirp); + else + oldlen = 0; + if (dirp->d_reclen - oldlen < newlen) + return (KEEPON); + newent.d_reclen = dirp->d_reclen - oldlen; + dirp->d_reclen = oldlen; + dirp = (struct direct *)(((char *)dirp) + oldlen); + dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + dirp->d_reclen = newent.d_reclen; + dirp->d_namlen = newent.d_namlen; + bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); + return (ALTERED|STOP); +} + +chgino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) + return (KEEPON); + dirp->d_ino = idesc->id_parent; + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + return (ALTERED|STOP); +} + +linkup(orphan, parentdir) + ino_t orphan; + ino_t parentdir; +{ + register struct dinode *dp; + int lostdir; + ino_t oldlfdir; + struct inodesc idesc; + char tempname[BUFSIZ]; + extern int pass4check(); + + bzero((char *)&idesc, sizeof(struct inodesc)); + dp = ginode(orphan); + lostdir = (dp->di_mode & IFMT) == IFDIR; + pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); + pinode(orphan); + if (preen && dp->di_size == 0) + return (0); + if (preen) + printf(" (RECONNECTED)\n"); + else + if (reply("RECONNECT") == 0) + return (0); + if (lfdir == 0) { + dp = ginode(ROOTINO); + idesc.id_name = lfname; + idesc.id_type = DATA; + idesc.id_func = findino; + idesc.id_number = ROOTINO; + if ((ckinode(dp, &idesc) & FOUND) != 0) { + lfdir = idesc.id_parent; + } else { + pwarn("NO lost+found DIRECTORY"); + if (preen || reply("CREATE")) { + lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); + if (lfdir != 0) { + if (makeentry(ROOTINO, lfdir, lfname) != 0) { + if (preen) + printf(" (CREATED)\n"); + } else { + freedir(lfdir, ROOTINO); + lfdir = 0; + if (preen) + printf("\n"); + } + } + } + } + if (lfdir == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + } + dp = ginode(lfdir); + if ((dp->di_mode & IFMT) != IFDIR) { + pfatal("lost+found IS NOT A DIRECTORY"); + if (reply("REALLOCATE") == 0) + return (0); + oldlfdir = lfdir; + if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + inodirty(); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = oldlfdir; + adjust(&idesc, lncntp[oldlfdir] + 1); + lncntp[oldlfdir] = 0; + dp = ginode(lfdir); + } + if (statemap[lfdir] != DFOUND) { + pfatal("SORRY. NO lost+found DIRECTORY\n\n"); + return (0); + } + (void)lftempname(tempname, orphan); + if (makeentry(lfdir, orphan, tempname) == 0) { + pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + lncntp[orphan]--; + if (lostdir) { + if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + parentdir != (ino_t)-1) + (void)makeentry(orphan, lfdir, ".."); + dp = ginode(lfdir); + dp->di_nlink++; + inodirty(); + lncntp[lfdir]++; + pwarn("DIR I=%lu CONNECTED. ", orphan); + if (parentdir != (ino_t)-1) + printf("PARENT WAS I=%lu\n", parentdir); + if (preen == 0) + printf("\n"); + } + return (1); +} + +/* + * fix an entry in a directory. + */ +changeino(dir, name, newnum) + ino_t dir; + char *name; + ino_t newnum; +{ + struct inodesc idesc; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = chgino; + idesc.id_number = dir; + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + idesc.id_parent = newnum; /* new value for name */ + return (ckinode(ginode(dir), &idesc)); +} + +/* + * make an entry in a directory + */ +makeentry(parent, ino, name) + ino_t parent, ino; + char *name; +{ + struct dinode *dp; + struct inodesc idesc; + char pathbuf[MAXPATHLEN + 1]; + + if (parent < ROOTINO || parent >= maxino || + ino < ROOTINO || ino >= maxino) + return (0); + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = mkentry; + idesc.id_number = parent; + idesc.id_parent = ino; /* this is the inode to enter */ + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + dp = ginode(parent); + if (dp->di_size % DIRBLKSIZ) { + dp->di_size = roundup(dp->di_size, DIRBLKSIZ); + inodirty(); + } + if ((ckinode(dp, &idesc) & ALTERED) != 0) + return (1); + getpathname(pathbuf, parent, parent); + dp = ginode(parent); + if (expanddir(dp, pathbuf) == 0) + return (0); + return (ckinode(dp, &idesc) & ALTERED); +} + +/* + * Attempt to expand the size of a directory + */ +expanddir(dp, name) + register struct dinode *dp; + char *name; +{ + daddr_t lastbn, newblk; + register struct bufarea *bp; + char *cp, firstblk[DIRBLKSIZ]; + + lastbn = lblkno(&sblock, dp->di_size); + if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) + return (0); + if ((newblk = allocblk(sblock.fs_frag)) == 0) + return (0); + dp->di_db[lastbn + 1] = dp->di_db[lastbn]; + dp->di_db[lastbn] = newblk; + dp->di_size += sblock.fs_bsize; + dp->di_blocks += btodb(sblock.fs_bsize); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); + bp = getdirblk(newblk, sblock.fs_bsize); + if (bp->b_errs) + goto bad; + bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_bsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); + pwarn("NO SPACE LEFT IN %s", name); + if (preen) + printf(" (EXPANDED)\n"); + else if (reply("EXPAND") == 0) + goto bad; + dirty(bp); + inodirty(); + return (1); +bad: + dp->di_db[lastbn] = dp->di_db[lastbn + 1]; + dp->di_db[lastbn + 1] = 0; + dp->di_size -= sblock.fs_bsize; + dp->di_blocks -= btodb(sblock.fs_bsize); + freeblk(newblk, sblock.fs_frag); + return (0); +} + +/* + * allocate a new directory + */ +allocdir(parent, request, mode) + ino_t parent, request; + int mode; +{ + ino_t ino; + char *cp; + struct dinode *dp; + register struct bufarea *bp; + struct dirtemplate *dirp; + + ino = allocino(request, IFDIR|mode); + if (newinofmt) + dirp = &dirhead; + else + dirp = (struct dirtemplate *)&odirhead; + dirp->dot_ino = ino; + dirp->dotdot_ino = parent; + dp = ginode(ino); + bp = getdirblk(dp->di_db[0], sblock.fs_fsize); + if (bp->b_errs) { + freeino(ino); + return (0); + } + bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_fsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + dp->di_nlink = 2; + inodirty(); + if (ino == ROOTINO) { + lncntp[ino] = dp->di_nlink; + cacheino(dp, ino); + return(ino); + } + if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { + freeino(ino); + return (0); + } + cacheino(dp, ino); + statemap[ino] = statemap[parent]; + if (statemap[ino] == DSTATE) { + lncntp[ino] = dp->di_nlink; + lncntp[parent]++; + } + dp = ginode(parent); + dp->di_nlink++; + inodirty(); + return (ino); +} + +/* + * free a directory inode + */ +freedir(ino, parent) + ino_t ino, parent; +{ + struct dinode *dp; + + if (ino != parent) { + dp = ginode(parent); + dp->di_nlink--; + inodirty(); + } + freeino(ino); +} + +/* + * generate a temporary name for the lost+found directory. + */ +lftempname(bufp, ino) + char *bufp; + ino_t ino; +{ + register ino_t in; + register char *cp; + int namlen; + + cp = bufp + 2; + for (in = maxino; in > 0; in /= 10) + cp++; + *--cp = 0; + namlen = cp - bufp; + in = ino; + while (cp > bufp) { + *--cp = (in % 10) + '0'; + in /= 10; + } + *cp = '#'; + return (namlen); +} + +/* + * Get a directory block. + * Insure that it is held until another is requested. + */ +struct bufarea * +getdirblk(blkno, size) + daddr_t blkno; + long size; +{ + + if (pdirbp != 0) + pdirbp->b_flags &= ~B_INUSE; + pdirbp = getdatablk(blkno, size); + return (pdirbp); +} diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h new file mode 100644 index 000000000000..7fa831f6c52e --- /dev/null +++ b/sbin/fsck_ffs/fsck.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fsck.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXDUP 10 /* limit on dup blks (per inode) */ +#define MAXBAD 10 /* limit on bad blks (per inode) */ +#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */ +#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */ + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +#define USTATE 01 /* inode not allocated */ +#define FSTATE 02 /* inode is file */ +#define DSTATE 03 /* inode is directory */ +#define DFOUND 04 /* directory found during descent */ +#define DCLEAR 05 /* directory is to be cleared */ +#define FCLEAR 06 /* file is to be cleared */ + +/* + * buffer cache structure. + */ +struct bufarea { + struct bufarea *b_next; /* free list queue */ + struct bufarea *b_prev; /* free list queue */ + daddr_t b_bno; + int b_size; + int b_errs; + int b_flags; + union { + char *b_buf; /* buffer space */ + daddr_t *b_indir; /* indirect block */ + struct fs *b_fs; /* super block */ + struct cg *b_cg; /* cylinder group */ + struct dinode *b_dinode; /* inode block */ + } b_un; + char b_dirty; +}; + +#define B_INUSE 1 + +#define MINBUFS 5 /* minimum number of buffers required */ +struct bufarea bufhead; /* head of list of other blks in filesys */ +struct bufarea sblk; /* file system superblock */ +struct bufarea cgblk; /* cylinder group blocks */ +struct bufarea *pdirbp; /* current directory contents */ +struct bufarea *pbp; /* current inode block */ +struct bufarea *getdatablk(); + +#define dirty(bp) (bp)->b_dirty = 1 +#define initbarea(bp) \ + (bp)->b_dirty = 0; \ + (bp)->b_bno = (daddr_t)-1; \ + (bp)->b_flags = 0; + +#define sbdirty() sblk.b_dirty = 1 +#define cgdirty() cgblk.b_dirty = 1 +#define sblock (*sblk.b_un.b_fs) +#define cgrp (*cgblk.b_un.b_cg) + +enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; + +struct inodesc { + enum fixstate id_fix; /* policy on fixing errors */ + int (*id_func)(); /* function to be applied to blocks of inode */ + ino_t id_number; /* inode number described */ + ino_t id_parent; /* for DATA nodes, their parent */ + daddr_t id_blkno; /* current block number being examined */ + int id_numfrags; /* number of frags contained in block */ + quad_t id_filesize; /* for DATA nodes, the size of the directory */ + int id_loc; /* for DATA nodes, current location in dir */ + int id_entryno; /* for DATA nodes, current entry number */ + struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ + char *id_name; /* for DATA nodes, name to find or enter */ + char id_type; /* type of descriptor, DATA or ADDR */ +}; +/* file types */ +#define DATA 1 +#define ADDR 2 + +/* + * Linked list of duplicate blocks. + * + * The list is composed of two parts. The first part of the + * list (from duplist through the node pointed to by muldup) + * contains a single copy of each duplicate block that has been + * found. The second part of the list (from muldup to the end) + * contains duplicate blocks that have been found more than once. + * To check if a block has been found as a duplicate it is only + * necessary to search from duplist through muldup. To find the + * total number of times that a block has been found as a duplicate + * the entire list must be searched for occurences of the block + * in question. The following diagram shows a sample list where + * w (found twice), x (found once), y (found three times), and z + * (found once) are duplicate block numbers: + * + * w -> y -> x -> z -> y -> w -> y + * ^ ^ + * | | + * duplist muldup + */ +struct dups { + struct dups *next; + daddr_t dup; +}; +struct dups *duplist; /* head of dup list */ +struct dups *muldup; /* end of unique duplicate dup block numbers */ + +/* + * Linked list of inodes with zero link counts. + */ +struct zlncnt { + struct zlncnt *next; + ino_t zlncnt; +}; +struct zlncnt *zlnhead; /* head of zero link count list */ + +/* + * Inode cache data structures. + */ +struct inoinfo { + struct inoinfo *i_nexthash; /* next entry in hash chain */ + ino_t i_number; /* inode number of this entry */ + ino_t i_parent; /* inode number of parent */ + ino_t i_dotdot; /* inode number of `..' */ + size_t i_isize; /* size of inode */ + u_int i_numblks; /* size of block array in bytes */ + daddr_t i_blks[1]; /* actually longer */ +} **inphead, **inpsort; +long numdirs, listmax, inplast; + +char *cdevname; /* name of device being checked */ +long dev_bsize; /* computed value of DEV_BSIZE */ +long secsize; /* actual disk sector size */ +char nflag; /* assume a no response */ +char yflag; /* assume a yes response */ +int bflag; /* location of alternate super block */ +int debug; /* output debugging info */ +int cvtlevel; /* convert to newer file system format */ +int doinglevel1; /* converting to new cylinder group format */ +int doinglevel2; /* converting to new inode format */ +int newinofmt; /* filesystem has new inode format */ +char preen; /* just fix normal inconsistencies */ +char hotroot; /* checking root device */ +char havesb; /* superblock has been read */ +int fsmodified; /* 1 => write done to file system */ +int fsreadfd; /* file descriptor for reading file system */ +int fswritefd; /* file descriptor for writing file system */ + +daddr_t maxfsblock; /* number of blocks in the file system */ +char *blockmap; /* ptr to primary blk allocation map */ +ino_t maxino; /* number of inodes in file system */ +ino_t lastino; /* last inode in use */ +char *statemap; /* ptr to inode state table */ +char *typemap; /* ptr to inode type table */ +short *lncntp; /* ptr to link count table */ + +ino_t lfdir; /* lost & found directory inode number */ +char *lfname; /* lost & found directory name */ +int lfmode; /* lost & found directory creation mode */ + +daddr_t n_blks; /* number of blocks in use */ +daddr_t n_files; /* number of files in use */ + +#define clearinode(dp) (*(dp) = zino) +struct dinode zino; + +#define setbmap(blkno) setbit(blockmap, blkno) +#define testbmap(blkno) isset(blockmap, blkno) +#define clrbmap(blkno) clrbit(blockmap, blkno) + +#define STOP 0x01 +#define SKIP 0x02 +#define KEEPON 0x04 +#define ALTERED 0x08 +#define FOUND 0x10 + +time_t time(); +struct dinode *ginode(); +struct inoinfo *getinoinfo(); +void getblk(); +ino_t allocino(); +int findino(); diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8 new file mode 100644 index 000000000000..cf8c499a35e9 --- /dev/null +++ b/sbin/fsck_ffs/fsck_ffs.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt FSCK 8 +.Os BSD 4 +.Sh NAME +.Nm fsck +.Nd filesystem consistency check and interactive repair +.Sh SYNOPSIS +.Nm fsck +.Fl p +.Op Fl m Ar mode +.Nm fsck +.Op Fl b Ar block# +.Op Fl c Ar level +.Op Fl l Ar maxparallel +.Op Fl y +.Op Fl n +.Op Fl m Ar mode +.Op Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm fsck +preens a standard set of filesystems or the specified filesystems. +It is normally used in the script +.Pa /etc/rc +during automatic reboot. +Here +.Nm fsck +reads the table +.Pa /etc/fstab +to determine which filesystems to check. +Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro'' +and that have non-zero pass number are checked. +Filesystems with pass number 1 (normally just the root filesystem) +are checked one at a time. +When pass 1 completes, all remaining filesystems are checked, +running one process per disk drive. +The disk drive containing each filesystem is inferred from the longest prefix +of the device name that ends in a digit; the remaining characters are assumed +to be the partition designator. +.Pp +The kernel takes care that only a restricted class of innocuous filesystem +inconsistencies can happen unless hardware or software failures intervene. +These are limited to the following: +.Bl -item -compact +.It +Unreferenced inodes +.It +Link counts in inodes too large +.It +Missing blocks in the free map +.It +Blocks in the free map also in files +.It +Counts in the super-block wrong +.El +.Pp +These are the only inconsistencies that +.Nm fsck +with the +.Fl p +option will correct; if it encounters other inconsistencies, it exits +with an abnormal return status and an automatic reboot will then fail. +For each corrected inconsistency one or more lines will be printed +identifying the filesystem on which the correction will take place, +and the nature of the correction. After successfully correcting a filesystem, +.Nm fsck +will print the number of files on that filesystem, +the number of used and free blocks, +and the percentage of fragmentation. +.Pp +If sent a +.Dv QUIT +signal, +.Nm fsck +will finish the filesystem checks, then exit with an abnormal +return status that causes an automatic reboot to fail. +This is useful when you want to finish the filesystem checks during an +automatic reboot, +but do not want the machine to come up multiuser after the checks complete. +.Pp +Without the +.Fl p +option, +.Nm fsck +audits and interactively repairs inconsistent conditions for filesystems. +If the filesystem is inconsistent the operator is prompted for concurrence +before each correction is attempted. +It should be noted that some of the corrective actions which are not +correctable under the +.Fl p +option will result in some loss of data. +The amount and severity of data lost may be determined from the diagnostic +output. +The default action for each consistency correction +is to wait for the operator to respond +.Li yes +or +.Li no . +If the operator does not have write permission on the filesystem +.Nm fsck +will default to a +.Fl n +action. +.Pp +.Nm Fsck +has more consistency checks than +its predecessors +.Em check , dcheck , fcheck , +and +.Em icheck +combined. +.Pp +The following flags are interpreted by +.Nm fsck . +.Bl -tag -width indent +.It Fl b +Use the block specified immediately after the flag as +the super block for the filesystem. Block 32 is usually +an alternate super block. +.It Fl l +Limit the number of parallel checks to the number specified in the following +argument. +By default, the limit is the number of disks, running one process per disk. +If a smaller limit is given, the disks are checked round-robin, one filesystem +at a time. +.It Fl m +Use the mode specified in octal immediately after the flag as the +permission bits to use when creating the +.Pa lost+found +directory rather than the default 1777. +In particular, systems that do not wish to have lost files accessible +by all users on the system should use a more restrictive +set of permissions such as 700. +.It Fl y +Assume a yes response to all questions asked by +.Nm fsck ; +this should be used with great caution as this is a free license +to continue after essentially unlimited trouble has been encountered. +.It Fl n +Assume a no response to all questions asked by +.Nm fsck +except for +.Ql CONTINUE? , +which is assumed to be affirmative; +do not open the filesystem for writing. +.It Fl c +Convert the filesystem to the specified level. +Note that the level of a filesystem can only be raised. +.Bl -tag -width indent +There are currently three levels defined: +.It 0 +The filesystem is in the old (static table) format. +.It 1 +The filesystem is in the new (dynamic table) format. +.It 2 +The filesystem supports 32-bit uid's and gid's, +short symbolic links are stored in the inode, +and directories have an added field showing the file type. +.El +.Pp +In interactive mode, +.Nm fsck +will list the conversion to be made +and ask whether the conversion should be done. +If a negative answer is given, +no further operations are done on the filesystem. +In preen mode, +the conversion is listed and done if +possible without user interaction. +Conversion in preen mode is best used when all the filesystems +are being converted at once. +The format of a filesystem can be determined from the +first line of output from +.Xr dumpfs 8 . +.El +.Pp +If no filesystems are given to +.Nm fsck +then a default list of filesystems is read from +the file +.Pa /etc/fstab . +.Pp +.Bl -enum -indent indent -compact +Inconsistencies checked are as follows: +.It +Blocks claimed by more than one inode or the free map. +.It +Blocks claimed by an inode outside the range of the filesystem. +.It +Incorrect link counts. +.It +Size checks: +.Bl -item -indent indent -compact +.It +Directory size not a multiple of DIRBLKSIZ. +.It +Partially truncated file. +.El +.It +Bad inode format. +.It +Blocks not accounted for anywhere. +.It +Directory checks: +.Bl -item -indent indent -compact +.It +File pointing to unallocated inode. +.It +Inode number out of range. +.It +Dot or dot-dot not the first two entries of a directory +or having the wrong inode number. +.El +.It +Super Block checks: +.Bl -item -indent indent -compact +.It +More blocks for inodes than there are in the filesystem. +.It +Bad free block map format. +.It +Total free block and/or free inode count incorrect. +.El +.El +.Pp +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the +.Pa lost+found +directory. +The name assigned is the inode number. +If the +.Pa lost+found +directory does not exist, it is created. +If there is insufficient space its size is increased. +.Pp +Because of inconsistencies between the block device and the buffer cache, +the raw device should always be used. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +contains default list of filesystems to check. +.El +.Sh DIAGNOSTICS +The diagnostics produced by +.Nm fsck +are fully enumerated and explained in Appendix A of +.Rs +.%T "Fsck \- The UNIX File System Check Program" +.Re +.Sh SEE ALSO +.Xr fstab 5 , +.Xr fs 5 , +.Xr fsdb 8 , +.Xr newfs 8 , +.Xr mkfs 8 , +.Xr reboot 8 diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c new file mode 100644 index 000000000000..f1c1758f746a --- /dev/null +++ b/sbin/fsck_ffs/inode.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static ino_t startinum; + +ckinode(dp, idesc) + struct dinode *dp; + register struct inodesc *idesc; +{ + register daddr_t *ap; + long ret, n, ndb, offset; + struct dinode dino; + quad_t remsize, sizepb; + mode_t mode; + + if (idesc->id_fix != IGNORE) + idesc->id_fix = DONTKNOW; + idesc->id_entryno = 0; + idesc->id_filesize = dp->di_size; + mode = dp->di_mode & IFMT; + if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && + dp->di_size < sblock.fs_maxsymlinklen)) + return (KEEPON); + dino = *dp; + ndb = howmany(dino.di_size, sblock.fs_bsize); + for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) + idesc->id_numfrags = + numfrags(&sblock, fragroundup(&sblock, offset)); + else + idesc->id_numfrags = sblock.fs_frag; + if (*ap == 0) + continue; + idesc->id_blkno = *ap; + if (idesc->id_type == ADDR) + ret = (*idesc->id_func)(idesc); + else + ret = dirscan(idesc); + if (ret & STOP) + return (ret); + } + idesc->id_numfrags = sblock.fs_frag; + remsize = dino.di_size - sblock.fs_bsize * NDADDR; + sizepb = sblock.fs_bsize; + for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + if (*ap) { + idesc->id_blkno = *ap; + ret = iblock(idesc, n, remsize); + if (ret & STOP) + return (ret); + } + sizepb *= NINDIR(&sblock); + remsize -= sizepb; + } + return (KEEPON); +} + +iblock(idesc, ilevel, isize) + struct inodesc *idesc; + long ilevel; + quad_t isize; +{ + register daddr_t *ap; + register daddr_t *aplim; + register struct bufarea *bp; + int i, n, (*func)(), nif; + quad_t sizepb; + char buf[BUFSIZ]; + extern int dirscan(), pass1check(); + + if (idesc->id_type == ADDR) { + func = idesc->id_func; + if (((n = (*func)(idesc)) & KEEPON) == 0) + return (n); + } else + func = dirscan; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) + return (SKIP); + bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); + ilevel--; + for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) + sizepb *= NINDIR(&sblock); + nif = howmany(isize , sizepb); + if (nif > NINDIR(&sblock)) + nif = NINDIR(&sblock); + if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { + aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; + for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { + if (*ap == 0) + continue; + (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", + idesc->id_number); + if (dofix(idesc, buf)) { + *ap = 0; + dirty(bp); + } + } + flush(fswritefd, bp); + } + aplim = &bp->b_un.b_indir[nif]; + for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (*ap) { + idesc->id_blkno = *ap; + if (ilevel == 0) + n = (*func)(idesc); + else + n = iblock(idesc, ilevel, isize); + if (n & STOP) { + bp->b_flags &= ~B_INUSE; + return (n); + } + } + isize -= sizepb; + } + bp->b_flags &= ~B_INUSE; + return (KEEPON); +} + +/* + * Check that a block in a legal block number. + * Return 0 if in range, 1 if out of range. + */ +chkrange(blk, cnt) + daddr_t blk; + int cnt; +{ + register int c; + + if ((unsigned)(blk + cnt) > maxfsblock) + return (1); + c = dtog(&sblock, blk); + if (blk < cgdmin(&sblock, c)) { + if ((blk + cnt) > cgsblock(&sblock, c)) { + if (debug) { + printf("blk %ld < cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > cgsbase %ld\n", + blk + cnt, cgsblock(&sblock, c)); + } + return (1); + } + } else { + if ((blk + cnt) > cgbase(&sblock, c+1)) { + if (debug) { + printf("blk %ld >= cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", + blk+cnt, sblock.fs_fpg); + } + return (1); + } + } + return (0); +} + +/* + * General purpose interface for reading inodes. + */ +struct dinode * +ginode(inumber) + ino_t inumber; +{ + daddr_t iblk; + + if (inumber < ROOTINO || inumber > maxino) + errexit("bad inode number %d to ginode\n", inumber); + if (startinum == 0 || + inumber < startinum || inumber >= startinum + INOPB(&sblock)) { + iblk = ino_to_fsba(&sblock, inumber); + if (pbp != 0) + pbp->b_flags &= ~B_INUSE; + pbp = getdatablk(iblk, sblock.fs_bsize); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]); +} + +/* + * Special purpose version of ginode used to optimize first pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; + +struct dinode * +getnextinode(inumber) + ino_t inumber; +{ + long size; + daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + errexit("bad inode number %d to nextinode\n", inumber); + if (inumber >= lastinum) { + readcnt++; + dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); + if (readcnt % readpercg == 0) { + size = partialsize; + lastinum += partialcnt; + } else { + size = inobufsize; + lastinum += fullcnt; + } + (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ + dp = inodebuf; + } + return (dp++); +} + +resetinodebuf() +{ + + startinum = 0; + nextino = 0; + lastinum = 0; + readcnt = 0; + inobufsize = blkroundup(&sblock, INOBUFSIZE); + fullcnt = inobufsize / sizeof(struct dinode); + readpercg = sblock.fs_ipg / fullcnt; + partialcnt = sblock.fs_ipg % fullcnt; + partialsize = partialcnt * sizeof(struct dinode); + if (partialcnt != 0) { + readpercg++; + } else { + partialcnt = fullcnt; + partialsize = inobufsize; + } + if (inodebuf == NULL && + (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) + errexit("Cannot allocate space for inode buffer\n"); + while (nextino < ROOTINO) + (void)getnextinode(nextino); +} + +freeinodebuf() +{ + + if (inodebuf != NULL) + free((char *)inodebuf); + inodebuf = NULL; +} + +/* + * Routines to maintain information about directory inodes. + * This is built during the first pass and used during the + * second and third passes. + * + * Enter inodes into the cache. + */ +cacheino(dp, inumber) + register struct dinode *dp; + ino_t inumber; +{ + register struct inoinfo *inp; + struct inoinfo **inpp; + unsigned int blks; + + blks = howmany(dp->di_size, sblock.fs_bsize); + if (blks > NDADDR) + blks = NDADDR + NIADDR; + inp = (struct inoinfo *) + malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); + if (inp == NULL) + return; + inpp = &inphead[inumber % numdirs]; + inp->i_nexthash = *inpp; + *inpp = inp; + inp->i_parent = (ino_t)0; + inp->i_dotdot = (ino_t)0; + inp->i_number = inumber; + inp->i_isize = dp->di_size; + inp->i_numblks = blks * sizeof(daddr_t); + bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], + (size_t)inp->i_numblks); + if (inplast == listmax) { + listmax += 100; + inpsort = (struct inoinfo **)realloc((char *)inpsort, + (unsigned)listmax * sizeof(struct inoinfo *)); + if (inpsort == NULL) + errexit("cannot increase directory list"); + } + inpsort[inplast++] = inp; +} + +/* + * Look up an inode cache structure. + */ +struct inoinfo * +getinoinfo(inumber) + ino_t inumber; +{ + register struct inoinfo *inp; + + for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { + if (inp->i_number != inumber) + continue; + return (inp); + } + errexit("cannot find inode %d\n", inumber); + return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +inocleanup() +{ + register struct inoinfo **inpp; + + if (inphead == NULL) + return; + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) + free((char *)(*inpp)); + free((char *)inphead); + free((char *)inpsort); + inphead = inpsort = NULL; +} + +inodirty() +{ + + dirty(pbp); +} + +clri(idesc, type, flag) + register struct inodesc *idesc; + char *type; + int flag; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (flag == 1) { + pwarn("%s %s", type, + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); + pinode(idesc->id_number); + } + if (preen || reply("CLEAR") == 1) { + if (preen) + printf(" (CLEARED)\n"); + n_files--; + (void)ckinode(dp, idesc); + clearinode(dp); + statemap[idesc->id_number] = USTATE; + inodirty(); + } +} + +findname(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino != idesc->id_parent) + return (KEEPON); + bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1); + return (STOP|FOUND); +} + +findino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino == 0) + return (KEEPON); + if (strcmp(dirp->d_name, idesc->id_name) == 0 && + dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { + idesc->id_parent = dirp->d_ino; + return (STOP|FOUND); + } + return (KEEPON); +} + +pinode(ino) + ino_t ino; +{ + register struct dinode *dp; + register char *p; + struct passwd *pw; + char *ctime(); + + printf(" I=%lu ", ino); + if (ino < ROOTINO || ino > maxino) + return; + dp = ginode(ino); + printf(" OWNER="); + if ((pw = getpwuid((int)dp->di_uid)) != 0) + printf("%s ", pw->pw_name); + else + printf("%u ", (unsigned)dp->di_uid); + printf("MODE=%o\n", dp->di_mode); + if (preen) + printf("%s: ", cdevname); + printf("SIZE=%qu ", dp->di_size); + p = ctime(&dp->di_mtime.ts_sec); + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +blkerror(ino, type, blk) + ino_t ino; + char *type; + daddr_t blk; +{ + + pfatal("%ld %s I=%lu", blk, type, ino); + printf("\n"); + switch (statemap[ino]) { + + case FSTATE: + statemap[ino] = FCLEAR; + return; + + case DSTATE: + statemap[ino] = DCLEAR; + return; + + case FCLEAR: + case DCLEAR: + return; + + default: + errexit("BAD STATE %d TO BLKERR", statemap[ino]); + /* NOTREACHED */ + } +} + +/* + * allocate an unused inode + */ +ino_t +allocino(request, type) + ino_t request; + int type; +{ + register ino_t ino; + register struct dinode *dp; + + if (request == 0) + request = ROOTINO; + else if (statemap[request] != USTATE) + return (0); + for (ino = request; ino < maxino; ino++) + if (statemap[ino] == USTATE) + break; + if (ino == maxino) + return (0); + switch (type & IFMT) { + case IFDIR: + statemap[ino] = DSTATE; + break; + case IFREG: + case IFLNK: + statemap[ino] = FSTATE; + break; + default: + return (0); + } + dp = ginode(ino); + dp->di_db[0] = allocblk((long)1); + if (dp->di_db[0] == 0) { + statemap[ino] = USTATE; + return (0); + } + dp->di_mode = type; + (void)time(&dp->di_atime.ts_sec); + dp->di_mtime = dp->di_ctime = dp->di_atime; + dp->di_size = sblock.fs_fsize; + dp->di_blocks = btodb(sblock.fs_fsize); + n_files++; + inodirty(); + if (newinofmt) + typemap[ino] = IFTODT(type); + return (ino); +} + +/* + * deallocate an inode + */ +freeino(ino) + ino_t ino; +{ + struct inodesc idesc; + extern int pass4check(); + struct dinode *dp; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = ino; + dp = ginode(ino); + (void)ckinode(dp, &idesc); + clearinode(dp); + inodirty(); + statemap[ino] = USTATE; + n_files--; +} diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c new file mode 100644 index 000000000000..2e69715fecf0 --- /dev/null +++ b/sbin/fsck_ffs/main.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +void catch(), catchquit(), voidquit(); +int returntosingle; + +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int ret, maxrun = 0; + extern int docheck(), checkfilesys(); + extern char *optarg, *blockcheck(); + extern int optind; + + sync(); + while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) { + switch (ch) { + case 'p': + preen++; + break; + + case 'b': + bflag = argtoi('b', "number", optarg, 10); + printf("Alternate super block location: %d\n", bflag); + break; + + case 'c': + cvtlevel = argtoi('c', "conversion level", optarg, 10); + break; + + case 'd': + debug++; + break; + + case 'l': + maxrun = argtoi('l', "number", optarg, 10); + break; + + case 'm': + lfmode = argtoi('m', "mode", optarg, 8); + if (lfmode &~ 07777) + errexit("bad mode to -m: %o\n", lfmode); + printf("** lost+found creation mode %o\n", lfmode); + break; + + case 'n': + case 'N': + nflag++; + yflag = 0; + break; + + case 'y': + case 'Y': + yflag++; + nflag = 0; + break; + + default: + errexit("%c option?\n", ch); + } + } + argc -= optind; + argv += optind; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, catch); + if (preen) + (void)signal(SIGQUIT, catchquit); + if (argc) { + while (argc-- > 0) + (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0); + exit(0); + } + ret = checkfstab(preen, maxrun, docheck, checkfilesys); + if (returntosingle) + exit(2); + exit(ret); +} + +argtoi(flag, req, str, base) + int flag; + char *req, *str; + int base; +{ + char *cp; + int ret; + + ret = (int)strtol(str, &cp, base); + if (cp == str || *cp) + errexit("-%c flag requires a %s\n", flag, req); + return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +docheck(fsp) + register struct fstab *fsp; +{ + + if (strcmp(fsp->fs_vfstype, "ufs") || + (strcmp(fsp->fs_type, FSTAB_RW) && + strcmp(fsp->fs_type, FSTAB_RO)) || + fsp->fs_passno == 0) + return (0); + return (1); +} + +/* + * Check the specified filesystem. + */ +/* ARGSUSED */ +checkfilesys(filesys, mntpt, auxdata, child) + char *filesys, *mntpt; + long auxdata; +{ + daddr_t n_ffree, n_bfree; + struct dups *dp; + struct zlncnt *zlnp; + int cylno; + + if (preen && child) + (void)signal(SIGQUIT, voidquit); + cdevname = filesys; + if (debug && preen) + pwarn("starting\n"); + if (setup(filesys) == 0) { + if (preen) + pfatal("CAN'T CHECK FILE SYSTEM."); + return (0); + } + /* + * 1: scan inodes tallying blocks used + */ + if (preen == 0) { + printf("** Last Mounted on %s\n", sblock.fs_fsmnt); + if (hotroot) + printf("** Root file system\n"); + printf("** Phase 1 - Check Blocks and Sizes\n"); + } + pass1(); + + /* + * 1b: locate first references to duplicates, if any + */ + if (duplist) { + if (preen) + pfatal("INTERNAL ERROR: dups with -p"); + printf("** Phase 1b - Rescan For More DUPS\n"); + pass1b(); + } + + /* + * 2: traverse directories from root to mark all connected directories + */ + if (preen == 0) + printf("** Phase 2 - Check Pathnames\n"); + pass2(); + + /* + * 3: scan inodes looking for disconnected directories + */ + if (preen == 0) + printf("** Phase 3 - Check Connectivity\n"); + pass3(); + + /* + * 4: scan inodes looking for disconnected files; check reference counts + */ + if (preen == 0) + printf("** Phase 4 - Check Reference Counts\n"); + pass4(); + + /* + * 5: check and repair resource counts in cylinder groups + */ + if (preen == 0) + printf("** Phase 5 - Check Cyl groups\n"); + pass5(); + + /* + * print out summary statistics + */ + n_ffree = sblock.fs_cstotal.cs_nffree; + n_bfree = sblock.fs_cstotal.cs_nbfree; + pwarn("%ld files, %ld used, %ld free ", + n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); + printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n", + n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize, + ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10); + if (debug && + (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) + printf("%ld files missing\n", n_files); + if (debug) { + n_blks += sblock.fs_ncg * + (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); + n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); + n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); + if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) + printf("%ld blocks missing\n", n_blks); + if (duplist != NULL) { + printf("The following duplicate blocks remain:"); + for (dp = duplist; dp; dp = dp->next) + printf(" %ld,", dp->dup); + printf("\n"); + } + if (zlnhead != NULL) { + printf("The following zero link count inodes remain:"); + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + printf(" %lu,", zlnp->zlncnt); + printf("\n"); + } + } + zlnhead = (struct zlncnt *)0; + duplist = (struct dups *)0; + muldup = (struct dups *)0; + inocleanup(); + if (fsmodified) { + (void)time(&sblock.fs_time); + sbdirty(); + } + if (cvtlevel && sblk.b_dirty) { + /* + * Write out the duplicate super blocks + */ + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) + bwrite(fswritefd, (char *)&sblock, + fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); + } + ckfini(); + free(blockmap); + free(statemap); + free((char *)lncntp); + if (!fsmodified) + return (0); + if (!preen) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + if (hotroot) { + struct statfs stfs_buf; + /* + * We modified the root. Do a mount update on + * it, unless it is read-write, so we can continue. + */ + if (statfs("/", &stfs_buf) == 0) { + long flags = stfs_buf.f_flags; + struct ufs_args args; + int ret; + + if (flags & MNT_RDONLY) { + args.fspec = 0; + args.export.ex_flags = 0; + args.export.ex_root = 0; + flags |= MNT_UPDATE | MNT_RELOAD; + ret = mount(MOUNT_UFS, "/", flags, &args); + if (ret == 0) + return(0); + } + } + if (!preen) + printf("\n***** REBOOT NOW *****\n"); + sync(); + return (4); + } + return (0); +} diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c new file mode 100644 index 000000000000..ca255fe78579 --- /dev/null +++ b/sbin/fsck_ffs/pass1.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static daddr_t badblk; +static daddr_t dupblk; +int pass1check(); +struct dinode *getnextinode(); + +pass1() +{ + ino_t inumber; + int c, i, cgd; + struct inodesc idesc; + + /* + * Set file system reserved blocks in used block map. + */ + for (c = 0; c < sblock.fs_ncg; c++) { + cgd = cgdmin(&sblock, c); + if (c == 0) { + i = cgbase(&sblock, c); + cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); + } else + i = cgsblock(&sblock, c); + for (; i < cgd; i++) + setbmap(i); + } + /* + * Find all allocated blocks. + */ + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1check; + inumber = 0; + n_files = n_blks = 0; + resetinodebuf(); + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + checkinode(inumber, &idesc); + } + } + freeinodebuf(); +} + +checkinode(inumber, idesc) + ino_t inumber; + register struct inodesc *idesc; +{ + register struct dinode *dp; + struct zlncnt *zlnp; + int ndb, j; + mode_t mode; + char symbuf[MAXSYMLINKLEN]; + + dp = getnextinode(inumber); + mode = dp->di_mode & IFMT; + if (mode == 0) { + if (bcmp((char *)dp->di_db, (char *)zino.di_db, + NDADDR * sizeof(daddr_t)) || + bcmp((char *)dp->di_ib, (char *)zino.di_ib, + NIADDR * sizeof(daddr_t)) || + dp->di_mode || dp->di_size) { + pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber); + if (reply("CLEAR") == 1) { + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } + } + statemap[inumber] = USTATE; + return; + } + lastino = inumber; + if (/* dp->di_size < 0 || */ + dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { + if (debug) + printf("bad size %qu:", dp->di_size); + goto unknown; + } + if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { + dp = ginode(inumber); + dp->di_size = sblock.fs_fsize; + dp->di_mode = IFREG|0600; + inodirty(); + } + ndb = howmany(dp->di_size, sblock.fs_bsize); + if (ndb < 0) { + if (debug) + printf("bad size %qu ndb %d:", + dp->di_size, ndb); + goto unknown; + } + if (mode == IFBLK || mode == IFCHR) + ndb++; + if (mode == IFLNK) { + if (doinglevel2 && + dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN && + dp->di_blocks != 0) { + if (bread(fsreadfd, symbuf, + fsbtodb(&sblock, dp->di_db[0]), + (long)dp->di_size) != 0) + errexit("cannot read symlink"); + if (debug) { + symbuf[dp->di_size] = 0; + printf("convert symlink %d(%s) of size %d\n", + inumber, symbuf, (long)dp->di_size); + } + dp = ginode(inumber); + bcopy(symbuf, (caddr_t)dp->di_shortlink, + (long)dp->di_size); + dp->di_blocks = 0; + inodirty(); + } + /* + * Fake ndb value so direct/indirect block checks below + * will detect any garbage after symlink string. + */ + if (dp->di_size < sblock.fs_maxsymlinklen) { + ndb = howmany(dp->di_size, sizeof(daddr_t)); + if (ndb > NDADDR) { + j = ndb - NDADDR; + for (ndb = 1; j > 1; j--) + ndb *= NINDIR(&sblock); + ndb += NDADDR; + } + } + } + for (j = ndb; j < NDADDR; j++) + if (dp->di_db[j] != 0) { + if (debug) + printf("bad direct addr: %ld\n", dp->di_db[j]); + goto unknown; + } + for (j = 0, ndb -= NDADDR; ndb > 0; j++) + ndb /= NINDIR(&sblock); + for (; j < NIADDR; j++) + if (dp->di_ib[j] != 0) { + if (debug) + printf("bad indirect addr: %ld\n", + dp->di_ib[j]); + goto unknown; + } + if (ftypeok(dp) == 0) + goto unknown; + n_files++; + lncntp[inumber] = dp->di_nlink; + if (dp->di_nlink <= 0) { + zlnp = (struct zlncnt *)malloc(sizeof *zlnp); + if (zlnp == NULL) { + pfatal("LINK COUNT TABLE OVERFLOW"); + if (reply("CONTINUE") == 0) + errexit(""); + } else { + zlnp->zlncnt = inumber; + zlnp->next = zlnhead; + zlnhead = zlnp; + } + } + if (mode == IFDIR) { + if (dp->di_size == 0) + statemap[inumber] = DCLEAR; + else + statemap[inumber] = DSTATE; + cacheino(dp, inumber); + } else + statemap[inumber] = FSTATE; + typemap[inumber] = IFTODT(mode); + if (doinglevel2 && + (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) { + dp = ginode(inumber); + dp->di_uid = dp->di_ouid; + dp->di_ouid = -1; + dp->di_gid = dp->di_ogid; + dp->di_ogid = -1; + inodirty(); + } + badblk = dupblk = 0; + idesc->id_number = inumber; + (void)ckinode(dp, idesc); + idesc->id_entryno *= btodb(sblock.fs_fsize); + if (dp->di_blocks != idesc->id_entryno) { + pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", + inumber, dp->di_blocks, idesc->id_entryno); + if (preen) + printf(" (CORRECTED)\n"); + else if (reply("CORRECT") == 0) + return; + dp = ginode(inumber); + dp->di_blocks = idesc->id_entryno; + inodirty(); + } + return; +unknown: + pfatal("UNKNOWN FILE TYPE I=%lu", inumber); + statemap[inumber] = FCLEAR; + if (reply("CLEAR") == 1) { + statemap[inumber] = USTATE; + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } +} + +pass1check(idesc) + register struct inodesc *idesc; +{ + int res = KEEPON; + int anyout, nfrags; + daddr_t blkno = idesc->id_blkno; + register struct dups *dlp; + struct dups *new; + + if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { + blkerror(idesc->id_number, "BAD", blkno); + if (badblk++ >= MAXBAD) { + pwarn("EXCESSIVE BAD BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + } + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (anyout && chkrange(blkno, 1)) { + res = SKIP; + } else if (!testbmap(blkno)) { + n_blks++; + setbmap(blkno); + } else { + blkerror(idesc->id_number, "DUP", blkno); + if (dupblk++ >= MAXDUP) { + pwarn("EXCESSIVE DUP BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new = (struct dups *)malloc(sizeof(struct dups)); + if (new == NULL) { + pfatal("DUP TABLE OVERFLOW."); + if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new->dup = blkno; + if (muldup == 0) { + duplist = muldup = new; + new->next = 0; + } else { + new->next = muldup->next; + muldup->next = new; + } + for (dlp = duplist; dlp != muldup; dlp = dlp->next) + if (dlp->dup == blkno) + break; + if (dlp == muldup && dlp->dup != blkno) + muldup = new; + } + /* + * count the number of blocks found in id_entryno + */ + idesc->id_entryno++; + } + return (res); +} diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c new file mode 100644 index 000000000000..27b2dfd6238c --- /dev/null +++ b/sbin/fsck_ffs/pass1b.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass1bcheck(); +static struct dups *duphead; + +pass1b() +{ + register int c, i; + register struct dinode *dp; + struct inodesc idesc; + ino_t inumber; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1bcheck; + duphead = duplist; + inumber = 0; + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + dp = ginode(inumber); + if (dp == NULL) + continue; + idesc.id_number = inumber; + if (statemap[inumber] != USTATE && + (ckinode(dp, &idesc) & STOP)) + return; + } + } +} + +pass1bcheck(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) + res = SKIP; + for (dlp = duphead; dlp; dlp = dlp->next) { + if (dlp->dup == blkno) { + blkerror(idesc->id_number, "DUP", blkno); + dlp->dup = duphead->dup; + duphead->dup = blkno; + duphead = duphead->next; + } + if (dlp == muldup) + break; + } + if (muldup == 0 || duphead == muldup->next) + return (STOP); + } + return (res); +} diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c new file mode 100644 index 000000000000..631475174372 --- /dev/null +++ b/sbin/fsck_ffs/pass2.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +int pass2check(), blksort(); + +pass2() +{ + register struct dinode *dp; + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + struct inodesc curino; + struct dinode dino; + char pathbuf[MAXPATHLEN + 1]; + + switch (statemap[ROOTINO]) { + + case USTATE: + pfatal("ROOT INODE UNALLOCATED"); + if (reply("ALLOCATE") == 0) + errexit(""); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("CONTINUE") == 0) + errexit(""); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("FIX") == 0) + errexit(""); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + break; + + default: + errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); + } + statemap[ROOTINO] = DFOUND; + /* + * Sort the directory list into disk block order. + */ + qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); + /* + * Check the integrity of each directory. + */ + bzero((char *)&curino, sizeof(struct inodesc)); + curino.id_type = DATA; + curino.id_func = pass2check; + dp = &dino; + inpend = &inpsort[inplast]; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_isize == 0) + continue; + if (inp->i_isize < MINDIRSIZE) { + direrror(inp->i_number, "DIRECTORY TOO SHORT"); + inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); + if (reply("FIX") == 1) { + dp = ginode(inp->i_number); + dp->di_size = inp->i_isize; + inodirty(); + dp = &dino; + } + } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { + getpathname(pathbuf, inp->i_number, inp->i_number); + pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", + pathbuf, inp->i_isize, DIRBLKSIZ); + if (preen) + printf(" (ADJUSTED)\n"); + inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); + if (preen || reply("ADJUST") == 1) { + dp = ginode(inp->i_number); + dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); + inodirty(); + dp = &dino; + } + } + bzero((char *)&dino, sizeof(struct dinode)); + dino.di_mode = IFDIR; + dp->di_size = inp->i_isize; + bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], + (size_t)inp->i_numblks); + curino.id_number = inp->i_number; + curino.id_parent = inp->i_parent; + (void)ckinode(dp, &curino); + } + /* + * Now that the parents of all directories have been found, + * make another pass to verify the value of `..' + */ + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0 || inp->i_isize == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) + statemap[inp->i_number] = DFOUND; + if (inp->i_dotdot == inp->i_parent || + inp->i_dotdot == (ino_t)-1) + continue; + if (inp->i_dotdot == 0) { + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); + if (reply("FIX") == 0) + continue; + (void)makeentry(inp->i_number, inp->i_parent, ".."); + lncntp[inp->i_parent]--; + continue; + } + fileerror(inp->i_parent, inp->i_number, + "BAD INODE NUMBER FOR '..'"); + if (reply("FIX") == 0) + continue; + lncntp[inp->i_dotdot]++; + lncntp[inp->i_parent]--; + inp->i_dotdot = inp->i_parent; + (void)changeino(inp->i_number, "..", inp->i_parent); + } + /* + * Mark all the directories that can be found from the root. + */ + propagate(); +} + +pass2check(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + register struct inoinfo *inp; + int n, entrysize, ret = 0; + struct dinode *dp; + char *errmsg; + struct direct proto; + char namebuf[MAXPATHLEN + 1]; + char pathbuf[MAXPATHLEN + 1]; + + /* + * If converting, set directory entry type. + */ + if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { + dirp->d_type = typemap[dirp->d_ino]; + ret |= ALTERED; + } + /* + * check for "." + */ + if (idesc->id_entryno != 0) + goto chk1; + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { + if (dirp->d_ino != idesc->id_number) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); + dirp->d_ino = idesc->id_number; + if (reply("FIX") == 1) + ret |= ALTERED; + } + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk1; + } + direrror(idesc->id_number, "MISSING '.'"); + proto.d_ino = idesc->id_number; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 1; + (void)strcpy(proto.d_name, "."); + entrysize = DIRSIZ(0, &proto); + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { + pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + } else if (dirp->d_reclen < entrysize) { + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); + } else if (dirp->d_reclen < 2 * entrysize) { + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + bzero((char *)dirp, (size_t)n); + dirp->d_reclen = n; + if (reply("FIX") == 1) + ret |= ALTERED; + } +chk1: + if (idesc->id_entryno > 1) + goto chk2; + inp = getinoinfo(idesc->id_number); + proto.d_ino = inp->i_parent; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 2; + (void)strcpy(proto.d_name, ".."); + entrysize = DIRSIZ(0, &proto); + if (idesc->id_entryno == 0) { + n = DIRSIZ(0, dirp); + if (dirp->d_reclen < n + entrysize) + goto chk2; + proto.d_reclen = dirp->d_reclen - n; + dirp->d_reclen = n; + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + n); + bzero((char *)dirp, (size_t)proto.d_reclen); + dirp->d_reclen = proto.d_reclen; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { + inp->i_dotdot = dirp->d_ino; + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk2; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + inp->i_dotdot = (ino_t)-1; + } else if (dirp->d_reclen < entrysize) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); + inp->i_dotdot = (ino_t)-1; + } else if (inp->i_parent != 0) { + /* + * We know the parent, so fix now. + */ + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } + idesc->id_entryno++; + if (dirp->d_ino != 0) + lncntp[dirp->d_ino]--; + return (ret|KEEPON); +chk2: + if (dirp->d_ino == 0) + return (ret|KEEPON); + if (dirp->d_namlen <= 2 && + dirp->d_name[0] == '.' && + idesc->id_entryno >= 2) { + if (dirp->d_namlen == 1) { + direrror(idesc->id_number, "EXTRA '.' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + if (dirp->d_name[1] == '.') { + direrror(idesc->id_number, "EXTRA '..' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + } + idesc->id_entryno++; + n = 0; + if (dirp->d_ino > maxino) { + fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); + n = reply("REMOVE"); + } else { +again: + switch (statemap[dirp->d_ino]) { + case USTATE: + if (idesc->id_entryno <= 2) + break; + fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); + n = reply("REMOVE"); + break; + + case DCLEAR: + case FCLEAR: + if (idesc->id_entryno <= 2) + break; + if (statemap[dirp->d_ino] == FCLEAR) + errmsg = "DUP/BAD"; + else if (!preen) + errmsg = "ZERO LENGTH DIRECTORY"; + else { + n = 1; + break; + } + fileerror(idesc->id_number, dirp->d_ino, errmsg); + if ((n = reply("REMOVE")) == 1) + break; + dp = ginode(dirp->d_ino); + statemap[dirp->d_ino] = + (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; + lncntp[dirp->d_ino] = dp->di_nlink; + goto again; + + case DSTATE: + if (statemap[idesc->id_number] == DFOUND) + statemap[dirp->d_ino] = DFOUND; + /* fall through */ + + case DFOUND: + inp = getinoinfo(dirp->d_ino); + if (inp->i_parent != 0 && idesc->id_entryno > 2) { + getpathname(pathbuf, idesc->id_number, + idesc->id_number); + getpathname(namebuf, dirp->d_ino, dirp->d_ino); + pwarn("%s %s %s\n", pathbuf, + "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", + namebuf); + if (preen) + printf(" (IGNORED)\n"); + else if ((n = reply("REMOVE")) == 1) + break; + } + if (idesc->id_entryno > 2) + inp->i_parent = idesc->id_number; + /* fall through */ + + case FSTATE: + if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { + fileerror(idesc->id_number, dirp->d_ino, + "BAD TYPE VALUE"); + dirp->d_type = typemap[dirp->d_ino]; + if (reply("FIX") == 1) + ret |= ALTERED; + } + lncntp[dirp->d_ino]--; + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[dirp->d_ino], dirp->d_ino); + } + } + if (n == 0) + return (ret|KEEPON); + dirp->d_ino = 0; + return (ret|KEEPON|ALTERED); +} + +/* + * Routine to sort disk blocks. + */ +blksort(inpp1, inpp2) + struct inoinfo **inpp1, **inpp2; +{ + + return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); +} diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c new file mode 100644 index 000000000000..5c6f09d041de --- /dev/null +++ b/sbin/fsck_ffs/pass3.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include "fsck.h" + +pass3() +{ + register struct inoinfo **inpp, *inp; + ino_t orphan; + int loopcnt; + + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { + inp = *inpp; + if (inp->i_number == ROOTINO || + !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE)) + continue; + if (statemap[inp->i_number] == DCLEAR) + continue; + for (loopcnt = 0; ; loopcnt++) { + orphan = inp->i_number; + if (inp->i_parent == 0 || + statemap[inp->i_parent] != DSTATE || + loopcnt > numdirs) + break; + inp = getinoinfo(inp->i_parent); + } + (void)linkup(orphan, inp->i_dotdot); + inp->i_parent = inp->i_dotdot = lfdir; + lncntp[lfdir]--; + statemap[orphan] = DFOUND; + propagate(); + } +} diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c new file mode 100644 index 000000000000..7450530e8a4e --- /dev/null +++ b/sbin/fsck_ffs/pass4.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass4check(); + +pass4() +{ + register ino_t inumber; + register struct zlncnt *zlnp; + struct dinode *dp; + struct inodesc idesc; + int n; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + for (inumber = ROOTINO; inumber <= lastino; inumber++) { + idesc.id_number = inumber; + switch (statemap[inumber]) { + + case FSTATE: + case DFOUND: + n = lncntp[inumber]; + if (n) + adjust(&idesc, (short)n); + else { + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + if (zlnp->zlncnt == inumber) { + zlnp->zlncnt = zlnhead->zlncnt; + zlnp = zlnhead; + zlnhead = zlnhead->next; + free((char *)zlnp); + clri(&idesc, "UNREF", 1); + break; + } + } + break; + + case DSTATE: + clri(&idesc, "UNREF", 1); + break; + + case DCLEAR: + dp = ginode(inumber); + if (dp->di_size == 0) { + clri(&idesc, "ZERO LENGTH", 1); + break; + } + /* fall through */ + case FCLEAR: + clri(&idesc, "BAD/DUP", 1); + break; + + case USTATE: + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[inumber], inumber); + } + } +} + +pass4check(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) { + res = SKIP; + } else if (testbmap(blkno)) { + for (dlp = duplist; dlp; dlp = dlp->next) { + if (dlp->dup != blkno) + continue; + dlp->dup = duplist->dup; + dlp = duplist; + duplist = duplist->next; + free((char *)dlp); + break; + } + if (dlp == 0) { + clrbmap(blkno); + n_blks--; + } + } + } + return (res); +} diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c new file mode 100644 index 000000000000..2e98f96b5ca8 --- /dev/null +++ b/sbin/fsck_ffs/pass5.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +pass5() +{ + int c, blk, frags, basesize, sumsize, mapsize, savednrpos; + register struct fs *fs = &sblock; + register struct cg *cg = &cgrp; + daddr_t dbase, dmax; + register daddr_t d; + register long i, j; + struct csum *cs; + struct csum cstotal; + struct inodesc idesc[3]; + char buf[MAXBSIZE]; + register struct cg *newcg = (struct cg *)buf; + struct ocg *ocg = (struct ocg *)buf; + + bzero((char *)newcg, (size_t)fs->fs_cgsize); + newcg->cg_niblk = fs->fs_ipg; + if (cvtlevel > 3) { + if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) { + if (preen) + pwarn("DELETING CLUSTERING MAPS\n"); + if (preen || reply("DELETE CLUSTERING MAPS")) { + fs->fs_contigsumsize = 0; + doinglevel1 = 1; + sbdirty(); + } + } + if (fs->fs_maxcontig > 1) { + char *doit = 0; + + if (fs->fs_contigsumsize < 1) { + doit = "CREAT"; + } else if (fs->fs_contigsumsize < fs->fs_maxcontig && + fs->fs_contigsumsize < FS_MAXCONTIG) { + doit = "EXPAND"; + } + if (doit) { + i = fs->fs_contigsumsize; + fs->fs_contigsumsize = + MIN(fs->fs_maxcontig, FS_MAXCONTIG); + if (CGSIZE(fs) > fs->fs_bsize) { + pwarn("CANNOT %s CLUSTER MAPS\n", doit); + fs->fs_contigsumsize = i; + } else if (preen || + reply("CREATE CLUSTER MAPS")) { + if (preen) + pwarn("%sING CLUSTER MAPS\n", + doit); + fs->fs_cgsize = + fragroundup(fs, CGSIZE(fs)); + doinglevel1 = 1; + sbdirty(); + } + } + } + } + switch ((int)fs->fs_postblformat) { + + case FS_42POSTBLFMT: + basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link); + sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]); + mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] - + (u_char *)&ocg->cg_iused[0]; + ocg->cg_magic = CG_MAGIC; + savednrpos = fs->fs_nrpos; + fs->fs_nrpos = 8; + break; + + case FS_DYNAMICPOSTBLFMT: + newcg->cg_btotoff = + &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + newcg->cg_boff = + newcg->cg_btotoff + fs->fs_cpg * sizeof(long); + newcg->cg_iusedoff = newcg->cg_boff + + fs->fs_cpg * fs->fs_nrpos * sizeof(short); + newcg->cg_freeoff = + newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY); + if (fs->fs_contigsumsize <= 0) { + newcg->cg_nextfreeoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY); + } else { + newcg->cg_clustersumoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) - + sizeof(long); + newcg->cg_clustersumoff = + roundup(newcg->cg_clustersumoff, sizeof(long)); + newcg->cg_clusteroff = newcg->cg_clustersumoff + + (fs->fs_contigsumsize + 1) * sizeof(long); + newcg->cg_nextfreeoff = newcg->cg_clusteroff + + howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY); + } + newcg->cg_magic = CG_MAGIC; + basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; + mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; + break; + + default: + errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n", + fs->fs_postblformat); + } + bzero((char *)&idesc[0], sizeof idesc); + for (i = 0; i < 3; i++) { + idesc[i].id_type = ADDR; + if (doinglevel2) + idesc[i].id_fix = FIX; + } + bzero((char *)&cstotal, sizeof(struct csum)); + j = blknum(fs, fs->fs_size + fs->fs_frag - 1); + for (i = fs->fs_size; i < j; i++) + setbmap(i); + for (c = 0; c < fs->fs_ncg; c++) { + getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize); + if (!cg_chkmagic(cg)) + pfatal("CG %d: BAD MAGIC NUMBER\n", c); + dbase = cgbase(fs, c); + dmax = dbase + fs->fs_fpg; + if (dmax > fs->fs_size) + dmax = fs->fs_size; + newcg->cg_time = cg->cg_time; + newcg->cg_cgx = c; + if (c == fs->fs_ncg - 1) + newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg; + else + newcg->cg_ncyl = fs->fs_cpg; + newcg->cg_ndblk = dmax - dbase; + if (fs->fs_contigsumsize > 0) + newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag; + newcg->cg_cs.cs_ndir = 0; + newcg->cg_cs.cs_nffree = 0; + newcg->cg_cs.cs_nbfree = 0; + newcg->cg_cs.cs_nifree = fs->fs_ipg; + if (cg->cg_rotor < newcg->cg_ndblk) + newcg->cg_rotor = cg->cg_rotor; + else + newcg->cg_rotor = 0; + if (cg->cg_frotor < newcg->cg_ndblk) + newcg->cg_frotor = cg->cg_frotor; + else + newcg->cg_frotor = 0; + if (cg->cg_irotor < newcg->cg_niblk) + newcg->cg_irotor = cg->cg_irotor; + else + newcg->cg_irotor = 0; + bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum); + bzero((char *)&cg_blktot(newcg)[0], + (size_t)(sumsize + mapsize)); + if (fs->fs_postblformat == FS_42POSTBLFMT) + ocg->cg_magic = CG_MAGIC; + j = fs->fs_ipg * c; + for (i = 0; i < fs->fs_ipg; j++, i++) { + switch (statemap[j]) { + + case USTATE: + break; + + case DSTATE: + case DCLEAR: + case DFOUND: + newcg->cg_cs.cs_ndir++; + /* fall through */ + + case FSTATE: + case FCLEAR: + newcg->cg_cs.cs_nifree--; + setbit(cg_inosused(newcg), i); + break; + + default: + if (j < ROOTINO) + break; + errexit("BAD STATE %d FOR INODE I=%d", + statemap[j], j); + } + } + if (c == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(newcg), i); + newcg->cg_cs.cs_nifree--; + } + for (i = 0, d = dbase; + d < dmax; + d += fs->fs_frag, i += fs->fs_frag) { + frags = 0; + for (j = 0; j < fs->fs_frag; j++) { + if (testbmap(d + j)) + continue; + setbit(cg_blksfree(newcg), i + j); + frags++; + } + if (frags == fs->fs_frag) { + newcg->cg_cs.cs_nbfree++; + j = cbtocylno(fs, i); + cg_blktot(newcg)[j]++; + cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++; + if (fs->fs_contigsumsize > 0) + setbit(cg_clustersfree(newcg), + i / fs->fs_frag); + } else if (frags > 0) { + newcg->cg_cs.cs_nffree += frags; + blk = blkmap(fs, cg_blksfree(newcg), i); + ffs_fragacct(fs, blk, newcg->cg_frsum, 1); + } + } + if (fs->fs_contigsumsize > 0) { + long *sump = cg_clustersum(newcg); + u_char *mapp = cg_clustersfree(newcg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < newcg->cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + } + } + cstotal.cs_nffree += newcg->cg_cs.cs_nffree; + cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree; + cstotal.cs_nifree += newcg->cg_cs.cs_nifree; + cstotal.cs_ndir += newcg->cg_cs.cs_ndir; + cs = &fs->fs_cs(fs, c); + if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 && + dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs); + sbdirty(); + } + if (doinglevel1) { + bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize); + cgdirty(); + continue; + } + if (bcmp(cg_inosused(newcg), + cg_inosused(cg), mapsize) != 0 && + dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { + bcopy(cg_inosused(newcg), cg_inosused(cg), + (size_t)mapsize); + cgdirty(); + } + if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 || + bcmp((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], sumsize) != 0) && + dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { + bcopy((char *)newcg, (char *)cg, (size_t)basesize); + bcopy((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], (size_t)sumsize); + cgdirty(); + } + } + if (fs->fs_postblformat == FS_42POSTBLFMT) + fs->fs_nrpos = savednrpos; + if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0 + && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs); + fs->fs_ronly = 0; + fs->fs_fmod = 0; + sbdirty(); + } +} diff --git a/sbin/fsck_ffs/preen.c b/sbin/fsck_ffs/preen.c new file mode 100644 index 000000000000..005a65d97989 --- /dev/null +++ b/sbin/fsck_ffs/preen.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char *rawname(), *unrawname(), *blockcheck(); + +struct part { + struct part *next; /* forward link of partitions on disk */ + char *name; /* device name */ + char *fsname; /* mounted filesystem name */ + long auxdata; /* auxillary data for application */ +} *badlist, **badnext = &badlist; + +struct disk { + char *name; /* disk base name */ + struct disk *next; /* forward link for list of disks */ + struct part *part; /* head of list of partitions on disk */ + int pid; /* If != 0, pid of proc working on */ +} *disks; + +int nrun, ndisks; +char hotroot; + +checkfstab(preen, maxrun, docheck, chkit) + int preen, maxrun; + int (*docheck)(), (*chkit)(); +{ + register struct fstab *fsp; + register struct disk *dk, *nextdisk; + register struct part *pt; + int ret, pid, retcode, passno, sumstatus, status; + long auxdata; + char *name; + + sumstatus = 0; + for (passno = 1; passno <= 2; passno++) { + if (setfsent() == 0) { + fprintf(stderr, "Can't open checklist file: %s\n", + _PATH_FSTAB); + return (8); + } + while ((fsp = getfsent()) != 0) { + if ((auxdata = (*docheck)(fsp)) == 0) + continue; + if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { + if (name = blockcheck(fsp->fs_spec)) { + if (sumstatus = (*chkit)(name, + fsp->fs_file, auxdata, 0)) + return (sumstatus); + } else if (preen) + return (8); + } else if (passno == 2 && fsp->fs_passno > 1) { + if ((name = blockcheck(fsp->fs_spec)) == NULL) { + fprintf(stderr, "BAD DISK NAME %s\n", + fsp->fs_spec); + sumstatus |= 8; + continue; + } + addpart(name, fsp->fs_file, auxdata); + } + } + if (preen == 0) + return (0); + } + if (preen) { + if (maxrun == 0) + maxrun = ndisks; + if (maxrun > ndisks) + maxrun = ndisks; + nextdisk = disks; + for (passno = 0; passno < maxrun; ++passno) { + while (ret = startdisk(nextdisk, chkit) && nrun > 0) + sleep(10); + if (ret) + return (ret); + nextdisk = nextdisk->next; + } + while ((pid = wait(&status)) != -1) { + for (dk = disks; dk; dk = dk->next) + if (dk->pid == pid) + break; + if (dk == 0) { + printf("Unknown pid %d\n", pid); + continue; + } + if (WIFEXITED(status)) + retcode = WEXITSTATUS(status); + else + retcode = 0; + if (WIFSIGNALED(status)) { + printf("%s (%s): EXITED WITH SIGNAL %d\n", + dk->part->name, dk->part->fsname, + WTERMSIG(status)); + retcode = 8; + } + if (retcode != 0) { + sumstatus |= retcode; + *badnext = dk->part; + badnext = &dk->part->next; + dk->part = dk->part->next; + *badnext = NULL; + } else + dk->part = dk->part->next; + dk->pid = 0; + nrun--; + if (dk->part == NULL) + ndisks--; + + if (nextdisk == NULL) { + if (dk->part) { + while (ret = startdisk(dk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } else if (nrun < maxrun && nrun < ndisks) { + for ( ;; ) { + if ((nextdisk = nextdisk->next) == NULL) + nextdisk = disks; + if (nextdisk->part != NULL && + nextdisk->pid == 0) + break; + } + while (ret = startdisk(nextdisk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } + } + if (sumstatus) { + if (badlist == 0) + return (sumstatus); + fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", + badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); + for (pt = badlist; pt; pt = pt->next) + fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname, + pt->next ? ", " : "\n"); + return (sumstatus); + } + (void)endfsent(); + return (0); +} + +struct disk * +finddisk(name) + char *name; +{ + register struct disk *dk, **dkp; + register char *p; + size_t len; + + for (p = name + strlen(name) - 1; p >= name; --p) + if (isdigit(*p)) { + len = p - name + 1; + break; + } + if (p < name) + len = strlen(name); + + for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { + if (strncmp(dk->name, name, len) == 0 && + dk->name[len] == 0) + return (dk); + } + if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + dk = *dkp; + if ((dk->name = malloc(len + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strncpy(dk->name, name, len); + dk->name[len] = '\0'; + dk->part = NULL; + dk->next = NULL; + dk->pid = 0; + ndisks++; + return (dk); +} + +addpart(name, fsname, auxdata) + char *name, *fsname; + long auxdata; +{ + struct disk *dk = finddisk(name); + register struct part *pt, **ppt = &dk->part; + + for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) + if (strcmp(pt->name, name) == 0) { + printf("%s in fstab more than once!\n", name); + return; + } + if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + pt = *ppt; + if ((pt->name = malloc(strlen(name) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->name, name); + if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->fsname, fsname); + pt->next = NULL; + pt->auxdata = auxdata; +} + +startdisk(dk, checkit) + register struct disk *dk; + int (*checkit)(); +{ + register struct part *pt = dk->part; + + dk->pid = fork(); + if (dk->pid < 0) { + perror("fork"); + return (8); + } + if (dk->pid == 0) + exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1)); + nrun++; + return (0); +} + +char * +blockcheck(name) + char *name; +{ + struct stat stslash, stblock, stchar; + char *raw; + int retried = 0; + + hotroot = 0; + if (stat("/", &stslash) < 0) { + perror("/"); + printf("Can't stat root\n"); + return (0); + } +retry: + if (stat(name, &stblock) < 0) { + perror(name); + printf("Can't stat %s\n", name); + return (0); + } + if ((stblock.st_mode & S_IFMT) == S_IFBLK) { + if (stslash.st_dev == stblock.st_rdev) + hotroot++; + raw = rawname(name); + if (stat(raw, &stchar) < 0) { + perror(raw); + printf("Can't stat %s\n", raw); + return (name); + } + if ((stchar.st_mode & S_IFMT) == S_IFCHR) { + return (raw); + } else { + printf("%s is not a character device\n", raw); + return (name); + } + } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { + name = unrawname(name); + retried++; + goto retry; + } + printf("Can't make sense out of name %s\n", name); + return (0); +} + +char * +unrawname(name) + char *name; +{ + char *dp; + struct stat stb; + + if ((dp = rindex(name, '/')) == 0) + return (name); + if (stat(name, &stb) < 0) + return (name); + if ((stb.st_mode & S_IFMT) != S_IFCHR) + return (name); + if (dp[1] != 'r') + return (name); + (void)strcpy(&dp[1], &dp[2]); + return (name); +} + +char * +rawname(name) + char *name; +{ + static char rawbuf[32]; + char *dp; + + if ((dp = rindex(name, '/')) == 0) + return (0); + *dp = 0; + (void)strcpy(rawbuf, name); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, &dp[1]); + return (rawbuf); +} diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c new file mode 100644 index 000000000000..a32b23cc294d --- /dev/null +++ b/sbin/fsck_ffs/setup.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94"; +#endif /* not lint */ + +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +struct disklabel *getdisklabel(); + +setup(dev) + char *dev; +{ + long cg, size, asked, i, j; + long bmapsize; + struct disklabel *lp; + off_t sizepb; + struct stat statb; + struct fs proto; + + havesb = 0; + fswritefd = -1; + if (stat(dev, &statb) < 0) { + printf("Can't stat %s: %s\n", dev, strerror(errno)); + return (0); + } + if ((statb.st_mode & S_IFMT) != S_IFCHR) { + pfatal("%s is not a character device", dev); + if (reply("CONTINUE") == 0) + return (0); + } + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + printf("Can't open %s: %s\n", dev, strerror(errno)); + return (0); + } + if (preen == 0) + printf("** %s", dev); + if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) { + fswritefd = -1; + if (preen) + pfatal("NO WRITE ACCESS"); + printf(" (NO WRITE)"); + } + if (preen == 0) + printf("\n"); + fsmodified = 0; + lfdir = 0; + initbarea(&sblk); + initbarea(&asblk); + sblk.b_un.b_buf = malloc(SBSIZE); + asblk.b_un.b_buf = malloc(SBSIZE); + if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) + errexit("cannot allocate space for superblock\n"); + if (lp = getdisklabel((char *)NULL, fsreadfd)) + dev_bsize = secsize = lp->d_secsize; + else + dev_bsize = secsize = DEV_BSIZE; + /* + * Read in the superblock, looking for alternates if necessary + */ + if (readsb(1) == 0) { + if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) + return(0); + if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) + return (0); + for (cg = 0; cg < proto.fs_ncg; cg++) { + bflag = fsbtodb(&proto, cgsblock(&proto, cg)); + if (readsb(0) != 0) + break; + } + if (cg >= proto.fs_ncg) { + printf("%s %s\n%s %s\n%s %s\n", + "SEARCH FOR ALTERNATE SUPER-BLOCK", + "FAILED. YOU MUST USE THE", + "-b OPTION TO FSCK TO SPECIFY THE", + "LOCATION OF AN ALTERNATE", + "SUPER-BLOCK TO SUPPLY NEEDED", + "INFORMATION; SEE fsck(8)."); + return(0); + } + pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); + } + maxfsblock = sblock.fs_size; + maxino = sblock.fs_ncg * sblock.fs_ipg; + /* + * Check and potentially fix certain fields in the super block. + */ + if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { + pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_optim = FS_OPTTIME; + sbdirty(); + } + } + if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { + pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", + sblock.fs_minfree); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_minfree = 10; + sbdirty(); + } + } + if (sblock.fs_interleave < 1 || + sblock.fs_interleave > sblock.fs_nsect) { + pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK", + sblock.fs_interleave); + sblock.fs_interleave = 1; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_npsect < sblock.fs_nsect || + sblock.fs_npsect > sblock.fs_nsect*2) { + pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK", + sblock.fs_npsect); + sblock.fs_npsect = sblock.fs_nsect; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_inodefmt >= FS_44INODEFMT) { + newinofmt = 1; + } else { + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + newinofmt = 0; + } + /* + * Convert to new inode format. + */ + if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) { + if (preen) + pwarn("CONVERTING TO NEW INODE FORMAT\n"); + else if (!reply("CONVERT TO NEW INODE FORMAT")) + return(0); + doinglevel2++; + sblock.fs_inodefmt = FS_44INODEFMT; + sizepb = sblock.fs_bsize; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + sblock.fs_maxsymlinklen = MAXSYMLINKLEN; + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + sbdirty(); + dirty(&asblk); + } + /* + * Convert to new cylinder group format. + */ + if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) { + if (preen) + pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n"); + else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT")) + return(0); + doinglevel1++; + sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; + sblock.fs_nrpos = 8; + sblock.fs_postbloff = + (char *)(&sblock.fs_opostbl[0][0]) - + (char *)(&sblock.fs_link); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + sblock.fs_cgsize = + fragroundup(&sblock, CGSIZE(&sblock)); + sbdirty(); + dirty(&asblk); + } + if (asblk.b_dirty) { + bcopy((char *)&sblock, (char *)&altsblock, + (size_t)sblock.fs_sbsize); + flush(fswritefd, &asblk); + } + /* + * read in the summary info. + */ + asked = 0; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + size = sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize; + sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); + if (bread(fsreadfd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + size) != 0 && !asked) { + pfatal("BAD SUMMARY INFORMATION"); + if (reply("CONTINUE") == 0) + errexit(""); + asked++; + } + } + /* + * allocate and initialize the necessary maps + */ + bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short)); + blockmap = calloc((unsigned)bmapsize, sizeof (char)); + if (blockmap == NULL) { + printf("cannot alloc %u bytes for blockmap\n", + (unsigned)bmapsize); + goto badsb; + } + statemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (statemap == NULL) { + printf("cannot alloc %u bytes for statemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + typemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (typemap == NULL) { + printf("cannot alloc %u bytes for typemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short)); + if (lncntp == NULL) { + printf("cannot alloc %u bytes for lncntp\n", + (unsigned)(maxino + 1) * sizeof(short)); + goto badsb; + } + numdirs = sblock.fs_cstotal.cs_ndir; + inplast = 0; + listmax = numdirs + 10; + inpsort = (struct inoinfo **)calloc((unsigned)listmax, + sizeof(struct inoinfo *)); + inphead = (struct inoinfo **)calloc((unsigned)numdirs, + sizeof(struct inoinfo *)); + if (inpsort == NULL || inphead == NULL) { + printf("cannot alloc %u bytes for inphead\n", + (unsigned)numdirs * sizeof(struct inoinfo *)); + goto badsb; + } + bufinit(); + return (1); + +badsb: + ckfini(); + return (0); +} + +/* + * Read in the super block and its summary info. + */ +readsb(listerr) + int listerr; +{ + daddr_t super = bflag ? bflag : SBOFF / dev_bsize; + + if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0) + return (0); + sblk.b_bno = super; + sblk.b_size = SBSIZE; + /* + * run a few consistency checks of the super block + */ + if (sblock.fs_magic != FS_MAGIC) + { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); } + if (sblock.fs_ncg < 1) + { badsb(listerr, "NCG OUT OF RANGE"); return (0); } + if (sblock.fs_cpg < 1) + { badsb(listerr, "CPG OUT OF RANGE"); return (0); } + if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || + (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) + { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); } + if (sblock.fs_sbsize > SBSIZE) + { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); } + /* + * Compute block size that the filesystem is based on, + * according to fsbtodb, and adjust superblock block number + * so we can tell if this is an alternate later. + */ + super *= dev_bsize; + dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); + sblk.b_bno = super / dev_bsize; + if (bflag) { + havesb = 1; + return (1); + } + /* + * Set all possible fields that could differ, then do check + * of whole super block against an alternate super block. + * When an alternate super-block is specified this check is skipped. + */ + getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); + if (asblk.b_errs) + return (0); + altsblock.fs_link = sblock.fs_link; + altsblock.fs_rlink = sblock.fs_rlink; + altsblock.fs_time = sblock.fs_time; + altsblock.fs_cstotal = sblock.fs_cstotal; + altsblock.fs_cgrotor = sblock.fs_cgrotor; + altsblock.fs_fmod = sblock.fs_fmod; + altsblock.fs_clean = sblock.fs_clean; + altsblock.fs_ronly = sblock.fs_ronly; + altsblock.fs_flags = sblock.fs_flags; + altsblock.fs_maxcontig = sblock.fs_maxcontig; + altsblock.fs_minfree = sblock.fs_minfree; + altsblock.fs_optim = sblock.fs_optim; + altsblock.fs_rotdelay = sblock.fs_rotdelay; + altsblock.fs_maxbpg = sblock.fs_maxbpg; + bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp, + sizeof sblock.fs_csp); + bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt, + sizeof sblock.fs_fsmnt); + bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon, + sizeof sblock.fs_sparecon); + /* + * The following should not have to be copied. + */ + altsblock.fs_fsbtodb = sblock.fs_fsbtodb; + altsblock.fs_interleave = sblock.fs_interleave; + altsblock.fs_npsect = sblock.fs_npsect; + altsblock.fs_nrpos = sblock.fs_nrpos; + altsblock.fs_qbmask = sblock.fs_qbmask; + altsblock.fs_qfmask = sblock.fs_qfmask; + altsblock.fs_state = sblock.fs_state; + altsblock.fs_maxfilesize = sblock.fs_maxfilesize; + if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) { + badsb(listerr, + "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); + return (0); + } + havesb = 1; + return (1); +} + +badsb(listerr, s) + int listerr; + char *s; +{ + + if (!listerr) + return; + if (preen) + printf("%s: ", cdevname); + pfatal("BAD SUPER BLOCK: %s\n", s); +} + +/* + * Calculate a prototype superblock based on information in the disk label. + * When done the cgsblock macro can be calculated and the fs_ncg field + * can be used. Do NOT attempt to use other macros without verifying that + * their needed information is available! + */ +calcsb(dev, devfd, fs) + char *dev; + int devfd; + register struct fs *fs; +{ + register struct disklabel *lp; + register struct partition *pp; + register char *cp; + int i; + + cp = index(dev, '\0') - 1; + if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) { + pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev); + return (0); + } + lp = getdisklabel(dev, devfd); + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_fstype != FS_BSDFFS) { + pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n", + dev, pp->p_fstype < FSMAXTYPES ? + fstypenames[pp->p_fstype] : "unknown"); + return (0); + } + bzero((char *)fs, sizeof(struct fs)); + fs->fs_fsize = pp->p_fsize; + fs->fs_frag = pp->p_frag; + fs->fs_cpg = pp->p_cpg; + fs->fs_size = pp->p_size; + fs->fs_ntrak = lp->d_ntracks; + fs->fs_nsect = lp->d_nsectors; + fs->fs_spc = lp->d_secpercyl; + fs->fs_nspf = fs->fs_fsize / lp->d_secsize; + fs->fs_sblkno = roundup( + howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize), + fs->fs_frag); + fs->fs_cgmask = 0xffffffff; + for (i = fs->fs_ntrak; i > 1; i >>= 1) + fs->fs_cgmask <<= 1; + if (!POWEROF2(fs->fs_ntrak)) + fs->fs_cgmask <<= 1; + fs->fs_cgoffset = roundup( + howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag); + fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs); + fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg); + for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1) + fs->fs_fsbtodb++; + dev_bsize = lp->d_secsize; + return (1); +} + +struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { + if (s == NULL) + return ((struct disklabel *)NULL); + pwarn("ioctl (GCINFO): %s\n", strerror(errno)); + errexit("%s: can't read disk label\n", s); + } + return (&lab); +} diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c new file mode 100644 index 000000000000..64a4cac19ab3 --- /dev/null +++ b/sbin/fsck_ffs/utilities.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +long diskreads, totalreads; /* Disk cache statistics */ + +ftypeok(dp) + struct dinode *dp; +{ + switch (dp->di_mode & IFMT) { + + case IFDIR: + case IFREG: + case IFBLK: + case IFCHR: + case IFLNK: + case IFSOCK: + case IFIFO: + return (1); + + default: + if (debug) + printf("bad file type 0%o\n", dp->di_mode); + return (0); + } +} + +reply(question) + char *question; +{ + int persevere; + char c; + + if (preen) + pfatal("INTERNAL ERROR: GOT TO reply()"); + persevere = !strcmp(question, "CONTINUE"); + printf("\n"); + if (!persevere && (nflag || fswritefd < 0)) { + printf("%s? no\n\n", question); + return (0); + } + if (yflag || (persevere && nflag)) { + printf("%s? yes\n\n", question); + return (1); + } + do { + printf("%s? [yn] ", question); + (void) fflush(stdout); + c = getc(stdin); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return (0); + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + printf("\n"); + if (c == 'y' || c == 'Y') + return (1); + return (0); +} + +/* + * Malloc buffers and set up cache. + */ +bufinit() +{ + register struct bufarea *bp; + long bufcnt, i; + char *bufp; + + pbp = pdirbp = (struct bufarea *)0; + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bufp == 0) + errexit("cannot allocate buffer pool\n"); + cgblk.b_un.b_buf = bufp; + initbarea(&cgblk); + bufhead.b_next = bufhead.b_prev = &bufhead; + bufcnt = MAXBUFSPACE / sblock.fs_bsize; + if (bufcnt < MINBUFS) + bufcnt = MINBUFS; + for (i = 0; i < bufcnt; i++) { + bp = (struct bufarea *)malloc(sizeof(struct bufarea)); + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bp == NULL || bufp == NULL) { + if (i >= MINBUFS) + break; + errexit("cannot allocate buffer pool\n"); + } + bp->b_un.b_buf = bufp; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + initbarea(bp); + } + bufhead.b_size = i; /* save number of buffers */ +} + +/* + * Manage a cache of directory blocks. + */ +struct bufarea * +getdatablk(blkno, size) + daddr_t blkno; + long size; +{ + register struct bufarea *bp; + + for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) + if (bp->b_bno == fsbtodb(&sblock, blkno)) + goto foundit; + for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) + if ((bp->b_flags & B_INUSE) == 0) + break; + if (bp == &bufhead) + errexit("deadlocked buffer pool\n"); + getblk(bp, blkno, size); + /* fall through */ +foundit: + totalreads++; + bp->b_prev->b_next = bp->b_next; + bp->b_next->b_prev = bp->b_prev; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + bp->b_flags |= B_INUSE; + return (bp); +} + +void +getblk(bp, blk, size) + register struct bufarea *bp; + daddr_t blk; + long size; +{ + daddr_t dblk; + + dblk = fsbtodb(&sblock, blk); + if (bp->b_bno != dblk) { + flush(fswritefd, bp); + diskreads++; + bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); + bp->b_bno = dblk; + bp->b_size = size; + } +} + +flush(fd, bp) + int fd; + register struct bufarea *bp; +{ + register int i, j; + + if (!bp->b_dirty) + return; + if (bp->b_errs != 0) + pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", + (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", + bp->b_bno); + bp->b_dirty = 0; + bp->b_errs = 0; + bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); + if (bp != &sblk) + return; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + bwrite(fswritefd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize); + } +} + +rwerror(mesg, blk) + char *mesg; + daddr_t blk; +{ + + if (preen == 0) + printf("\n"); + pfatal("CANNOT %s: BLK %ld", mesg, blk); + if (reply("CONTINUE") == 0) + errexit("Program terminated\n"); +} + +ckfini() +{ + register struct bufarea *bp, *nbp; + int cnt = 0; + + if (fswritefd < 0) { + (void)close(fsreadfd); + return; + } + flush(fswritefd, &sblk); + if (havesb && sblk.b_bno != SBOFF / dev_bsize && + !preen && reply("UPDATE STANDARD SUPERBLOCK")) { + sblk.b_bno = SBOFF / dev_bsize; + sbdirty(); + flush(fswritefd, &sblk); + } + flush(fswritefd, &cgblk); + free(cgblk.b_un.b_buf); + for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { + cnt++; + flush(fswritefd, bp); + nbp = bp->b_prev; + free(bp->b_un.b_buf); + free((char *)bp); + } + if (bufhead.b_size != cnt) + errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); + pbp = pdirbp = (struct bufarea *)0; + if (debug) + printf("cache missed %ld of %ld (%d%%)\n", diskreads, + totalreads, (int)(diskreads * 100 / totalreads)); + (void)close(fsreadfd); + (void)close(fswritefd); +} + +bread(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + char *cp; + int i, errs; + off_t offset; + + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (read(fd, buf, (int)size) == size) + return (0); + rwerror("READ", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + errs = 0; + bzero(buf, (size_t)size); + printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); + for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { + if (read(fd, cp, (int)secsize) != secsize) { + (void)lseek(fd, offset + i + secsize, 0); + if (secsize != dev_bsize && dev_bsize != 1) + printf(" %ld (%ld),", + (blk * dev_bsize + i) / secsize, + blk + i / dev_bsize); + else + printf(" %ld,", blk + i / dev_bsize); + errs++; + } + } + printf("\n"); + return (errs); +} + +bwrite(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + int i; + char *cp; + off_t offset; + + if (fd < 0) + return; + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (write(fd, buf, (int)size) == size) { + fsmodified = 1; + return; + } + rwerror("WRITE", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); + for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) + if (write(fd, cp, (int)dev_bsize) != dev_bsize) { + (void)lseek(fd, offset + i + dev_bsize, 0); + printf(" %ld,", blk + i / dev_bsize); + } + printf("\n"); + return; +} + +/* + * allocate a data block with the specified number of fragments + */ +allocblk(frags) + long frags; +{ + register int i, j, k; + + if (frags <= 0 || frags > sblock.fs_frag) + return (0); + for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (testbmap(i + j)) + continue; + for (k = 1; k < frags; k++) + if (testbmap(i + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + for (k = 0; k < frags; k++) + setbmap(i + j + k); + n_blks += frags; + return (i + j); + } + } + return (0); +} + +/* + * Free a previously allocated block + */ +freeblk(blkno, frags) + daddr_t blkno; + long frags; +{ + struct inodesc idesc; + + idesc.id_blkno = blkno; + idesc.id_numfrags = frags; + (void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +getpathname(namebuf, curdir, ino) + char *namebuf; + ino_t curdir, ino; +{ + int len; + register char *cp; + struct inodesc idesc; + static int busy = 0; + extern int findname(); + + if (curdir == ino && ino == ROOTINO) { + (void)strcpy(namebuf, "/"); + return; + } + if (busy || + (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { + (void)strcpy(namebuf, "?"); + return; + } + busy = 1; + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_fix = IGNORE; + cp = &namebuf[MAXPATHLEN - 1]; + *cp = '\0'; + if (curdir != ino) { + idesc.id_parent = curdir; + goto namelookup; + } + while (ino != ROOTINO) { + idesc.id_number = ino; + idesc.id_func = findino; + idesc.id_name = ".."; + if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) + break; + namelookup: + idesc.id_number = idesc.id_parent; + idesc.id_parent = ino; + idesc.id_func = findname; + idesc.id_name = namebuf; + if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) + break; + len = strlen(namebuf); + cp -= len; + bcopy(namebuf, cp, (size_t)len); + *--cp = '/'; + if (cp < &namebuf[MAXNAMLEN]) + break; + ino = idesc.id_number; + } + busy = 0; + if (ino != ROOTINO) + *--cp = '?'; + bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch() +{ + if (!doinglevel2) + ckfini(); + exit(12); +} + +/* + * When preening, allow a single quit to signal + * a special exit after filesystem checks complete + * so that reboot sequence may be interrupted. + */ +void +catchquit() +{ + extern returntosingle; + + printf("returning to single-user after filesystem check\n"); + returntosingle = 1; + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * Ignore a single quit signal; wait and flush just in case. + * Used by child processes in preen. + */ +void +voidquit() +{ + + sleep(1); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +dofix(idesc, msg) + register struct inodesc *idesc; + char *msg; +{ + + switch (idesc->id_fix) { + + case DONTKNOW: + if (idesc->id_type == DATA) + direrror(idesc->id_number, msg); + else + pwarn(msg); + if (preen) { + printf(" (SALVAGED)\n"); + idesc->id_fix = FIX; + return (ALTERED); + } + if (reply("SALVAGE") == 0) { + idesc->id_fix = NOFIX; + return (0); + } + idesc->id_fix = FIX; + return (ALTERED); + + case FIX: + return (ALTERED); + + case NOFIX: + case IGNORE: + return (0); + + default: + errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); + } + /* NOTREACHED */ +} + +/* VARARGS1 */ +errexit(s1, s2, s3, s4) + char *s1; +{ + printf(s1, s2, s3, s4); + exit(8); +} + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +/* VARARGS1 */ +pfatal(s, a1, a2, a3) + char *s; +{ + + if (preen) { + printf("%s: ", cdevname); + printf(s, a1, a2, a3); + printf("\n"); + printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", + cdevname); + exit(8); + } + printf(s, a1, a2, a3); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +/* VARARGS1 */ +pwarn(s, a1, a2, a3, a4, a5, a6) + char *s; +{ + + if (preen) + printf("%s: ", cdevname); + printf(s, a1, a2, a3, a4, a5, a6); +} + +#ifndef lint +/* + * Stub for routines from kernel. + */ +panic(s) + char *s; +{ + + pfatal("INTERNAL INCONSISTENCY:"); + errexit(s); +} +#endif diff --git a/sbin/fsck_ifs/Makefile b/sbin/fsck_ifs/Makefile new file mode 100644 index 000000000000..224f6b2200e7 --- /dev/null +++ b/sbin/fsck_ifs/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= fsck +MAN8= fsck.0 +SRCS= dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ + pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c +.PATH: ${.CURDIR}/../../sys/ufs/ffs + +.include diff --git a/sbin/fsck_ifs/dir.c b/sbin/fsck_ifs/dir.c new file mode 100644 index 000000000000..ee5d095e6c75 --- /dev/null +++ b/sbin/fsck_ifs/dir.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +char *lfname = "lost+found"; +int lfmode = 01777; +struct dirtemplate emptydir = { 0, DIRBLKSIZ }; +struct dirtemplate dirhead = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +struct odirtemplate odirhead = { + 0, 12, 1, ".", + 0, DIRBLKSIZ - 12, 2, ".." +}; + +struct direct *fsck_readdir(); +struct bufarea *getdirblk(); + +/* + * Propagate connected state through the tree. + */ +propagate() +{ + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + long change; + + inpend = &inpsort[inplast]; + do { + change = 0; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) { + statemap[inp->i_number] = DFOUND; + change++; + } + } + } while (change > 0); +} + +/* + * Scan each entry in a directory block. + */ +dirscan(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp; + register struct bufarea *bp; + int dsize, n; + long blksiz; + char dbuf[DIRBLKSIZ]; + + if (idesc->id_type != DATA) + errexit("wrong type to dirscan %d\n", idesc->id_type); + if (idesc->id_entryno == 0 && + (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) + idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); + blksiz = idesc->id_numfrags * sblock.fs_fsize; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { + idesc->id_filesize -= blksiz; + return (SKIP); + } + idesc->id_loc = 0; + for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { + dsize = dp->d_reclen; + bcopy((char *)dp, dbuf, (size_t)dsize); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + struct direct *tdp = (struct direct *)dbuf; + u_char tmp; + + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + idesc->id_dirp = (struct direct *)dbuf; + if ((n = (*idesc->id_func)(idesc)) & ALTERED) { +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt && !doinglevel2) { + struct direct *tdp; + u_char tmp; + + tdp = (struct direct *)dbuf; + tmp = tdp->d_namlen; + tdp->d_namlen = tdp->d_type; + tdp->d_type = tmp; + } +# endif + bp = getdirblk(idesc->id_blkno, blksiz); + bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, + (size_t)dsize); + dirty(bp); + sbdirty(); + } + if (n & STOP) + return (n); + } + return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +struct direct * +fsck_readdir(idesc) + register struct inodesc *idesc; +{ + register struct direct *dp, *ndp; + register struct bufarea *bp; + long size, blksiz, fix, dploc; + + blksiz = idesc->id_numfrags * sblock.fs_fsize; + bp = getdirblk(idesc->id_blkno, blksiz); + if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && + idesc->id_loc < blksiz) { + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (dircheck(idesc, dp)) + goto dpok; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + dp->d_reclen = DIRBLKSIZ; + dp->d_ino = 0; + dp->d_type = 0; + dp->d_namlen = 0; + dp->d_name[0] = '\0'; + if (fix) + dirty(bp); + idesc->id_loc += DIRBLKSIZ; + idesc->id_filesize -= DIRBLKSIZ; + return (dp); + } +dpok: + if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) + return NULL; + dploc = idesc->id_loc; + dp = (struct direct *)(bp->b_un.b_buf + dploc); + idesc->id_loc += dp->d_reclen; + idesc->id_filesize -= dp->d_reclen; + if ((idesc->id_loc % DIRBLKSIZ) == 0) + return (dp); + ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); + if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && + dircheck(idesc, ndp) == 0) { + size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); + idesc->id_loc += size; + idesc->id_filesize -= size; + fix = dofix(idesc, "DIRECTORY CORRUPTED"); + bp = getdirblk(idesc->id_blkno, blksiz); + dp = (struct direct *)(bp->b_un.b_buf + dploc); + dp->d_reclen += size; + if (fix) + dirty(bp); + } + return (dp); +} + +/* + * Verify that a directory entry is valid. + * This is a superset of the checks made in the kernel. + */ +dircheck(idesc, dp) + struct inodesc *idesc; + register struct direct *dp; +{ + register int size; + register char *cp; + u_char namlen, type; + int spaceleft; + + size = DIRSIZ(!newinofmt, dp); + spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (!newinofmt) { + type = dp->d_namlen; + namlen = dp->d_type; + } else { + namlen = dp->d_namlen; + type = dp->d_type; + } +# else + namlen = dp->d_namlen; + type = dp->d_type; +# endif + if (dp->d_ino < maxino && + dp->d_reclen != 0 && + dp->d_reclen <= spaceleft && + (dp->d_reclen & 0x3) == 0 && + dp->d_reclen >= size && + idesc->id_filesize >= size && + namlen <= MAXNAMLEN && + type <= 15) { + if (dp->d_ino == 0) + return (1); + for (cp = dp->d_name, size = 0; size < namlen; size++) + if (*cp == 0 || (*cp++ == '/')) + return (0); + if (*cp == 0) + return (1); + } + return (0); +} + +direrror(ino, errmesg) + ino_t ino; + char *errmesg; +{ + + fileerror(ino, ino, errmesg); +} + +fileerror(cwd, ino, errmesg) + ino_t cwd, ino; + char *errmesg; +{ + register struct dinode *dp; + char pathbuf[MAXPATHLEN + 1]; + + pwarn("%s ", errmesg); + pinode(ino); + printf("\n"); + getpathname(pathbuf, cwd, ino); + if (ino < ROOTINO || ino > maxino) { + pfatal("NAME=%s\n", pathbuf); + return; + } + dp = ginode(ino); + if (ftypeok(dp)) + pfatal("%s=%s\n", + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); + else + pfatal("NAME=%s\n", pathbuf); +} + +adjust(idesc, lcnt) + register struct inodesc *idesc; + short lcnt; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (dp->di_nlink == lcnt) { + if (linkup(idesc->id_number, (ino_t)0) == 0) + clri(idesc, "UNREF", 0); + } else { + pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : + ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); + pinode(idesc->id_number); + printf(" COUNT %d SHOULD BE %d", + dp->di_nlink, dp->di_nlink - lcnt); + if (preen) { + if (lcnt < 0) { + printf("\n"); + pfatal("LINK COUNT INCREASING"); + } + printf(" (ADJUSTED)\n"); + } + if (preen || reply("ADJUST") == 1) { + dp->di_nlink -= lcnt; + inodirty(); + } + } +} + +mkentry(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + struct direct newent; + int newlen, oldlen; + + newent.d_namlen = strlen(idesc->id_name); + newlen = DIRSIZ(0, &newent); + if (dirp->d_ino != 0) + oldlen = DIRSIZ(0, dirp); + else + oldlen = 0; + if (dirp->d_reclen - oldlen < newlen) + return (KEEPON); + newent.d_reclen = dirp->d_reclen - oldlen; + dirp->d_reclen = oldlen; + dirp = (struct direct *)(((char *)dirp) + oldlen); + dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + dirp->d_reclen = newent.d_reclen; + dirp->d_namlen = newent.d_namlen; + bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); + return (ALTERED|STOP); +} + +chgino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) + return (KEEPON); + dirp->d_ino = idesc->id_parent; + if (newinofmt) + dirp->d_type = typemap[idesc->id_parent]; + else + dirp->d_type = 0; + return (ALTERED|STOP); +} + +linkup(orphan, parentdir) + ino_t orphan; + ino_t parentdir; +{ + register struct dinode *dp; + int lostdir; + ino_t oldlfdir; + struct inodesc idesc; + char tempname[BUFSIZ]; + extern int pass4check(); + + bzero((char *)&idesc, sizeof(struct inodesc)); + dp = ginode(orphan); + lostdir = (dp->di_mode & IFMT) == IFDIR; + pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); + pinode(orphan); + if (preen && dp->di_size == 0) + return (0); + if (preen) + printf(" (RECONNECTED)\n"); + else + if (reply("RECONNECT") == 0) + return (0); + if (lfdir == 0) { + dp = ginode(ROOTINO); + idesc.id_name = lfname; + idesc.id_type = DATA; + idesc.id_func = findino; + idesc.id_number = ROOTINO; + if ((ckinode(dp, &idesc) & FOUND) != 0) { + lfdir = idesc.id_parent; + } else { + pwarn("NO lost+found DIRECTORY"); + if (preen || reply("CREATE")) { + lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); + if (lfdir != 0) { + if (makeentry(ROOTINO, lfdir, lfname) != 0) { + if (preen) + printf(" (CREATED)\n"); + } else { + freedir(lfdir, ROOTINO); + lfdir = 0; + if (preen) + printf("\n"); + } + } + } + } + if (lfdir == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + } + dp = ginode(lfdir); + if ((dp->di_mode & IFMT) != IFDIR) { + pfatal("lost+found IS NOT A DIRECTORY"); + if (reply("REALLOCATE") == 0) + return (0); + oldlfdir = lfdir; + if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { + pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); + return (0); + } + inodirty(); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = oldlfdir; + adjust(&idesc, lncntp[oldlfdir] + 1); + lncntp[oldlfdir] = 0; + dp = ginode(lfdir); + } + if (statemap[lfdir] != DFOUND) { + pfatal("SORRY. NO lost+found DIRECTORY\n\n"); + return (0); + } + (void)lftempname(tempname, orphan); + if (makeentry(lfdir, orphan, tempname) == 0) { + pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); + printf("\n\n"); + return (0); + } + lncntp[orphan]--; + if (lostdir) { + if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + parentdir != (ino_t)-1) + (void)makeentry(orphan, lfdir, ".."); + dp = ginode(lfdir); + dp->di_nlink++; + inodirty(); + lncntp[lfdir]++; + pwarn("DIR I=%lu CONNECTED. ", orphan); + if (parentdir != (ino_t)-1) + printf("PARENT WAS I=%lu\n", parentdir); + if (preen == 0) + printf("\n"); + } + return (1); +} + +/* + * fix an entry in a directory. + */ +changeino(dir, name, newnum) + ino_t dir; + char *name; + ino_t newnum; +{ + struct inodesc idesc; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = chgino; + idesc.id_number = dir; + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + idesc.id_parent = newnum; /* new value for name */ + return (ckinode(ginode(dir), &idesc)); +} + +/* + * make an entry in a directory + */ +makeentry(parent, ino, name) + ino_t parent, ino; + char *name; +{ + struct dinode *dp; + struct inodesc idesc; + char pathbuf[MAXPATHLEN + 1]; + + if (parent < ROOTINO || parent >= maxino || + ino < ROOTINO || ino >= maxino) + return (0); + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_func = mkentry; + idesc.id_number = parent; + idesc.id_parent = ino; /* this is the inode to enter */ + idesc.id_fix = DONTKNOW; + idesc.id_name = name; + dp = ginode(parent); + if (dp->di_size % DIRBLKSIZ) { + dp->di_size = roundup(dp->di_size, DIRBLKSIZ); + inodirty(); + } + if ((ckinode(dp, &idesc) & ALTERED) != 0) + return (1); + getpathname(pathbuf, parent, parent); + dp = ginode(parent); + if (expanddir(dp, pathbuf) == 0) + return (0); + return (ckinode(dp, &idesc) & ALTERED); +} + +/* + * Attempt to expand the size of a directory + */ +expanddir(dp, name) + register struct dinode *dp; + char *name; +{ + daddr_t lastbn, newblk; + register struct bufarea *bp; + char *cp, firstblk[DIRBLKSIZ]; + + lastbn = lblkno(&sblock, dp->di_size); + if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) + return (0); + if ((newblk = allocblk(sblock.fs_frag)) == 0) + return (0); + dp->di_db[lastbn + 1] = dp->di_db[lastbn]; + dp->di_db[lastbn] = newblk; + dp->di_size += sblock.fs_bsize; + dp->di_blocks += btodb(sblock.fs_bsize); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); + bp = getdirblk(newblk, sblock.fs_bsize); + if (bp->b_errs) + goto bad; + bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_bsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + bp = getdirblk(dp->di_db[lastbn + 1], + (long)dblksize(&sblock, dp, lastbn + 1)); + if (bp->b_errs) + goto bad; + bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); + pwarn("NO SPACE LEFT IN %s", name); + if (preen) + printf(" (EXPANDED)\n"); + else if (reply("EXPAND") == 0) + goto bad; + dirty(bp); + inodirty(); + return (1); +bad: + dp->di_db[lastbn] = dp->di_db[lastbn + 1]; + dp->di_db[lastbn + 1] = 0; + dp->di_size -= sblock.fs_bsize; + dp->di_blocks -= btodb(sblock.fs_bsize); + freeblk(newblk, sblock.fs_frag); + return (0); +} + +/* + * allocate a new directory + */ +allocdir(parent, request, mode) + ino_t parent, request; + int mode; +{ + ino_t ino; + char *cp; + struct dinode *dp; + register struct bufarea *bp; + struct dirtemplate *dirp; + + ino = allocino(request, IFDIR|mode); + if (newinofmt) + dirp = &dirhead; + else + dirp = (struct dirtemplate *)&odirhead; + dirp->dot_ino = ino; + dirp->dotdot_ino = parent; + dp = ginode(ino); + bp = getdirblk(dp->di_db[0], sblock.fs_fsize); + if (bp->b_errs) { + freeino(ino); + return (0); + } + bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); + for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; + cp < &bp->b_un.b_buf[sblock.fs_fsize]; + cp += DIRBLKSIZ) + bcopy((char *)&emptydir, cp, sizeof emptydir); + dirty(bp); + dp->di_nlink = 2; + inodirty(); + if (ino == ROOTINO) { + lncntp[ino] = dp->di_nlink; + cacheino(dp, ino); + return(ino); + } + if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { + freeino(ino); + return (0); + } + cacheino(dp, ino); + statemap[ino] = statemap[parent]; + if (statemap[ino] == DSTATE) { + lncntp[ino] = dp->di_nlink; + lncntp[parent]++; + } + dp = ginode(parent); + dp->di_nlink++; + inodirty(); + return (ino); +} + +/* + * free a directory inode + */ +freedir(ino, parent) + ino_t ino, parent; +{ + struct dinode *dp; + + if (ino != parent) { + dp = ginode(parent); + dp->di_nlink--; + inodirty(); + } + freeino(ino); +} + +/* + * generate a temporary name for the lost+found directory. + */ +lftempname(bufp, ino) + char *bufp; + ino_t ino; +{ + register ino_t in; + register char *cp; + int namlen; + + cp = bufp + 2; + for (in = maxino; in > 0; in /= 10) + cp++; + *--cp = 0; + namlen = cp - bufp; + in = ino; + while (cp > bufp) { + *--cp = (in % 10) + '0'; + in /= 10; + } + *cp = '#'; + return (namlen); +} + +/* + * Get a directory block. + * Insure that it is held until another is requested. + */ +struct bufarea * +getdirblk(blkno, size) + daddr_t blkno; + long size; +{ + + if (pdirbp != 0) + pdirbp->b_flags &= ~B_INUSE; + pdirbp = getdatablk(blkno, size); + return (pdirbp); +} diff --git a/sbin/fsck_ifs/fsck.h b/sbin/fsck_ifs/fsck.h new file mode 100644 index 000000000000..7fa831f6c52e --- /dev/null +++ b/sbin/fsck_ifs/fsck.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fsck.h 8.1 (Berkeley) 6/5/93 + */ + +#define MAXDUP 10 /* limit on dup blks (per inode) */ +#define MAXBAD 10 /* limit on bad blks (per inode) */ +#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */ +#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */ + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +#define USTATE 01 /* inode not allocated */ +#define FSTATE 02 /* inode is file */ +#define DSTATE 03 /* inode is directory */ +#define DFOUND 04 /* directory found during descent */ +#define DCLEAR 05 /* directory is to be cleared */ +#define FCLEAR 06 /* file is to be cleared */ + +/* + * buffer cache structure. + */ +struct bufarea { + struct bufarea *b_next; /* free list queue */ + struct bufarea *b_prev; /* free list queue */ + daddr_t b_bno; + int b_size; + int b_errs; + int b_flags; + union { + char *b_buf; /* buffer space */ + daddr_t *b_indir; /* indirect block */ + struct fs *b_fs; /* super block */ + struct cg *b_cg; /* cylinder group */ + struct dinode *b_dinode; /* inode block */ + } b_un; + char b_dirty; +}; + +#define B_INUSE 1 + +#define MINBUFS 5 /* minimum number of buffers required */ +struct bufarea bufhead; /* head of list of other blks in filesys */ +struct bufarea sblk; /* file system superblock */ +struct bufarea cgblk; /* cylinder group blocks */ +struct bufarea *pdirbp; /* current directory contents */ +struct bufarea *pbp; /* current inode block */ +struct bufarea *getdatablk(); + +#define dirty(bp) (bp)->b_dirty = 1 +#define initbarea(bp) \ + (bp)->b_dirty = 0; \ + (bp)->b_bno = (daddr_t)-1; \ + (bp)->b_flags = 0; + +#define sbdirty() sblk.b_dirty = 1 +#define cgdirty() cgblk.b_dirty = 1 +#define sblock (*sblk.b_un.b_fs) +#define cgrp (*cgblk.b_un.b_cg) + +enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; + +struct inodesc { + enum fixstate id_fix; /* policy on fixing errors */ + int (*id_func)(); /* function to be applied to blocks of inode */ + ino_t id_number; /* inode number described */ + ino_t id_parent; /* for DATA nodes, their parent */ + daddr_t id_blkno; /* current block number being examined */ + int id_numfrags; /* number of frags contained in block */ + quad_t id_filesize; /* for DATA nodes, the size of the directory */ + int id_loc; /* for DATA nodes, current location in dir */ + int id_entryno; /* for DATA nodes, current entry number */ + struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ + char *id_name; /* for DATA nodes, name to find or enter */ + char id_type; /* type of descriptor, DATA or ADDR */ +}; +/* file types */ +#define DATA 1 +#define ADDR 2 + +/* + * Linked list of duplicate blocks. + * + * The list is composed of two parts. The first part of the + * list (from duplist through the node pointed to by muldup) + * contains a single copy of each duplicate block that has been + * found. The second part of the list (from muldup to the end) + * contains duplicate blocks that have been found more than once. + * To check if a block has been found as a duplicate it is only + * necessary to search from duplist through muldup. To find the + * total number of times that a block has been found as a duplicate + * the entire list must be searched for occurences of the block + * in question. The following diagram shows a sample list where + * w (found twice), x (found once), y (found three times), and z + * (found once) are duplicate block numbers: + * + * w -> y -> x -> z -> y -> w -> y + * ^ ^ + * | | + * duplist muldup + */ +struct dups { + struct dups *next; + daddr_t dup; +}; +struct dups *duplist; /* head of dup list */ +struct dups *muldup; /* end of unique duplicate dup block numbers */ + +/* + * Linked list of inodes with zero link counts. + */ +struct zlncnt { + struct zlncnt *next; + ino_t zlncnt; +}; +struct zlncnt *zlnhead; /* head of zero link count list */ + +/* + * Inode cache data structures. + */ +struct inoinfo { + struct inoinfo *i_nexthash; /* next entry in hash chain */ + ino_t i_number; /* inode number of this entry */ + ino_t i_parent; /* inode number of parent */ + ino_t i_dotdot; /* inode number of `..' */ + size_t i_isize; /* size of inode */ + u_int i_numblks; /* size of block array in bytes */ + daddr_t i_blks[1]; /* actually longer */ +} **inphead, **inpsort; +long numdirs, listmax, inplast; + +char *cdevname; /* name of device being checked */ +long dev_bsize; /* computed value of DEV_BSIZE */ +long secsize; /* actual disk sector size */ +char nflag; /* assume a no response */ +char yflag; /* assume a yes response */ +int bflag; /* location of alternate super block */ +int debug; /* output debugging info */ +int cvtlevel; /* convert to newer file system format */ +int doinglevel1; /* converting to new cylinder group format */ +int doinglevel2; /* converting to new inode format */ +int newinofmt; /* filesystem has new inode format */ +char preen; /* just fix normal inconsistencies */ +char hotroot; /* checking root device */ +char havesb; /* superblock has been read */ +int fsmodified; /* 1 => write done to file system */ +int fsreadfd; /* file descriptor for reading file system */ +int fswritefd; /* file descriptor for writing file system */ + +daddr_t maxfsblock; /* number of blocks in the file system */ +char *blockmap; /* ptr to primary blk allocation map */ +ino_t maxino; /* number of inodes in file system */ +ino_t lastino; /* last inode in use */ +char *statemap; /* ptr to inode state table */ +char *typemap; /* ptr to inode type table */ +short *lncntp; /* ptr to link count table */ + +ino_t lfdir; /* lost & found directory inode number */ +char *lfname; /* lost & found directory name */ +int lfmode; /* lost & found directory creation mode */ + +daddr_t n_blks; /* number of blocks in use */ +daddr_t n_files; /* number of files in use */ + +#define clearinode(dp) (*(dp) = zino) +struct dinode zino; + +#define setbmap(blkno) setbit(blockmap, blkno) +#define testbmap(blkno) isset(blockmap, blkno) +#define clrbmap(blkno) clrbit(blockmap, blkno) + +#define STOP 0x01 +#define SKIP 0x02 +#define KEEPON 0x04 +#define ALTERED 0x08 +#define FOUND 0x10 + +time_t time(); +struct dinode *ginode(); +struct inoinfo *getinoinfo(); +void getblk(); +ino_t allocino(); +int findino(); diff --git a/sbin/fsck_ifs/fsck_ifs.8 b/sbin/fsck_ifs/fsck_ifs.8 new file mode 100644 index 000000000000..cf8c499a35e9 --- /dev/null +++ b/sbin/fsck_ifs/fsck_ifs.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fsck.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt FSCK 8 +.Os BSD 4 +.Sh NAME +.Nm fsck +.Nd filesystem consistency check and interactive repair +.Sh SYNOPSIS +.Nm fsck +.Fl p +.Op Fl m Ar mode +.Nm fsck +.Op Fl b Ar block# +.Op Fl c Ar level +.Op Fl l Ar maxparallel +.Op Fl y +.Op Fl n +.Op Fl m Ar mode +.Op Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm fsck +preens a standard set of filesystems or the specified filesystems. +It is normally used in the script +.Pa /etc/rc +during automatic reboot. +Here +.Nm fsck +reads the table +.Pa /etc/fstab +to determine which filesystems to check. +Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro'' +and that have non-zero pass number are checked. +Filesystems with pass number 1 (normally just the root filesystem) +are checked one at a time. +When pass 1 completes, all remaining filesystems are checked, +running one process per disk drive. +The disk drive containing each filesystem is inferred from the longest prefix +of the device name that ends in a digit; the remaining characters are assumed +to be the partition designator. +.Pp +The kernel takes care that only a restricted class of innocuous filesystem +inconsistencies can happen unless hardware or software failures intervene. +These are limited to the following: +.Bl -item -compact +.It +Unreferenced inodes +.It +Link counts in inodes too large +.It +Missing blocks in the free map +.It +Blocks in the free map also in files +.It +Counts in the super-block wrong +.El +.Pp +These are the only inconsistencies that +.Nm fsck +with the +.Fl p +option will correct; if it encounters other inconsistencies, it exits +with an abnormal return status and an automatic reboot will then fail. +For each corrected inconsistency one or more lines will be printed +identifying the filesystem on which the correction will take place, +and the nature of the correction. After successfully correcting a filesystem, +.Nm fsck +will print the number of files on that filesystem, +the number of used and free blocks, +and the percentage of fragmentation. +.Pp +If sent a +.Dv QUIT +signal, +.Nm fsck +will finish the filesystem checks, then exit with an abnormal +return status that causes an automatic reboot to fail. +This is useful when you want to finish the filesystem checks during an +automatic reboot, +but do not want the machine to come up multiuser after the checks complete. +.Pp +Without the +.Fl p +option, +.Nm fsck +audits and interactively repairs inconsistent conditions for filesystems. +If the filesystem is inconsistent the operator is prompted for concurrence +before each correction is attempted. +It should be noted that some of the corrective actions which are not +correctable under the +.Fl p +option will result in some loss of data. +The amount and severity of data lost may be determined from the diagnostic +output. +The default action for each consistency correction +is to wait for the operator to respond +.Li yes +or +.Li no . +If the operator does not have write permission on the filesystem +.Nm fsck +will default to a +.Fl n +action. +.Pp +.Nm Fsck +has more consistency checks than +its predecessors +.Em check , dcheck , fcheck , +and +.Em icheck +combined. +.Pp +The following flags are interpreted by +.Nm fsck . +.Bl -tag -width indent +.It Fl b +Use the block specified immediately after the flag as +the super block for the filesystem. Block 32 is usually +an alternate super block. +.It Fl l +Limit the number of parallel checks to the number specified in the following +argument. +By default, the limit is the number of disks, running one process per disk. +If a smaller limit is given, the disks are checked round-robin, one filesystem +at a time. +.It Fl m +Use the mode specified in octal immediately after the flag as the +permission bits to use when creating the +.Pa lost+found +directory rather than the default 1777. +In particular, systems that do not wish to have lost files accessible +by all users on the system should use a more restrictive +set of permissions such as 700. +.It Fl y +Assume a yes response to all questions asked by +.Nm fsck ; +this should be used with great caution as this is a free license +to continue after essentially unlimited trouble has been encountered. +.It Fl n +Assume a no response to all questions asked by +.Nm fsck +except for +.Ql CONTINUE? , +which is assumed to be affirmative; +do not open the filesystem for writing. +.It Fl c +Convert the filesystem to the specified level. +Note that the level of a filesystem can only be raised. +.Bl -tag -width indent +There are currently three levels defined: +.It 0 +The filesystem is in the old (static table) format. +.It 1 +The filesystem is in the new (dynamic table) format. +.It 2 +The filesystem supports 32-bit uid's and gid's, +short symbolic links are stored in the inode, +and directories have an added field showing the file type. +.El +.Pp +In interactive mode, +.Nm fsck +will list the conversion to be made +and ask whether the conversion should be done. +If a negative answer is given, +no further operations are done on the filesystem. +In preen mode, +the conversion is listed and done if +possible without user interaction. +Conversion in preen mode is best used when all the filesystems +are being converted at once. +The format of a filesystem can be determined from the +first line of output from +.Xr dumpfs 8 . +.El +.Pp +If no filesystems are given to +.Nm fsck +then a default list of filesystems is read from +the file +.Pa /etc/fstab . +.Pp +.Bl -enum -indent indent -compact +Inconsistencies checked are as follows: +.It +Blocks claimed by more than one inode or the free map. +.It +Blocks claimed by an inode outside the range of the filesystem. +.It +Incorrect link counts. +.It +Size checks: +.Bl -item -indent indent -compact +.It +Directory size not a multiple of DIRBLKSIZ. +.It +Partially truncated file. +.El +.It +Bad inode format. +.It +Blocks not accounted for anywhere. +.It +Directory checks: +.Bl -item -indent indent -compact +.It +File pointing to unallocated inode. +.It +Inode number out of range. +.It +Dot or dot-dot not the first two entries of a directory +or having the wrong inode number. +.El +.It +Super Block checks: +.Bl -item -indent indent -compact +.It +More blocks for inodes than there are in the filesystem. +.It +Bad free block map format. +.It +Total free block and/or free inode count incorrect. +.El +.El +.Pp +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the +.Pa lost+found +directory. +The name assigned is the inode number. +If the +.Pa lost+found +directory does not exist, it is created. +If there is insufficient space its size is increased. +.Pp +Because of inconsistencies between the block device and the buffer cache, +the raw device should always be used. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +contains default list of filesystems to check. +.El +.Sh DIAGNOSTICS +The diagnostics produced by +.Nm fsck +are fully enumerated and explained in Appendix A of +.Rs +.%T "Fsck \- The UNIX File System Check Program" +.Re +.Sh SEE ALSO +.Xr fstab 5 , +.Xr fs 5 , +.Xr fsdb 8 , +.Xr newfs 8 , +.Xr mkfs 8 , +.Xr reboot 8 diff --git a/sbin/fsck_ifs/inode.c b/sbin/fsck_ifs/inode.c new file mode 100644 index 000000000000..f1c1758f746a --- /dev/null +++ b/sbin/fsck_ifs/inode.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)inode.c 8.4 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static ino_t startinum; + +ckinode(dp, idesc) + struct dinode *dp; + register struct inodesc *idesc; +{ + register daddr_t *ap; + long ret, n, ndb, offset; + struct dinode dino; + quad_t remsize, sizepb; + mode_t mode; + + if (idesc->id_fix != IGNORE) + idesc->id_fix = DONTKNOW; + idesc->id_entryno = 0; + idesc->id_filesize = dp->di_size; + mode = dp->di_mode & IFMT; + if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && + dp->di_size < sblock.fs_maxsymlinklen)) + return (KEEPON); + dino = *dp; + ndb = howmany(dino.di_size, sblock.fs_bsize); + for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { + if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) + idesc->id_numfrags = + numfrags(&sblock, fragroundup(&sblock, offset)); + else + idesc->id_numfrags = sblock.fs_frag; + if (*ap == 0) + continue; + idesc->id_blkno = *ap; + if (idesc->id_type == ADDR) + ret = (*idesc->id_func)(idesc); + else + ret = dirscan(idesc); + if (ret & STOP) + return (ret); + } + idesc->id_numfrags = sblock.fs_frag; + remsize = dino.di_size - sblock.fs_bsize * NDADDR; + sizepb = sblock.fs_bsize; + for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { + if (*ap) { + idesc->id_blkno = *ap; + ret = iblock(idesc, n, remsize); + if (ret & STOP) + return (ret); + } + sizepb *= NINDIR(&sblock); + remsize -= sizepb; + } + return (KEEPON); +} + +iblock(idesc, ilevel, isize) + struct inodesc *idesc; + long ilevel; + quad_t isize; +{ + register daddr_t *ap; + register daddr_t *aplim; + register struct bufarea *bp; + int i, n, (*func)(), nif; + quad_t sizepb; + char buf[BUFSIZ]; + extern int dirscan(), pass1check(); + + if (idesc->id_type == ADDR) { + func = idesc->id_func; + if (((n = (*func)(idesc)) & KEEPON) == 0) + return (n); + } else + func = dirscan; + if (chkrange(idesc->id_blkno, idesc->id_numfrags)) + return (SKIP); + bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); + ilevel--; + for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) + sizepb *= NINDIR(&sblock); + nif = howmany(isize , sizepb); + if (nif > NINDIR(&sblock)) + nif = NINDIR(&sblock); + if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { + aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; + for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { + if (*ap == 0) + continue; + (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", + idesc->id_number); + if (dofix(idesc, buf)) { + *ap = 0; + dirty(bp); + } + } + flush(fswritefd, bp); + } + aplim = &bp->b_un.b_indir[nif]; + for (ap = bp->b_un.b_indir; ap < aplim; ap++) { + if (*ap) { + idesc->id_blkno = *ap; + if (ilevel == 0) + n = (*func)(idesc); + else + n = iblock(idesc, ilevel, isize); + if (n & STOP) { + bp->b_flags &= ~B_INUSE; + return (n); + } + } + isize -= sizepb; + } + bp->b_flags &= ~B_INUSE; + return (KEEPON); +} + +/* + * Check that a block in a legal block number. + * Return 0 if in range, 1 if out of range. + */ +chkrange(blk, cnt) + daddr_t blk; + int cnt; +{ + register int c; + + if ((unsigned)(blk + cnt) > maxfsblock) + return (1); + c = dtog(&sblock, blk); + if (blk < cgdmin(&sblock, c)) { + if ((blk + cnt) > cgsblock(&sblock, c)) { + if (debug) { + printf("blk %ld < cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > cgsbase %ld\n", + blk + cnt, cgsblock(&sblock, c)); + } + return (1); + } + } else { + if ((blk + cnt) > cgbase(&sblock, c+1)) { + if (debug) { + printf("blk %ld >= cgdmin %ld;", + blk, cgdmin(&sblock, c)); + printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", + blk+cnt, sblock.fs_fpg); + } + return (1); + } + } + return (0); +} + +/* + * General purpose interface for reading inodes. + */ +struct dinode * +ginode(inumber) + ino_t inumber; +{ + daddr_t iblk; + + if (inumber < ROOTINO || inumber > maxino) + errexit("bad inode number %d to ginode\n", inumber); + if (startinum == 0 || + inumber < startinum || inumber >= startinum + INOPB(&sblock)) { + iblk = ino_to_fsba(&sblock, inumber); + if (pbp != 0) + pbp->b_flags &= ~B_INUSE; + pbp = getdatablk(iblk, sblock.fs_bsize); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]); +} + +/* + * Special purpose version of ginode used to optimize first pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; + +struct dinode * +getnextinode(inumber) + ino_t inumber; +{ + long size; + daddr_t dblk; + static struct dinode *dp; + + if (inumber != nextino++ || inumber > maxino) + errexit("bad inode number %d to nextinode\n", inumber); + if (inumber >= lastinum) { + readcnt++; + dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); + if (readcnt % readpercg == 0) { + size = partialsize; + lastinum += partialcnt; + } else { + size = inobufsize; + lastinum += fullcnt; + } + (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ + dp = inodebuf; + } + return (dp++); +} + +resetinodebuf() +{ + + startinum = 0; + nextino = 0; + lastinum = 0; + readcnt = 0; + inobufsize = blkroundup(&sblock, INOBUFSIZE); + fullcnt = inobufsize / sizeof(struct dinode); + readpercg = sblock.fs_ipg / fullcnt; + partialcnt = sblock.fs_ipg % fullcnt; + partialsize = partialcnt * sizeof(struct dinode); + if (partialcnt != 0) { + readpercg++; + } else { + partialcnt = fullcnt; + partialsize = inobufsize; + } + if (inodebuf == NULL && + (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) + errexit("Cannot allocate space for inode buffer\n"); + while (nextino < ROOTINO) + (void)getnextinode(nextino); +} + +freeinodebuf() +{ + + if (inodebuf != NULL) + free((char *)inodebuf); + inodebuf = NULL; +} + +/* + * Routines to maintain information about directory inodes. + * This is built during the first pass and used during the + * second and third passes. + * + * Enter inodes into the cache. + */ +cacheino(dp, inumber) + register struct dinode *dp; + ino_t inumber; +{ + register struct inoinfo *inp; + struct inoinfo **inpp; + unsigned int blks; + + blks = howmany(dp->di_size, sblock.fs_bsize); + if (blks > NDADDR) + blks = NDADDR + NIADDR; + inp = (struct inoinfo *) + malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); + if (inp == NULL) + return; + inpp = &inphead[inumber % numdirs]; + inp->i_nexthash = *inpp; + *inpp = inp; + inp->i_parent = (ino_t)0; + inp->i_dotdot = (ino_t)0; + inp->i_number = inumber; + inp->i_isize = dp->di_size; + inp->i_numblks = blks * sizeof(daddr_t); + bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], + (size_t)inp->i_numblks); + if (inplast == listmax) { + listmax += 100; + inpsort = (struct inoinfo **)realloc((char *)inpsort, + (unsigned)listmax * sizeof(struct inoinfo *)); + if (inpsort == NULL) + errexit("cannot increase directory list"); + } + inpsort[inplast++] = inp; +} + +/* + * Look up an inode cache structure. + */ +struct inoinfo * +getinoinfo(inumber) + ino_t inumber; +{ + register struct inoinfo *inp; + + for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { + if (inp->i_number != inumber) + continue; + return (inp); + } + errexit("cannot find inode %d\n", inumber); + return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +inocleanup() +{ + register struct inoinfo **inpp; + + if (inphead == NULL) + return; + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) + free((char *)(*inpp)); + free((char *)inphead); + free((char *)inpsort); + inphead = inpsort = NULL; +} + +inodirty() +{ + + dirty(pbp); +} + +clri(idesc, type, flag) + register struct inodesc *idesc; + char *type; + int flag; +{ + register struct dinode *dp; + + dp = ginode(idesc->id_number); + if (flag == 1) { + pwarn("%s %s", type, + (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); + pinode(idesc->id_number); + } + if (preen || reply("CLEAR") == 1) { + if (preen) + printf(" (CLEARED)\n"); + n_files--; + (void)ckinode(dp, idesc); + clearinode(dp); + statemap[idesc->id_number] = USTATE; + inodirty(); + } +} + +findname(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino != idesc->id_parent) + return (KEEPON); + bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1); + return (STOP|FOUND); +} + +findino(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + + if (dirp->d_ino == 0) + return (KEEPON); + if (strcmp(dirp->d_name, idesc->id_name) == 0 && + dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { + idesc->id_parent = dirp->d_ino; + return (STOP|FOUND); + } + return (KEEPON); +} + +pinode(ino) + ino_t ino; +{ + register struct dinode *dp; + register char *p; + struct passwd *pw; + char *ctime(); + + printf(" I=%lu ", ino); + if (ino < ROOTINO || ino > maxino) + return; + dp = ginode(ino); + printf(" OWNER="); + if ((pw = getpwuid((int)dp->di_uid)) != 0) + printf("%s ", pw->pw_name); + else + printf("%u ", (unsigned)dp->di_uid); + printf("MODE=%o\n", dp->di_mode); + if (preen) + printf("%s: ", cdevname); + printf("SIZE=%qu ", dp->di_size); + p = ctime(&dp->di_mtime.ts_sec); + printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +blkerror(ino, type, blk) + ino_t ino; + char *type; + daddr_t blk; +{ + + pfatal("%ld %s I=%lu", blk, type, ino); + printf("\n"); + switch (statemap[ino]) { + + case FSTATE: + statemap[ino] = FCLEAR; + return; + + case DSTATE: + statemap[ino] = DCLEAR; + return; + + case FCLEAR: + case DCLEAR: + return; + + default: + errexit("BAD STATE %d TO BLKERR", statemap[ino]); + /* NOTREACHED */ + } +} + +/* + * allocate an unused inode + */ +ino_t +allocino(request, type) + ino_t request; + int type; +{ + register ino_t ino; + register struct dinode *dp; + + if (request == 0) + request = ROOTINO; + else if (statemap[request] != USTATE) + return (0); + for (ino = request; ino < maxino; ino++) + if (statemap[ino] == USTATE) + break; + if (ino == maxino) + return (0); + switch (type & IFMT) { + case IFDIR: + statemap[ino] = DSTATE; + break; + case IFREG: + case IFLNK: + statemap[ino] = FSTATE; + break; + default: + return (0); + } + dp = ginode(ino); + dp->di_db[0] = allocblk((long)1); + if (dp->di_db[0] == 0) { + statemap[ino] = USTATE; + return (0); + } + dp->di_mode = type; + (void)time(&dp->di_atime.ts_sec); + dp->di_mtime = dp->di_ctime = dp->di_atime; + dp->di_size = sblock.fs_fsize; + dp->di_blocks = btodb(sblock.fs_fsize); + n_files++; + inodirty(); + if (newinofmt) + typemap[ino] = IFTODT(type); + return (ino); +} + +/* + * deallocate an inode + */ +freeino(ino) + ino_t ino; +{ + struct inodesc idesc; + extern int pass4check(); + struct dinode *dp; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + idesc.id_number = ino; + dp = ginode(ino); + (void)ckinode(dp, &idesc); + clearinode(dp); + inodirty(); + statemap[ino] = USTATE; + n_files--; +} diff --git a/sbin/fsck_ifs/main.c b/sbin/fsck_ifs/main.c new file mode 100644 index 000000000000..2e69715fecf0 --- /dev/null +++ b/sbin/fsck_ifs/main.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +void catch(), catchquit(), voidquit(); +int returntosingle; + +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int ret, maxrun = 0; + extern int docheck(), checkfilesys(); + extern char *optarg, *blockcheck(); + extern int optind; + + sync(); + while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) { + switch (ch) { + case 'p': + preen++; + break; + + case 'b': + bflag = argtoi('b', "number", optarg, 10); + printf("Alternate super block location: %d\n", bflag); + break; + + case 'c': + cvtlevel = argtoi('c', "conversion level", optarg, 10); + break; + + case 'd': + debug++; + break; + + case 'l': + maxrun = argtoi('l', "number", optarg, 10); + break; + + case 'm': + lfmode = argtoi('m', "mode", optarg, 8); + if (lfmode &~ 07777) + errexit("bad mode to -m: %o\n", lfmode); + printf("** lost+found creation mode %o\n", lfmode); + break; + + case 'n': + case 'N': + nflag++; + yflag = 0; + break; + + case 'y': + case 'Y': + yflag++; + nflag = 0; + break; + + default: + errexit("%c option?\n", ch); + } + } + argc -= optind; + argv += optind; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, catch); + if (preen) + (void)signal(SIGQUIT, catchquit); + if (argc) { + while (argc-- > 0) + (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0); + exit(0); + } + ret = checkfstab(preen, maxrun, docheck, checkfilesys); + if (returntosingle) + exit(2); + exit(ret); +} + +argtoi(flag, req, str, base) + int flag; + char *req, *str; + int base; +{ + char *cp; + int ret; + + ret = (int)strtol(str, &cp, base); + if (cp == str || *cp) + errexit("-%c flag requires a %s\n", flag, req); + return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +docheck(fsp) + register struct fstab *fsp; +{ + + if (strcmp(fsp->fs_vfstype, "ufs") || + (strcmp(fsp->fs_type, FSTAB_RW) && + strcmp(fsp->fs_type, FSTAB_RO)) || + fsp->fs_passno == 0) + return (0); + return (1); +} + +/* + * Check the specified filesystem. + */ +/* ARGSUSED */ +checkfilesys(filesys, mntpt, auxdata, child) + char *filesys, *mntpt; + long auxdata; +{ + daddr_t n_ffree, n_bfree; + struct dups *dp; + struct zlncnt *zlnp; + int cylno; + + if (preen && child) + (void)signal(SIGQUIT, voidquit); + cdevname = filesys; + if (debug && preen) + pwarn("starting\n"); + if (setup(filesys) == 0) { + if (preen) + pfatal("CAN'T CHECK FILE SYSTEM."); + return (0); + } + /* + * 1: scan inodes tallying blocks used + */ + if (preen == 0) { + printf("** Last Mounted on %s\n", sblock.fs_fsmnt); + if (hotroot) + printf("** Root file system\n"); + printf("** Phase 1 - Check Blocks and Sizes\n"); + } + pass1(); + + /* + * 1b: locate first references to duplicates, if any + */ + if (duplist) { + if (preen) + pfatal("INTERNAL ERROR: dups with -p"); + printf("** Phase 1b - Rescan For More DUPS\n"); + pass1b(); + } + + /* + * 2: traverse directories from root to mark all connected directories + */ + if (preen == 0) + printf("** Phase 2 - Check Pathnames\n"); + pass2(); + + /* + * 3: scan inodes looking for disconnected directories + */ + if (preen == 0) + printf("** Phase 3 - Check Connectivity\n"); + pass3(); + + /* + * 4: scan inodes looking for disconnected files; check reference counts + */ + if (preen == 0) + printf("** Phase 4 - Check Reference Counts\n"); + pass4(); + + /* + * 5: check and repair resource counts in cylinder groups + */ + if (preen == 0) + printf("** Phase 5 - Check Cyl groups\n"); + pass5(); + + /* + * print out summary statistics + */ + n_ffree = sblock.fs_cstotal.cs_nffree; + n_bfree = sblock.fs_cstotal.cs_nbfree; + pwarn("%ld files, %ld used, %ld free ", + n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); + printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n", + n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize, + ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10); + if (debug && + (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) + printf("%ld files missing\n", n_files); + if (debug) { + n_blks += sblock.fs_ncg * + (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); + n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); + n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); + if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) + printf("%ld blocks missing\n", n_blks); + if (duplist != NULL) { + printf("The following duplicate blocks remain:"); + for (dp = duplist; dp; dp = dp->next) + printf(" %ld,", dp->dup); + printf("\n"); + } + if (zlnhead != NULL) { + printf("The following zero link count inodes remain:"); + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + printf(" %lu,", zlnp->zlncnt); + printf("\n"); + } + } + zlnhead = (struct zlncnt *)0; + duplist = (struct dups *)0; + muldup = (struct dups *)0; + inocleanup(); + if (fsmodified) { + (void)time(&sblock.fs_time); + sbdirty(); + } + if (cvtlevel && sblk.b_dirty) { + /* + * Write out the duplicate super blocks + */ + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) + bwrite(fswritefd, (char *)&sblock, + fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); + } + ckfini(); + free(blockmap); + free(statemap); + free((char *)lncntp); + if (!fsmodified) + return (0); + if (!preen) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + if (hotroot) { + struct statfs stfs_buf; + /* + * We modified the root. Do a mount update on + * it, unless it is read-write, so we can continue. + */ + if (statfs("/", &stfs_buf) == 0) { + long flags = stfs_buf.f_flags; + struct ufs_args args; + int ret; + + if (flags & MNT_RDONLY) { + args.fspec = 0; + args.export.ex_flags = 0; + args.export.ex_root = 0; + flags |= MNT_UPDATE | MNT_RELOAD; + ret = mount(MOUNT_UFS, "/", flags, &args); + if (ret == 0) + return(0); + } + } + if (!preen) + printf("\n***** REBOOT NOW *****\n"); + sync(); + return (4); + } + return (0); +} diff --git a/sbin/fsck_ifs/pass1.c b/sbin/fsck_ifs/pass1.c new file mode 100644 index 000000000000..ca255fe78579 --- /dev/null +++ b/sbin/fsck_ifs/pass1.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +static daddr_t badblk; +static daddr_t dupblk; +int pass1check(); +struct dinode *getnextinode(); + +pass1() +{ + ino_t inumber; + int c, i, cgd; + struct inodesc idesc; + + /* + * Set file system reserved blocks in used block map. + */ + for (c = 0; c < sblock.fs_ncg; c++) { + cgd = cgdmin(&sblock, c); + if (c == 0) { + i = cgbase(&sblock, c); + cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); + } else + i = cgsblock(&sblock, c); + for (; i < cgd; i++) + setbmap(i); + } + /* + * Find all allocated blocks. + */ + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1check; + inumber = 0; + n_files = n_blks = 0; + resetinodebuf(); + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + checkinode(inumber, &idesc); + } + } + freeinodebuf(); +} + +checkinode(inumber, idesc) + ino_t inumber; + register struct inodesc *idesc; +{ + register struct dinode *dp; + struct zlncnt *zlnp; + int ndb, j; + mode_t mode; + char symbuf[MAXSYMLINKLEN]; + + dp = getnextinode(inumber); + mode = dp->di_mode & IFMT; + if (mode == 0) { + if (bcmp((char *)dp->di_db, (char *)zino.di_db, + NDADDR * sizeof(daddr_t)) || + bcmp((char *)dp->di_ib, (char *)zino.di_ib, + NIADDR * sizeof(daddr_t)) || + dp->di_mode || dp->di_size) { + pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber); + if (reply("CLEAR") == 1) { + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } + } + statemap[inumber] = USTATE; + return; + } + lastino = inumber; + if (/* dp->di_size < 0 || */ + dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { + if (debug) + printf("bad size %qu:", dp->di_size); + goto unknown; + } + if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { + dp = ginode(inumber); + dp->di_size = sblock.fs_fsize; + dp->di_mode = IFREG|0600; + inodirty(); + } + ndb = howmany(dp->di_size, sblock.fs_bsize); + if (ndb < 0) { + if (debug) + printf("bad size %qu ndb %d:", + dp->di_size, ndb); + goto unknown; + } + if (mode == IFBLK || mode == IFCHR) + ndb++; + if (mode == IFLNK) { + if (doinglevel2 && + dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN && + dp->di_blocks != 0) { + if (bread(fsreadfd, symbuf, + fsbtodb(&sblock, dp->di_db[0]), + (long)dp->di_size) != 0) + errexit("cannot read symlink"); + if (debug) { + symbuf[dp->di_size] = 0; + printf("convert symlink %d(%s) of size %d\n", + inumber, symbuf, (long)dp->di_size); + } + dp = ginode(inumber); + bcopy(symbuf, (caddr_t)dp->di_shortlink, + (long)dp->di_size); + dp->di_blocks = 0; + inodirty(); + } + /* + * Fake ndb value so direct/indirect block checks below + * will detect any garbage after symlink string. + */ + if (dp->di_size < sblock.fs_maxsymlinklen) { + ndb = howmany(dp->di_size, sizeof(daddr_t)); + if (ndb > NDADDR) { + j = ndb - NDADDR; + for (ndb = 1; j > 1; j--) + ndb *= NINDIR(&sblock); + ndb += NDADDR; + } + } + } + for (j = ndb; j < NDADDR; j++) + if (dp->di_db[j] != 0) { + if (debug) + printf("bad direct addr: %ld\n", dp->di_db[j]); + goto unknown; + } + for (j = 0, ndb -= NDADDR; ndb > 0; j++) + ndb /= NINDIR(&sblock); + for (; j < NIADDR; j++) + if (dp->di_ib[j] != 0) { + if (debug) + printf("bad indirect addr: %ld\n", + dp->di_ib[j]); + goto unknown; + } + if (ftypeok(dp) == 0) + goto unknown; + n_files++; + lncntp[inumber] = dp->di_nlink; + if (dp->di_nlink <= 0) { + zlnp = (struct zlncnt *)malloc(sizeof *zlnp); + if (zlnp == NULL) { + pfatal("LINK COUNT TABLE OVERFLOW"); + if (reply("CONTINUE") == 0) + errexit(""); + } else { + zlnp->zlncnt = inumber; + zlnp->next = zlnhead; + zlnhead = zlnp; + } + } + if (mode == IFDIR) { + if (dp->di_size == 0) + statemap[inumber] = DCLEAR; + else + statemap[inumber] = DSTATE; + cacheino(dp, inumber); + } else + statemap[inumber] = FSTATE; + typemap[inumber] = IFTODT(mode); + if (doinglevel2 && + (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) { + dp = ginode(inumber); + dp->di_uid = dp->di_ouid; + dp->di_ouid = -1; + dp->di_gid = dp->di_ogid; + dp->di_ogid = -1; + inodirty(); + } + badblk = dupblk = 0; + idesc->id_number = inumber; + (void)ckinode(dp, idesc); + idesc->id_entryno *= btodb(sblock.fs_fsize); + if (dp->di_blocks != idesc->id_entryno) { + pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", + inumber, dp->di_blocks, idesc->id_entryno); + if (preen) + printf(" (CORRECTED)\n"); + else if (reply("CORRECT") == 0) + return; + dp = ginode(inumber); + dp->di_blocks = idesc->id_entryno; + inodirty(); + } + return; +unknown: + pfatal("UNKNOWN FILE TYPE I=%lu", inumber); + statemap[inumber] = FCLEAR; + if (reply("CLEAR") == 1) { + statemap[inumber] = USTATE; + dp = ginode(inumber); + clearinode(dp); + inodirty(); + } +} + +pass1check(idesc) + register struct inodesc *idesc; +{ + int res = KEEPON; + int anyout, nfrags; + daddr_t blkno = idesc->id_blkno; + register struct dups *dlp; + struct dups *new; + + if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { + blkerror(idesc->id_number, "BAD", blkno); + if (badblk++ >= MAXBAD) { + pwarn("EXCESSIVE BAD BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + } + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (anyout && chkrange(blkno, 1)) { + res = SKIP; + } else if (!testbmap(blkno)) { + n_blks++; + setbmap(blkno); + } else { + blkerror(idesc->id_number, "DUP", blkno); + if (dupblk++ >= MAXDUP) { + pwarn("EXCESSIVE DUP BLKS I=%lu", + idesc->id_number); + if (preen) + printf(" (SKIPPING)\n"); + else if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new = (struct dups *)malloc(sizeof(struct dups)); + if (new == NULL) { + pfatal("DUP TABLE OVERFLOW."); + if (reply("CONTINUE") == 0) + errexit(""); + return (STOP); + } + new->dup = blkno; + if (muldup == 0) { + duplist = muldup = new; + new->next = 0; + } else { + new->next = muldup->next; + muldup->next = new; + } + for (dlp = duplist; dlp != muldup; dlp = dlp->next) + if (dlp->dup == blkno) + break; + if (dlp == muldup && dlp->dup != blkno) + muldup = new; + } + /* + * count the number of blocks found in id_entryno + */ + idesc->id_entryno++; + } + return (res); +} diff --git a/sbin/fsck_ifs/pass1b.c b/sbin/fsck_ifs/pass1b.c new file mode 100644 index 000000000000..27b2dfd6238c --- /dev/null +++ b/sbin/fsck_ifs/pass1b.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1b.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass1bcheck(); +static struct dups *duphead; + +pass1b() +{ + register int c, i; + register struct dinode *dp; + struct inodesc idesc; + ino_t inumber; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass1bcheck; + duphead = duplist; + inumber = 0; + for (c = 0; c < sblock.fs_ncg; c++) { + for (i = 0; i < sblock.fs_ipg; i++, inumber++) { + if (inumber < ROOTINO) + continue; + dp = ginode(inumber); + if (dp == NULL) + continue; + idesc.id_number = inumber; + if (statemap[inumber] != USTATE && + (ckinode(dp, &idesc) & STOP)) + return; + } + } +} + +pass1bcheck(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) + res = SKIP; + for (dlp = duphead; dlp; dlp = dlp->next) { + if (dlp->dup == blkno) { + blkerror(idesc->id_number, "DUP", blkno); + dlp->dup = duphead->dup; + duphead->dup = blkno; + duphead = duphead->next; + } + if (dlp == muldup) + break; + } + if (muldup == 0 || duphead == muldup->next) + return (STOP); + } + return (res); +} diff --git a/sbin/fsck_ifs/pass2.c b/sbin/fsck_ifs/pass2.c new file mode 100644 index 000000000000..631475174372 --- /dev/null +++ b/sbin/fsck_ifs/pass2.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass2.c 8.2 (Berkeley) 2/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +#define MINDIRSIZE (sizeof (struct dirtemplate)) + +int pass2check(), blksort(); + +pass2() +{ + register struct dinode *dp; + register struct inoinfo **inpp, *inp; + struct inoinfo **inpend; + struct inodesc curino; + struct dinode dino; + char pathbuf[MAXPATHLEN + 1]; + + switch (statemap[ROOTINO]) { + + case USTATE: + pfatal("ROOT INODE UNALLOCATED"); + if (reply("ALLOCATE") == 0) + errexit(""); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + + case DCLEAR: + pfatal("DUPS/BAD IN ROOT INODE"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("CONTINUE") == 0) + errexit(""); + break; + + case FSTATE: + case FCLEAR: + pfatal("ROOT INODE NOT DIRECTORY"); + if (reply("REALLOCATE")) { + freeino(ROOTINO); + if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) + errexit("CANNOT ALLOCATE ROOT INODE\n"); + break; + } + if (reply("FIX") == 0) + errexit(""); + dp = ginode(ROOTINO); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + break; + + case DSTATE: + break; + + default: + errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); + } + statemap[ROOTINO] = DFOUND; + /* + * Sort the directory list into disk block order. + */ + qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); + /* + * Check the integrity of each directory. + */ + bzero((char *)&curino, sizeof(struct inodesc)); + curino.id_type = DATA; + curino.id_func = pass2check; + dp = &dino; + inpend = &inpsort[inplast]; + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_isize == 0) + continue; + if (inp->i_isize < MINDIRSIZE) { + direrror(inp->i_number, "DIRECTORY TOO SHORT"); + inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); + if (reply("FIX") == 1) { + dp = ginode(inp->i_number); + dp->di_size = inp->i_isize; + inodirty(); + dp = &dino; + } + } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { + getpathname(pathbuf, inp->i_number, inp->i_number); + pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", + pathbuf, inp->i_isize, DIRBLKSIZ); + if (preen) + printf(" (ADJUSTED)\n"); + inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); + if (preen || reply("ADJUST") == 1) { + dp = ginode(inp->i_number); + dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); + inodirty(); + dp = &dino; + } + } + bzero((char *)&dino, sizeof(struct dinode)); + dino.di_mode = IFDIR; + dp->di_size = inp->i_isize; + bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], + (size_t)inp->i_numblks); + curino.id_number = inp->i_number; + curino.id_parent = inp->i_parent; + (void)ckinode(dp, &curino); + } + /* + * Now that the parents of all directories have been found, + * make another pass to verify the value of `..' + */ + for (inpp = inpsort; inpp < inpend; inpp++) { + inp = *inpp; + if (inp->i_parent == 0 || inp->i_isize == 0) + continue; + if (statemap[inp->i_parent] == DFOUND && + statemap[inp->i_number] == DSTATE) + statemap[inp->i_number] = DFOUND; + if (inp->i_dotdot == inp->i_parent || + inp->i_dotdot == (ino_t)-1) + continue; + if (inp->i_dotdot == 0) { + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); + if (reply("FIX") == 0) + continue; + (void)makeentry(inp->i_number, inp->i_parent, ".."); + lncntp[inp->i_parent]--; + continue; + } + fileerror(inp->i_parent, inp->i_number, + "BAD INODE NUMBER FOR '..'"); + if (reply("FIX") == 0) + continue; + lncntp[inp->i_dotdot]++; + lncntp[inp->i_parent]--; + inp->i_dotdot = inp->i_parent; + (void)changeino(inp->i_number, "..", inp->i_parent); + } + /* + * Mark all the directories that can be found from the root. + */ + propagate(); +} + +pass2check(idesc) + struct inodesc *idesc; +{ + register struct direct *dirp = idesc->id_dirp; + register struct inoinfo *inp; + int n, entrysize, ret = 0; + struct dinode *dp; + char *errmsg; + struct direct proto; + char namebuf[MAXPATHLEN + 1]; + char pathbuf[MAXPATHLEN + 1]; + + /* + * If converting, set directory entry type. + */ + if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { + dirp->d_type = typemap[dirp->d_ino]; + ret |= ALTERED; + } + /* + * check for "." + */ + if (idesc->id_entryno != 0) + goto chk1; + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { + if (dirp->d_ino != idesc->id_number) { + direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); + dirp->d_ino = idesc->id_number; + if (reply("FIX") == 1) + ret |= ALTERED; + } + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk1; + } + direrror(idesc->id_number, "MISSING '.'"); + proto.d_ino = idesc->id_number; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 1; + (void)strcpy(proto.d_name, "."); + entrysize = DIRSIZ(0, &proto); + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { + pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + } else if (dirp->d_reclen < entrysize) { + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); + } else if (dirp->d_reclen < 2 * entrysize) { + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } else { + n = dirp->d_reclen - entrysize; + proto.d_reclen = entrysize; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + entrysize); + bzero((char *)dirp, (size_t)n); + dirp->d_reclen = n; + if (reply("FIX") == 1) + ret |= ALTERED; + } +chk1: + if (idesc->id_entryno > 1) + goto chk2; + inp = getinoinfo(idesc->id_number); + proto.d_ino = inp->i_parent; + if (newinofmt) + proto.d_type = DT_DIR; + else + proto.d_type = 0; + proto.d_namlen = 2; + (void)strcpy(proto.d_name, ".."); + entrysize = DIRSIZ(0, &proto); + if (idesc->id_entryno == 0) { + n = DIRSIZ(0, dirp); + if (dirp->d_reclen < n + entrysize) + goto chk2; + proto.d_reclen = dirp->d_reclen - n; + dirp->d_reclen = n; + idesc->id_entryno++; + lncntp[dirp->d_ino]--; + dirp = (struct direct *)((char *)(dirp) + n); + bzero((char *)dirp, (size_t)proto.d_reclen); + dirp->d_reclen = proto.d_reclen; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { + inp->i_dotdot = dirp->d_ino; + if (newinofmt && dirp->d_type != DT_DIR) { + direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); + dirp->d_type = DT_DIR; + if (reply("FIX") == 1) + ret |= ALTERED; + } + goto chk2; + } + if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", + dirp->d_name); + inp->i_dotdot = (ino_t)-1; + } else if (dirp->d_reclen < entrysize) { + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); + inp->i_dotdot = (ino_t)-1; + } else if (inp->i_parent != 0) { + /* + * We know the parent, so fix now. + */ + inp->i_dotdot = inp->i_parent; + fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); + proto.d_reclen = dirp->d_reclen; + bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); + if (reply("FIX") == 1) + ret |= ALTERED; + } + idesc->id_entryno++; + if (dirp->d_ino != 0) + lncntp[dirp->d_ino]--; + return (ret|KEEPON); +chk2: + if (dirp->d_ino == 0) + return (ret|KEEPON); + if (dirp->d_namlen <= 2 && + dirp->d_name[0] == '.' && + idesc->id_entryno >= 2) { + if (dirp->d_namlen == 1) { + direrror(idesc->id_number, "EXTRA '.' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + if (dirp->d_name[1] == '.') { + direrror(idesc->id_number, "EXTRA '..' ENTRY"); + dirp->d_ino = 0; + if (reply("FIX") == 1) + ret |= ALTERED; + return (KEEPON | ret); + } + } + idesc->id_entryno++; + n = 0; + if (dirp->d_ino > maxino) { + fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); + n = reply("REMOVE"); + } else { +again: + switch (statemap[dirp->d_ino]) { + case USTATE: + if (idesc->id_entryno <= 2) + break; + fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); + n = reply("REMOVE"); + break; + + case DCLEAR: + case FCLEAR: + if (idesc->id_entryno <= 2) + break; + if (statemap[dirp->d_ino] == FCLEAR) + errmsg = "DUP/BAD"; + else if (!preen) + errmsg = "ZERO LENGTH DIRECTORY"; + else { + n = 1; + break; + } + fileerror(idesc->id_number, dirp->d_ino, errmsg); + if ((n = reply("REMOVE")) == 1) + break; + dp = ginode(dirp->d_ino); + statemap[dirp->d_ino] = + (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; + lncntp[dirp->d_ino] = dp->di_nlink; + goto again; + + case DSTATE: + if (statemap[idesc->id_number] == DFOUND) + statemap[dirp->d_ino] = DFOUND; + /* fall through */ + + case DFOUND: + inp = getinoinfo(dirp->d_ino); + if (inp->i_parent != 0 && idesc->id_entryno > 2) { + getpathname(pathbuf, idesc->id_number, + idesc->id_number); + getpathname(namebuf, dirp->d_ino, dirp->d_ino); + pwarn("%s %s %s\n", pathbuf, + "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", + namebuf); + if (preen) + printf(" (IGNORED)\n"); + else if ((n = reply("REMOVE")) == 1) + break; + } + if (idesc->id_entryno > 2) + inp->i_parent = idesc->id_number; + /* fall through */ + + case FSTATE: + if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { + fileerror(idesc->id_number, dirp->d_ino, + "BAD TYPE VALUE"); + dirp->d_type = typemap[dirp->d_ino]; + if (reply("FIX") == 1) + ret |= ALTERED; + } + lncntp[dirp->d_ino]--; + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[dirp->d_ino], dirp->d_ino); + } + } + if (n == 0) + return (ret|KEEPON); + dirp->d_ino = 0; + return (ret|KEEPON|ALTERED); +} + +/* + * Routine to sort disk blocks. + */ +blksort(inpp1, inpp2) + struct inoinfo **inpp1, **inpp2; +{ + + return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); +} diff --git a/sbin/fsck_ifs/pass3.c b/sbin/fsck_ifs/pass3.c new file mode 100644 index 000000000000..5c6f09d041de --- /dev/null +++ b/sbin/fsck_ifs/pass3.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass3.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include "fsck.h" + +pass3() +{ + register struct inoinfo **inpp, *inp; + ino_t orphan; + int loopcnt; + + for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { + inp = *inpp; + if (inp->i_number == ROOTINO || + !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE)) + continue; + if (statemap[inp->i_number] == DCLEAR) + continue; + for (loopcnt = 0; ; loopcnt++) { + orphan = inp->i_number; + if (inp->i_parent == 0 || + statemap[inp->i_parent] != DSTATE || + loopcnt > numdirs) + break; + inp = getinoinfo(inp->i_parent); + } + (void)linkup(orphan, inp->i_dotdot); + inp->i_parent = inp->i_dotdot = lfdir; + lncntp[lfdir]--; + statemap[orphan] = DFOUND; + propagate(); + } +} diff --git a/sbin/fsck_ifs/pass4.c b/sbin/fsck_ifs/pass4.c new file mode 100644 index 000000000000..7450530e8a4e --- /dev/null +++ b/sbin/fsck_ifs/pass4.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass4.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +int pass4check(); + +pass4() +{ + register ino_t inumber; + register struct zlncnt *zlnp; + struct dinode *dp; + struct inodesc idesc; + int n; + + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = ADDR; + idesc.id_func = pass4check; + for (inumber = ROOTINO; inumber <= lastino; inumber++) { + idesc.id_number = inumber; + switch (statemap[inumber]) { + + case FSTATE: + case DFOUND: + n = lncntp[inumber]; + if (n) + adjust(&idesc, (short)n); + else { + for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) + if (zlnp->zlncnt == inumber) { + zlnp->zlncnt = zlnhead->zlncnt; + zlnp = zlnhead; + zlnhead = zlnhead->next; + free((char *)zlnp); + clri(&idesc, "UNREF", 1); + break; + } + } + break; + + case DSTATE: + clri(&idesc, "UNREF", 1); + break; + + case DCLEAR: + dp = ginode(inumber); + if (dp->di_size == 0) { + clri(&idesc, "ZERO LENGTH", 1); + break; + } + /* fall through */ + case FCLEAR: + clri(&idesc, "BAD/DUP", 1); + break; + + case USTATE: + break; + + default: + errexit("BAD STATE %d FOR INODE I=%d", + statemap[inumber], inumber); + } + } +} + +pass4check(idesc) + register struct inodesc *idesc; +{ + register struct dups *dlp; + int nfrags, res = KEEPON; + daddr_t blkno = idesc->id_blkno; + + for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { + if (chkrange(blkno, 1)) { + res = SKIP; + } else if (testbmap(blkno)) { + for (dlp = duplist; dlp; dlp = dlp->next) { + if (dlp->dup != blkno) + continue; + dlp->dup = duplist->dup; + dlp = duplist; + duplist = duplist->next; + free((char *)dlp); + break; + } + if (dlp == 0) { + clrbmap(blkno); + n_blks--; + } + } + } + return (res); +} diff --git a/sbin/fsck_ifs/pass5.c b/sbin/fsck_ifs/pass5.c new file mode 100644 index 000000000000..2e98f96b5ca8 --- /dev/null +++ b/sbin/fsck_ifs/pass5.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass5.c 8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "fsck.h" + +pass5() +{ + int c, blk, frags, basesize, sumsize, mapsize, savednrpos; + register struct fs *fs = &sblock; + register struct cg *cg = &cgrp; + daddr_t dbase, dmax; + register daddr_t d; + register long i, j; + struct csum *cs; + struct csum cstotal; + struct inodesc idesc[3]; + char buf[MAXBSIZE]; + register struct cg *newcg = (struct cg *)buf; + struct ocg *ocg = (struct ocg *)buf; + + bzero((char *)newcg, (size_t)fs->fs_cgsize); + newcg->cg_niblk = fs->fs_ipg; + if (cvtlevel > 3) { + if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) { + if (preen) + pwarn("DELETING CLUSTERING MAPS\n"); + if (preen || reply("DELETE CLUSTERING MAPS")) { + fs->fs_contigsumsize = 0; + doinglevel1 = 1; + sbdirty(); + } + } + if (fs->fs_maxcontig > 1) { + char *doit = 0; + + if (fs->fs_contigsumsize < 1) { + doit = "CREAT"; + } else if (fs->fs_contigsumsize < fs->fs_maxcontig && + fs->fs_contigsumsize < FS_MAXCONTIG) { + doit = "EXPAND"; + } + if (doit) { + i = fs->fs_contigsumsize; + fs->fs_contigsumsize = + MIN(fs->fs_maxcontig, FS_MAXCONTIG); + if (CGSIZE(fs) > fs->fs_bsize) { + pwarn("CANNOT %s CLUSTER MAPS\n", doit); + fs->fs_contigsumsize = i; + } else if (preen || + reply("CREATE CLUSTER MAPS")) { + if (preen) + pwarn("%sING CLUSTER MAPS\n", + doit); + fs->fs_cgsize = + fragroundup(fs, CGSIZE(fs)); + doinglevel1 = 1; + sbdirty(); + } + } + } + } + switch ((int)fs->fs_postblformat) { + + case FS_42POSTBLFMT: + basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link); + sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]); + mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] - + (u_char *)&ocg->cg_iused[0]; + ocg->cg_magic = CG_MAGIC; + savednrpos = fs->fs_nrpos; + fs->fs_nrpos = 8; + break; + + case FS_DYNAMICPOSTBLFMT: + newcg->cg_btotoff = + &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + newcg->cg_boff = + newcg->cg_btotoff + fs->fs_cpg * sizeof(long); + newcg->cg_iusedoff = newcg->cg_boff + + fs->fs_cpg * fs->fs_nrpos * sizeof(short); + newcg->cg_freeoff = + newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY); + if (fs->fs_contigsumsize <= 0) { + newcg->cg_nextfreeoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY); + } else { + newcg->cg_clustersumoff = newcg->cg_freeoff + + howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) - + sizeof(long); + newcg->cg_clustersumoff = + roundup(newcg->cg_clustersumoff, sizeof(long)); + newcg->cg_clusteroff = newcg->cg_clustersumoff + + (fs->fs_contigsumsize + 1) * sizeof(long); + newcg->cg_nextfreeoff = newcg->cg_clusteroff + + howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY); + } + newcg->cg_magic = CG_MAGIC; + basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); + sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; + mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; + break; + + default: + errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n", + fs->fs_postblformat); + } + bzero((char *)&idesc[0], sizeof idesc); + for (i = 0; i < 3; i++) { + idesc[i].id_type = ADDR; + if (doinglevel2) + idesc[i].id_fix = FIX; + } + bzero((char *)&cstotal, sizeof(struct csum)); + j = blknum(fs, fs->fs_size + fs->fs_frag - 1); + for (i = fs->fs_size; i < j; i++) + setbmap(i); + for (c = 0; c < fs->fs_ncg; c++) { + getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize); + if (!cg_chkmagic(cg)) + pfatal("CG %d: BAD MAGIC NUMBER\n", c); + dbase = cgbase(fs, c); + dmax = dbase + fs->fs_fpg; + if (dmax > fs->fs_size) + dmax = fs->fs_size; + newcg->cg_time = cg->cg_time; + newcg->cg_cgx = c; + if (c == fs->fs_ncg - 1) + newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg; + else + newcg->cg_ncyl = fs->fs_cpg; + newcg->cg_ndblk = dmax - dbase; + if (fs->fs_contigsumsize > 0) + newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag; + newcg->cg_cs.cs_ndir = 0; + newcg->cg_cs.cs_nffree = 0; + newcg->cg_cs.cs_nbfree = 0; + newcg->cg_cs.cs_nifree = fs->fs_ipg; + if (cg->cg_rotor < newcg->cg_ndblk) + newcg->cg_rotor = cg->cg_rotor; + else + newcg->cg_rotor = 0; + if (cg->cg_frotor < newcg->cg_ndblk) + newcg->cg_frotor = cg->cg_frotor; + else + newcg->cg_frotor = 0; + if (cg->cg_irotor < newcg->cg_niblk) + newcg->cg_irotor = cg->cg_irotor; + else + newcg->cg_irotor = 0; + bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum); + bzero((char *)&cg_blktot(newcg)[0], + (size_t)(sumsize + mapsize)); + if (fs->fs_postblformat == FS_42POSTBLFMT) + ocg->cg_magic = CG_MAGIC; + j = fs->fs_ipg * c; + for (i = 0; i < fs->fs_ipg; j++, i++) { + switch (statemap[j]) { + + case USTATE: + break; + + case DSTATE: + case DCLEAR: + case DFOUND: + newcg->cg_cs.cs_ndir++; + /* fall through */ + + case FSTATE: + case FCLEAR: + newcg->cg_cs.cs_nifree--; + setbit(cg_inosused(newcg), i); + break; + + default: + if (j < ROOTINO) + break; + errexit("BAD STATE %d FOR INODE I=%d", + statemap[j], j); + } + } + if (c == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(newcg), i); + newcg->cg_cs.cs_nifree--; + } + for (i = 0, d = dbase; + d < dmax; + d += fs->fs_frag, i += fs->fs_frag) { + frags = 0; + for (j = 0; j < fs->fs_frag; j++) { + if (testbmap(d + j)) + continue; + setbit(cg_blksfree(newcg), i + j); + frags++; + } + if (frags == fs->fs_frag) { + newcg->cg_cs.cs_nbfree++; + j = cbtocylno(fs, i); + cg_blktot(newcg)[j]++; + cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++; + if (fs->fs_contigsumsize > 0) + setbit(cg_clustersfree(newcg), + i / fs->fs_frag); + } else if (frags > 0) { + newcg->cg_cs.cs_nffree += frags; + blk = blkmap(fs, cg_blksfree(newcg), i); + ffs_fragacct(fs, blk, newcg->cg_frsum, 1); + } + } + if (fs->fs_contigsumsize > 0) { + long *sump = cg_clustersum(newcg); + u_char *mapp = cg_clustersfree(newcg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < newcg->cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > fs->fs_contigsumsize) + run = fs->fs_contigsumsize; + sump[run]++; + } + } + cstotal.cs_nffree += newcg->cg_cs.cs_nffree; + cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree; + cstotal.cs_nifree += newcg->cg_cs.cs_nifree; + cstotal.cs_ndir += newcg->cg_cs.cs_ndir; + cs = &fs->fs_cs(fs, c); + if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 && + dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs); + sbdirty(); + } + if (doinglevel1) { + bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize); + cgdirty(); + continue; + } + if (bcmp(cg_inosused(newcg), + cg_inosused(cg), mapsize) != 0 && + dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { + bcopy(cg_inosused(newcg), cg_inosused(cg), + (size_t)mapsize); + cgdirty(); + } + if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 || + bcmp((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], sumsize) != 0) && + dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { + bcopy((char *)newcg, (char *)cg, (size_t)basesize); + bcopy((char *)&cg_blktot(newcg)[0], + (char *)&cg_blktot(cg)[0], (size_t)sumsize); + cgdirty(); + } + } + if (fs->fs_postblformat == FS_42POSTBLFMT) + fs->fs_nrpos = savednrpos; + if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0 + && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { + bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs); + fs->fs_ronly = 0; + fs->fs_fmod = 0; + sbdirty(); + } +} diff --git a/sbin/fsck_ifs/preen.c b/sbin/fsck_ifs/preen.c new file mode 100644 index 000000000000..005a65d97989 --- /dev/null +++ b/sbin/fsck_ifs/preen.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)preen.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char *rawname(), *unrawname(), *blockcheck(); + +struct part { + struct part *next; /* forward link of partitions on disk */ + char *name; /* device name */ + char *fsname; /* mounted filesystem name */ + long auxdata; /* auxillary data for application */ +} *badlist, **badnext = &badlist; + +struct disk { + char *name; /* disk base name */ + struct disk *next; /* forward link for list of disks */ + struct part *part; /* head of list of partitions on disk */ + int pid; /* If != 0, pid of proc working on */ +} *disks; + +int nrun, ndisks; +char hotroot; + +checkfstab(preen, maxrun, docheck, chkit) + int preen, maxrun; + int (*docheck)(), (*chkit)(); +{ + register struct fstab *fsp; + register struct disk *dk, *nextdisk; + register struct part *pt; + int ret, pid, retcode, passno, sumstatus, status; + long auxdata; + char *name; + + sumstatus = 0; + for (passno = 1; passno <= 2; passno++) { + if (setfsent() == 0) { + fprintf(stderr, "Can't open checklist file: %s\n", + _PATH_FSTAB); + return (8); + } + while ((fsp = getfsent()) != 0) { + if ((auxdata = (*docheck)(fsp)) == 0) + continue; + if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { + if (name = blockcheck(fsp->fs_spec)) { + if (sumstatus = (*chkit)(name, + fsp->fs_file, auxdata, 0)) + return (sumstatus); + } else if (preen) + return (8); + } else if (passno == 2 && fsp->fs_passno > 1) { + if ((name = blockcheck(fsp->fs_spec)) == NULL) { + fprintf(stderr, "BAD DISK NAME %s\n", + fsp->fs_spec); + sumstatus |= 8; + continue; + } + addpart(name, fsp->fs_file, auxdata); + } + } + if (preen == 0) + return (0); + } + if (preen) { + if (maxrun == 0) + maxrun = ndisks; + if (maxrun > ndisks) + maxrun = ndisks; + nextdisk = disks; + for (passno = 0; passno < maxrun; ++passno) { + while (ret = startdisk(nextdisk, chkit) && nrun > 0) + sleep(10); + if (ret) + return (ret); + nextdisk = nextdisk->next; + } + while ((pid = wait(&status)) != -1) { + for (dk = disks; dk; dk = dk->next) + if (dk->pid == pid) + break; + if (dk == 0) { + printf("Unknown pid %d\n", pid); + continue; + } + if (WIFEXITED(status)) + retcode = WEXITSTATUS(status); + else + retcode = 0; + if (WIFSIGNALED(status)) { + printf("%s (%s): EXITED WITH SIGNAL %d\n", + dk->part->name, dk->part->fsname, + WTERMSIG(status)); + retcode = 8; + } + if (retcode != 0) { + sumstatus |= retcode; + *badnext = dk->part; + badnext = &dk->part->next; + dk->part = dk->part->next; + *badnext = NULL; + } else + dk->part = dk->part->next; + dk->pid = 0; + nrun--; + if (dk->part == NULL) + ndisks--; + + if (nextdisk == NULL) { + if (dk->part) { + while (ret = startdisk(dk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } else if (nrun < maxrun && nrun < ndisks) { + for ( ;; ) { + if ((nextdisk = nextdisk->next) == NULL) + nextdisk = disks; + if (nextdisk->part != NULL && + nextdisk->pid == 0) + break; + } + while (ret = startdisk(nextdisk, chkit) && + nrun > 0) + sleep(10); + if (ret) + return (ret); + } + } + } + if (sumstatus) { + if (badlist == 0) + return (sumstatus); + fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", + badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); + for (pt = badlist; pt; pt = pt->next) + fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname, + pt->next ? ", " : "\n"); + return (sumstatus); + } + (void)endfsent(); + return (0); +} + +struct disk * +finddisk(name) + char *name; +{ + register struct disk *dk, **dkp; + register char *p; + size_t len; + + for (p = name + strlen(name) - 1; p >= name; --p) + if (isdigit(*p)) { + len = p - name + 1; + break; + } + if (p < name) + len = strlen(name); + + for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { + if (strncmp(dk->name, name, len) == 0 && + dk->name[len] == 0) + return (dk); + } + if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + dk = *dkp; + if ((dk->name = malloc(len + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strncpy(dk->name, name, len); + dk->name[len] = '\0'; + dk->part = NULL; + dk->next = NULL; + dk->pid = 0; + ndisks++; + return (dk); +} + +addpart(name, fsname, auxdata) + char *name, *fsname; + long auxdata; +{ + struct disk *dk = finddisk(name); + register struct part *pt, **ppt = &dk->part; + + for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) + if (strcmp(pt->name, name) == 0) { + printf("%s in fstab more than once!\n", name); + return; + } + if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + pt = *ppt; + if ((pt->name = malloc(strlen(name) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->name, name); + if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) { + fprintf(stderr, "out of memory"); + exit (8); + } + (void)strcpy(pt->fsname, fsname); + pt->next = NULL; + pt->auxdata = auxdata; +} + +startdisk(dk, checkit) + register struct disk *dk; + int (*checkit)(); +{ + register struct part *pt = dk->part; + + dk->pid = fork(); + if (dk->pid < 0) { + perror("fork"); + return (8); + } + if (dk->pid == 0) + exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1)); + nrun++; + return (0); +} + +char * +blockcheck(name) + char *name; +{ + struct stat stslash, stblock, stchar; + char *raw; + int retried = 0; + + hotroot = 0; + if (stat("/", &stslash) < 0) { + perror("/"); + printf("Can't stat root\n"); + return (0); + } +retry: + if (stat(name, &stblock) < 0) { + perror(name); + printf("Can't stat %s\n", name); + return (0); + } + if ((stblock.st_mode & S_IFMT) == S_IFBLK) { + if (stslash.st_dev == stblock.st_rdev) + hotroot++; + raw = rawname(name); + if (stat(raw, &stchar) < 0) { + perror(raw); + printf("Can't stat %s\n", raw); + return (name); + } + if ((stchar.st_mode & S_IFMT) == S_IFCHR) { + return (raw); + } else { + printf("%s is not a character device\n", raw); + return (name); + } + } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { + name = unrawname(name); + retried++; + goto retry; + } + printf("Can't make sense out of name %s\n", name); + return (0); +} + +char * +unrawname(name) + char *name; +{ + char *dp; + struct stat stb; + + if ((dp = rindex(name, '/')) == 0) + return (name); + if (stat(name, &stb) < 0) + return (name); + if ((stb.st_mode & S_IFMT) != S_IFCHR) + return (name); + if (dp[1] != 'r') + return (name); + (void)strcpy(&dp[1], &dp[2]); + return (name); +} + +char * +rawname(name) + char *name; +{ + static char rawbuf[32]; + char *dp; + + if ((dp = rindex(name, '/')) == 0) + return (0); + *dp = 0; + (void)strcpy(rawbuf, name); + *dp = '/'; + (void)strcat(rawbuf, "/r"); + (void)strcat(rawbuf, &dp[1]); + return (rawbuf); +} diff --git a/sbin/fsck_ifs/setup.c b/sbin/fsck_ifs/setup.c new file mode 100644 index 000000000000..a32b23cc294d --- /dev/null +++ b/sbin/fsck_ifs/setup.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)setup.c 8.2 (Berkeley) 2/21/94"; +#endif /* not lint */ + +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +struct disklabel *getdisklabel(); + +setup(dev) + char *dev; +{ + long cg, size, asked, i, j; + long bmapsize; + struct disklabel *lp; + off_t sizepb; + struct stat statb; + struct fs proto; + + havesb = 0; + fswritefd = -1; + if (stat(dev, &statb) < 0) { + printf("Can't stat %s: %s\n", dev, strerror(errno)); + return (0); + } + if ((statb.st_mode & S_IFMT) != S_IFCHR) { + pfatal("%s is not a character device", dev); + if (reply("CONTINUE") == 0) + return (0); + } + if ((fsreadfd = open(dev, O_RDONLY)) < 0) { + printf("Can't open %s: %s\n", dev, strerror(errno)); + return (0); + } + if (preen == 0) + printf("** %s", dev); + if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) { + fswritefd = -1; + if (preen) + pfatal("NO WRITE ACCESS"); + printf(" (NO WRITE)"); + } + if (preen == 0) + printf("\n"); + fsmodified = 0; + lfdir = 0; + initbarea(&sblk); + initbarea(&asblk); + sblk.b_un.b_buf = malloc(SBSIZE); + asblk.b_un.b_buf = malloc(SBSIZE); + if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) + errexit("cannot allocate space for superblock\n"); + if (lp = getdisklabel((char *)NULL, fsreadfd)) + dev_bsize = secsize = lp->d_secsize; + else + dev_bsize = secsize = DEV_BSIZE; + /* + * Read in the superblock, looking for alternates if necessary + */ + if (readsb(1) == 0) { + if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) + return(0); + if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) + return (0); + for (cg = 0; cg < proto.fs_ncg; cg++) { + bflag = fsbtodb(&proto, cgsblock(&proto, cg)); + if (readsb(0) != 0) + break; + } + if (cg >= proto.fs_ncg) { + printf("%s %s\n%s %s\n%s %s\n", + "SEARCH FOR ALTERNATE SUPER-BLOCK", + "FAILED. YOU MUST USE THE", + "-b OPTION TO FSCK TO SPECIFY THE", + "LOCATION OF AN ALTERNATE", + "SUPER-BLOCK TO SUPPLY NEEDED", + "INFORMATION; SEE fsck(8)."); + return(0); + } + pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); + } + maxfsblock = sblock.fs_size; + maxino = sblock.fs_ncg * sblock.fs_ipg; + /* + * Check and potentially fix certain fields in the super block. + */ + if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { + pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_optim = FS_OPTTIME; + sbdirty(); + } + } + if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { + pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", + sblock.fs_minfree); + if (reply("SET TO DEFAULT") == 1) { + sblock.fs_minfree = 10; + sbdirty(); + } + } + if (sblock.fs_interleave < 1 || + sblock.fs_interleave > sblock.fs_nsect) { + pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK", + sblock.fs_interleave); + sblock.fs_interleave = 1; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_npsect < sblock.fs_nsect || + sblock.fs_npsect > sblock.fs_nsect*2) { + pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK", + sblock.fs_npsect); + sblock.fs_npsect = sblock.fs_nsect; + if (preen) + printf(" (FIXED)\n"); + if (preen || reply("SET TO DEFAULT") == 1) { + sbdirty(); + dirty(&asblk); + } + } + if (sblock.fs_inodefmt >= FS_44INODEFMT) { + newinofmt = 1; + } else { + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + newinofmt = 0; + } + /* + * Convert to new inode format. + */ + if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) { + if (preen) + pwarn("CONVERTING TO NEW INODE FORMAT\n"); + else if (!reply("CONVERT TO NEW INODE FORMAT")) + return(0); + doinglevel2++; + sblock.fs_inodefmt = FS_44INODEFMT; + sizepb = sblock.fs_bsize; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + sblock.fs_maxsymlinklen = MAXSYMLINKLEN; + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + sbdirty(); + dirty(&asblk); + } + /* + * Convert to new cylinder group format. + */ + if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) { + if (preen) + pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n"); + else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT")) + return(0); + doinglevel1++; + sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; + sblock.fs_nrpos = 8; + sblock.fs_postbloff = + (char *)(&sblock.fs_opostbl[0][0]) - + (char *)(&sblock.fs_link); + sblock.fs_rotbloff = &sblock.fs_space[0] - + (u_char *)(&sblock.fs_link); + sblock.fs_cgsize = + fragroundup(&sblock, CGSIZE(&sblock)); + sbdirty(); + dirty(&asblk); + } + if (asblk.b_dirty) { + bcopy((char *)&sblock, (char *)&altsblock, + (size_t)sblock.fs_sbsize); + flush(fswritefd, &asblk); + } + /* + * read in the summary info. + */ + asked = 0; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + size = sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize; + sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); + if (bread(fsreadfd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + size) != 0 && !asked) { + pfatal("BAD SUMMARY INFORMATION"); + if (reply("CONTINUE") == 0) + errexit(""); + asked++; + } + } + /* + * allocate and initialize the necessary maps + */ + bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short)); + blockmap = calloc((unsigned)bmapsize, sizeof (char)); + if (blockmap == NULL) { + printf("cannot alloc %u bytes for blockmap\n", + (unsigned)bmapsize); + goto badsb; + } + statemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (statemap == NULL) { + printf("cannot alloc %u bytes for statemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + typemap = calloc((unsigned)(maxino + 1), sizeof(char)); + if (typemap == NULL) { + printf("cannot alloc %u bytes for typemap\n", + (unsigned)(maxino + 1)); + goto badsb; + } + lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short)); + if (lncntp == NULL) { + printf("cannot alloc %u bytes for lncntp\n", + (unsigned)(maxino + 1) * sizeof(short)); + goto badsb; + } + numdirs = sblock.fs_cstotal.cs_ndir; + inplast = 0; + listmax = numdirs + 10; + inpsort = (struct inoinfo **)calloc((unsigned)listmax, + sizeof(struct inoinfo *)); + inphead = (struct inoinfo **)calloc((unsigned)numdirs, + sizeof(struct inoinfo *)); + if (inpsort == NULL || inphead == NULL) { + printf("cannot alloc %u bytes for inphead\n", + (unsigned)numdirs * sizeof(struct inoinfo *)); + goto badsb; + } + bufinit(); + return (1); + +badsb: + ckfini(); + return (0); +} + +/* + * Read in the super block and its summary info. + */ +readsb(listerr) + int listerr; +{ + daddr_t super = bflag ? bflag : SBOFF / dev_bsize; + + if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0) + return (0); + sblk.b_bno = super; + sblk.b_size = SBSIZE; + /* + * run a few consistency checks of the super block + */ + if (sblock.fs_magic != FS_MAGIC) + { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); } + if (sblock.fs_ncg < 1) + { badsb(listerr, "NCG OUT OF RANGE"); return (0); } + if (sblock.fs_cpg < 1) + { badsb(listerr, "CPG OUT OF RANGE"); return (0); } + if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || + (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) + { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); } + if (sblock.fs_sbsize > SBSIZE) + { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); } + /* + * Compute block size that the filesystem is based on, + * according to fsbtodb, and adjust superblock block number + * so we can tell if this is an alternate later. + */ + super *= dev_bsize; + dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); + sblk.b_bno = super / dev_bsize; + if (bflag) { + havesb = 1; + return (1); + } + /* + * Set all possible fields that could differ, then do check + * of whole super block against an alternate super block. + * When an alternate super-block is specified this check is skipped. + */ + getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); + if (asblk.b_errs) + return (0); + altsblock.fs_link = sblock.fs_link; + altsblock.fs_rlink = sblock.fs_rlink; + altsblock.fs_time = sblock.fs_time; + altsblock.fs_cstotal = sblock.fs_cstotal; + altsblock.fs_cgrotor = sblock.fs_cgrotor; + altsblock.fs_fmod = sblock.fs_fmod; + altsblock.fs_clean = sblock.fs_clean; + altsblock.fs_ronly = sblock.fs_ronly; + altsblock.fs_flags = sblock.fs_flags; + altsblock.fs_maxcontig = sblock.fs_maxcontig; + altsblock.fs_minfree = sblock.fs_minfree; + altsblock.fs_optim = sblock.fs_optim; + altsblock.fs_rotdelay = sblock.fs_rotdelay; + altsblock.fs_maxbpg = sblock.fs_maxbpg; + bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp, + sizeof sblock.fs_csp); + bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt, + sizeof sblock.fs_fsmnt); + bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon, + sizeof sblock.fs_sparecon); + /* + * The following should not have to be copied. + */ + altsblock.fs_fsbtodb = sblock.fs_fsbtodb; + altsblock.fs_interleave = sblock.fs_interleave; + altsblock.fs_npsect = sblock.fs_npsect; + altsblock.fs_nrpos = sblock.fs_nrpos; + altsblock.fs_qbmask = sblock.fs_qbmask; + altsblock.fs_qfmask = sblock.fs_qfmask; + altsblock.fs_state = sblock.fs_state; + altsblock.fs_maxfilesize = sblock.fs_maxfilesize; + if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) { + badsb(listerr, + "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); + return (0); + } + havesb = 1; + return (1); +} + +badsb(listerr, s) + int listerr; + char *s; +{ + + if (!listerr) + return; + if (preen) + printf("%s: ", cdevname); + pfatal("BAD SUPER BLOCK: %s\n", s); +} + +/* + * Calculate a prototype superblock based on information in the disk label. + * When done the cgsblock macro can be calculated and the fs_ncg field + * can be used. Do NOT attempt to use other macros without verifying that + * their needed information is available! + */ +calcsb(dev, devfd, fs) + char *dev; + int devfd; + register struct fs *fs; +{ + register struct disklabel *lp; + register struct partition *pp; + register char *cp; + int i; + + cp = index(dev, '\0') - 1; + if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) { + pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev); + return (0); + } + lp = getdisklabel(dev, devfd); + if (isdigit(*cp)) + pp = &lp->d_partitions[0]; + else + pp = &lp->d_partitions[*cp - 'a']; + if (pp->p_fstype != FS_BSDFFS) { + pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n", + dev, pp->p_fstype < FSMAXTYPES ? + fstypenames[pp->p_fstype] : "unknown"); + return (0); + } + bzero((char *)fs, sizeof(struct fs)); + fs->fs_fsize = pp->p_fsize; + fs->fs_frag = pp->p_frag; + fs->fs_cpg = pp->p_cpg; + fs->fs_size = pp->p_size; + fs->fs_ntrak = lp->d_ntracks; + fs->fs_nsect = lp->d_nsectors; + fs->fs_spc = lp->d_secpercyl; + fs->fs_nspf = fs->fs_fsize / lp->d_secsize; + fs->fs_sblkno = roundup( + howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize), + fs->fs_frag); + fs->fs_cgmask = 0xffffffff; + for (i = fs->fs_ntrak; i > 1; i >>= 1) + fs->fs_cgmask <<= 1; + if (!POWEROF2(fs->fs_ntrak)) + fs->fs_cgmask <<= 1; + fs->fs_cgoffset = roundup( + howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag); + fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs); + fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg); + for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1) + fs->fs_fsbtodb++; + dev_bsize = lp->d_secsize; + return (1); +} + +struct disklabel * +getdisklabel(s, fd) + char *s; + int fd; +{ + static struct disklabel lab; + + if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { + if (s == NULL) + return ((struct disklabel *)NULL); + pwarn("ioctl (GCINFO): %s\n", strerror(errno)); + errexit("%s: can't read disk label\n", s); + } + return (&lab); +} diff --git a/sbin/fsck_ifs/utilities.c b/sbin/fsck_ifs/utilities.c new file mode 100644 index 000000000000..64a4cac19ab3 --- /dev/null +++ b/sbin/fsck_ifs/utilities.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsck.h" + +long diskreads, totalreads; /* Disk cache statistics */ + +ftypeok(dp) + struct dinode *dp; +{ + switch (dp->di_mode & IFMT) { + + case IFDIR: + case IFREG: + case IFBLK: + case IFCHR: + case IFLNK: + case IFSOCK: + case IFIFO: + return (1); + + default: + if (debug) + printf("bad file type 0%o\n", dp->di_mode); + return (0); + } +} + +reply(question) + char *question; +{ + int persevere; + char c; + + if (preen) + pfatal("INTERNAL ERROR: GOT TO reply()"); + persevere = !strcmp(question, "CONTINUE"); + printf("\n"); + if (!persevere && (nflag || fswritefd < 0)) { + printf("%s? no\n\n", question); + return (0); + } + if (yflag || (persevere && nflag)) { + printf("%s? yes\n\n", question); + return (1); + } + do { + printf("%s? [yn] ", question); + (void) fflush(stdout); + c = getc(stdin); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return (0); + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + printf("\n"); + if (c == 'y' || c == 'Y') + return (1); + return (0); +} + +/* + * Malloc buffers and set up cache. + */ +bufinit() +{ + register struct bufarea *bp; + long bufcnt, i; + char *bufp; + + pbp = pdirbp = (struct bufarea *)0; + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bufp == 0) + errexit("cannot allocate buffer pool\n"); + cgblk.b_un.b_buf = bufp; + initbarea(&cgblk); + bufhead.b_next = bufhead.b_prev = &bufhead; + bufcnt = MAXBUFSPACE / sblock.fs_bsize; + if (bufcnt < MINBUFS) + bufcnt = MINBUFS; + for (i = 0; i < bufcnt; i++) { + bp = (struct bufarea *)malloc(sizeof(struct bufarea)); + bufp = malloc((unsigned int)sblock.fs_bsize); + if (bp == NULL || bufp == NULL) { + if (i >= MINBUFS) + break; + errexit("cannot allocate buffer pool\n"); + } + bp->b_un.b_buf = bufp; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + initbarea(bp); + } + bufhead.b_size = i; /* save number of buffers */ +} + +/* + * Manage a cache of directory blocks. + */ +struct bufarea * +getdatablk(blkno, size) + daddr_t blkno; + long size; +{ + register struct bufarea *bp; + + for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) + if (bp->b_bno == fsbtodb(&sblock, blkno)) + goto foundit; + for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) + if ((bp->b_flags & B_INUSE) == 0) + break; + if (bp == &bufhead) + errexit("deadlocked buffer pool\n"); + getblk(bp, blkno, size); + /* fall through */ +foundit: + totalreads++; + bp->b_prev->b_next = bp->b_next; + bp->b_next->b_prev = bp->b_prev; + bp->b_prev = &bufhead; + bp->b_next = bufhead.b_next; + bufhead.b_next->b_prev = bp; + bufhead.b_next = bp; + bp->b_flags |= B_INUSE; + return (bp); +} + +void +getblk(bp, blk, size) + register struct bufarea *bp; + daddr_t blk; + long size; +{ + daddr_t dblk; + + dblk = fsbtodb(&sblock, blk); + if (bp->b_bno != dblk) { + flush(fswritefd, bp); + diskreads++; + bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); + bp->b_bno = dblk; + bp->b_size = size; + } +} + +flush(fd, bp) + int fd; + register struct bufarea *bp; +{ + register int i, j; + + if (!bp->b_dirty) + return; + if (bp->b_errs != 0) + pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", + (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", + bp->b_bno); + bp->b_dirty = 0; + bp->b_errs = 0; + bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); + if (bp != &sblk) + return; + for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { + bwrite(fswritefd, (char *)sblock.fs_csp[j], + fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), + sblock.fs_cssize - i < sblock.fs_bsize ? + sblock.fs_cssize - i : sblock.fs_bsize); + } +} + +rwerror(mesg, blk) + char *mesg; + daddr_t blk; +{ + + if (preen == 0) + printf("\n"); + pfatal("CANNOT %s: BLK %ld", mesg, blk); + if (reply("CONTINUE") == 0) + errexit("Program terminated\n"); +} + +ckfini() +{ + register struct bufarea *bp, *nbp; + int cnt = 0; + + if (fswritefd < 0) { + (void)close(fsreadfd); + return; + } + flush(fswritefd, &sblk); + if (havesb && sblk.b_bno != SBOFF / dev_bsize && + !preen && reply("UPDATE STANDARD SUPERBLOCK")) { + sblk.b_bno = SBOFF / dev_bsize; + sbdirty(); + flush(fswritefd, &sblk); + } + flush(fswritefd, &cgblk); + free(cgblk.b_un.b_buf); + for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { + cnt++; + flush(fswritefd, bp); + nbp = bp->b_prev; + free(bp->b_un.b_buf); + free((char *)bp); + } + if (bufhead.b_size != cnt) + errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); + pbp = pdirbp = (struct bufarea *)0; + if (debug) + printf("cache missed %ld of %ld (%d%%)\n", diskreads, + totalreads, (int)(diskreads * 100 / totalreads)); + (void)close(fsreadfd); + (void)close(fswritefd); +} + +bread(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + char *cp; + int i, errs; + off_t offset; + + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (read(fd, buf, (int)size) == size) + return (0); + rwerror("READ", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + errs = 0; + bzero(buf, (size_t)size); + printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); + for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { + if (read(fd, cp, (int)secsize) != secsize) { + (void)lseek(fd, offset + i + secsize, 0); + if (secsize != dev_bsize && dev_bsize != 1) + printf(" %ld (%ld),", + (blk * dev_bsize + i) / secsize, + blk + i / dev_bsize); + else + printf(" %ld,", blk + i / dev_bsize); + errs++; + } + } + printf("\n"); + return (errs); +} + +bwrite(fd, buf, blk, size) + int fd; + char *buf; + daddr_t blk; + long size; +{ + int i; + char *cp; + off_t offset; + + if (fd < 0) + return; + offset = blk; + offset *= dev_bsize; + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + else if (write(fd, buf, (int)size) == size) { + fsmodified = 1; + return; + } + rwerror("WRITE", blk); + if (lseek(fd, offset, 0) < 0) + rwerror("SEEK", blk); + printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); + for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) + if (write(fd, cp, (int)dev_bsize) != dev_bsize) { + (void)lseek(fd, offset + i + dev_bsize, 0); + printf(" %ld,", blk + i / dev_bsize); + } + printf("\n"); + return; +} + +/* + * allocate a data block with the specified number of fragments + */ +allocblk(frags) + long frags; +{ + register int i, j, k; + + if (frags <= 0 || frags > sblock.fs_frag) + return (0); + for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { + for (j = 0; j <= sblock.fs_frag - frags; j++) { + if (testbmap(i + j)) + continue; + for (k = 1; k < frags; k++) + if (testbmap(i + j + k)) + break; + if (k < frags) { + j += k; + continue; + } + for (k = 0; k < frags; k++) + setbmap(i + j + k); + n_blks += frags; + return (i + j); + } + } + return (0); +} + +/* + * Free a previously allocated block + */ +freeblk(blkno, frags) + daddr_t blkno; + long frags; +{ + struct inodesc idesc; + + idesc.id_blkno = blkno; + idesc.id_numfrags = frags; + (void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +getpathname(namebuf, curdir, ino) + char *namebuf; + ino_t curdir, ino; +{ + int len; + register char *cp; + struct inodesc idesc; + static int busy = 0; + extern int findname(); + + if (curdir == ino && ino == ROOTINO) { + (void)strcpy(namebuf, "/"); + return; + } + if (busy || + (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { + (void)strcpy(namebuf, "?"); + return; + } + busy = 1; + bzero((char *)&idesc, sizeof(struct inodesc)); + idesc.id_type = DATA; + idesc.id_fix = IGNORE; + cp = &namebuf[MAXPATHLEN - 1]; + *cp = '\0'; + if (curdir != ino) { + idesc.id_parent = curdir; + goto namelookup; + } + while (ino != ROOTINO) { + idesc.id_number = ino; + idesc.id_func = findino; + idesc.id_name = ".."; + if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) + break; + namelookup: + idesc.id_number = idesc.id_parent; + idesc.id_parent = ino; + idesc.id_func = findname; + idesc.id_name = namebuf; + if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) + break; + len = strlen(namebuf); + cp -= len; + bcopy(namebuf, cp, (size_t)len); + *--cp = '/'; + if (cp < &namebuf[MAXNAMLEN]) + break; + ino = idesc.id_number; + } + busy = 0; + if (ino != ROOTINO) + *--cp = '?'; + bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch() +{ + if (!doinglevel2) + ckfini(); + exit(12); +} + +/* + * When preening, allow a single quit to signal + * a special exit after filesystem checks complete + * so that reboot sequence may be interrupted. + */ +void +catchquit() +{ + extern returntosingle; + + printf("returning to single-user after filesystem check\n"); + returntosingle = 1; + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * Ignore a single quit signal; wait and flush just in case. + * Used by child processes in preen. + */ +void +voidquit() +{ + + sleep(1); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +dofix(idesc, msg) + register struct inodesc *idesc; + char *msg; +{ + + switch (idesc->id_fix) { + + case DONTKNOW: + if (idesc->id_type == DATA) + direrror(idesc->id_number, msg); + else + pwarn(msg); + if (preen) { + printf(" (SALVAGED)\n"); + idesc->id_fix = FIX; + return (ALTERED); + } + if (reply("SALVAGE") == 0) { + idesc->id_fix = NOFIX; + return (0); + } + idesc->id_fix = FIX; + return (ALTERED); + + case FIX: + return (ALTERED); + + case NOFIX: + case IGNORE: + return (0); + + default: + errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); + } + /* NOTREACHED */ +} + +/* VARARGS1 */ +errexit(s1, s2, s3, s4) + char *s1; +{ + printf(s1, s2, s3, s4); + exit(8); +} + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +/* VARARGS1 */ +pfatal(s, a1, a2, a3) + char *s; +{ + + if (preen) { + printf("%s: ", cdevname); + printf(s, a1, a2, a3); + printf("\n"); + printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", + cdevname); + exit(8); + } + printf(s, a1, a2, a3); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +/* VARARGS1 */ +pwarn(s, a1, a2, a3, a4, a5, a6) + char *s; +{ + + if (preen) + printf("%s: ", cdevname); + printf(s, a1, a2, a3, a4, a5, a6); +} + +#ifndef lint +/* + * Stub for routines from kernel. + */ +panic(s) + char *s; +{ + + pfatal("INTERNAL INCONSISTENCY:"); + errexit(s); +} +#endif diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile new file mode 100644 index 000000000000..8be219395873 --- /dev/null +++ b/sbin/ifconfig/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= ifconfig +MAN8= ifconfig.0 + +.include diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 new file mode 100644 index 000000000000..58a21638630e --- /dev/null +++ b/sbin/ifconfig/ifconfig.8 @@ -0,0 +1,275 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 +.\" +.Dd January 5, 1994 +.Dt IFCONFIG 8 +.Os BSD 4.2 +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm ifconfig +.Ar interface address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm ifconfig +.Ar interface +.Op Ar protocol_family +.Sh DESCRIPTION +.Nm Ifconfig +is used to assign an address +to a network interface and/or configure +network interface parameters. +.Nm Ifconfig +must be used at boot time to define the network address +of each interface present on a machine; it may also be used at +a later time to redefine an interface's address +or other operating parameters. +.Pp +Available operands for +.Nm ifconfig: +.Bl -tag -width Ds +.It Ar Address +For the +.Tn DARPA-Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +For the Xerox Network Systems(tm) family, +addresses are +.Ar net:a.b.c.d.e.f , +where +.Ar net +is the assigned network number (in decimal), +and each of the six bytes of the host number, +.Ar a +through +.Ar f , +are specified in hexadecimal. +The host number may be omitted on 10Mb/s Ethernet interfaces, +which use the hardware physical address, +and on interfaces other than the first. +For the +.Tn ISO +family, addresses are specified as a long hexadecimal string, +as in the Xerox family. However, two consecutive dots imply a zero +byte, and the dots are optional, if the user wishes to (carefully) +count out long strings of digits in network byte order. +.It Ar address_family +Specifies the +.Ar address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommeded. +The address or protocol families currently +supported are +.Dq inet , +.Dq iso , +and +.Dq ns . +.It Ar Interface +The +.Ar interface +parameter is a string of the form +.Dq name unit , +for example, +.Dq en0 +.El +.Pp +The following parameters may be set with +.Nm ifconfig : +.Bl -tag -width dest_addressxx +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +.It Cm arp +Enable the use of the Address Resolution Protocol in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and 10Mb/s Ethernet addresses. +.It Fl arp +Disable the use of the Address Resolution Protocol. +.It Cm broadcast +(Inet only) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.It Cm delete +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Cm down +Mark an interface ``down''. When an interface is +marked ``down'', the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +This action does not automatically disable routes using the interface. +.It Cm ipdst +This is used to specify an Internet host who is willing to receive +ip packets encapsulating NS packets bound for a remote network. +An apparent point to point link is constructed, and +the address specified will be taken as the NS address and network +of the destination. +IP encapsulation of +.Tn CLNP +packets is done differently. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as addition hops +to the destination network or host. +.It Cm netmask Ar mask +(Inet and ISO) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading 0x, with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.\" see +.\" Xr eon 5 . +.It Cm nsellength Ar n +.Pf ( Tn ISO +only) +This specifies a trailing number of bytes for a received +.Tn NSAP +used for local identification, the remaining leading part of which is +taken to be the +.Tn NET +(Network Entity Title). +The default value is 1, which is conformant to US +.Tn GOSIP . +When an ISO address is set in an ifconfig command, +it is really the +.Tn NSAP +which is being specified. +For example, in +.Tn US GOSIP , +20 hex digits should be +specified in the +.Tn ISO NSAP +to be assigned to the interface. +There is some evidence that a number different from 1 may be useful +for +.Tn AFI +37 type addresses. +.It Cm trailers +Request the use of a ``trailer'' link level encapsulation when +sending (default). +If a network interface supports +.Cm trailers , +the system will, when possible, encapsulate outgoing +messages in a manner which minimizes the number of +memory to memory copy operations performed by the receiver. +On networks that support the Address Resolution Protocol (see +.Xr arp 4 ; +currently, only 10 Mb/s Ethernet), +this flag indicates that the system should request that other +systems use trailers when sending to this host. +Similarly, trailer encapsulations will be sent to other +hosts that have made such requests. +Currently used by Internet protocols only. +.It Fl trailers +Disable the use of a ``trailer'' link level encapsulation. +.It Cm link[0-2] +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. An example +of this is to enable SLIP compression. Currently, only used by SLIP. +.It Fl link[0-2] +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface ``up''. +This may be used to enable an interface after an ``ifconfig down.'' +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +.Pp +.Nm Ifconfig +displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +Ifconfig will report only the details specific to that protocol family. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exit, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr rc 8 , +.Xr routed 8 , +.\" .Xr eon 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c new file mode 100644 index 000000000000..e9751b98e00b --- /dev/null +++ b/sbin/ifconfig/ifconfig.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#define NSIP +#include +#include +#include + +#define EON +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct ifreq ifr, ridreq; +struct ifaliasreq addreq; +struct iso_ifreq iso_ridreq; +struct iso_aliasreq iso_addreq; +struct sockaddr_in netmask; + +char name[30]; +int flags; +int metric; +int nsellength = 1; +int setaddr; +int setipdst; +int doalias; +int clearaddr; +int newaddr = 1; +int s; +extern int errno; + +int setifflags(), setifaddr(), setifdstaddr(), setifnetmask(); +int setifmetric(), setifbroadaddr(), setifipdst(); +int notealias(), setsnpaoffset(), setnsellength(), notrailers(); + +#define NEXTARG 0xffffff + +struct cmd { + char *c_name; + int c_parameter; /* NEXTARG means next argv */ + int (*c_func)(); +} cmds[] = { + { "up", IFF_UP, setifflags } , + { "down", -IFF_UP, setifflags }, + { "trailers", -1, notrailers }, + { "-trailers", 1, notrailers }, + { "arp", -IFF_NOARP, setifflags }, + { "-arp", IFF_NOARP, setifflags }, + { "debug", IFF_DEBUG, setifflags }, + { "-debug", -IFF_DEBUG, setifflags }, + { "alias", IFF_UP, notealias }, + { "-alias", -IFF_UP, notealias }, + { "delete", -IFF_UP, notealias }, +#ifdef notdef +#define EN_SWABIPS 0x1000 + { "swabips", EN_SWABIPS, setifflags }, + { "-swabips", -EN_SWABIPS, setifflags }, +#endif + { "netmask", NEXTARG, setifnetmask }, + { "metric", NEXTARG, setifmetric }, + { "broadcast", NEXTARG, setifbroadaddr }, + { "ipdst", NEXTARG, setifipdst }, + { "snpaoffset", NEXTARG, setsnpaoffset }, + { "nsellength", NEXTARG, setnsellength }, + { "link0", IFF_LINK0, setifflags } , + { "-link0", -IFF_LINK0, setifflags } , + { "link1", IFF_LINK1, setifflags } , + { "-link1", -IFF_LINK1, setifflags } , + { "link2", IFF_LINK2, setifflags } , + { "-link2", -IFF_LINK2, setifflags } , + { 0, 0, setifaddr }, + { 0, 0, setifdstaddr }, +}; + +/* + * XNS support liberally adapted from code written at the University of + * Maryland principally by James O'Toole and Chris Torek. + */ +int in_status(), in_getaddr(); +int xns_status(), xns_getaddr(); +int iso_status(), iso_getaddr(); + +/* Known address families */ +struct afswtch { + char *af_name; + short af_af; + int (*af_status)(); + int (*af_getaddr)(); + int af_difaddr; + int af_aifaddr; + caddr_t af_ridreq; + caddr_t af_addreq; +} afs[] = { +#define C(x) ((caddr_t) &x) + { "inet", AF_INET, in_status, in_getaddr, + SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, + { "ns", AF_NS, xns_status, xns_getaddr, + SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, + { "iso", AF_ISO, iso_status, iso_getaddr, + SIOCDIFADDR_ISO, SIOCAIFADDR_ISO, C(iso_ridreq), C(iso_addreq) }, + { 0, 0, 0, 0 } +}; + +struct afswtch *afp; /*the address family being set or asked about*/ + +main(argc, argv) + int argc; + char *argv[]; +{ + int af = AF_INET; + register struct afswtch *rafp; + + if (argc < 2) { + fprintf(stderr, "usage: ifconfig interface\n%s%s%s%s%s", + "\t[ af [ address [ dest_addr ] ] [ up ] [ down ]", + "[ netmask mask ] ]\n", + "\t[ metric n ]\n", + "\t[ arp | -arp ]\n", + "\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ] \n"); + exit(1); + } + argc--, argv++; + strncpy(name, *argv, sizeof(name)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + argc--, argv++; + if (argc > 0) { + for (afp = rafp = afs; rafp->af_name; rafp++) + if (strcmp(rafp->af_name, *argv) == 0) { + afp = rafp; argc--; argv++; + break; + } + rafp = afp; + af = ifr.ifr_addr.sa_family = rafp->af_af; + } + s = socket(af, SOCK_DGRAM, 0); + if (s < 0) { + perror("ifconfig: socket"); + exit(1); + } + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + flags = ifr.ifr_flags; + if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0) + perror("ioctl (SIOCGIFMETRIC)"); + else + metric = ifr.ifr_metric; + if (argc == 0) { + status(); + exit(0); + } + while (argc > 0) { + register struct cmd *p; + + for (p = cmds; p->c_name; p++) + if (strcmp(*argv, p->c_name) == 0) + break; + if (p->c_name == 0 && setaddr) + p++; /* got src, do dst */ + if (p->c_func) { + if (p->c_parameter == NEXTARG) { + if (argv[1] == NULL) + errx(1, "'%s' requires argument", + p->c_name); + (*p->c_func)(argv[1]); + argc--, argv++; + } else + (*p->c_func)(*argv, p->c_parameter); + } + argc--, argv++; + } + if (af == AF_ISO) + adjust_nsellength(); + if (setipdst && af==AF_NS) { + struct nsip_req rq; + int size = sizeof(rq); + + rq.rq_ns = addreq.ifra_addr; + rq.rq_ip = addreq.ifra_dstaddr; + + if (setsockopt(s, 0, SO_NSIP_ROUTE, &rq, size) < 0) + Perror("Encapsulation Routing"); + } + if (clearaddr) { + int ret; + strncpy(rafp->af_ridreq, name, sizeof ifr.ifr_name); + if ((ret = ioctl(s, rafp->af_difaddr, rafp->af_ridreq)) < 0) { + if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + /* means no previous address for interface */ + } else + Perror("ioctl (SIOCDIFADDR)"); + } + } + if (newaddr) { + strncpy(rafp->af_addreq, name, sizeof ifr.ifr_name); + if (ioctl(s, rafp->af_aifaddr, rafp->af_addreq) < 0) + Perror("ioctl (SIOCAIFADDR)"); + } + exit(0); +} +#define RIDADDR 0 +#define ADDR 1 +#define MASK 2 +#define DSTADDR 3 + +/*ARGSUSED*/ +setifaddr(addr, param) + char *addr; + short param; +{ + /* + * Delay the ioctl to set the interface addr until flags are all set. + * The address interpretation may depend on the flags, + * and the flags may change when the address is set. + */ + setaddr++; + if (doalias == 0) + clearaddr = 1; + (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR)); +} + +setifnetmask(addr) + char *addr; +{ + (*afp->af_getaddr)(addr, MASK); +} + +setifbroadaddr(addr) + char *addr; +{ + (*afp->af_getaddr)(addr, DSTADDR); +} + +setifipdst(addr) + char *addr; +{ + in_getaddr(addr, DSTADDR); + setipdst++; + clearaddr = 0; + newaddr = 0; +} +#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) +/*ARGSUSED*/ +notealias(addr, param) + char *addr; +{ + if (setaddr && doalias == 0 && param < 0) + bcopy((caddr_t)rqtosa(af_addreq), + (caddr_t)rqtosa(af_ridreq), + rqtosa(af_addreq)->sa_len); + doalias = param; + if (param < 0) { + clearaddr = 1; + newaddr = 0; + } else + clearaddr = 0; +} + +/*ARGSUSED*/ +notrailers(vname, value) + char *vname; + int value; +{ + printf("Note: trailers are no longer sent, but always received\n"); +} + +/*ARGSUSED*/ +setifdstaddr(addr, param) + char *addr; + int param; +{ + (*afp->af_getaddr)(addr, DSTADDR); +} + +setifflags(vname, value) + char *vname; + short value; +{ + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + flags = ifr.ifr_flags; + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + ifr.ifr_flags = flags; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) + Perror(vname); +} + +setifmetric(val) + char *val; +{ + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_metric = atoi(val); + if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) + perror("ioctl (set metric)"); +} + +setsnpaoffset(val) + char *val; +{ + iso_addreq.ifra_snpaoffset = atoi(val); +} + +#define IFFBITS \ +"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\ +\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST" + +/* + * Print the status of the interface. If an address family was + * specified, show it and it only; otherwise, show them all. + */ +status() +{ + register struct afswtch *p = afp; + short af = ifr.ifr_addr.sa_family; + + printf("%s: ", name); + printb("flags", flags, IFFBITS); + if (metric) + printf(" metric %d", metric); + putchar('\n'); + if ((p = afp) != NULL) { + (*p->af_status)(1); + } else for (p = afs; p->af_name; p++) { + ifr.ifr_addr.sa_family = p->af_af; + (*p->af_status)(0); + } +} + +in_status(force) + int force; +{ + struct sockaddr_in *sin; + char *inet_ntoa(); + + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + perror("ioctl (SIOCGIFADDR)"); + } + sin = (struct sockaddr_in *)&ifr.ifr_addr; + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl (SIOCGIFNETMASK)"); + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + netmask.sin_addr = + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + if (flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + perror("ioctl (SIOCGIFDSTADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_dstaddr; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + printf("netmask 0x%x ", ntohl(netmask.sin_addr.s_addr)); + if (flags & IFF_BROADCAST) { + if (ioctl(s, SIOCGIFBRDADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + perror("ioctl (SIOCGIFADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_addr; + if (sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} + + +xns_status(force) + int force; +{ + struct sockaddr_ns *sns; + + close(s); + s = socket(AF_NS, SOCK_DGRAM, 0); + if (s < 0) { + if (errno == EPROTONOSUPPORT) + return; + perror("ifconfig: socket"); + exit(1); + } + if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + } else + perror("ioctl (SIOCGIFADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + sns = (struct sockaddr_ns *)&ifr.ifr_addr; + printf("\tns %s ", ns_ntoa(sns->sns_addr)); + if (flags & IFF_POINTOPOINT) { /* by W. Nesheim@Cornell */ + if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + else + Perror("ioctl (SIOCGIFDSTADDR)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + sns = (struct sockaddr_ns *)&ifr.ifr_dstaddr; + printf("--> %s ", ns_ntoa(sns->sns_addr)); + } + putchar('\n'); +} + +iso_status(force) + int force; +{ + struct sockaddr_iso *siso; + struct iso_ifreq ifr; + + close(s); + s = socket(AF_ISO, SOCK_DGRAM, 0); + if (s < 0) { + if (errno == EPROTONOSUPPORT) + return; + perror("ifconfig: socket"); + exit(1); + } + bzero((caddr_t)&ifr, sizeof(ifr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR_ISO, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) { + if (!force) + return; + bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr)); + } else { + perror("ioctl (SIOCGIFADDR_ISO)"); + exit(1); + } + } + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + siso = &ifr.ifr_Addr; + printf("\tiso %s ", iso_ntoa(&siso->siso_addr)); + if (ioctl(s, SIOCGIFNETMASK_ISO, (caddr_t)&ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl (SIOCGIFNETMASK_ISO)"); + } else { + printf(" netmask %s ", iso_ntoa(&siso->siso_addr)); + } + if (flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR_ISO, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + bzero((char *)&ifr.ifr_Addr, sizeof(ifr.ifr_Addr)); + else + Perror("ioctl (SIOCGIFDSTADDR_ISO)"); + } + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + siso = &ifr.ifr_Addr; + printf("--> %s ", iso_ntoa(&siso->siso_addr)); + } + putchar('\n'); +} + +Perror(cmd) + char *cmd; +{ + extern int errno; + + switch (errno) { + + case ENXIO: + errx(1, "%s: no such interface", cmd); + break; + + case EPERM: + errx(1, "%s: permission denied", cmd); + break; + + default: + err(1, "%s", cmd); + } +} + +struct in_addr inet_makeaddr(); + +#define SIN(x) ((struct sockaddr_in *) &(x)) +struct sockaddr_in *sintab[] = { +SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr), +SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)}; + +in_getaddr(s, which) + char *s; +{ + register struct sockaddr_in *sin = sintab[which]; + struct hostent *hp; + struct netent *np; + int val; + + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if ((val = inet_addr(s)) != -1) + sin->sin_addr.s_addr = val; + else if (hp = gethostbyname(s)) + bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length); + else if (np = getnetbyname(s)) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", s); +} + +/* + * Print a value a la the %b format of the kernel's printf + */ +printb(s, v, bits) + char *s; + register char *bits; + register unsigned short v; +{ + register int i, any = 0; + register char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while (i = *bits++) { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +#define SNS(x) ((struct sockaddr_ns *) &(x)) +struct sockaddr_ns *snstab[] = { +SNS(ridreq.ifr_addr), SNS(addreq.ifra_addr), +SNS(addreq.ifra_mask), SNS(addreq.ifra_broadaddr)}; + +xns_getaddr(addr, which) +char *addr; +{ + struct sockaddr_ns *sns = snstab[which]; + struct ns_addr ns_addr(); + + sns->sns_family = AF_NS; + sns->sns_len = sizeof(*sns); + sns->sns_addr = ns_addr(addr); + if (which == MASK) + printf("Attempt to set XNS netmask will be ineffectual\n"); +} + +#define SISO(x) ((struct sockaddr_iso *) &(x)) +struct sockaddr_iso *sisotab[] = { +SISO(iso_ridreq.ifr_Addr), SISO(iso_addreq.ifra_addr), +SISO(iso_addreq.ifra_mask), SISO(iso_addreq.ifra_dstaddr)}; + +iso_getaddr(addr, which) +char *addr; +{ + register struct sockaddr_iso *siso = sisotab[which]; + struct iso_addr *iso_addr(); + siso->siso_addr = *iso_addr(addr); + + if (which == MASK) { + siso->siso_len = TSEL(siso) - (caddr_t)(siso); + siso->siso_nlen = 0; + } else { + siso->siso_len = sizeof(*siso); + siso->siso_family = AF_ISO; + } +} + +setnsellength(val) + char *val; +{ + nsellength = atoi(val); + if (nsellength < 0) + errx(1, "Negative NSEL length is absurd"); + if (afp == 0 || afp->af_af != AF_ISO) + errx(1, "Setting NSEL length valid only for iso"); +} + +fixnsel(s) +register struct sockaddr_iso *s; +{ + if (s->siso_family == 0) + return; + s->siso_tlen = nsellength; +} + +adjust_nsellength() +{ + fixnsel(sisotab[RIDADDR]); + fixnsel(sisotab[ADDR]); + fixnsel(sisotab[DSTADDR]); +} diff --git a/sbin/init/Makefile b/sbin/init/Makefile new file mode 100644 index 000000000000..1a796a1069f7 --- /dev/null +++ b/sbin/init/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= init +MAN8= init.0 +DPADD= ${LIBUTIL} +LDADD= -lutil +BINMODE=500 +INSTALLFLAGS=-fschg +CFLAGS+=-DDEBUGSHELL -DSECURE + +.include diff --git a/sbin/init/NOTES b/sbin/init/NOTES new file mode 100644 index 000000000000..bf75101680ab --- /dev/null +++ b/sbin/init/NOTES @@ -0,0 +1,112 @@ +POSIX and init: +-------------- + +POSIX.1 does not define 'init' but it mentions it in a few places. + +B.2.2.2, p205 line 873: + + This is part of the extensive 'job control' glossary entry. + This specific reference says that 'init' must by default provide + protection from job control signals to jobs it starts -- + it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN. + +B.2.2.2, p206 line 889: + + Here is a reference to 'vhangup'. It says, 'POSIX.1 does + not specify how controlling terminal access is affected by + a user logging out (that is, by a controlling process + terminating).' vhangup() is recognized as one way to handle + the problem. I'm not clear what happens in Reno; I have + the impression that when the controlling process terminates, + references to the controlling terminal are converted to + references to a 'dead' vnode. I don't know whether vhangup() + is required. + +B.2.2.2, p206 line 921: + + Orphaned process groups bear indirectly on this issue. A + session leader's process group is considered to be orphaned; + that is, it's immune to job control signals from the terminal. + +B.2.2.2, p233 line 2055: + + 'Historically, the implementation-dependent process that + inherits children whose parents have terminated without + waiting on them is called "init" and has a process ID of 1.' + + It goes on to note that it used to be the case that 'init' + was responsible for sending SIGHUP to the foreground process + group of a tty whose controlling process has exited, using + vhangup(). It is now the responsibility of the kernel to + do this when the controlling process calls _exit(). The + kernel is also responsible for sending SIGCONT to stopped + process groups that become orphaned. This is like old BSD + but entire process groups are signaled instead of individual + processes. + + In general it appears that the kernel now automatically + takes care of orphans, relieving 'init' of any responsibility. + Specifics are listed on the _exit() page (p50). + +On setsid(): +----------- + +It appears that neither getty nor login call setsid(), so init must +do this -- seems reasonable. B.4.3.2 p 248 implies that this is the +way that 'init' should work; it says that setsid() should be called +after forking. + +Process group leaders cannot call setsid() -- another reason to +fork! Of course setsid() causes the current process to become a +process group leader, so we can only call setsid() once. Note that +the controlling terminal acquires the session leader's process +group when opened. + +Controlling terminals: +--------------------- + +B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to +allocate a controlling terminal. This is normally done by a system +utility (such as 'getty') and is considered ... outside the scope +of POSIX.1.' It goes on to say that historically the first open() +of a tty in a session sets the controlling terminal. P130 has the +full details; nothing particularly surprising. + +The glossary p12 describes a 'controlling process' as the first +process in a session that acquires a controlling terminal. Access +to the terminal from the session is revoked if the controlling +process exits (see p50, in the discussion of process termination). + +Design notes: +------------ + +your generic finite state machine +we are fascist about which signals we elect to receive, + even signals purportedly generated by hardware +handle fatal errors gracefully if possible (we reboot if we goof!!) + if we get a segmentation fault etc., print a message on the console + and spin for a while before rebooting + (this at least decreases the amount of paper consumed :-) +apply hysteresis to rapidly exiting gettys +check wait status of children we reap + don't wait for stopped children +don't use SIGCHILD, it's too expensive + but it may close windows and avoid races, sigh +look for EINTR in case we need to change state +init is responsible for utmp and wtmp maintenance (ick) + maybe now we can consider replacements? maintain them in parallel + init only removes utmp and closes out wtmp entries... + +necessary states and state transitions (gleaned from the man page): + 1: single user shell (with password checking?); on exit, go to 2 + 2: rc script: on exit 0, go to 3; on exit N (error), go to 1 + 3: read ttys file: on completion, go to 4 + 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5; + on SIGTSTP, go to 6 + 5: clean up mode (re-read ttys file, killing off controlling processes + on lines that are now 'off', starting them on lines newly 'on') + on completion, go to 4 + 6: boring mode (no new sessions); signals as in 4 + 7: death: send SIGHUP to all controlling processes, reap for 30 seconds, + then go to 1 (warn if not all processes died, i.e. wait blocks) +Given the -s flag, we start at state 1; otherwise state 2 diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 000000000000..71e318427d82 --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)init.8 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 8 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +defined in the file +.Pa /sys/compile/MACHINE/param.c +and initialize it to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill -HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c new file mode 100644 index 000000000000..ff03598932a8 --- /dev/null +++ b/sbin/init/init.c @@ -0,0 +1,1297 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef SECURE +#include +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +state_t requested_transition = runcom; + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * Establish an initial user so that programs running + * single user do not freak out and die (like passwd). + */ + if (setlogin("root") < 0) + warning("setlogin() failed: %m"); + + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + bzero(clear, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + bcopy(data.data, (char *)&ret, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + bzero(sp, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h new file mode 100644 index 000000000000..abb874a4b988 --- /dev/null +++ b/sbin/init/pathnames.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + */ + +#include + +#define _PATH_SLOGGER "/sbin/session_logger" +#define _PATH_RUNCOM "/etc/rc" diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile new file mode 100644 index 000000000000..272753faacea --- /dev/null +++ b/sbin/mknod/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= mknod +MAN8= mknod.0 + +.include diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8 new file mode 100644 index 000000000000..73f71d360ea1 --- /dev/null +++ b/sbin/mknod/mknod.8 @@ -0,0 +1,99 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MKNOD 8 +.Os BSD 4 +.Sh NAME +.Nm mknod +.Nd build special file +.Sh SYNOPSIS +.Nm mknod +.Ar name +.Op Cm c | Cm b +.Ar major minor +.Sh DESCRIPTION +The +.Nm mknod +command creates device special files. +Normally the shell script +.Pa /dev/MAKEDEV +is used to create special files for commonly known devices; it executes +.Nm mknod +with the appropriate arguments and can make all the files required for the +device. +.Pp +To make nodes manually, the four required arguments are: +.Pp +.Bl -tag -width majorx +.It Ar name +Device name, for example +.Dq sd +for a SCSI disk on an HP300 or a +.Dq pty +for pseudo-devices. +.It Cm b | Cm c +Type of device. If the +device is a block type device such as a tape or disk drive which needs +both cooked and raw special files, +the type is +.Cm b . +All other devices are character type devices, such as terminal +and pseudo devices, and are type +.Cm c . +.It Ar major +The major device number is an integer number which tells the kernel +which device driver entry point to use. To learn what +major device number to use for a particular device, check the file +.Pa /dev/MAKEDEV +to see if the device is known, or check +the system dependent device configuration file: +.Bd -filled -offset indent +.Dq Pa /usr/src/sys/conf/device. Ns Em architecture +.Ed +.Pp +(for example +.Pa device.hp300 ) . +.It Ar minor +The minor device number tells the kernel which subunit +the node corresponds to on the device; for example, +a subunit may be a filesystem partition +or a tty line. +.El +.Sh SEE ALSO +.Xr mknod 2 , +.Xr makedev 8 +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c new file mode 100644 index 000000000000..b70acf1e0919 --- /dev/null +++ b/sbin/mknod/mknod.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include + +main(argc, argv) + int argc; + char **argv; +{ + extern int errno; + u_short mode; + char *strerror(); + + if (argc != 5) { + (void)fprintf(stderr, + "usage: mknod name [b | c] major minor\n"); + exit(1); + } + + mode = 0666; + if (argv[2][0] == 'c') + mode |= S_IFCHR; + else if (argv[2][0] == 'b') + mode |= S_IFBLK; + else { + (void)fprintf(stderr, + "mknod: node must be type 'b' or 'c'.\n"); + exit(1); + } + + if (mknod(argv[1], mode, makedev(atoi(argv[3]), atoi(argv[4]))) < 0) { + (void)fprintf(stderr, + "mknod: %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/sbin/mount/Makefile b/sbin/mount/Makefile new file mode 100644 index 000000000000..b3efd4ef63c4 --- /dev/null +++ b/sbin/mount/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.5 (Berkeley) 3/27/94 + +PROG= mount +SRCS= mount.c mount_ufs.c getmntopts.c +MAN8= mount.0 +# We do NOT install the getmntopts.3 man page. + +.include diff --git a/sbin/mount/getmntopts.3 b/sbin/mount/getmntopts.3 new file mode 100644 index 000000000000..642c57aa361d --- /dev/null +++ b/sbin/mount/getmntopts.3 @@ -0,0 +1,163 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getmntopts.3 8.1 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt GETMNTOPTS 3 +.Os BSD 4.4 +.Sh NAME +.Nm getmntopts +.Nd scan mount options +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fn getmntopts "char *options" "struct mntopt *mopts" "int *flagp" +.Sh DESCRIPTION +The +.Nm getmntopts +function takes a comma separated option list and a list +of valid option names, and computes the bitmask +corresponding to the requested set of options. +.Pp +The string +.Dv options +is broken down into a sequence of comma separated tokens. +Each token is looked up in the table described by +.Dv mopts +and the bits in +the word referenced by +.Dv flagp +are updated. +The flag word is not initialized by +.Nm getmntopt . +The table, +.Dv mopts , +has the following format: +.Bd -literal +struct mntopt { + char *m_option; /* option name */ + int m_inverse; /* is this a negative option, eg "dev" */ + int m_flag; /* bit to set, eg MNT_RDONLY */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width m_inverse +.It Fa m_option +the option name, +for example +.Dq suid . +.It Fa m_inverse +tells +.Nm getmntopts +that the name has the inverse meaning of the +bit. +For example, +.Dq suid +is the string, whereas the +mount flag is +.Dv MNT_NOSUID . +In this case, the sense of the string and the flag +are inverted, so the +.Dv m_inverse +flag should be set. +.It Fa m_flag +the value of the bit to be set or cleared in +the flag word when the option is recognized. +The bit is set when the option is discovered, +but cleared if the option name was preceded +by the letters +.Dq no . +The +.Dv m_inverse +flag causes these two operations to be reversed. +.El +.Pp +Each of the user visible +.Dv MNT_ +flags has a corresponding +.Dv MOPT_ +macro which defines an appropriate +.Li "struct mntopt" +entry. +To simplify the program interface and ensure consistency across all +programs, a general purpose macro, +.Dv MOPT_STDOPTS , +is defined which +contains an entry for all the generic VFS options. +In addition, the macros +.Dv MOPT_FORCE +and +.Dv MOPT_UPDATE +exist to enable the +.Dv MNT_FORCE +and +.Dv MNT_UPDATE +flags to be set. +Finally, the table must be terminated by an entry with a NULL +first element. +.Sh EXAMPLES +Most commands will use the standard option set. +Local filesystems which support the +.Dv MNT_UPDATE +flag, would also have an +.Dv MOPT_UPDATE +entry. +This can be declared and used as follows: +.Bd -literal +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + + ... + mntflags = 0; + ... + getmntopts(options, mopts, &mntflags) + ... +.Ed +.Sh DIAGNOSTICS +The +.Nm getmntopts +function displays an error message and exits if an +unrecognized option is encountered. +.Sh SEE ALSO +.Xr err 3 , +.Xr mount 8 +.Sh HISTORY +The +.Fn getmntopts +function appeared in +.Bx 4.4 . diff --git a/sbin/mount/getmntopts.c b/sbin/mount/getmntopts.c new file mode 100644 index 000000000000..2c84ae56e3d5 --- /dev/null +++ b/sbin/mount/getmntopts.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getmntopts.c 8.1 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +void +getmntopts(options, m0, flagp) + const char *options; + const struct mntopt *m0; + int *flagp; +{ + const struct mntopt *m; + int negative; + char *opt, *optbuf; + + /* Copy option string, since it is about to be torn asunder... */ + if ((optbuf = strdup(options)) == NULL) + err(1, NULL); + + for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { + /* Check for "no" prefix. */ + if (opt[0] == 'n' && opt[1] == 'o') { + negative = 1; + opt += 2; + } else + negative = 0; + + /* Scan option table. */ + for (m = m0; m->m_option != NULL; ++m) + if (strcasecmp(opt, m->m_option) == 0) + break; + + /* Save flag, or fail if option is not recognised. */ + if (m->m_option) { + if (negative == m->m_inverse) + *flagp |= m->m_flag; + else + *flagp &= ~m->m_flag; + } else + errx(1, "-o %s: option not supported", opt); + } + + free(optbuf); +} diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h new file mode 100644 index 000000000000..a89f63dc19ff --- /dev/null +++ b/sbin/mount/mntopts.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mntopts.h 8.3 (Berkeley) 3/27/94 + */ + +struct mntopt { + const char *m_option; /* option name */ + int m_inverse; /* if a negative option, eg "dev" */ + int m_flag; /* bit to set, eg. MNT_RDONLY */ +}; + +/* User-visible MNT_ flags. */ +#define MOPT_ASYNC { "async", 0, MNT_ASYNC } +#define MOPT_NODEV { "dev", 1, MNT_NODEV } +#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC } +#define MOPT_NOSUID { "suid", 1, MNT_NOSUID } +#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY } +#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS } +#define MOPT_UNION { "union", 0, MNT_UNION } + +/* Control flags. */ +#define MOPT_FORCE { "force", 1, MNT_FORCE } +#define MOPT_UPDATE { "update", 0, MNT_UPDATE } + +/* Support for old-style "ro", "rw" flags. */ +#define MOPT_RO { "ro", 0, MNT_RDONLY } +#define MOPT_RW { "rw", 1, MNT_RDONLY } + +#define MOPT_FSTAB_COMPAT \ + MOPT_RO, \ + MOPT_RW + +/* Standard options which all mounts can understand. */ +#define MOPT_STDOPTS \ + MOPT_FSTAB_COMPAT, \ + MOPT_NODEV, \ + MOPT_NOEXEC, \ + MOPT_NOSUID, \ + MOPT_RDONLY, \ + MOPT_UNION + +void getmntopts __P((const char *, const struct mntopt *, int *)); diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 new file mode 100644 index 000000000000..e2c53c8ffec8 --- /dev/null +++ b/sbin/mount/mount.8 @@ -0,0 +1,264 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount.8 8.7 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm mount +.Nd mount file systems +.Sh SYNOPSIS +.Nm mount +.Op Fl adfruvw +.Op Fl t Ar ufs | lfs | external_type +.Nm mount +.Op Fl dfruvw +.Ar special | node +.Nm mount +.Op Fl dfruvw +.Op Fl o Ar options +.Op Fl t Ar ufs | lfs | external_type +.Ar special node +.Sh DESCRIPTION +The +.Nm mount +command +calls the +.Xr mount 2 +system call to prepare and graft a +.Ar "special device" +or the remote node (rhost:path) on to the file system tree at the point +.Ar node . +If either +.Ar special +or +.Ar node +are not provided, the appropriate information is taken from the +.Xr fstab 5 +file. +.Pp +The system maintains a list of currently mounted file systems. +If no arguments are given to +.Nm mount, +this list is printed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl d +Causes everything to be done except for the actual system call. +This option is useful in conjunction with the +.Fl v +flag to +determine what the +.Nm mount +command is trying to do. +.It Fl f +Forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +The following options are available: +.Bl -tag -width indent +.It async +All +.Tn I/O +to the file system should be done asynchronously. +This is a +.Em dangerous +flag to set, +and should not be used unless you are prepared to recreate the file +system should your system crash. +.It force +The same as +.Fl f ; +forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It nodev +Do not interpret character or block special devices on the file system. +This option is useful for a server that has file systems containing +special devices for architectures other than its own. +.It noexec +Do not allow execution of any binaries on the mounted file system. +This option is useful for a server that has file systems containing +binaries for architectures other than its own. +.It nosuid +Do not allow set-user-identifier or set-group-identifier bits to take effect. +.It rdonly +The same as +.Fl r ; +mount the file system read-only (even the super-user may not write it). +.It sync +All +.Tn I/O +to the file system should be done synchronously. +.It update +The same as +.Fl u ; +indicate that the status of an already mounted file system should be changed. +.It union +Causes the namespace at the mount point to appear as the union +of the mounted filesystem root and the existing directory. +Lookups will be done in the mounted filesystem first. +If those operations fail due to a non-existent file the underlying +directory is then accessed. +All creates are done in the mounted filesystem. +.El +.Pp +Any additional options specific to a filesystem type that is not +one of the internally known types (see the +.Fl t +option) may be passed as a comma separated list; these options are +distinguished by a leading +.Dq \&- +(dash). +Options that take a value are specified using the syntax -option=value. +For example, the mount command: +.Bd -literal -offset indent +mount -t mfs -o nosuid,-N,-s=4000 /dev/dk0b /tmp +.Ed +.Pp +causes +.Nm mount +to execute the equivalent of: +.Bd -literal -offset indent +/sbin/mount_mfs -o nosuid -N -s 4000 /dev/dk0b /tmp +.Ed +.It Fl r +The file system is to be mounted read-only. +Mount the file system read-only (even the super-user may not write it). +The same as the +.Dq rdonly +argument to the +.Fl o +option. +.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type" +The argument following the +.Fl t +is used to indicate the file system type. +The type +.Ar ufs +is the default. +The \fI-t\fP option can be used +to indicate that the actions should only be taken on +filesystems of the specified type. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm mount +command: +.Bd -literal -offset indent +mount -a -t nonfs,mfs +.Ed +.Pp +mounts all filesystems except those of type +.Tn NFS +and +.Tn MFS . +.Pp +If the type is not one of the internally known types, +mount will attempt to execute a program in +.Pa /sbin/mount_ Ns Em XXX +where +.Em XXX +is replaced by the type name. +For example, nfs filesystems are mounted by the program +.Pa /sbin/mount_nfs . +.It Fl u +The +.Fl u +flag indicates that the status of an already mounted file +system should be changed. +Any of the options discussed above (the +.Fl o +option) +may be changed; +also a file system can be changed from read-only to read-write +or vice versa. +An attempt to change from read-write to read-only will fail if any +files on the filesystem are currently open for writing unless the +.Fl f +flag is also specified. +The set of options is determined by first extracting the options +for the file system from the +.Xr fstab +table, +then applying any options specified by the +.Fl o +argument, +and finally applying the +.Fl r +or +.Fl w +option. +.It Fl v +Verbose mode. +.It Fl w +The file system object is to be read and write. +.Pp +The options specific to NFS filesystems are described in the +.Xr mount_nfs 8 +manual page. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +file system table +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr fstab 5 , +.Xr mount_cd9660 8 , +.Xr mount_fdesc 8 , +.Xr mount_kernfs 8 , +.Xr mount_lfs 8 , +.Xr mount_lofs 8 , +.Xr mount_mfs 8 , +.Xr mount_nfs 8 , +.Xr mount_null 8 , +.Xr mount_portal 8 , +.Xr mount_procfs 8 , +.Xr mount_umap 8 , +.Xr mount_union 8 , +.Xr umount 8 +.Sh BUGS +It is possible for a corrupted file system to cause a crash. +.Sh HISTORY +A +.Nm mount +command appeared in +.At v6 . diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c new file mode 100644 index 000000000000..f0ff782f4e0e --- /dev/null +++ b/sbin/mount/mount.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 1980, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount.c 8.19 (Berkeley) 4/19/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +int debug, verbose, skipvfs; + +int badvfsname __P((const char *, const char **)); +int badvfstype __P((int, const char **)); +char *catopt __P((char *, const char *)); +struct statfs + *getmntpt __P((const char *)); +const char + **makevfslist __P((char *)); +void mangle __P((char *, int *, const char **)); +int mountfs __P((const char *, const char *, const char *, + int, const char *, const char *)); +void prmount __P((const char *, const char *, int)); +void usage __P((void)); + +/* From mount_ufs.c. */ +int mount_ufs __P((int, char * const *)); + +/* Map from mount otions to printable formats. */ +static struct opt { + int o_opt; + const char *o_name; +} optnames[] = { + { MNT_ASYNC, "asynchronous" }, + { MNT_EXPORTED, "NFS exported" }, + { MNT_LOCAL, "local" }, + { MNT_NODEV, "nodev" }, + { MNT_NOEXEC, "noexec" }, + { MNT_NOSUID, "nosuid" }, + { MNT_QUOTA, "with quotas" }, + { MNT_RDONLY, "read-only" }, + { MNT_SYNCHRONOUS, "synchronous" }, + { MNT_UNION, "union" }, + { MNT_USER, "user mount" }, + { NULL } +}; + +int +main(argc, argv) + int argc; + char * const argv[]; +{ + const char *mntonname, **vfslist, *vfstype; + struct fstab *fs; + struct statfs *mntbuf; + FILE *mountdfp; + pid_t pid; + int all, ch, i, init_flags, mntsize, rval; + char *options; + + all = init_flags = 0; + options = NULL; + vfslist = NULL; + vfstype = "ufs"; + while ((ch = getopt(argc, argv, "adfo:rwt:uv")) != EOF) + switch (ch) { + case 'a': + all = 1; + break; + case 'd': + debug = 1; + break; + case 'f': + init_flags |= MNT_FORCE; + break; + case 'o': + if (*optarg) + options = catopt(options, optarg); + break; + case 'r': + init_flags |= MNT_RDONLY; + break; + case 't': + if (vfslist != NULL) + errx(1, "only one -t option may be specified."); + vfslist = makevfslist(optarg); + vfstype = optarg; + break; + case 'u': + init_flags |= MNT_UPDATE; + break; + case 'v': + verbose = 1; + break; + case 'w': + init_flags &= ~MNT_RDONLY; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + +#define BADTYPE(type) \ + (strcmp(type, FSTAB_RO) && \ + strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) + + rval = 0; + switch (argc) { + case 0: + if (all) + while ((fs = getfsent()) != NULL) { + if (BADTYPE(fs->fs_type)) + continue; + if (badvfsname(fs->fs_vfstype, vfslist)) + continue; + if (mountfs(fs->fs_vfstype, fs->fs_spec, + fs->fs_file, init_flags, options, + fs->fs_mntops)) + rval = 1; + } + else { + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) + err(1, "getmntinfo"); + for (i = 0; i < mntsize; i++) { + if (badvfstype(mntbuf[i].f_type, vfslist)) + continue; + prmount(mntbuf[i].f_mntfromname, + mntbuf[i].f_mntonname, mntbuf[i].f_flags); + } + } + exit(rval); + case 1: + if (vfslist != NULL) + usage(); + + if (init_flags & MNT_UPDATE) { + if ((mntbuf = getmntpt(*argv)) == NULL) + errx(1, + "unknown special file or file system %s.", + *argv); + if ((fs = getfsfile(mntbuf->f_mntonname)) == NULL) + errx(1, "can't find fstab entry for %s.", + *argv); + /* If it's an update, ignore the fstab file options. */ + fs->fs_mntops = NULL; + mntonname = mntbuf->f_mntonname; + } else { + if ((fs = getfsfile(*argv)) == NULL && + (fs = getfsspec(*argv)) == NULL) + errx(1, + "%s: unknown special file or file system.", + *argv); + if (BADTYPE(fs->fs_type)) + errx(1, "%s has unknown file system type.", + *argv); + mntonname = fs->fs_file; + } + rval = mountfs(fs->fs_vfstype, fs->fs_spec, + mntonname, init_flags, options, fs->fs_mntops); + break; + case 2: + /* + * If -t flag has not been specified, and spec contains either + * a ':' or a '@' then assume that an NFS filesystem is being + * specified ala Sun. + */ + if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL) + vfstype = "nfs"; + rval = mountfs(vfstype, + argv[0], argv[1], init_flags, options, NULL); + break; + default: + usage(); + /* NOTREACHED */ + } + + /* + * If the mount was successfully, and done by root, tell mountd the + * good news. Pid checks are probably unnecessary, but don't hurt. + */ + if (rval == 0 && getuid() == 0 && + (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { + if (fscanf(mountdfp, "%ld", &pid) == 1 && + pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) + err(1, "signal mountd"); + (void)fclose(mountdfp); + } + + exit(rval); +} + +int +mountfs(vfstype, spec, name, flags, options, mntopts) + const char *vfstype, *spec, *name, *options, *mntopts; + int flags; +{ + /* List of directories containing mount_xxx subcommands. */ + static const char *edirs[] = { + _PATH_SBIN, + _PATH_USRSBIN, + NULL + }; + const char *argv[100], **edir; + struct statfs sf; + pid_t pid; + int argc, i, status; + char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN]; + + if (realpath(name, mntpath) == NULL) { + warn("%s", mntpath); + return (1); + } + + name = mntpath; + + if (options == NULL) { + if (mntopts == NULL || *mntopts == '\0') + options = "rw"; + else + options = mntopts; + mntopts = ""; + } + optbuf = catopt(strdup(mntopts), options); + + if (strcmp(name, "/") == 0) + flags |= MNT_UPDATE; + if (flags & MNT_FORCE) + optbuf = catopt(optbuf, "force"); + if (flags & MNT_RDONLY) + optbuf = catopt(optbuf, "ro"); + /* + * XXX + * The mount_mfs (newfs) command uses -o to select the + * optimisation mode. We don't pass the default "-o rw" + * for that reason. + */ + if (flags & MNT_UPDATE) + optbuf = catopt(optbuf, "update"); + + argc = 0; + argv[argc++] = vfstype; + mangle(optbuf, &argc, argv); + argv[argc++] = spec; + argv[argc++] = name; + argv[argc] = NULL; + + if (debug) { + (void)printf("exec: mount_%s", vfstype); + for (i = 1; i < argc; i++) + (void)printf(" %s", argv[i]); + (void)printf("\n"); + return (0); + } + + switch (pid = vfork()) { + case -1: /* Error. */ + warn("vfork"); + free(optbuf); + return (1); + case 0: /* Child. */ + if (strcmp(vfstype, "ufs") == 0) + exit(mount_ufs(argc, (char * const *) argv)); + + /* Go find an executable. */ + edir = edirs; + do { + (void)snprintf(execname, + sizeof(execname), "%s/mount_%s", *edir, vfstype); + execv(execname, (char * const *)argv); + if (errno != ENOENT) + warn("exec %s for %s", execname, name); + } while (*++edir != NULL); + + if (errno == ENOENT) + warn("exec %s for %s", execname, name); + exit(1); + /* NOTREACHED */ + default: /* Parent. */ + free(optbuf); + + if (waitpid(pid, &status, 0) < 0) { + warn("waitpid"); + return (1); + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + return (WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]); + return (1); + } + + if (verbose) { + if (statfs(name, &sf) < 0) { + warn("%s", name); + return (1); + } + prmount(sf.f_mntfromname, sf.f_mntonname, sf.f_flags); + } + break; + } + + return (0); +} + +void +prmount(spec, name, flags) + const char *spec, *name; + int flags; +{ + struct opt *o; + int f; + + (void)printf("%s on %s", spec, name); + + flags &= MNT_VISFLAGMASK; + for (f = 0, o = optnames; flags && o->o_opt; o++) + if (flags & o->o_opt) { + (void)printf("%s%s", !f++ ? " (" : ", ", o->o_name); + flags &= ~o->o_opt; + } + (void)printf(f ? ")\n" : "\n"); +} + +struct statfs * +getmntpt(name) + const char *name; +{ + struct statfs *mntbuf; + int i, mntsize; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) + if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || + strcmp(mntbuf[i].f_mntonname, name) == 0) + return (&mntbuf[i]); + return (NULL); +} + +int +badvfsname(vfsname, vfslist) + const char *vfsname; + const char **vfslist; +{ + + if (vfslist == NULL) + return (0); + while (*vfslist != NULL) { + if (strcmp(vfsname, *vfslist) == 0) + return (skipvfs); + ++vfslist; + } + return (!skipvfs); +} + +int +badvfstype(vfstype, vfslist) + int vfstype; + const char **vfslist; +{ +static const char *vfsnames[] = INITMOUNTNAMES; + + if ((vfstype < 0) || (vfstype > MOUNT_MAXTYPE)) + return (0); + + return (badvfsname(vfsnames[vfstype], vfslist)); +} + +const char ** +makevfslist(fslist) + char *fslist; +{ + const char **av; + int i; + char *nextcp; + + if (fslist == NULL) + return (NULL); + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + skipvfs = 1; + } + for (i = 0, nextcp = fslist; *nextcp; nextcp++) + if (*nextcp == ',') + i++; + if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { + warn(NULL); + return (NULL); + } + nextcp = fslist; + i = 0; + av[i++] = nextcp; + while ((nextcp = strchr(nextcp, ',')) != NULL) { + *nextcp++ = '\0'; + av[i++] = nextcp; + } + av[i++] = NULL; + return (av); +} + +char * +catopt(s0, s1) + char *s0; + const char *s1; +{ + size_t i; + char *cp; + + if (s0 && *s0) { + i = strlen(s0) + strlen(s1) + 1 + 1; + if ((cp = malloc(i)) == NULL) + err(1, NULL); + (void)snprintf(cp, i, "%s,%s", s0, s1); + } else + cp = strdup(s1); + + if (s0) + free(s0); + return (cp); +} + +void +mangle(options, argcp, argv) + char *options; + int *argcp; + const char **argv; +{ + char *p, *s; + int argc; + + argc = *argcp; + for (s = options; (p = strsep(&s, ",")) != NULL;) + if (*p != '\0') + if (*p == '-') { + argv[argc++] = p; + p = strchr(p, '='); + if (p) { + *p = '\0'; + argv[argc++] = p+1; + } + } else if (strcmp(p, "rw") != 0) { + argv[argc++] = "-o"; + argv[argc++] = p; + } + + *argcp = argc; +} + +void +usage() +{ + + (void)fprintf(stderr, + "usage: mount %s %s\n mount %s\n mount %s\n", + "[-dfruvw] [-o options] [-t ufs | external_type]", + "special node", + "[-adfruvw] [-t ufs | external_type]", + "[-dfruvw] special | node"); + exit(1); +} diff --git a/sbin/mount/mount_ufs.c b/sbin/mount/mount_ufs.c new file mode 100644 index 000000000000..babb760a07d8 --- /dev/null +++ b/sbin/mount/mount_ufs.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_ufs.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" + +void ufs_usage __P((void)); + +static struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_ASYNC, + MOPT_SYNC, + MOPT_UPDATE, + { NULL } +}; + +int +mount_ufs(argc, argv) + int argc; + char * const argv[]; +{ + extern int optreset; + struct ufs_args args; + int ch, mntflags; + char *fs_name; + + mntflags = 0; + optind = optreset = 1; /* Reset for parse of new argv. */ + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + ufs_usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + ufs_usage(); + + args.fspec = argv[0]; /* The name of the device file. */ + fs_name = argv[1]; /* The mount point. */ + +#define DEFAULT_ROOTUID -2 + args.export.ex_root = DEFAULT_ROOTUID; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + + if (mount(MOUNT_UFS, fs_name, mntflags, &args) < 0) { + (void)fprintf(stderr, "%s on %s: ", args.fspec, fs_name); + switch (errno) { + case EMFILE: + (void)fprintf(stderr, "mount table full.\n"); + break; + case EINVAL: + if (mntflags & MNT_UPDATE) + (void)fprintf(stderr, + "Specified device does not match mounted device.\n"); + else + (void)fprintf(stderr, + "Incorrect super block.\n"); + break; + default: + (void)fprintf(stderr, "%s\n", strerror(errno)); + break; + } + return (1); + } + return (0); +} + +void +ufs_usage() +{ + (void)fprintf(stderr, "usage: mount_ufs [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount/pathnames.h b/sbin/mount/pathnames.h new file mode 100644 index 000000000000..45a4a010c1cf --- /dev/null +++ b/sbin/mount/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.2 (Berkeley) 3/27/94 + */ + +#define _PATH_SBIN "/sbin" +#define _PATH_USRSBIN "/usr/sbin" +#define _PATH_MOUNTDPID "/var/run/mountd.pid" diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile new file mode 100644 index 000000000000..2491dff65559 --- /dev/null +++ b/sbin/mount_cd9660/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_cd9660 +SRCS= mount_cd9660.c getmntopts.c +MAN8= mount_cd9660.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_cd9660/mount_cd9660.8 b/sbin/mount_cd9660/mount_cd9660.8 new file mode 100644 index 000000000000..920dc6c87fd3 --- /dev/null +++ b/sbin/mount_cd9660/mount_cd9660.8 @@ -0,0 +1,99 @@ +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Christopher G. Demetriou. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_cd9660.8 8.3 (Berkeley) 3/27/94 +.Dd March 27, 1994 +.Dt MOUNT_CD9660 8 +.Os BSD 4 +.Sh NAME +.Nm mount_cd9660 +.Nd mount an ISO-9660 filesystem +.Sh SYNOPSIS +.Nm mount_cd9660 +.Op Fl egr +.Op Fl o Ar options +.Ar special | node +.Sh DESCRIPTION +The +.Nm mount_cd9660 +command attaches the ISO-9660 filesystem residing on the device +.Pa special +to the global filesystem namespace at the location indicated by +.Pa node . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl e +Enable the use of extended attributes. +.It Fl g +Do not strip version numbers on files. +(By default, if there are files with different version numbers on the disk, +only the last one will be listed.) +In either case, files may be opened without explicitly stating a +version number. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl r +Do not use any Rockridge extensions included in the filesystem. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh BUGS +The cd9660 filesystem does not support the original "High Sierra" +("CDROM001") format. +.Pp +POSIX device node mapping is currently not supported. +.Pp +Version numbers are not stripped if Rockridge extensions are in use. +In this case, accessing files that don't have Rockridge names without +version numbers gets the one with the lowest version number and not +the one with the highest. +.Pp +There is no ECMA support. +.Sh HISTORY +The +.Nm mount_cd9660 +utility first appeared 4.4BSD. diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c new file mode 100644 index 000000000000..9ef716df9d2f --- /dev/null +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley + * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension + * Support code is derived from software contributed to Berkeley + * by Atsushi Murai (amurai@spec.co.jp). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mount_cd9660.c 8.4 (Berkeley) 3/27/94 + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_cd9660.c 8.4 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#define CD9660 +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char **argv; +{ + struct iso_args args; + int ch, mntflags, opts; + char *dev, *dir, *options; + + options = NULL; + mntflags = opts = 0; + while ((ch = getopt(argc, argv, "ego:r")) != EOF) + switch (ch) { + case 'e': + opts |= ISOFSMNT_EXTATT; + break; + case 'g': + opts |= ISOFSMNT_GENS; + break; + case 'o': + getmntopts(options, mopts, &mntflags); + break; + case 'r': + opts |= ISOFSMNT_NORRIP; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + dev = argv[0]; + dir = argv[1]; + +#define DEFAULT_ROOTUID -2 + args.fspec = dev; + args.export.ex_root = DEFAULT_ROOTUID; + + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + args.flags = opts; + + if (mount(MOUNT_CD9660, dir, mntflags, &args) < 0) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_cd9660 [-egrt] [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount_fdesc/Makefile b/sbin/mount_fdesc/Makefile new file mode 100644 index 000000000000..2f8f3cd7df10 --- /dev/null +++ b/sbin/mount_fdesc/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_fdesc +SRCS= mount_fdesc.c getmntopts.c +MAN8= mount_fdesc.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_fdesc/mount_fdesc.8 b/sbin/mount_fdesc/mount_fdesc.8 new file mode 100644 index 000000000000..a61c952bb4a4 --- /dev/null +++ b/sbin/mount_fdesc/mount_fdesc.8 @@ -0,0 +1,171 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_fdesc.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_FDESC 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_fdesc +.Nd mount the file-descriptor file system +.Sh SYNOPSIS +.Nm mount_fdesc +.Op Fl o Ar options +.Ar fdesc +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_fdesc +command attaches an instance of the per-process file descriptor +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /dev +and the filesystem should be union mounted in order to augment, +rather than replace, the existing entries in +.Pa /dev . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The contents of the mount point are +.Pa fd , +.Pa stderr , +.Pa stdin , +.Pa stdout +and +.Pa tty . +.Pp +.Pa fd +is a directory whose contents +appear as a list of numbered files +which correspond to the open files of the process reading the +directory. +The files +.Pa /dev/fd/0 +through +.Pa /dev/fd/# +refer to file descriptors which can be accessed through the file +system. +If the file descriptor is open and the mode the file is being opened +with is a subset of the mode of the existing descriptor, the call: +.Bd -literal -offset indent +fd = open("/dev/fd/0", mode); +.Ed +.Pp +and the call: +.Bd -literal -offset indent +fd = fcntl(0, F_DUPFD, 0); +.Ed +.Pp +are equivalent. +.Pp +The files +.Pa /dev/stdin , +.Pa /dev/stdout +and +.Pa /dev/stderr +appear as symlinks to the relevant entry in the +.Pa /dev/fd +sub-directory. +Opening them is equivalent to the following calls: +.Bd -literal -offset indent +fd = fcntl(STDIN_FILENO, F_DUPFD, 0); +fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); +fd = fcntl(STDERR_FILENO, F_DUPFD, 0); +.Ed +.Pp +Flags to the +.Xr open 2 +call other than +.Dv O_RDONLY , +.Dv O_WRONLY +and +.Dv O_RDWR +are ignored. +.Pp +The +.Pa /dev/tty +entry is an indirect reference to the current process's controlling terminal. +It appears as a named pipe (FIFO) but behaves in exactly the same way as +the real controlling terminal device. +.Sh FILES +.Bl -tag -width /dev/stderr -compact +.It Pa /dev/fd/# +.It Pa /dev/stdin +.It Pa /dev/stdout +.It Pa /dev/stderr +.It Pa /dev/tty +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr tty 4 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +No +.Pa . +and +.Pa .. +entries appear when listing the contents of the +.Pa /dev/fd +directory. +This makes sense in the context of this filesystem, but is inconsistent +with usual filesystem conventions. +However, it is still possible to refer to both +.Pa . +and +.Pa .. +in a pathname. +.Pp +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_fdesc +utility first appeared in 4.4BSD. diff --git a/sbin/mount_fdesc/mount_fdesc.c b/sbin/mount_fdesc/mount_fdesc.c new file mode 100644 index 000000000000..55927d8efee1 --- /dev/null +++ b/sbin/mount_fdesc/mount_fdesc.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_fdesc.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_FDESC, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_fdesc [-o options] fdesc mount_point\n"); + exit(1); +} diff --git a/sbin/mount_ifs/Makefile b/sbin/mount_ifs/Makefile new file mode 100644 index 000000000000..b3efd4ef63c4 --- /dev/null +++ b/sbin/mount_ifs/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.5 (Berkeley) 3/27/94 + +PROG= mount +SRCS= mount.c mount_ufs.c getmntopts.c +MAN8= mount.0 +# We do NOT install the getmntopts.3 man page. + +.include diff --git a/sbin/mount_ifs/getmntopts.3 b/sbin/mount_ifs/getmntopts.3 new file mode 100644 index 000000000000..642c57aa361d --- /dev/null +++ b/sbin/mount_ifs/getmntopts.3 @@ -0,0 +1,163 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getmntopts.3 8.1 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt GETMNTOPTS 3 +.Os BSD 4.4 +.Sh NAME +.Nm getmntopts +.Nd scan mount options +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fn getmntopts "char *options" "struct mntopt *mopts" "int *flagp" +.Sh DESCRIPTION +The +.Nm getmntopts +function takes a comma separated option list and a list +of valid option names, and computes the bitmask +corresponding to the requested set of options. +.Pp +The string +.Dv options +is broken down into a sequence of comma separated tokens. +Each token is looked up in the table described by +.Dv mopts +and the bits in +the word referenced by +.Dv flagp +are updated. +The flag word is not initialized by +.Nm getmntopt . +The table, +.Dv mopts , +has the following format: +.Bd -literal +struct mntopt { + char *m_option; /* option name */ + int m_inverse; /* is this a negative option, eg "dev" */ + int m_flag; /* bit to set, eg MNT_RDONLY */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width m_inverse +.It Fa m_option +the option name, +for example +.Dq suid . +.It Fa m_inverse +tells +.Nm getmntopts +that the name has the inverse meaning of the +bit. +For example, +.Dq suid +is the string, whereas the +mount flag is +.Dv MNT_NOSUID . +In this case, the sense of the string and the flag +are inverted, so the +.Dv m_inverse +flag should be set. +.It Fa m_flag +the value of the bit to be set or cleared in +the flag word when the option is recognized. +The bit is set when the option is discovered, +but cleared if the option name was preceded +by the letters +.Dq no . +The +.Dv m_inverse +flag causes these two operations to be reversed. +.El +.Pp +Each of the user visible +.Dv MNT_ +flags has a corresponding +.Dv MOPT_ +macro which defines an appropriate +.Li "struct mntopt" +entry. +To simplify the program interface and ensure consistency across all +programs, a general purpose macro, +.Dv MOPT_STDOPTS , +is defined which +contains an entry for all the generic VFS options. +In addition, the macros +.Dv MOPT_FORCE +and +.Dv MOPT_UPDATE +exist to enable the +.Dv MNT_FORCE +and +.Dv MNT_UPDATE +flags to be set. +Finally, the table must be terminated by an entry with a NULL +first element. +.Sh EXAMPLES +Most commands will use the standard option set. +Local filesystems which support the +.Dv MNT_UPDATE +flag, would also have an +.Dv MOPT_UPDATE +entry. +This can be declared and used as follows: +.Bd -literal +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + + ... + mntflags = 0; + ... + getmntopts(options, mopts, &mntflags) + ... +.Ed +.Sh DIAGNOSTICS +The +.Nm getmntopts +function displays an error message and exits if an +unrecognized option is encountered. +.Sh SEE ALSO +.Xr err 3 , +.Xr mount 8 +.Sh HISTORY +The +.Fn getmntopts +function appeared in +.Bx 4.4 . diff --git a/sbin/mount_ifs/getmntopts.c b/sbin/mount_ifs/getmntopts.c new file mode 100644 index 000000000000..2c84ae56e3d5 --- /dev/null +++ b/sbin/mount_ifs/getmntopts.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getmntopts.c 8.1 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +void +getmntopts(options, m0, flagp) + const char *options; + const struct mntopt *m0; + int *flagp; +{ + const struct mntopt *m; + int negative; + char *opt, *optbuf; + + /* Copy option string, since it is about to be torn asunder... */ + if ((optbuf = strdup(options)) == NULL) + err(1, NULL); + + for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { + /* Check for "no" prefix. */ + if (opt[0] == 'n' && opt[1] == 'o') { + negative = 1; + opt += 2; + } else + negative = 0; + + /* Scan option table. */ + for (m = m0; m->m_option != NULL; ++m) + if (strcasecmp(opt, m->m_option) == 0) + break; + + /* Save flag, or fail if option is not recognised. */ + if (m->m_option) { + if (negative == m->m_inverse) + *flagp |= m->m_flag; + else + *flagp &= ~m->m_flag; + } else + errx(1, "-o %s: option not supported", opt); + } + + free(optbuf); +} diff --git a/sbin/mount_ifs/mntopts.h b/sbin/mount_ifs/mntopts.h new file mode 100644 index 000000000000..a89f63dc19ff --- /dev/null +++ b/sbin/mount_ifs/mntopts.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mntopts.h 8.3 (Berkeley) 3/27/94 + */ + +struct mntopt { + const char *m_option; /* option name */ + int m_inverse; /* if a negative option, eg "dev" */ + int m_flag; /* bit to set, eg. MNT_RDONLY */ +}; + +/* User-visible MNT_ flags. */ +#define MOPT_ASYNC { "async", 0, MNT_ASYNC } +#define MOPT_NODEV { "dev", 1, MNT_NODEV } +#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC } +#define MOPT_NOSUID { "suid", 1, MNT_NOSUID } +#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY } +#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS } +#define MOPT_UNION { "union", 0, MNT_UNION } + +/* Control flags. */ +#define MOPT_FORCE { "force", 1, MNT_FORCE } +#define MOPT_UPDATE { "update", 0, MNT_UPDATE } + +/* Support for old-style "ro", "rw" flags. */ +#define MOPT_RO { "ro", 0, MNT_RDONLY } +#define MOPT_RW { "rw", 1, MNT_RDONLY } + +#define MOPT_FSTAB_COMPAT \ + MOPT_RO, \ + MOPT_RW + +/* Standard options which all mounts can understand. */ +#define MOPT_STDOPTS \ + MOPT_FSTAB_COMPAT, \ + MOPT_NODEV, \ + MOPT_NOEXEC, \ + MOPT_NOSUID, \ + MOPT_RDONLY, \ + MOPT_UNION + +void getmntopts __P((const char *, const struct mntopt *, int *)); diff --git a/sbin/mount_ifs/mount.8 b/sbin/mount_ifs/mount.8 new file mode 100644 index 000000000000..e2c53c8ffec8 --- /dev/null +++ b/sbin/mount_ifs/mount.8 @@ -0,0 +1,264 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount.8 8.7 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm mount +.Nd mount file systems +.Sh SYNOPSIS +.Nm mount +.Op Fl adfruvw +.Op Fl t Ar ufs | lfs | external_type +.Nm mount +.Op Fl dfruvw +.Ar special | node +.Nm mount +.Op Fl dfruvw +.Op Fl o Ar options +.Op Fl t Ar ufs | lfs | external_type +.Ar special node +.Sh DESCRIPTION +The +.Nm mount +command +calls the +.Xr mount 2 +system call to prepare and graft a +.Ar "special device" +or the remote node (rhost:path) on to the file system tree at the point +.Ar node . +If either +.Ar special +or +.Ar node +are not provided, the appropriate information is taken from the +.Xr fstab 5 +file. +.Pp +The system maintains a list of currently mounted file systems. +If no arguments are given to +.Nm mount, +this list is printed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl d +Causes everything to be done except for the actual system call. +This option is useful in conjunction with the +.Fl v +flag to +determine what the +.Nm mount +command is trying to do. +.It Fl f +Forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +The following options are available: +.Bl -tag -width indent +.It async +All +.Tn I/O +to the file system should be done asynchronously. +This is a +.Em dangerous +flag to set, +and should not be used unless you are prepared to recreate the file +system should your system crash. +.It force +The same as +.Fl f ; +forces the revocation of write access when trying to downgrade +a filesystem mount status from read-write to read-only. +.It nodev +Do not interpret character or block special devices on the file system. +This option is useful for a server that has file systems containing +special devices for architectures other than its own. +.It noexec +Do not allow execution of any binaries on the mounted file system. +This option is useful for a server that has file systems containing +binaries for architectures other than its own. +.It nosuid +Do not allow set-user-identifier or set-group-identifier bits to take effect. +.It rdonly +The same as +.Fl r ; +mount the file system read-only (even the super-user may not write it). +.It sync +All +.Tn I/O +to the file system should be done synchronously. +.It update +The same as +.Fl u ; +indicate that the status of an already mounted file system should be changed. +.It union +Causes the namespace at the mount point to appear as the union +of the mounted filesystem root and the existing directory. +Lookups will be done in the mounted filesystem first. +If those operations fail due to a non-existent file the underlying +directory is then accessed. +All creates are done in the mounted filesystem. +.El +.Pp +Any additional options specific to a filesystem type that is not +one of the internally known types (see the +.Fl t +option) may be passed as a comma separated list; these options are +distinguished by a leading +.Dq \&- +(dash). +Options that take a value are specified using the syntax -option=value. +For example, the mount command: +.Bd -literal -offset indent +mount -t mfs -o nosuid,-N,-s=4000 /dev/dk0b /tmp +.Ed +.Pp +causes +.Nm mount +to execute the equivalent of: +.Bd -literal -offset indent +/sbin/mount_mfs -o nosuid -N -s 4000 /dev/dk0b /tmp +.Ed +.It Fl r +The file system is to be mounted read-only. +Mount the file system read-only (even the super-user may not write it). +The same as the +.Dq rdonly +argument to the +.Fl o +option. +.It Fl t Ar "ufs \\*(Ba lfs \\*(Ba external type" +The argument following the +.Fl t +is used to indicate the file system type. +The type +.Ar ufs +is the default. +The \fI-t\fP option can be used +to indicate that the actions should only be taken on +filesystems of the specified type. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm mount +command: +.Bd -literal -offset indent +mount -a -t nonfs,mfs +.Ed +.Pp +mounts all filesystems except those of type +.Tn NFS +and +.Tn MFS . +.Pp +If the type is not one of the internally known types, +mount will attempt to execute a program in +.Pa /sbin/mount_ Ns Em XXX +where +.Em XXX +is replaced by the type name. +For example, nfs filesystems are mounted by the program +.Pa /sbin/mount_nfs . +.It Fl u +The +.Fl u +flag indicates that the status of an already mounted file +system should be changed. +Any of the options discussed above (the +.Fl o +option) +may be changed; +also a file system can be changed from read-only to read-write +or vice versa. +An attempt to change from read-write to read-only will fail if any +files on the filesystem are currently open for writing unless the +.Fl f +flag is also specified. +The set of options is determined by first extracting the options +for the file system from the +.Xr fstab +table, +then applying any options specified by the +.Fl o +argument, +and finally applying the +.Fl r +or +.Fl w +option. +.It Fl v +Verbose mode. +.It Fl w +The file system object is to be read and write. +.Pp +The options specific to NFS filesystems are described in the +.Xr mount_nfs 8 +manual page. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +file system table +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr fstab 5 , +.Xr mount_cd9660 8 , +.Xr mount_fdesc 8 , +.Xr mount_kernfs 8 , +.Xr mount_lfs 8 , +.Xr mount_lofs 8 , +.Xr mount_mfs 8 , +.Xr mount_nfs 8 , +.Xr mount_null 8 , +.Xr mount_portal 8 , +.Xr mount_procfs 8 , +.Xr mount_umap 8 , +.Xr mount_union 8 , +.Xr umount 8 +.Sh BUGS +It is possible for a corrupted file system to cause a crash. +.Sh HISTORY +A +.Nm mount +command appeared in +.At v6 . diff --git a/sbin/mount_ifs/mount.c b/sbin/mount_ifs/mount.c new file mode 100644 index 000000000000..f0ff782f4e0e --- /dev/null +++ b/sbin/mount_ifs/mount.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 1980, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount.c 8.19 (Berkeley) 4/19/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +int debug, verbose, skipvfs; + +int badvfsname __P((const char *, const char **)); +int badvfstype __P((int, const char **)); +char *catopt __P((char *, const char *)); +struct statfs + *getmntpt __P((const char *)); +const char + **makevfslist __P((char *)); +void mangle __P((char *, int *, const char **)); +int mountfs __P((const char *, const char *, const char *, + int, const char *, const char *)); +void prmount __P((const char *, const char *, int)); +void usage __P((void)); + +/* From mount_ufs.c. */ +int mount_ufs __P((int, char * const *)); + +/* Map from mount otions to printable formats. */ +static struct opt { + int o_opt; + const char *o_name; +} optnames[] = { + { MNT_ASYNC, "asynchronous" }, + { MNT_EXPORTED, "NFS exported" }, + { MNT_LOCAL, "local" }, + { MNT_NODEV, "nodev" }, + { MNT_NOEXEC, "noexec" }, + { MNT_NOSUID, "nosuid" }, + { MNT_QUOTA, "with quotas" }, + { MNT_RDONLY, "read-only" }, + { MNT_SYNCHRONOUS, "synchronous" }, + { MNT_UNION, "union" }, + { MNT_USER, "user mount" }, + { NULL } +}; + +int +main(argc, argv) + int argc; + char * const argv[]; +{ + const char *mntonname, **vfslist, *vfstype; + struct fstab *fs; + struct statfs *mntbuf; + FILE *mountdfp; + pid_t pid; + int all, ch, i, init_flags, mntsize, rval; + char *options; + + all = init_flags = 0; + options = NULL; + vfslist = NULL; + vfstype = "ufs"; + while ((ch = getopt(argc, argv, "adfo:rwt:uv")) != EOF) + switch (ch) { + case 'a': + all = 1; + break; + case 'd': + debug = 1; + break; + case 'f': + init_flags |= MNT_FORCE; + break; + case 'o': + if (*optarg) + options = catopt(options, optarg); + break; + case 'r': + init_flags |= MNT_RDONLY; + break; + case 't': + if (vfslist != NULL) + errx(1, "only one -t option may be specified."); + vfslist = makevfslist(optarg); + vfstype = optarg; + break; + case 'u': + init_flags |= MNT_UPDATE; + break; + case 'v': + verbose = 1; + break; + case 'w': + init_flags &= ~MNT_RDONLY; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + +#define BADTYPE(type) \ + (strcmp(type, FSTAB_RO) && \ + strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) + + rval = 0; + switch (argc) { + case 0: + if (all) + while ((fs = getfsent()) != NULL) { + if (BADTYPE(fs->fs_type)) + continue; + if (badvfsname(fs->fs_vfstype, vfslist)) + continue; + if (mountfs(fs->fs_vfstype, fs->fs_spec, + fs->fs_file, init_flags, options, + fs->fs_mntops)) + rval = 1; + } + else { + if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) + err(1, "getmntinfo"); + for (i = 0; i < mntsize; i++) { + if (badvfstype(mntbuf[i].f_type, vfslist)) + continue; + prmount(mntbuf[i].f_mntfromname, + mntbuf[i].f_mntonname, mntbuf[i].f_flags); + } + } + exit(rval); + case 1: + if (vfslist != NULL) + usage(); + + if (init_flags & MNT_UPDATE) { + if ((mntbuf = getmntpt(*argv)) == NULL) + errx(1, + "unknown special file or file system %s.", + *argv); + if ((fs = getfsfile(mntbuf->f_mntonname)) == NULL) + errx(1, "can't find fstab entry for %s.", + *argv); + /* If it's an update, ignore the fstab file options. */ + fs->fs_mntops = NULL; + mntonname = mntbuf->f_mntonname; + } else { + if ((fs = getfsfile(*argv)) == NULL && + (fs = getfsspec(*argv)) == NULL) + errx(1, + "%s: unknown special file or file system.", + *argv); + if (BADTYPE(fs->fs_type)) + errx(1, "%s has unknown file system type.", + *argv); + mntonname = fs->fs_file; + } + rval = mountfs(fs->fs_vfstype, fs->fs_spec, + mntonname, init_flags, options, fs->fs_mntops); + break; + case 2: + /* + * If -t flag has not been specified, and spec contains either + * a ':' or a '@' then assume that an NFS filesystem is being + * specified ala Sun. + */ + if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL) + vfstype = "nfs"; + rval = mountfs(vfstype, + argv[0], argv[1], init_flags, options, NULL); + break; + default: + usage(); + /* NOTREACHED */ + } + + /* + * If the mount was successfully, and done by root, tell mountd the + * good news. Pid checks are probably unnecessary, but don't hurt. + */ + if (rval == 0 && getuid() == 0 && + (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { + if (fscanf(mountdfp, "%ld", &pid) == 1 && + pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) + err(1, "signal mountd"); + (void)fclose(mountdfp); + } + + exit(rval); +} + +int +mountfs(vfstype, spec, name, flags, options, mntopts) + const char *vfstype, *spec, *name, *options, *mntopts; + int flags; +{ + /* List of directories containing mount_xxx subcommands. */ + static const char *edirs[] = { + _PATH_SBIN, + _PATH_USRSBIN, + NULL + }; + const char *argv[100], **edir; + struct statfs sf; + pid_t pid; + int argc, i, status; + char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN]; + + if (realpath(name, mntpath) == NULL) { + warn("%s", mntpath); + return (1); + } + + name = mntpath; + + if (options == NULL) { + if (mntopts == NULL || *mntopts == '\0') + options = "rw"; + else + options = mntopts; + mntopts = ""; + } + optbuf = catopt(strdup(mntopts), options); + + if (strcmp(name, "/") == 0) + flags |= MNT_UPDATE; + if (flags & MNT_FORCE) + optbuf = catopt(optbuf, "force"); + if (flags & MNT_RDONLY) + optbuf = catopt(optbuf, "ro"); + /* + * XXX + * The mount_mfs (newfs) command uses -o to select the + * optimisation mode. We don't pass the default "-o rw" + * for that reason. + */ + if (flags & MNT_UPDATE) + optbuf = catopt(optbuf, "update"); + + argc = 0; + argv[argc++] = vfstype; + mangle(optbuf, &argc, argv); + argv[argc++] = spec; + argv[argc++] = name; + argv[argc] = NULL; + + if (debug) { + (void)printf("exec: mount_%s", vfstype); + for (i = 1; i < argc; i++) + (void)printf(" %s", argv[i]); + (void)printf("\n"); + return (0); + } + + switch (pid = vfork()) { + case -1: /* Error. */ + warn("vfork"); + free(optbuf); + return (1); + case 0: /* Child. */ + if (strcmp(vfstype, "ufs") == 0) + exit(mount_ufs(argc, (char * const *) argv)); + + /* Go find an executable. */ + edir = edirs; + do { + (void)snprintf(execname, + sizeof(execname), "%s/mount_%s", *edir, vfstype); + execv(execname, (char * const *)argv); + if (errno != ENOENT) + warn("exec %s for %s", execname, name); + } while (*++edir != NULL); + + if (errno == ENOENT) + warn("exec %s for %s", execname, name); + exit(1); + /* NOTREACHED */ + default: /* Parent. */ + free(optbuf); + + if (waitpid(pid, &status, 0) < 0) { + warn("waitpid"); + return (1); + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + return (WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]); + return (1); + } + + if (verbose) { + if (statfs(name, &sf) < 0) { + warn("%s", name); + return (1); + } + prmount(sf.f_mntfromname, sf.f_mntonname, sf.f_flags); + } + break; + } + + return (0); +} + +void +prmount(spec, name, flags) + const char *spec, *name; + int flags; +{ + struct opt *o; + int f; + + (void)printf("%s on %s", spec, name); + + flags &= MNT_VISFLAGMASK; + for (f = 0, o = optnames; flags && o->o_opt; o++) + if (flags & o->o_opt) { + (void)printf("%s%s", !f++ ? " (" : ", ", o->o_name); + flags &= ~o->o_opt; + } + (void)printf(f ? ")\n" : "\n"); +} + +struct statfs * +getmntpt(name) + const char *name; +{ + struct statfs *mntbuf; + int i, mntsize; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) + if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || + strcmp(mntbuf[i].f_mntonname, name) == 0) + return (&mntbuf[i]); + return (NULL); +} + +int +badvfsname(vfsname, vfslist) + const char *vfsname; + const char **vfslist; +{ + + if (vfslist == NULL) + return (0); + while (*vfslist != NULL) { + if (strcmp(vfsname, *vfslist) == 0) + return (skipvfs); + ++vfslist; + } + return (!skipvfs); +} + +int +badvfstype(vfstype, vfslist) + int vfstype; + const char **vfslist; +{ +static const char *vfsnames[] = INITMOUNTNAMES; + + if ((vfstype < 0) || (vfstype > MOUNT_MAXTYPE)) + return (0); + + return (badvfsname(vfsnames[vfstype], vfslist)); +} + +const char ** +makevfslist(fslist) + char *fslist; +{ + const char **av; + int i; + char *nextcp; + + if (fslist == NULL) + return (NULL); + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + skipvfs = 1; + } + for (i = 0, nextcp = fslist; *nextcp; nextcp++) + if (*nextcp == ',') + i++; + if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { + warn(NULL); + return (NULL); + } + nextcp = fslist; + i = 0; + av[i++] = nextcp; + while ((nextcp = strchr(nextcp, ',')) != NULL) { + *nextcp++ = '\0'; + av[i++] = nextcp; + } + av[i++] = NULL; + return (av); +} + +char * +catopt(s0, s1) + char *s0; + const char *s1; +{ + size_t i; + char *cp; + + if (s0 && *s0) { + i = strlen(s0) + strlen(s1) + 1 + 1; + if ((cp = malloc(i)) == NULL) + err(1, NULL); + (void)snprintf(cp, i, "%s,%s", s0, s1); + } else + cp = strdup(s1); + + if (s0) + free(s0); + return (cp); +} + +void +mangle(options, argcp, argv) + char *options; + int *argcp; + const char **argv; +{ + char *p, *s; + int argc; + + argc = *argcp; + for (s = options; (p = strsep(&s, ",")) != NULL;) + if (*p != '\0') + if (*p == '-') { + argv[argc++] = p; + p = strchr(p, '='); + if (p) { + *p = '\0'; + argv[argc++] = p+1; + } + } else if (strcmp(p, "rw") != 0) { + argv[argc++] = "-o"; + argv[argc++] = p; + } + + *argcp = argc; +} + +void +usage() +{ + + (void)fprintf(stderr, + "usage: mount %s %s\n mount %s\n mount %s\n", + "[-dfruvw] [-o options] [-t ufs | external_type]", + "special node", + "[-adfruvw] [-t ufs | external_type]", + "[-dfruvw] special | node"); + exit(1); +} diff --git a/sbin/mount_ifs/mount_ufs.c b/sbin/mount_ifs/mount_ufs.c new file mode 100644 index 000000000000..babb760a07d8 --- /dev/null +++ b/sbin/mount_ifs/mount_ufs.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_ufs.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" + +void ufs_usage __P((void)); + +static struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_ASYNC, + MOPT_SYNC, + MOPT_UPDATE, + { NULL } +}; + +int +mount_ufs(argc, argv) + int argc; + char * const argv[]; +{ + extern int optreset; + struct ufs_args args; + int ch, mntflags; + char *fs_name; + + mntflags = 0; + optind = optreset = 1; /* Reset for parse of new argv. */ + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + ufs_usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + ufs_usage(); + + args.fspec = argv[0]; /* The name of the device file. */ + fs_name = argv[1]; /* The mount point. */ + +#define DEFAULT_ROOTUID -2 + args.export.ex_root = DEFAULT_ROOTUID; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + + if (mount(MOUNT_UFS, fs_name, mntflags, &args) < 0) { + (void)fprintf(stderr, "%s on %s: ", args.fspec, fs_name); + switch (errno) { + case EMFILE: + (void)fprintf(stderr, "mount table full.\n"); + break; + case EINVAL: + if (mntflags & MNT_UPDATE) + (void)fprintf(stderr, + "Specified device does not match mounted device.\n"); + else + (void)fprintf(stderr, + "Incorrect super block.\n"); + break; + default: + (void)fprintf(stderr, "%s\n", strerror(errno)); + break; + } + return (1); + } + return (0); +} + +void +ufs_usage() +{ + (void)fprintf(stderr, "usage: mount_ufs [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount_ifs/pathnames.h b/sbin/mount_ifs/pathnames.h new file mode 100644 index 000000000000..45a4a010c1cf --- /dev/null +++ b/sbin/mount_ifs/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.2 (Berkeley) 3/27/94 + */ + +#define _PATH_SBIN "/sbin" +#define _PATH_USRSBIN "/usr/sbin" +#define _PATH_MOUNTDPID "/var/run/mountd.pid" diff --git a/sbin/mount_kernfs/Makefile b/sbin/mount_kernfs/Makefile new file mode 100644 index 000000000000..6bd6d7c65e61 --- /dev/null +++ b/sbin/mount_kernfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_kernfs +SRCS= mount_kernfs.c getmntopts.c +MAN8= mount_kernfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_kernfs/mount_kernfs.8 b/sbin/mount_kernfs/mount_kernfs.8 new file mode 100644 index 000000000000..787c174416b7 --- /dev/null +++ b/sbin/mount_kernfs/mount_kernfs.8 @@ -0,0 +1,131 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_kernfs.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_KERNFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_kernfs +.Nd mount the /kern file system +.Sh SYNOPSIS +.Nm mount_kernfs +.Op Fl o Ar options +.Ar /kern +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_kern +command attaches an instance of the kernel parameter +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /kern . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The filesystem includes several regular files which can be read, +some of which can also be written. +The contents of the files is in a machine-independent format, +either a string, or an integer in decimal ASCII. +Where numbers are returned, a trailing newline character is also added. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Sh FILES +.Bl -tag -width copyright -compact +.It Pa boottime +the time at which the system was last booted (decimal ASCII). +.It Pa copyright +kernel copyright message. +.It Pa hostname +the hostname, with a trailing newline. +The hostname can be changed by writing to this file. +A trailing newline will be stripped from the hostname being written. +.It Pa hz +the frequency of the system clock (decimal ASCII). +.It Pa loadavg +the 1, 5 and 15 minute load average in kernel fixed-point format. +The final integer is the fix-point scaling factor. +All numbers are in decimal ASCII. +.It Pa pagesize +the machine pagesize (decimal ASCII). +.It Pa physmem +the number of pages of physical memory in the machine (decimal ASCII). +.It Pa root +the system root directory. +In a chroot'ed environment, +.Nm +can be used to create a new +.Pa /kern +mount point. +.Pa /kern/root +will then refer to the system global root, not the current process root. +.It Pa rootdev +the root device. +.It Pa rrootdev +the raw root device. +.It Pa time +the second and microsecond value of the system clock. +Both numbers are in decimal ASCII. +.It Pa version +the kernel version string. +The head line for +.Pa /etc/motd +can be generated by running: +.Dq Ic "sed 1q /kern/version" +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_kernfs +utility first appeared in 4.4BSD. diff --git a/sbin/mount_kernfs/mount_kernfs.c b/sbin/mount_kernfs/mount_kernfs.c new file mode 100644 index 000000000000..33a6b5e12dd6 --- /dev/null +++ b/sbin/mount_kernfs/mount_kernfs.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_kernfs.c 8.2 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_KERNFS, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_kernfs [-o options] /kern mount_point\n"); + exit(1); +} diff --git a/sbin/mount_lfs/Makefile b/sbin/mount_lfs/Makefile new file mode 100644 index 000000000000..d3fb12bae49d --- /dev/null +++ b/sbin/mount_lfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_lfs +SRCS= mount_lfs.c getmntopts.c +MAN8= mount_lfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_lfs/mount_lfs.8 b/sbin/mount_lfs/mount_lfs.8 new file mode 100644 index 000000000000..bd1bd8c19aaa --- /dev/null +++ b/sbin/mount_lfs/mount_lfs.8 @@ -0,0 +1,125 @@ +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_lfs.8 8.5 (Berkeley) 3/30/94 +.\" +.Dd "March 30, 1994" +.Dt MOUNT_LFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_lfs +.Nd mount a log-structured file system +.Sh SYNOPSIS +.Nm mount_lfs +.Op Fl dns +.Op Fl o Ar options +.Ar special +.Ar node +.Sh DESCRIPTION +The +.Nm mount_lfs +command attaches a log-structured file system +.Ar special +device on to the file system tree at the point +.Ar node . +In addition, the +.Xr lfs_cleanerd 8 +utility is invoked to clean the file system periodically. +.Pp +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Run +.Xr lfs_cleanerd 8 +in debug mode. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl n +Don't start +.Xr lfs_cleanerd 8 +on the file system. +.It Fl s +Cause +.Xr lfs_cleanerd 8 +to read data in small chunks when cleaning the file system. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr lfs_cleanerd 8 , +.Xr mount 8 +.sp +.Rs +.%A Ousterhout and Douglis +.%D 1989 +.%T "Beating the I/O Bottleneck: A Case for Log-structured File Systems" +.%J "Operating Systems Review" +.%V Vol. 23 +.%N No. 1 +.%P pp. 11-27 +.%O "also available as Technical Report UCB/CSD 88/467" +.Re +.Rs +.%A Rosenblum and Ousterhout +.%D 1991 +.%T "The Design and Implementation of a Log-Structured File System" +.%J "ACM SIGOPS Operating Systems Review" +.%V Vol. 25 +.%N No. 5 +.Re +.Rs +.%A Seltzer +.%D 1992 +.%T "File System Performance and Transaction Support" +.%B "PhD Thesis, University of California, Berkeley" +.%O "also available as Technical Report UCB/ERL M92" +.Re +.Rs +.%A Seltzer, Bostic, McKusick and Staelin +.%D 1993 +.%T "An Implementation of a Log-Structured File System for UNIX" +.%J "Proc. of the Winter 1993 USENIX Conf." +.%P pp. 315-331 +.Re +.Sh HISTORY +The +.Nm mount_lfs +function first appeared in 4.4BSD. diff --git a/sbin/mount_lfs/mount_lfs.c b/sbin/mount_lfs/mount_lfs.c new file mode 100644 index 000000000000..374c9304cca6 --- /dev/null +++ b/sbin/mount_lfs/mount_lfs.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_lfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" +#include "pathnames.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + +void usage __P((void)); +void invoke_cleaner __P((char *)); + +int short_rds, cleaner_debug; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct ufs_args args; + int ch, mntflags, noclean; + char *fs_name, *options; + + options = NULL; + mntflags = noclean = 0; + while ((ch = getopt(argc, argv, "dno:s")) != EOF) + switch (ch) { + case 'd': + cleaner_debug = 1; + break; + case 'n': + noclean = 1; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 's': + short_rds = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + args.fspec = argv[0]; /* the name of the device file */ + fs_name = argv[1]; /* the mount point */ + +#define DEFAULT_ROOTUID -2 + args.export.ex_root = DEFAULT_ROOTUID; + if (mntflags & MNT_RDONLY) + args.export.ex_flags = MNT_EXRDONLY; + else + args.export.ex_flags = 0; + + if (mount(MOUNT_LFS, fs_name, mntflags, &args)) + err(1, NULL); + + if (!noclean) + invoke_cleaner(fs_name); + /* NOTREACHED */ + + exit(0); +} + +void +invoke_cleaner(name) + char *name; +{ + char *args[6], **ap = args; + + /* Build the argument list. */ + *ap++ = _PATH_LFS_CLEANERD; + if (short_rds) + *ap++ = "-s"; + if (cleaner_debug) + *ap++ = "-d"; + *ap++ = name; + *ap = NULL; + + execv(args[0], args); + err(1, "exec %s", _PATH_LFS_CLEANERD); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_lfs [-dns] [-o options] special node\n"); + exit(1); +} diff --git a/sbin/mount_lfs/pathnames.h b/sbin/mount_lfs/pathnames.h new file mode 100644 index 000000000000..fafbf714db52 --- /dev/null +++ b/sbin/mount_lfs/pathnames.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/8/93 + */ + +#define _PATH_LFS_CLEANERD "/usr/libexec/lfs_cleanerd" diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile new file mode 100644 index 000000000000..f33b461560cf --- /dev/null +++ b/sbin/mount_nfs/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.2 (Berkeley) 3/27/94 + +PROG= mount_nfs +SRCS= mount_nfs.c getmntopts.c +MAN8= mount_nfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -DNFS -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8 new file mode 100644 index 000000000000..decac6efefed --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.8 @@ -0,0 +1,222 @@ +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_nfs.8 8.2 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT_NFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_nfs +.Nd mount nfs file systems +.Sh SYNOPSIS +.Nm mount_nfs +.Op Fl KMPTbcdiklqs +.Op Fl D Ar deadthresh +.Op Fl L Ar leaseterm +.Op Fl R Ar retrycnt +.Op Fl a Ar maxreadahead +.Op Fl g Ar maxgroups +.Op Fl m Ar realm +.Op Fl o Ar options +.Op Fl r Ar readsize +.Op Fl t Ar timeout +.Op Fl w Ar writesize +.Op Fl x Ar retrans +.Ar rhost:path node +.Sh DESCRIPTION +The +.Nm mount_nfs +command +calls the +.Xr mount 2 +system call to prepare and graft a remote nfs file system (rhost:path) +on to the file system tree at the point +.Ar node. +This command is normally executed by +.Xr mount 8 . +It implements the mount protocol as described in RFC 1094, Appendix A. +.Pp +The options are: +.Bl -tag -width indent +.It Fl D +Used with NQNFS to set the +.Dq "dead server threshold" +to the specified number of round trip timeout intervals. +After a +.Dq "dead server threshold" +of retransmit timeouts, +cached data for the unresponsive server is assumed to still be valid. +Values may be set in the range of 1 - 9, with 9 referring to an +.Dq "infinite dead threshold" +(i.e. never assume cached data still valid). +This option is not generally recommended and is really an experimental +feature. +.It Fl K +Pass Kerberos authenticators to the server for client-to-server +user-credential mapping. +This may only be used over TCP mounts between 4.4BSD clients and servers. +.It Fl L +Used with NQNFS to set the lease term to the specified number of seconds. +Only use this argument for mounts with a large round trip delay. +Values are normally in the 10-30 second range. +.It Fl M +Assume that other clients are not writing a file concurrently with this client. +This implements a slightly less strict consistency criteria than 4.3BSD-Reno +did, that is more in line with most commercial client implementations. +This is recommended for servers that do not support leasing. +.It Fl P +Use a reserved socket port number. +This is useful for mounting servers that require clients to use a +reserved port number. +.It Fl R +Set the retry count for doing the mount to the specified value. +.It Fl T +Use TCP transport instead of UDP. +This is recommended for servers that are not on the same LAN cable as +the client. +(NB: This is NOT supported by most non-BSD servers.) +.It Fl a +Set the read-ahead count to the specified value. +This may be in the range of 0 - 4, and determines how many blocks +will be read ahead when a large file is being read sequentially. +This is recommended for mounts with a large bandwidth * delay product. +.It Fl b +If an initial attempt to contact the server fails, fork off a child to keep +trying the mount in the background. +Useful for +.Xr fstab 5 , +where the filesystem mount is not critical to multiuser operation. +.It Fl c +For UDP mount points, do not do a +.Xr connect 2 . +This must be used for servers that do not reply to requests from the +standard port number. +.It Fl d +Do not estimate retransmit timeout dynamically. +This may be useful for UDP mounts that exhibit high retry rates. +.It Fl g +Set the maximum size of the group list for the credentials to the +specified value. +This should be used for mounts on old servers that cannot handle a +group list size of 16, as specified in RFC 1057. +Try 8, if users in a lot of groups cannot get response from the mount +point. +.It Fl i +Make the mount interruptible, which implies that file system calls that +are delayed due to an unresponsive server will fail with EINTR when a +termination signal is posted for the process. +.It Fl k +Used with NQNFS to specify +.Dq get a lease +for the file name being looked up. +This is recommended unless the server is complaining about excessive +lease load. +.It Fl l +Used with NQNFS to specify that the \fBReaddir_and_Lookup\fR RPC should +be used. +This option reduces RPC traffic for cases such as +.Dq "ls -l" , +but increases the lease load on the server. +This is recommended unless the server is complaining about excessive +lease load. +.It Fl m +Set the Kerberos realm to the string argument. +Used with the +.Fl K +option for mounts to other realms. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl q +Use the leasing extensions to the protocol to maintain cache consistency. +This protocol, referred to as Not Quite Nfs (NQNFS), +is only supported by 4.4BSD servers. +.It Fl r +Set the read data size to the specified value. +It should be a power of 2 greater than or equal to 1024. +This should be used for UDP mounts when the +.Dq "fragments dropped due to timeout" +value is getting large while actively using a mount point. +(Use +.Xr netstat 1 +with the +.Fl s +option to see what the +.Dq "fragments dropped due to timeout" +value is.) +See the +.Fl w +option as well. +.It Fl s +A soft mount, which implies that file system calls will fail +after \fBRetry\fR round trip timeout intervals. +.It Fl t +Set the initial retransmit timeout to the specified value. +May be useful for fine tuning UDP mounts over internetworks +with high packet loss rates or an overloaded server. +Try increasing the interval if +.Xr nfsstat 1 +shows high retransmit rates while the file system is active or reducing the +value if there is a low retransmit rate but long response delay observed. +.It Fl w +Set the write data size to the specified value. +Ditto the comments w.r.t. the +.Fl r +option, but using the +.Dq "fragments dropped due to timeout" +value on the server instead of the client. +Note that both the +.Fl r +and +.Fl w +options should only be used as a last ditch effort at improving performance +when mounting servers that do not support TCP mounts. +.It Fl x +Set the retransmit timeout count for soft mounts to the specified value. +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh BUGS +Due to the way that Sun RPC is implemented on top of UDP (unreliable datagram) +transport, tuning such mounts is really a black art that can only be expected +to have limited success. +For clients mounting servers that are not on the same +LAN cable or that tend to be overloaded, +TCP transport is strongly recommended, +but unfortunately this is restricted to mostly 4.4BSD servers. diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c new file mode 100644 index 000000000000..cbb3ddbc583e --- /dev/null +++ b/sbin/mount_nfs/mount_nfs.c @@ -0,0 +1,551 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_nfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef ISO +#include +#endif + +#ifdef KERBEROS +#include +#include +#endif + +#include +#include +#define KERNEL +#include +#undef KERNEL +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_FORCE, + MOPT_UPDATE, + { NULL } +}; + +struct nfs_args nfsdefargs = { + (struct sockaddr *)0, + sizeof (struct sockaddr_in), + SOCK_DGRAM, + 0, + (nfsv2fh_t *)0, + 0, + NFS_WSIZE, + NFS_RSIZE, + NFS_TIMEO, + NFS_RETRANS, + NFS_MAXGRPS, + NFS_DEFRAHEAD, + NQ_DEFLEASE, + NQ_DEADTHRESH, + (char *)0, +}; + +struct nfhret { + u_long stat; + nfsv2fh_t nfh; +}; +#define DEF_RETRY 10000 +#define BGRND 1 +#define ISBGRND 2 +int retrycnt = DEF_RETRY; +int opflags = 0; + +#ifdef KERBEROS +char inst[INST_SZ]; +char realm[REALM_SZ]; +KTEXT_ST kt; +#endif + +int getnfsargs __P((char *, struct nfs_args *)); +#ifdef ISO +struct iso_addr *iso_addr __P((const char *)); +#endif +void set_rpc_maxgrouplist __P((int)); +__dead void usage __P((void)); +int xdr_dir __P((XDR *, char *)); +int xdr_fh __P((XDR *, struct nfhret *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int c; + register struct nfs_args *nfsargsp; + struct nfs_args nfsargs; + struct nfsd_cargs ncd; + int mntflags, i, nfssvc_flag, num; + char *name, *p, *spec; + int error = 0; +#ifdef KERBEROS + uid_t last_ruid; +#endif + +#ifdef KERBEROS + last_ruid = -1; + (void)strcpy(realm, KRB_REALM); +#endif + retrycnt = DEF_RETRY; + + mntflags = 0; + nfsargs = nfsdefargs; + nfsargsp = &nfsargs; + while ((c = getopt(argc, argv, + "a:bcdD:g:iKklL:Mm:o:PpqR:r:sTt:w:x:")) != EOF) + switch (c) { + case 'a': + num = strtol(optarg, &p, 10); + if (*p || num < 0) + errx(1, "illegal -a value -- %s", optarg); + nfsargsp->readahead = num; + nfsargsp->flags |= NFSMNT_READAHEAD; + break; + case 'b': + opflags |= BGRND; + break; + case 'c': + nfsargsp->flags |= NFSMNT_NOCONN; + break; + case 'D': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -D value -- %s", optarg); + nfsargsp->deadthresh = num; + nfsargsp->flags |= NFSMNT_DEADTHRESH; + break; + case 'd': + nfsargsp->flags |= NFSMNT_DUMBTIMR; + break; + case 'g': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -g value -- %s", optarg); + set_rpc_maxgrouplist(num); + nfsargsp->maxgrouplist = num; + nfsargsp->flags |= NFSMNT_MAXGRPS; + break; + case 'i': + nfsargsp->flags |= NFSMNT_INT; + break; +#ifdef KERBEROS + case 'K': + nfsargsp->flags |= NFSMNT_KERB; + break; +#endif + case 'k': + nfsargsp->flags |= NFSMNT_NQLOOKLEASE; + break; + case 'L': + num = strtol(optarg, &p, 10); + if (*p || num < 2) + errx(1, "illegal -L value -- %s", optarg); + nfsargsp->leaseterm = num; + nfsargsp->flags |= NFSMNT_LEASETERM; + break; + case 'l': + nfsargsp->flags |= NFSMNT_RDIRALOOK; + break; + case 'M': + nfsargsp->flags |= NFSMNT_MYWRITE; + break; +#ifdef KERBEROS + case 'm': + (void)strncpy(realm, optarg, REALM_SZ - 1); + realm[REALM_SZ - 1] = '\0'; + break; +#endif + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'P': + nfsargsp->flags |= NFSMNT_RESVPORT; + break; +#ifdef ISO + case 'p': + nfsargsp->sotype = SOCK_SEQPACKET; + break; +#endif + case 'q': + nfsargsp->flags |= NFSMNT_NQNFS; + break; + case 'R': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -R value -- %s", optarg); + retrycnt = num; + break; + case 'r': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -r value -- %s", optarg); + nfsargsp->rsize = num; + nfsargsp->flags |= NFSMNT_RSIZE; + break; + case 's': + nfsargsp->flags |= NFSMNT_SOFT; + break; + case 'T': + nfsargsp->sotype = SOCK_STREAM; + break; + case 't': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -t value -- %s", optarg); + nfsargsp->timeo = num; + nfsargsp->flags |= NFSMNT_TIMEO; + break; + case 'w': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -w value -- %s", optarg); + nfsargsp->wsize = num; + nfsargsp->flags |= NFSMNT_WSIZE; + break; + case 'x': + num = strtol(optarg, &p, 10); + if (*p || num <= 0) + errx(1, "illegal -x value -- %s", optarg); + nfsargsp->retrans = num; + nfsargsp->flags |= NFSMNT_RETRANS; + break; + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc != 2) + error = 1; + + spec = *argv++; + name = *argv; + + if (!getnfsargs(spec, nfsargsp)) + exit(1); + if (mount(MOUNT_NFS, name, mntflags, nfsargsp)) + err(1, "%s", name); + if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) { + if ((opflags & ISBGRND) == 0) { + if (i = fork()) { + if (i == -1) + err(1, "nqnfs 1"); + exit(0); + } + (void) setsid(); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + (void) chdir("/"); + } + openlog("mount_nfs:", LOG_PID, LOG_DAEMON); + nfssvc_flag = NFSSVC_MNTD; + ncd.ncd_dirp = name; + while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) { + if (errno != ENEEDAUTH) { + syslog(LOG_ERR, "nfssvc err %m"); + continue; + } + nfssvc_flag = + NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL; +#ifdef KERBEROS + /* + * Set up as ncd_authuid for the kerberos call. + * Must set ruid to ncd_authuid and reset the + * ticket name iff ncd_authuid is not the same + * as last time, so that the right ticket file + * is found. + */ + if (ncd.ncd_authuid != last_ruid) { + krb_set_tkt_string(""); + last_ruid = ncd.ncd_authuid; + } + setreuid(ncd.ncd_authuid, 0); + if (krb_mk_req(&kt, "rcmd", inst, realm, 0) == + KSUCCESS && + kt.length <= (RPCAUTH_MAXSIZ - 2 * NFSX_UNSIGNED)) { + ncd.ncd_authtype = RPCAUTH_NQNFS; + ncd.ncd_authlen = kt.length; + ncd.ncd_authstr = (char *)kt.dat; + nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH; + } + setreuid(0, 0); +#endif /* KERBEROS */ + } + } + exit(0); +} + +int +getnfsargs(spec, nfsargsp) + char *spec; + struct nfs_args *nfsargsp; +{ + register CLIENT *clp; + struct hostent *hp; + static struct sockaddr_in saddr; +#ifdef ISO + static struct sockaddr_iso isoaddr; + struct iso_addr *isop; + int isoflag = 0; +#endif + struct timeval pertry, try; + enum clnt_stat clnt_stat; + int so = RPC_ANYSOCK, i; + char *hostp, *delimp; +#ifdef KERBEROS + char *cp; +#endif + u_short tport; + static struct nfhret nfhret; + static char nam[MNAMELEN + 1]; + + strncpy(nam, spec, MNAMELEN); + nam[MNAMELEN] = '\0'; + if ((delimp = strchr(spec, '@')) != NULL) { + hostp = delimp + 1; + } else if ((delimp = strchr(spec, ':')) != NULL) { + hostp = spec; + spec = delimp + 1; + } else { + warnx("no : or @ spec"); + return (0); + } + *delimp = '\0'; + /* + * DUMB!! Until the mount protocol works on iso transport, we must + * supply both an iso and an inet address for the host. + */ +#ifdef ISO + if (!strncmp(hostp, "iso=", 4)) { + u_short isoport; + + hostp += 4; + isoflag++; + if ((delimp = strchr(hostp, '+')) == NULL) { + warnx("no iso+inet address"); + return (0); + } + *delimp = '\0'; + if ((isop = iso_addr(hostp)) == NULL) { + warnx("bad ISO address"); + return (0); + } + bzero((caddr_t)&isoaddr, sizeof (isoaddr)); + bcopy((caddr_t)isop, (caddr_t)&isoaddr.siso_addr, + sizeof (struct iso_addr)); + isoaddr.siso_len = sizeof (isoaddr); + isoaddr.siso_family = AF_ISO; + isoaddr.siso_tlen = 2; + isoport = htons(NFS_PORT); + bcopy((caddr_t)&isoport, TSEL(&isoaddr), isoaddr.siso_tlen); + hostp = delimp + 1; + } +#endif /* ISO */ + + /* + * Handle an internet host address and reverse resolve it if + * doing Kerberos. + */ + if (isdigit(*hostp)) { + if ((saddr.sin_addr.s_addr = inet_addr(hostp)) == -1) { + warnx("bad net address %s", hostp); + return (0); + } + if ((nfsargsp->flags & NFSMNT_KERB) && + (hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr, + sizeof (u_long), AF_INET)) == (struct hostent *)0) { + warnx("can't reverse resolve net address"); + return (0); + } + } else if ((hp = gethostbyname(hostp)) == NULL) { + warnx("can't get net id for host"); + return (0); + } +#ifdef KERBEROS + if (nfsargsp->flags & NFSMNT_KERB) { + strncpy(inst, hp->h_name, INST_SZ); + inst[INST_SZ - 1] = '\0'; + if (cp = strchr(inst, '.')) + *cp = '\0'; + } +#endif /* KERBEROS */ + + bcopy(hp->h_addr, (caddr_t)&saddr.sin_addr, hp->h_length); + nfhret.stat = EACCES; /* Mark not yet successful */ + while (retrycnt > 0) { + saddr.sin_family = AF_INET; + saddr.sin_port = htons(PMAPPORT); + if ((tport = pmap_getport(&saddr, RPCPROG_NFS, + NFS_VER2, IPPROTO_UDP)) == 0) { + if ((opflags & ISBGRND) == 0) + clnt_pcreateerror("NFS Portmap"); + } else { + saddr.sin_port = 0; + pertry.tv_sec = 10; + pertry.tv_usec = 0; + if ((clp = clntudp_create(&saddr, RPCPROG_MNT, + RPCMNT_VER1, pertry, &so)) == NULL) { + if ((opflags & ISBGRND) == 0) + clnt_pcreateerror("Cannot MNT PRC"); + } else { + clp->cl_auth = authunix_create_default(); + try.tv_sec = 10; + try.tv_usec = 0; + clnt_stat = clnt_call(clp, RPCMNT_MOUNT, + xdr_dir, spec, xdr_fh, &nfhret, try); + if (clnt_stat != RPC_SUCCESS) { + if ((opflags & ISBGRND) == 0) + warnx("%s", clnt_sperror(clp, + "bad MNT RPC")); + } else { + auth_destroy(clp->cl_auth); + clnt_destroy(clp); + retrycnt = 0; + } + } + } + if (--retrycnt > 0) { + if (opflags & BGRND) { + opflags &= ~BGRND; + if (i = fork()) { + if (i == -1) + err(1, "nqnfs 2"); + exit(0); + } + (void) setsid(); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + (void) chdir("/"); + opflags |= ISBGRND; + } + sleep(60); + } + } + if (nfhret.stat) { + if (opflags & ISBGRND) + exit(1); + warn("can't access %s", spec); + return (0); + } + saddr.sin_port = htons(tport); +#ifdef ISO + if (isoflag) { + nfsargsp->addr = (struct sockaddr *) &isoaddr; + nfsargsp->addrlen = sizeof (isoaddr); + } else +#endif /* ISO */ + { + nfsargsp->addr = (struct sockaddr *) &saddr; + nfsargsp->addrlen = sizeof (saddr); + } + nfsargsp->fh = &nfhret.nfh; + nfsargsp->hostname = nam; + return (1); +} + +/* + * xdr routines for mount rpc's + */ +int +xdr_dir(xdrsp, dirp) + XDR *xdrsp; + char *dirp; +{ + return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); +} + +int +xdr_fh(xdrsp, np) + XDR *xdrsp; + struct nfhret *np; +{ + if (!xdr_u_long(xdrsp, &(np->stat))) + return (0); + if (np->stat) + return (1); + return (xdr_opaque(xdrsp, (caddr_t)&(np->nfh), NFSX_FH)); +} + +__dead void +usage() +{ + (void)fprintf(stderr, "usage: mount_nfs %s\n%s\n%s\n%s\n", +"[-bcdiKklMPqsT] [-a maxreadahead] [-D deadthresh]", +"\t[-g maxgroups] [-L leaseterm] [-m realm] [-o options] [-R retrycnt]", +"\t[-r readsize] [-t timeout] [-w writesize] [-x retrans]", +"\trhost:path node"); + exit(1); +} diff --git a/sbin/mount_null/Makefile b/sbin/mount_null/Makefile new file mode 100644 index 000000000000..aae87e11b2db --- /dev/null +++ b/sbin/mount_null/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_null +SRCS= mount_null.c getmntopts.c +MAN8= mount_null.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_null/mount_null.8 b/sbin/mount_null/mount_null.8 new file mode 100644 index 000000000000..6c8106fd1cfa --- /dev/null +++ b/sbin/mount_null/mount_null.8 @@ -0,0 +1,220 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" John Heidemann of the UCLA Ficus project. +.\" +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_null.8 8.4 (Berkeley) 4/19/94 +.\" +.\" +.Dd April 19, 1994 +.Dt MOUNT_NULL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_null +.Nd demonstrate the use of a null file system layer +.Sh SYNOPSIS +.Nm mount_null +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Sh DESCRIPTION +The +.Nm mount_null +command creates a +null layer, duplicating a sub-tree of the file system +name space under another part of the global file system namespace. +In this respect, it is +similar to the loopback file system (see +.Xr mount_lofs 8 ) . +It differs from +the loopback file system in two respects: it is implemented using +a stackable layers techniques, and it's +.Do +null-node +.Dc s +stack above +all lower-layer vnodes, not just over directory vnodes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The null layer has two purposes. +First, it serves as a demonstration of layering by proving a layer +which does nothing. +(It actually does everything the loopback file system does, +which is slightly more than nothing.) +Second, the null layer can serve as a prototype layer. +Since it provides all necessary layer framework, +new file system layers can be created very easily be starting +with a null layer. +.Pp +The remainder of this man page examines the null layer as a basis +for constructing new layers. +.\" +.\" +.Sh INSTANTIATING NEW NULL LAYERS +New null layers are created with +.Xr mount_null 8 . +.Xr Mount_null 8 +takes two arguments, the pathname +of the lower vfs (target-pn) and the pathname where the null +layer will appear in the namespace (mount-point-pn). After +the null layer is put into place, the contents +of target-pn subtree will be aliased under mount-point-pn. +.\" +.\" +.Sh OPERATION OF A NULL LAYER +The null layer is the minimum file system layer, +simply bypassing all possible operations to the lower layer +for processing there. The majority of its activity centers +on the bypass routine, though which nearly all vnode operations +pass. +.Pp +The bypass routine accepts arbitrary vnode operations for +handling by the lower layer. It begins by examing vnode +operation arguments and replacing any null-nodes by their +lower-layer equivalents. It then invokes the operation +on the lower layer. Finally, it replaces the null-nodes +in the arguments and, if a vnode is returned by the operation, +stacks a null-node on top of the returned vnode. +.Pp +Although bypass handles most operations, +.Em vop_getattr , +.Em vop_inactive , +.Em vop_reclaim , +and +.Em vop_print +are not bypassed. +.Em Vop_getattr +must change the fsid being returned. +.Em Vop_inactive +and vop_reclaim are not bypassed so that +they can handle freeing null-layer specific data. +.Em Vop_print +is not bypassed to avoid excessive debugging +information. +.\" +.\" +.Sh INSTANTIATING VNODE STACKS +Mounting associates the null layer with a lower layer, +in effect stacking two VFSes. Vnode stacks are instead +created on demand as files are accessed. +.Pp +The initial mount creates a single vnode stack for the +root of the new null layer. All other vnode stacks +are created as a result of vnode operations on +this or other null vnode stacks. +.Pp +New vnode stacks come into existence as a result of +an operation which returns a vnode. +The bypass routine stacks a null-node above the new +vnode before returning it to the caller. +.Pp +For example, imagine mounting a null layer with +.Bd -literal -offset indent +mount_null /usr/include /dev/layer/null +.Ed +Changing directory to +.Pa /dev/layer/null +will assign +the root null-node (which was created when the null layer was mounted). +Now consider opening +.Pa sys . +A vop_lookup would be +done on the root null-node. This operation would bypass through +to the lower layer which would return a vnode representing +the UFS +.Pa sys . +Null_bypass then builds a null-node +aliasing the UFS +.Pa sys +and returns this to the caller. +Later operations on the null-node +.Pa sys +will repeat this +process when constructing other vnode stacks. +.\" +.\" +.Sh CREATING OTHER FILE SYSTEM LAYERS +One of the easiest ways to construct new file system layers is to make +a copy of the null layer, rename all files and variables, and +then begin modifyng the copy. Sed can be used to easily rename +all variables. +.Pp +The umap layer is an example of a layer descended from the +null layer. +.\" +.\" +.Sh INVOKING OPERATIONS ON LOWER LAYERS +There are two techniques to invoke operations on a lower layer +when the operation cannot be completely bypassed. Each method +is appropriate in different situations. In both cases, +it is the responsibility of the aliasing layer to make +the operation arguments "correct" for the lower layer +by mapping an vnode arguments to the lower layer. +.Pp +The first approach is to call the aliasing layer's bypass routine. +This method is most suitable when you wish to invoke the operation +currently being handled on the lower layer. It has the advantage +the the bypass routine already must do argument mapping. +An example of this is +.Em null_getattrs +in the null layer. +.Pp +A second approach is to directly invoked vnode operations on +the lower layer with the +.Em VOP_OPERATIONNAME +interface. +The advantage of this method is that it is easy to invoke +arbitrary operations on the lower layer. The disadvantage +is that vnodes arguments must be manually mapped. +.\" +.\" +.Sh SEE ALSO +.Xr mount 8 +.sp +UCLA Technical Report CSD-910056, +.Em "Stackable Layers: an Architecture for File System Development" . +.Sh HISTORY +The +.Nm mount_null +utility first appeared in 4.4BSD. diff --git a/sbin/mount_null/mount_null.c b/sbin/mount_null/mount_null.c new file mode 100644 index 000000000000..e8c26df1b8f9 --- /dev/null +++ b/sbin/mount_null/mount_null.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_null.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct null_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch(ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_NULL, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_null [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_nullfs/Makefile b/sbin/mount_nullfs/Makefile new file mode 100644 index 000000000000..aae87e11b2db --- /dev/null +++ b/sbin/mount_nullfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_null +SRCS= mount_null.c getmntopts.c +MAN8= mount_null.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8 new file mode 100644 index 000000000000..6c8106fd1cfa --- /dev/null +++ b/sbin/mount_nullfs/mount_nullfs.8 @@ -0,0 +1,220 @@ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" John Heidemann of the UCLA Ficus project. +.\" +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_null.8 8.4 (Berkeley) 4/19/94 +.\" +.\" +.Dd April 19, 1994 +.Dt MOUNT_NULL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_null +.Nd demonstrate the use of a null file system layer +.Sh SYNOPSIS +.Nm mount_null +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Sh DESCRIPTION +The +.Nm mount_null +command creates a +null layer, duplicating a sub-tree of the file system +name space under another part of the global file system namespace. +In this respect, it is +similar to the loopback file system (see +.Xr mount_lofs 8 ) . +It differs from +the loopback file system in two respects: it is implemented using +a stackable layers techniques, and it's +.Do +null-node +.Dc s +stack above +all lower-layer vnodes, not just over directory vnodes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The null layer has two purposes. +First, it serves as a demonstration of layering by proving a layer +which does nothing. +(It actually does everything the loopback file system does, +which is slightly more than nothing.) +Second, the null layer can serve as a prototype layer. +Since it provides all necessary layer framework, +new file system layers can be created very easily be starting +with a null layer. +.Pp +The remainder of this man page examines the null layer as a basis +for constructing new layers. +.\" +.\" +.Sh INSTANTIATING NEW NULL LAYERS +New null layers are created with +.Xr mount_null 8 . +.Xr Mount_null 8 +takes two arguments, the pathname +of the lower vfs (target-pn) and the pathname where the null +layer will appear in the namespace (mount-point-pn). After +the null layer is put into place, the contents +of target-pn subtree will be aliased under mount-point-pn. +.\" +.\" +.Sh OPERATION OF A NULL LAYER +The null layer is the minimum file system layer, +simply bypassing all possible operations to the lower layer +for processing there. The majority of its activity centers +on the bypass routine, though which nearly all vnode operations +pass. +.Pp +The bypass routine accepts arbitrary vnode operations for +handling by the lower layer. It begins by examing vnode +operation arguments and replacing any null-nodes by their +lower-layer equivalents. It then invokes the operation +on the lower layer. Finally, it replaces the null-nodes +in the arguments and, if a vnode is returned by the operation, +stacks a null-node on top of the returned vnode. +.Pp +Although bypass handles most operations, +.Em vop_getattr , +.Em vop_inactive , +.Em vop_reclaim , +and +.Em vop_print +are not bypassed. +.Em Vop_getattr +must change the fsid being returned. +.Em Vop_inactive +and vop_reclaim are not bypassed so that +they can handle freeing null-layer specific data. +.Em Vop_print +is not bypassed to avoid excessive debugging +information. +.\" +.\" +.Sh INSTANTIATING VNODE STACKS +Mounting associates the null layer with a lower layer, +in effect stacking two VFSes. Vnode stacks are instead +created on demand as files are accessed. +.Pp +The initial mount creates a single vnode stack for the +root of the new null layer. All other vnode stacks +are created as a result of vnode operations on +this or other null vnode stacks. +.Pp +New vnode stacks come into existence as a result of +an operation which returns a vnode. +The bypass routine stacks a null-node above the new +vnode before returning it to the caller. +.Pp +For example, imagine mounting a null layer with +.Bd -literal -offset indent +mount_null /usr/include /dev/layer/null +.Ed +Changing directory to +.Pa /dev/layer/null +will assign +the root null-node (which was created when the null layer was mounted). +Now consider opening +.Pa sys . +A vop_lookup would be +done on the root null-node. This operation would bypass through +to the lower layer which would return a vnode representing +the UFS +.Pa sys . +Null_bypass then builds a null-node +aliasing the UFS +.Pa sys +and returns this to the caller. +Later operations on the null-node +.Pa sys +will repeat this +process when constructing other vnode stacks. +.\" +.\" +.Sh CREATING OTHER FILE SYSTEM LAYERS +One of the easiest ways to construct new file system layers is to make +a copy of the null layer, rename all files and variables, and +then begin modifyng the copy. Sed can be used to easily rename +all variables. +.Pp +The umap layer is an example of a layer descended from the +null layer. +.\" +.\" +.Sh INVOKING OPERATIONS ON LOWER LAYERS +There are two techniques to invoke operations on a lower layer +when the operation cannot be completely bypassed. Each method +is appropriate in different situations. In both cases, +it is the responsibility of the aliasing layer to make +the operation arguments "correct" for the lower layer +by mapping an vnode arguments to the lower layer. +.Pp +The first approach is to call the aliasing layer's bypass routine. +This method is most suitable when you wish to invoke the operation +currently being handled on the lower layer. It has the advantage +the the bypass routine already must do argument mapping. +An example of this is +.Em null_getattrs +in the null layer. +.Pp +A second approach is to directly invoked vnode operations on +the lower layer with the +.Em VOP_OPERATIONNAME +interface. +The advantage of this method is that it is easy to invoke +arbitrary operations on the lower layer. The disadvantage +is that vnodes arguments must be manually mapped. +.\" +.\" +.Sh SEE ALSO +.Xr mount 8 +.sp +UCLA Technical Report CSD-910056, +.Em "Stackable Layers: an Architecture for File System Development" . +.Sh HISTORY +The +.Nm mount_null +utility first appeared in 4.4BSD. diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c new file mode 100644 index 000000000000..e8c26df1b8f9 --- /dev/null +++ b/sbin/mount_nullfs/mount_nullfs.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_null.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct null_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch(ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_NULL, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_null [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_portal/Makefile b/sbin/mount_portal/Makefile new file mode 100644 index 000000000000..1f17d245ed20 --- /dev/null +++ b/sbin/mount_portal/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_portal +SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \ + pt_exec.c pt_file.c pt_tcp.c +MAN8= mount_portal.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= $(LIBCOMPAT) +LDADD= -lcompat + +.include diff --git a/sbin/mount_portal/activate.c b/sbin/mount_portal/activate.c new file mode 100644 index 000000000000..33617988f7df --- /dev/null +++ b/sbin/mount_portal/activate.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)activate.c 8.2 (Berkeley) 3/27/94 + * + * $Id: activate.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +/* + * Scan the providers list and call the + * appropriate function. + */ +static int activate_argv(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + provider *pr; + + for (pr = providers; pr->pr_match; pr++) + if (strcmp(v[0], pr->pr_match) == 0) + return ((*pr->pr_func)(pcr, key, v, so, fdp)); + + return (ENOENT); +} + +static int get_request(so, pcr, key, klen) +int so; +struct portal_cred *pcr; +char *key; +int klen; +{ + struct iovec iov[2]; + struct msghdr msg; + int n; + + iov[0].iov_base = (caddr_t) pcr; + iov[0].iov_len = sizeof(*pcr); + iov[1].iov_base = key; + iov[1].iov_len = klen; + + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + n = recvmsg(so, &msg, 0); + if (n < 0) + return (errno); + + if (n <= sizeof(*pcr)) + return (EINVAL); + + n -= sizeof(*pcr); + key[n] = '\0'; + + return (0); +} + +static void send_reply(so, fd, error) +int so; +int fd; +int error; +{ + int n; + struct iovec iov; + struct msghdr msg; + struct { + struct cmsghdr cmsg; + int fd; + } ctl; + + /* + * Line up error code. Don't worry about byte ordering + * because we must be sending to the local machine. + */ + iov.iov_base = (caddr_t) &error; + iov.iov_len = sizeof(error); + + /* + * Build a msghdr + */ + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* + * If there is a file descriptor to send then + * construct a suitable rights control message. + */ + if (fd >= 0) { + ctl.fd = fd; + ctl.cmsg.cmsg_len = sizeof(ctl); + ctl.cmsg.cmsg_level = SOL_SOCKET; + ctl.cmsg.cmsg_type = SCM_RIGHTS; + msg.msg_control = (caddr_t) &ctl; + msg.msg_controllen = ctl.cmsg.cmsg_len; + } + + /* + * Send to kernel... + */ + if ((n = sendmsg(so, &msg, MSG_EOR)) < 0) + syslog(LOG_ERR, "send: %s", strerror(errno)); +#ifdef DEBUG + fprintf(stderr, "sent %d bytes\n", n); +#endif + sleep(1); /*XXX*/ +#ifdef notdef + if (shutdown(so, 2) < 0) + syslog(LOG_ERR, "shutdown: %s", strerror(errno)); +#endif + /* + * Throw away the open file descriptor + */ + (void) close(fd); +} + +void activate(q, so) +qelem *q; +int so; +{ + struct portal_cred pcred; + char key[MAXPATHLEN+1]; + int error; + char **v; + int fd = -1; + + /* + * Read the key from the socket + */ + error = get_request(so, &pcred, key, sizeof(key)); + if (error) { + syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error)); + goto drop; + } + +#ifdef DEBUG + fprintf(stderr, "lookup key %s\n", key); +#endif + + /* + * Find a match in the configuration file + */ + v = conf_match(q, key); + + /* + * If a match existed, then find an appropriate portal + * otherwise simply return ENOENT. + */ + if (v) { + error = activate_argv(&pcred, key, v, so, &fd); + if (error) + fd = -1; + else if (fd < 0) + error = -1; + } else { + error = ENOENT; + } + + if (error >= 0) + send_reply(so, fd, error); + +drop:; + close(so); +} diff --git a/sbin/mount_portal/conf.c b/sbin/mount_portal/conf.c new file mode 100644 index 000000000000..18833b608093 --- /dev/null +++ b/sbin/mount_portal/conf.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)conf.c 8.2 (Berkeley) 3/27/94 + * + * $Id: conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +#define ALLOC(ty) (xmalloc(sizeof(ty))) + +typedef struct path path; +struct path { + qelem p_q; /* 2-way linked list */ + int p_lno; /* Line number of this record */ + char *p_args; /* copy of arg string (malloc) */ + char *p_key; /* Pathname to match (also p_argv[0]) */ + regexp *p_re; /* RE to match against pathname (malloc) */ + int p_argc; /* number of elements in arg string */ + char **p_argv; /* argv[] pointers into arg string (malloc) */ +}; + +static char *conf_file; /* XXX for regerror */ +static path *curp; /* XXX for regerror */ + +/* + * Add an element to a 2-way list, + * just after (pred) + */ +static void ins_que(elem, pred) +qelem *elem, *pred; +{ + qelem *p = pred->q_forw; + elem->q_back = pred; + elem->q_forw = p; + pred->q_forw = elem; + p->q_back = elem; +} + +/* + * Remove an element from a 2-way list + */ +static void rem_que(elem) +qelem *elem; +{ + qelem *p = elem->q_forw; + qelem *p2 = elem->q_back; + p2->q_forw = p; + p->q_back = p2; +} + +/* + * Error checking malloc + */ +static void *xmalloc(siz) +unsigned siz; +{ + void *p = malloc(siz); + if (p) + return (p); + syslog(LOG_ALERT, "malloc: failed to get %d bytes", siz); + exit(1); +} + +/* + * Insert the path in the list. + * If there is already an element with the same key then + * the *second* one is ignored (return 0). If the key is + * not found then the path is added to the end of the list + * and 1 is returned. + */ +static int pinsert(p0, q0) +path *p0; +qelem *q0; +{ + qelem *q; + + if (p0->p_argc == 0) + return (0); + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (strcmp(p->p_key, p0->p_key) == 0) + return (0); + } + ins_que(&p0->p_q, q0->q_back); + return (1); + +} + +void regerror(s) +const char *s; +{ + syslog(LOG_ERR, "%s:%s: regcomp %s: %s", + conf_file, curp->p_lno, curp->p_key, s); +} + +static path *palloc(cline, lno) +char *cline; +int lno; +{ + int c; + char *s; + char *key; + path *p; + char **ap; + + /* + * Implement comment chars + */ + s = strchr(cline, '#'); + if (s) + *s = 0; + + /* + * Do a pass through the string to count the number + * of arguments + */ + c = 0; + key = strdup(cline); + for (s = key; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) + c++; + } + c++; + free(key); + + if (c <= 1) + return (0); + + /* + * Now do another pass and generate a new path structure + */ + p = ALLOC(path); + p->p_argc = 0; + p->p_argv = xmalloc(c * sizeof(char *)); + p->p_args = strdup(cline); + ap = p->p_argv; + for (s = p->p_args; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) { + *ap++ = val; + p->p_argc++; + } + } + *ap = 0; + +#ifdef DEBUG + for (c = 0; c < p->p_argc; c++) + printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]); +#endif + + p->p_key = p->p_argv[0]; + if (strpbrk(p->p_key, RE_CHARS)) { + curp = p; /* XXX */ + p->p_re = regcomp(p->p_key); + curp = 0; /* XXX */ + } else { + p->p_re = 0; + } + p->p_lno = lno; + + return (p); +} + +/* + * Free a path structure + */ +static void pfree(p) +path *p; +{ + free(p->p_args); + if (p->p_re) + free((char *) p->p_re); + free((char *) p->p_argv); + free((char *) p); +} + +/* + * Discard all currently held path structures on q0. + * and add all the ones on xq. + */ +static void preplace(q0, xq) +qelem *q0; +qelem *xq; +{ + /* + * While the list is not empty, + * take the first element off the list + * and free it. + */ + while (q0->q_forw != q0) { + qelem *q = q0->q_forw; + rem_que(q); + pfree((path *) q); + } + while (xq->q_forw != xq) { + qelem *q = xq->q_forw; + rem_que(q); + ins_que(q, q0); + } +} + +/* + * Read the lines from the configuration file and + * add them to the list of paths. + */ +static void readfp(q0, fp) +qelem *q0; +FILE *fp; +{ + char cline[LINE_MAX]; + int nread = 0; + qelem q; + + /* + * Make a new empty list. + */ + q.q_forw = q.q_back = &q; + + /* + * Read the lines from the configuration file. + */ + while (fgets(cline, sizeof(cline), fp)) { + path *p = palloc(cline, nread+1); + if (p && !pinsert(p, &q)) + pfree(p); + nread++; + } + + /* + * If some records were read, then throw + * away the old list and replace with the + * new one. + */ + if (nread) + preplace(q0, &q); +} + +/* + * Read the configuration file (conf) and replace + * the existing path list with the new version. + * If the file is not readable, then no changes take place + */ +void conf_read(q, conf) +qelem *q; +char *conf; +{ + FILE *fp = fopen(conf, "r"); + if (fp) { + conf_file = conf; /* XXX */ + readfp(q, fp); + conf_file = 0; /* XXX */ + (void) fclose(fp); + } else { + syslog(LOG_ERR, "open config file \"%s\": %s", conf, strerror(errno)); + } +} + + +char **conf_match(q0, key) +qelem *q0; +char *key; +{ + qelem *q; + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (p->p_re) { + if (regexec(p->p_re, key)) + return (p->p_argv+1); + } else { + if (strncmp(p->p_key, key, strlen(p->p_key)) == 0) + return (p->p_argv+1); + } + } + + return (0); +} diff --git a/sbin/mount_portal/mount_portal.8 b/sbin/mount_portal/mount_portal.8 new file mode 100644 index 000000000000..0df65316c2ad --- /dev/null +++ b/sbin/mount_portal/mount_portal.8 @@ -0,0 +1,136 @@ +.\" +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_PORTAL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_portal +.Nd mount the portal daemon +.Sh SYNOPSIS +.Nm mount_portal +.Op Fl o Ar options +.Ar /etc/portal.conf +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_portal +command attaches an instance of the portal daemon +to the global filesystem namespace. +The conventional mount point is +.Pa /p . +.PA /dev . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The portal daemon provides an +.Em open +service. +Objects opened under the portal mount point are +dynamically created by the portal daemon according +to rules specified in the named configuration file. +Using this mechanism allows descriptors such as sockets +to be made available in the filesystem namespace. +.Pp +The portal daemon works by being passed the full pathname +of the object being opened. +The daemon creates an appropriate descriptor according +to the rules in the configuration file, and then passes the descriptor back +to the calling process as the result of the open system call. +.Sh NAMESPACE +By convention, the portal daemon divides the namespace into sub-namespaces, +each of which handles objects of a particular type. +.Pp +Currently, two sub-namespaces are implemented: +.Pa tcp +and +.Pa fs . +The +.Pa tcp +namespace takes a hostname and a port (slash separated) and +creates an open TCP/IP connection. +The +.Pa fs +namespace opens the named file, starting back at the root directory. +This can be used to provide a controlled escape path from +a chrooted environment. +.Sh "CONFIGURATION FILE" +The configuration file contains a list of rules. +Each rule takes one line and consists of two or more +whitespace separated fields. +A hash (``#'') character causes the remainder of a line to +be ignored. Blank lines are ignored. +.Pp +The first field is a pathname prefix to match +against the requested pathname. +If a match is found, the second field +tells the daemon what type of object to create. +Subsequent fields are passed to the creation function. +.Bd -literal +# @(#)portal.conf 5.1 (Berkeley) 7/13/92 +tcp/ tcp tcp/ +fs/ file fs/ +.Ed +.Sh FILES +.Bl -tag -width /p/* -compact +.It Pa /p/* +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_portal +utility first appeared in 4.4BSD. diff --git a/sbin/mount_portal/mount_portal.c b/sbin/mount_portal/mount_portal.c new file mode 100644 index 000000000000..ae5345d3049f --- /dev/null +++ b/sbin/mount_portal/mount_portal.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_portal.c 8.4 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" +#include "pathnames.h" +#include "portald.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +static void usage __P((void)); + +static sig_atomic_t readcf; /* Set when SIGHUP received */ + +static void sigchld(sig) +int sig; +{ + pid_t pid; + + while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) + ; + if (pid < 0) + syslog(LOG_WARNING, "waitpid: %s", strerror(errno)); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct portal_args args; + struct sockaddr_un un; + char *conf; + char *mountpt; + int mntflags = 0; + char tag[32]; + + qelem q; + int rc; + int so; + int error = 0; + + /* + * Crack command line args + */ + int ch; + + while ((ch = getopt(argc, argv, "o:")) != EOF) { + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + default: + error = 1; + break; + } + } + + if (optind != (argc - 2)) + error = 1; + + if (error) + usage(); + + /* + * Get config file and mount point + */ + conf = argv[optind]; + mountpt = argv[optind+1]; + + /* + * Construct the listening socket + */ + un.sun_family = AF_UNIX; + if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) { + fprintf(stderr, "mount_portal: portal socket name too long\n"); + exit(1); + } + strcpy(un.sun_path, _PATH_TMPPORTAL); + mktemp(un.sun_path); + un.sun_len = strlen(un.sun_path); + + so = socket(AF_UNIX, SOCK_STREAM, 0); + if (so < 0) { + fprintf(stderr, "mount_portal: socket: %s\n", strerror(errno)); + exit(1); + } + (void) unlink(un.sun_path); + if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0) + err(1, NULL); + (void) unlink(un.sun_path); + + (void) listen(so, 5); + + args.pa_socket = so; + sprintf(tag, "portal:%d", getpid()); + args.pa_config = tag; + + rc = mount(MOUNT_PORTAL, mountpt, mntflags, &args); + if (rc < 0) + err(1, NULL); + +#ifdef notdef + /* + * Everything is ready to go - now is a good time to fork + */ + daemon(0, 0); +#endif + + /* + * Start logging (and change name) + */ + openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON); + + q.q_forw = q.q_back = &q; + readcf = 1; + + signal(SIGCHLD, sigchld); + + /* + * Just loop waiting for new connections and activating them + */ + for (;;) { + struct sockaddr_un un2; + int len2 = sizeof(un2); + int so2; + pid_t pid; + fd_set fdset; + int rc; + + /* + * Check whether we need to re-read the configuration file + */ + if (readcf) { + readcf = 0; + conf_read(&q, conf); + continue; + } + + /* + * Accept a new connection + * Will get EINTR if a signal has arrived, so just + * ignore that error code + */ + FD_SET(so, &fdset); + rc = select(so+1, &fdset, (void *) 0, (void *) 0, (void *) 0); + if (rc < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "select: %s", strerror(errno)); + exit(1); + } + if (rc == 0) + break; + so2 = accept(so, (struct sockaddr *) &un2, &len2); + if (so2 < 0) { + /* + * The unmount function does a shutdown on the socket + * which will generated ECONNABORTED on the accept. + */ + if (errno == ECONNABORTED) + break; + if (errno != EINTR) { + syslog(LOG_ERR, "accept: %s", strerror(errno)); + exit(1); + } + continue; + } + + /* + * Now fork a new child to deal with the connection + */ + eagain:; + switch (pid = fork()) { + case -1: + if (errno == EAGAIN) { + sleep(1); + goto eagain; + } + syslog(LOG_ERR, "fork: %s", strerror(errno)); + break; + case 0: + (void) close(so); + activate(&q, so2); + break; + default: + (void) close(so2); + break; + } + } + syslog(LOG_INFO, "%s unmounted", mountpt); + exit(0); +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: mount_portal [-o options] config mount-point\n"); + exit(1); +} diff --git a/sbin/mount_portal/pathnames.h b/sbin/mount_portal/pathnames.h new file mode 100644 index 000000000000..25321145d990 --- /dev/null +++ b/sbin/mount_portal/pathnames.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $Id: pathnames.h,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include + +#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */ diff --git a/sbin/mount_portal/portal.conf b/sbin/mount_portal/portal.conf new file mode 100644 index 000000000000..5b5a773eaa3c --- /dev/null +++ b/sbin/mount_portal/portal.conf @@ -0,0 +1,7 @@ +# @(#)portal.conf 8.1 (Berkeley) 6/5/93 +# $Id: portal.conf,v 1.1 1992/05/27 06:50:13 jsp Exp jsp $ +tcplisten/ tcplisten tcplisten/ +tcp/ tcp tcp/ +fs/ file fs/ +pipe/ pipe +foo/ exec ./bar bar baz diff --git a/sbin/mount_portal/portald.h b/sbin/mount_portal/portald.h new file mode 100644 index 000000000000..fbe111b1a7ad --- /dev/null +++ b/sbin/mount_portal/portald.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)portald.h 8.1 (Berkeley) 6/5/93 + * + * $Id: portald.h,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include + +/* + * Meta-chars in an RE. Paths in the config file containing + * any of these characters will be matched using regexec, other + * paths will be prefix-matched. + */ +#define RE_CHARS ".|()[]*+?\\^$" + +typedef struct qelem qelem; + +struct qelem { + qelem *q_forw; + qelem *q_back; +}; + +typedef struct provider provider; +struct provider { + char *pr_match; + int (*pr_func) __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +}; +extern provider providers[]; + +/* + * Portal providers + */ +extern int portal_exec __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_file __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_tcp __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); + +/* + * Global functions + */ +extern void activate __P((qelem *q, int so)); +extern char **conf_match __P((qelem *q, char *key)); +extern void conf_read __P((qelem *q, char *conf)); diff --git a/sbin/mount_portal/pt_conf.c b/sbin/mount_portal/pt_conf.c new file mode 100644 index 000000000000..d1eba94ea3fe --- /dev/null +++ b/sbin/mount_portal/pt_conf.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include "portald.h" + +provider providers[] = { + { "exec", portal_exec }, + { "file", portal_file }, + { "tcp", portal_tcp }, + { 0, 0 } +}; diff --git a/sbin/mount_portal/pt_exec.c b/sbin/mount_portal/pt_exec.c new file mode 100644 index 000000000000..06e3382da85b --- /dev/null +++ b/sbin/mount_portal/pt_exec.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_exec.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +int portal_exec(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + return (ENOEXEC); +} + diff --git a/sbin/mount_portal/pt_file.c b/sbin/mount_portal/pt_file.c new file mode 100644 index 000000000000..ace35c04fe59 --- /dev/null +++ b/sbin/mount_portal/pt_file.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_file.c 8.2 (Berkeley) 3/27/94 + * + * $Id: pt_file.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +int portal_file(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + int fd; + char pbuf[MAXPATHLEN]; + int error; + int gidset[NGROUPS]; + int i; + + pbuf[0] = '/'; + strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0)); + +#ifdef DEBUG + printf("path = %s, uid = %d, gid = %d\n", pbuf, pcr->pcr_uid, pcr->pcr_groups[0]); +#endif + + for (i = 0; i < pcr->pcr_ngroups; i++) + gidset[i] = pcr->pcr_groups[i]; + + if (setgroups(pcr->pcr_ngroups, gidset) < 0) + return (errno); + + if (seteuid(pcr->pcr_uid) < 0) + return (errno); + + fd = open(pbuf, O_RDWR|O_CREAT, 0666); + if (fd < 0) + error = errno; + else + error = 0; + + if (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */ + error = errno; + syslog(LOG_ERR, "setcred: %s", strerror(error)); + if (fd >= 0) { + (void) close(fd); + fd = -1; + } + } + + if (error == 0) + *fdp = fd; + +#ifdef DEBUG + fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error); +#endif + + return (error); +} diff --git a/sbin/mount_portal/pt_tcp.c b/sbin/mount_portal/pt_tcp.c new file mode 100644 index 000000000000..18a53ce26aba --- /dev/null +++ b/sbin/mount_portal/pt_tcp.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_tcp.c 8.3 (Berkeley) 3/27/94 + * + * $Id: pt_tcp.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +/* + * Key will be tcp/host/port[/"priv"] + * Create a TCP socket connected to the + * requested host and port. + * Some trailing suffix values have special meanings. + * An unrecognised suffix is an error. + */ +int portal_tcp(pcr, key, v, kso, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int kso; +int *fdp; +{ + char host[MAXHOSTNAMELEN]; + char port[MAXHOSTNAMELEN]; + char *p = key + (v[1] ? strlen(v[1]) : 0); + char *q; + struct hostent *hp; + struct servent *sp; + struct in_addr **ipp; + struct in_addr *ip[2]; + struct in_addr ina; + int s_port; + int priv = 0; + struct sockaddr_in sain; + + q = strchr(p, '/'); + if (q == 0 || q - p >= sizeof(host)) + return (EINVAL); + *q = '\0'; + strcpy(host, p); + p = q + 1; + + q = strchr(p, '/'); + if (q) + *q = '\0'; + if (strlen(p) >= sizeof(port)) + return (EINVAL); + strcpy(port, p); + if (q) { + p = q + 1; + if (strcmp(p, "priv") == 0) { + if (pcr->pcr_uid == 0) + priv = 1; + else + return (EPERM); + } else { + return (EINVAL); + } + } + + hp = gethostbyname(host); + if (hp != 0) { + ipp = (struct in_addr **) hp->h_addr_list; + } else { + ina.s_addr = inet_addr(host); + if (ina.s_addr == INADDR_NONE) + return (EINVAL); + ip[0] = &ina; + ip[1] = 0; + ipp = ip; + } + + sp = getservbyname(port, "tcp"); + if (sp != 0) + s_port = sp->s_port; + else { + s_port = atoi(port); + if (s_port == 0) + return (EINVAL); + } + + bzero(&sain, sizeof(sain)); + sain.sin_len = sizeof(sain); + sain.sin_family = AF_INET; + sain.sin_port = s_port; + + while (ipp[0]) { + int so; + + if (priv) + so = rresvport((int *) 0); + else + so = socket(AF_INET, SOCK_STREAM, 0); + if (so < 0) { + syslog(LOG_ERR, "socket: %m"); + return (errno); + } + + sain.sin_addr = *ipp[0]; + if (connect(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) { + *fdp = so; + return (0); + } + (void) close(so); + + ipp++; + } + + return (errno); +} diff --git a/sbin/mount_portalfs/Makefile b/sbin/mount_portalfs/Makefile new file mode 100644 index 000000000000..1f17d245ed20 --- /dev/null +++ b/sbin/mount_portalfs/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_portal +SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \ + pt_exec.c pt_file.c pt_tcp.c +MAN8= mount_portal.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= $(LIBCOMPAT) +LDADD= -lcompat + +.include diff --git a/sbin/mount_portalfs/activate.c b/sbin/mount_portalfs/activate.c new file mode 100644 index 000000000000..33617988f7df --- /dev/null +++ b/sbin/mount_portalfs/activate.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)activate.c 8.2 (Berkeley) 3/27/94 + * + * $Id: activate.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +/* + * Scan the providers list and call the + * appropriate function. + */ +static int activate_argv(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + provider *pr; + + for (pr = providers; pr->pr_match; pr++) + if (strcmp(v[0], pr->pr_match) == 0) + return ((*pr->pr_func)(pcr, key, v, so, fdp)); + + return (ENOENT); +} + +static int get_request(so, pcr, key, klen) +int so; +struct portal_cred *pcr; +char *key; +int klen; +{ + struct iovec iov[2]; + struct msghdr msg; + int n; + + iov[0].iov_base = (caddr_t) pcr; + iov[0].iov_len = sizeof(*pcr); + iov[1].iov_base = key; + iov[1].iov_len = klen; + + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + n = recvmsg(so, &msg, 0); + if (n < 0) + return (errno); + + if (n <= sizeof(*pcr)) + return (EINVAL); + + n -= sizeof(*pcr); + key[n] = '\0'; + + return (0); +} + +static void send_reply(so, fd, error) +int so; +int fd; +int error; +{ + int n; + struct iovec iov; + struct msghdr msg; + struct { + struct cmsghdr cmsg; + int fd; + } ctl; + + /* + * Line up error code. Don't worry about byte ordering + * because we must be sending to the local machine. + */ + iov.iov_base = (caddr_t) &error; + iov.iov_len = sizeof(error); + + /* + * Build a msghdr + */ + bzero((char *) &msg, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* + * If there is a file descriptor to send then + * construct a suitable rights control message. + */ + if (fd >= 0) { + ctl.fd = fd; + ctl.cmsg.cmsg_len = sizeof(ctl); + ctl.cmsg.cmsg_level = SOL_SOCKET; + ctl.cmsg.cmsg_type = SCM_RIGHTS; + msg.msg_control = (caddr_t) &ctl; + msg.msg_controllen = ctl.cmsg.cmsg_len; + } + + /* + * Send to kernel... + */ + if ((n = sendmsg(so, &msg, MSG_EOR)) < 0) + syslog(LOG_ERR, "send: %s", strerror(errno)); +#ifdef DEBUG + fprintf(stderr, "sent %d bytes\n", n); +#endif + sleep(1); /*XXX*/ +#ifdef notdef + if (shutdown(so, 2) < 0) + syslog(LOG_ERR, "shutdown: %s", strerror(errno)); +#endif + /* + * Throw away the open file descriptor + */ + (void) close(fd); +} + +void activate(q, so) +qelem *q; +int so; +{ + struct portal_cred pcred; + char key[MAXPATHLEN+1]; + int error; + char **v; + int fd = -1; + + /* + * Read the key from the socket + */ + error = get_request(so, &pcred, key, sizeof(key)); + if (error) { + syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error)); + goto drop; + } + +#ifdef DEBUG + fprintf(stderr, "lookup key %s\n", key); +#endif + + /* + * Find a match in the configuration file + */ + v = conf_match(q, key); + + /* + * If a match existed, then find an appropriate portal + * otherwise simply return ENOENT. + */ + if (v) { + error = activate_argv(&pcred, key, v, so, &fd); + if (error) + fd = -1; + else if (fd < 0) + error = -1; + } else { + error = ENOENT; + } + + if (error >= 0) + send_reply(so, fd, error); + +drop:; + close(so); +} diff --git a/sbin/mount_portalfs/conf.c b/sbin/mount_portalfs/conf.c new file mode 100644 index 000000000000..18833b608093 --- /dev/null +++ b/sbin/mount_portalfs/conf.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)conf.c 8.2 (Berkeley) 3/27/94 + * + * $Id: conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +#define ALLOC(ty) (xmalloc(sizeof(ty))) + +typedef struct path path; +struct path { + qelem p_q; /* 2-way linked list */ + int p_lno; /* Line number of this record */ + char *p_args; /* copy of arg string (malloc) */ + char *p_key; /* Pathname to match (also p_argv[0]) */ + regexp *p_re; /* RE to match against pathname (malloc) */ + int p_argc; /* number of elements in arg string */ + char **p_argv; /* argv[] pointers into arg string (malloc) */ +}; + +static char *conf_file; /* XXX for regerror */ +static path *curp; /* XXX for regerror */ + +/* + * Add an element to a 2-way list, + * just after (pred) + */ +static void ins_que(elem, pred) +qelem *elem, *pred; +{ + qelem *p = pred->q_forw; + elem->q_back = pred; + elem->q_forw = p; + pred->q_forw = elem; + p->q_back = elem; +} + +/* + * Remove an element from a 2-way list + */ +static void rem_que(elem) +qelem *elem; +{ + qelem *p = elem->q_forw; + qelem *p2 = elem->q_back; + p2->q_forw = p; + p->q_back = p2; +} + +/* + * Error checking malloc + */ +static void *xmalloc(siz) +unsigned siz; +{ + void *p = malloc(siz); + if (p) + return (p); + syslog(LOG_ALERT, "malloc: failed to get %d bytes", siz); + exit(1); +} + +/* + * Insert the path in the list. + * If there is already an element with the same key then + * the *second* one is ignored (return 0). If the key is + * not found then the path is added to the end of the list + * and 1 is returned. + */ +static int pinsert(p0, q0) +path *p0; +qelem *q0; +{ + qelem *q; + + if (p0->p_argc == 0) + return (0); + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (strcmp(p->p_key, p0->p_key) == 0) + return (0); + } + ins_que(&p0->p_q, q0->q_back); + return (1); + +} + +void regerror(s) +const char *s; +{ + syslog(LOG_ERR, "%s:%s: regcomp %s: %s", + conf_file, curp->p_lno, curp->p_key, s); +} + +static path *palloc(cline, lno) +char *cline; +int lno; +{ + int c; + char *s; + char *key; + path *p; + char **ap; + + /* + * Implement comment chars + */ + s = strchr(cline, '#'); + if (s) + *s = 0; + + /* + * Do a pass through the string to count the number + * of arguments + */ + c = 0; + key = strdup(cline); + for (s = key; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) + c++; + } + c++; + free(key); + + if (c <= 1) + return (0); + + /* + * Now do another pass and generate a new path structure + */ + p = ALLOC(path); + p->p_argc = 0; + p->p_argv = xmalloc(c * sizeof(char *)); + p->p_args = strdup(cline); + ap = p->p_argv; + for (s = p->p_args; s != NULL; ) { + char *val; + while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0') + ; + if (val) { + *ap++ = val; + p->p_argc++; + } + } + *ap = 0; + +#ifdef DEBUG + for (c = 0; c < p->p_argc; c++) + printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]); +#endif + + p->p_key = p->p_argv[0]; + if (strpbrk(p->p_key, RE_CHARS)) { + curp = p; /* XXX */ + p->p_re = regcomp(p->p_key); + curp = 0; /* XXX */ + } else { + p->p_re = 0; + } + p->p_lno = lno; + + return (p); +} + +/* + * Free a path structure + */ +static void pfree(p) +path *p; +{ + free(p->p_args); + if (p->p_re) + free((char *) p->p_re); + free((char *) p->p_argv); + free((char *) p); +} + +/* + * Discard all currently held path structures on q0. + * and add all the ones on xq. + */ +static void preplace(q0, xq) +qelem *q0; +qelem *xq; +{ + /* + * While the list is not empty, + * take the first element off the list + * and free it. + */ + while (q0->q_forw != q0) { + qelem *q = q0->q_forw; + rem_que(q); + pfree((path *) q); + } + while (xq->q_forw != xq) { + qelem *q = xq->q_forw; + rem_que(q); + ins_que(q, q0); + } +} + +/* + * Read the lines from the configuration file and + * add them to the list of paths. + */ +static void readfp(q0, fp) +qelem *q0; +FILE *fp; +{ + char cline[LINE_MAX]; + int nread = 0; + qelem q; + + /* + * Make a new empty list. + */ + q.q_forw = q.q_back = &q; + + /* + * Read the lines from the configuration file. + */ + while (fgets(cline, sizeof(cline), fp)) { + path *p = palloc(cline, nread+1); + if (p && !pinsert(p, &q)) + pfree(p); + nread++; + } + + /* + * If some records were read, then throw + * away the old list and replace with the + * new one. + */ + if (nread) + preplace(q0, &q); +} + +/* + * Read the configuration file (conf) and replace + * the existing path list with the new version. + * If the file is not readable, then no changes take place + */ +void conf_read(q, conf) +qelem *q; +char *conf; +{ + FILE *fp = fopen(conf, "r"); + if (fp) { + conf_file = conf; /* XXX */ + readfp(q, fp); + conf_file = 0; /* XXX */ + (void) fclose(fp); + } else { + syslog(LOG_ERR, "open config file \"%s\": %s", conf, strerror(errno)); + } +} + + +char **conf_match(q0, key) +qelem *q0; +char *key; +{ + qelem *q; + + for (q = q0->q_forw; q != q0; q = q->q_forw) { + path *p = (path *) q; + if (p->p_re) { + if (regexec(p->p_re, key)) + return (p->p_argv+1); + } else { + if (strncmp(p->p_key, key, strlen(p->p_key)) == 0) + return (p->p_argv+1); + } + } + + return (0); +} diff --git a/sbin/mount_portalfs/mount_portalfs.8 b/sbin/mount_portalfs/mount_portalfs.8 new file mode 100644 index 000000000000..0df65316c2ad --- /dev/null +++ b/sbin/mount_portalfs/mount_portalfs.8 @@ -0,0 +1,136 @@ +.\" +.\" Copyright (c) 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_PORTAL 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_portal +.Nd mount the portal daemon +.Sh SYNOPSIS +.Nm mount_portal +.Op Fl o Ar options +.Ar /etc/portal.conf +.Ar mount_point +.Sh DESCRIPTION +The +.Nm mount_portal +command attaches an instance of the portal daemon +to the global filesystem namespace. +The conventional mount point is +.Pa /p . +.PA /dev . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The portal daemon provides an +.Em open +service. +Objects opened under the portal mount point are +dynamically created by the portal daemon according +to rules specified in the named configuration file. +Using this mechanism allows descriptors such as sockets +to be made available in the filesystem namespace. +.Pp +The portal daemon works by being passed the full pathname +of the object being opened. +The daemon creates an appropriate descriptor according +to the rules in the configuration file, and then passes the descriptor back +to the calling process as the result of the open system call. +.Sh NAMESPACE +By convention, the portal daemon divides the namespace into sub-namespaces, +each of which handles objects of a particular type. +.Pp +Currently, two sub-namespaces are implemented: +.Pa tcp +and +.Pa fs . +The +.Pa tcp +namespace takes a hostname and a port (slash separated) and +creates an open TCP/IP connection. +The +.Pa fs +namespace opens the named file, starting back at the root directory. +This can be used to provide a controlled escape path from +a chrooted environment. +.Sh "CONFIGURATION FILE" +The configuration file contains a list of rules. +Each rule takes one line and consists of two or more +whitespace separated fields. +A hash (``#'') character causes the remainder of a line to +be ignored. Blank lines are ignored. +.Pp +The first field is a pathname prefix to match +against the requested pathname. +If a match is found, the second field +tells the daemon what type of object to create. +Subsequent fields are passed to the creation function. +.Bd -literal +# @(#)portal.conf 5.1 (Berkeley) 7/13/92 +tcp/ tcp tcp/ +fs/ file fs/ +.Ed +.Sh FILES +.Bl -tag -width /p/* -compact +.It Pa /p/* +.El +.Sh SEE ALSO +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 +.Sh CAVEATS +This filesystem may not be NFS-exported. +.Sh HISTORY +The +.Nm mount_portal +utility first appeared in 4.4BSD. diff --git a/sbin/mount_portalfs/mount_portalfs.c b/sbin/mount_portalfs/mount_portalfs.c new file mode 100644 index 000000000000..ae5345d3049f --- /dev/null +++ b/sbin/mount_portalfs/mount_portalfs.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_portal.c 8.4 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" +#include "pathnames.h" +#include "portald.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +static void usage __P((void)); + +static sig_atomic_t readcf; /* Set when SIGHUP received */ + +static void sigchld(sig) +int sig; +{ + pid_t pid; + + while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) + ; + if (pid < 0) + syslog(LOG_WARNING, "waitpid: %s", strerror(errno)); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct portal_args args; + struct sockaddr_un un; + char *conf; + char *mountpt; + int mntflags = 0; + char tag[32]; + + qelem q; + int rc; + int so; + int error = 0; + + /* + * Crack command line args + */ + int ch; + + while ((ch = getopt(argc, argv, "o:")) != EOF) { + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + default: + error = 1; + break; + } + } + + if (optind != (argc - 2)) + error = 1; + + if (error) + usage(); + + /* + * Get config file and mount point + */ + conf = argv[optind]; + mountpt = argv[optind+1]; + + /* + * Construct the listening socket + */ + un.sun_family = AF_UNIX; + if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) { + fprintf(stderr, "mount_portal: portal socket name too long\n"); + exit(1); + } + strcpy(un.sun_path, _PATH_TMPPORTAL); + mktemp(un.sun_path); + un.sun_len = strlen(un.sun_path); + + so = socket(AF_UNIX, SOCK_STREAM, 0); + if (so < 0) { + fprintf(stderr, "mount_portal: socket: %s\n", strerror(errno)); + exit(1); + } + (void) unlink(un.sun_path); + if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0) + err(1, NULL); + (void) unlink(un.sun_path); + + (void) listen(so, 5); + + args.pa_socket = so; + sprintf(tag, "portal:%d", getpid()); + args.pa_config = tag; + + rc = mount(MOUNT_PORTAL, mountpt, mntflags, &args); + if (rc < 0) + err(1, NULL); + +#ifdef notdef + /* + * Everything is ready to go - now is a good time to fork + */ + daemon(0, 0); +#endif + + /* + * Start logging (and change name) + */ + openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON); + + q.q_forw = q.q_back = &q; + readcf = 1; + + signal(SIGCHLD, sigchld); + + /* + * Just loop waiting for new connections and activating them + */ + for (;;) { + struct sockaddr_un un2; + int len2 = sizeof(un2); + int so2; + pid_t pid; + fd_set fdset; + int rc; + + /* + * Check whether we need to re-read the configuration file + */ + if (readcf) { + readcf = 0; + conf_read(&q, conf); + continue; + } + + /* + * Accept a new connection + * Will get EINTR if a signal has arrived, so just + * ignore that error code + */ + FD_SET(so, &fdset); + rc = select(so+1, &fdset, (void *) 0, (void *) 0, (void *) 0); + if (rc < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "select: %s", strerror(errno)); + exit(1); + } + if (rc == 0) + break; + so2 = accept(so, (struct sockaddr *) &un2, &len2); + if (so2 < 0) { + /* + * The unmount function does a shutdown on the socket + * which will generated ECONNABORTED on the accept. + */ + if (errno == ECONNABORTED) + break; + if (errno != EINTR) { + syslog(LOG_ERR, "accept: %s", strerror(errno)); + exit(1); + } + continue; + } + + /* + * Now fork a new child to deal with the connection + */ + eagain:; + switch (pid = fork()) { + case -1: + if (errno == EAGAIN) { + sleep(1); + goto eagain; + } + syslog(LOG_ERR, "fork: %s", strerror(errno)); + break; + case 0: + (void) close(so); + activate(&q, so2); + break; + default: + (void) close(so2); + break; + } + } + syslog(LOG_INFO, "%s unmounted", mountpt); + exit(0); +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: mount_portal [-o options] config mount-point\n"); + exit(1); +} diff --git a/sbin/mount_portalfs/pathnames.h b/sbin/mount_portalfs/pathnames.h new file mode 100644 index 000000000000..25321145d990 --- /dev/null +++ b/sbin/mount_portalfs/pathnames.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $Id: pathnames.h,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include + +#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */ diff --git a/sbin/mount_portalfs/portal.conf b/sbin/mount_portalfs/portal.conf new file mode 100644 index 000000000000..5b5a773eaa3c --- /dev/null +++ b/sbin/mount_portalfs/portal.conf @@ -0,0 +1,7 @@ +# @(#)portal.conf 8.1 (Berkeley) 6/5/93 +# $Id: portal.conf,v 1.1 1992/05/27 06:50:13 jsp Exp jsp $ +tcplisten/ tcplisten tcplisten/ +tcp/ tcp tcp/ +fs/ file fs/ +pipe/ pipe +foo/ exec ./bar bar baz diff --git a/sbin/mount_portalfs/portald.h b/sbin/mount_portalfs/portald.h new file mode 100644 index 000000000000..fbe111b1a7ad --- /dev/null +++ b/sbin/mount_portalfs/portald.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)portald.h 8.1 (Berkeley) 6/5/93 + * + * $Id: portald.h,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include + +/* + * Meta-chars in an RE. Paths in the config file containing + * any of these characters will be matched using regexec, other + * paths will be prefix-matched. + */ +#define RE_CHARS ".|()[]*+?\\^$" + +typedef struct qelem qelem; + +struct qelem { + qelem *q_forw; + qelem *q_back; +}; + +typedef struct provider provider; +struct provider { + char *pr_match; + int (*pr_func) __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +}; +extern provider providers[]; + +/* + * Portal providers + */ +extern int portal_exec __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_file __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); +extern int portal_tcp __P((struct portal_cred *, + char *key, char **v, int so, int *fdp)); + +/* + * Global functions + */ +extern void activate __P((qelem *q, int so)); +extern char **conf_match __P((qelem *q, char *key)); +extern void conf_read __P((qelem *q, char *conf)); diff --git a/sbin/mount_portalfs/pt_conf.c b/sbin/mount_portalfs/pt_conf.c new file mode 100644 index 000000000000..d1eba94ea3fe --- /dev/null +++ b/sbin/mount_portalfs/pt_conf.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp jsp $ + */ + +#include +#include +#include "portald.h" + +provider providers[] = { + { "exec", portal_exec }, + { "file", portal_file }, + { "tcp", portal_tcp }, + { 0, 0 } +}; diff --git a/sbin/mount_portalfs/pt_exec.c b/sbin/mount_portalfs/pt_exec.c new file mode 100644 index 000000000000..06e3382da85b --- /dev/null +++ b/sbin/mount_portalfs/pt_exec.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93 + * + * $Id: pt_exec.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +int portal_exec(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + return (ENOEXEC); +} + diff --git a/sbin/mount_portalfs/pt_file.c b/sbin/mount_portalfs/pt_file.c new file mode 100644 index 000000000000..ace35c04fe59 --- /dev/null +++ b/sbin/mount_portalfs/pt_file.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_file.c 8.2 (Berkeley) 3/27/94 + * + * $Id: pt_file.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +int portal_file(pcr, key, v, so, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int so; +int *fdp; +{ + int fd; + char pbuf[MAXPATHLEN]; + int error; + int gidset[NGROUPS]; + int i; + + pbuf[0] = '/'; + strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0)); + +#ifdef DEBUG + printf("path = %s, uid = %d, gid = %d\n", pbuf, pcr->pcr_uid, pcr->pcr_groups[0]); +#endif + + for (i = 0; i < pcr->pcr_ngroups; i++) + gidset[i] = pcr->pcr_groups[i]; + + if (setgroups(pcr->pcr_ngroups, gidset) < 0) + return (errno); + + if (seteuid(pcr->pcr_uid) < 0) + return (errno); + + fd = open(pbuf, O_RDWR|O_CREAT, 0666); + if (fd < 0) + error = errno; + else + error = 0; + + if (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */ + error = errno; + syslog(LOG_ERR, "setcred: %s", strerror(error)); + if (fd >= 0) { + (void) close(fd); + fd = -1; + } + } + + if (error == 0) + *fdp = fd; + +#ifdef DEBUG + fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error); +#endif + + return (error); +} diff --git a/sbin/mount_portalfs/pt_tcp.c b/sbin/mount_portalfs/pt_tcp.c new file mode 100644 index 000000000000..18a53ce26aba --- /dev/null +++ b/sbin/mount_portalfs/pt_tcp.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pt_tcp.c 8.3 (Berkeley) 3/27/94 + * + * $Id: pt_tcp.c,v 1.1 1992/05/25 21:43:09 jsp Exp jsp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portald.h" + +/* + * Key will be tcp/host/port[/"priv"] + * Create a TCP socket connected to the + * requested host and port. + * Some trailing suffix values have special meanings. + * An unrecognised suffix is an error. + */ +int portal_tcp(pcr, key, v, kso, fdp) +struct portal_cred *pcr; +char *key; +char **v; +int kso; +int *fdp; +{ + char host[MAXHOSTNAMELEN]; + char port[MAXHOSTNAMELEN]; + char *p = key + (v[1] ? strlen(v[1]) : 0); + char *q; + struct hostent *hp; + struct servent *sp; + struct in_addr **ipp; + struct in_addr *ip[2]; + struct in_addr ina; + int s_port; + int priv = 0; + struct sockaddr_in sain; + + q = strchr(p, '/'); + if (q == 0 || q - p >= sizeof(host)) + return (EINVAL); + *q = '\0'; + strcpy(host, p); + p = q + 1; + + q = strchr(p, '/'); + if (q) + *q = '\0'; + if (strlen(p) >= sizeof(port)) + return (EINVAL); + strcpy(port, p); + if (q) { + p = q + 1; + if (strcmp(p, "priv") == 0) { + if (pcr->pcr_uid == 0) + priv = 1; + else + return (EPERM); + } else { + return (EINVAL); + } + } + + hp = gethostbyname(host); + if (hp != 0) { + ipp = (struct in_addr **) hp->h_addr_list; + } else { + ina.s_addr = inet_addr(host); + if (ina.s_addr == INADDR_NONE) + return (EINVAL); + ip[0] = &ina; + ip[1] = 0; + ipp = ip; + } + + sp = getservbyname(port, "tcp"); + if (sp != 0) + s_port = sp->s_port; + else { + s_port = atoi(port); + if (s_port == 0) + return (EINVAL); + } + + bzero(&sain, sizeof(sain)); + sain.sin_len = sizeof(sain); + sain.sin_family = AF_INET; + sain.sin_port = s_port; + + while (ipp[0]) { + int so; + + if (priv) + so = rresvport((int *) 0); + else + so = socket(AF_INET, SOCK_STREAM, 0); + if (so < 0) { + syslog(LOG_ERR, "socket: %m"); + return (errno); + } + + sain.sin_addr = *ipp[0]; + if (connect(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) { + *fdp = so; + return (0); + } + (void) close(so); + + ipp++; + } + + return (errno); +} diff --git a/sbin/mount_procfs/Makefile b/sbin/mount_procfs/Makefile new file mode 100644 index 000000000000..ca0c872520b0 --- /dev/null +++ b/sbin/mount_procfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.4 (Berkeley) 3/27/94 + +PROG= mount_procfs +SRCS= mount_procfs.c getmntopts.c +MAN8= mount_procfs.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_procfs/mount_procfs.8 b/sbin/mount_procfs/mount_procfs.8 new file mode 100644 index 000000000000..3aa20e0a4c2e --- /dev/null +++ b/sbin/mount_procfs/mount_procfs.8 @@ -0,0 +1,253 @@ +.\" +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_procfs.8 8.2 (Berkeley) 3/27/94 +.\" +.\" +.Dd March 27, 1994 +.Dt MOUNT_PROCFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_procfs +.Nd mount the process file system +.Sh SYNOPSIS +.Nm mount_procfs +.Op Fl o Ar options +.Pa /proc +.Pa mount_point +.Sh DESCRIPTION +The +.Nm mount_procfs +command attaches an instance of the process +namespace to the global filesystem namespace. +The conventional mount point is +.Pa /proc . +This command is normally executed by +.Xr mount 8 +at boot time. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The root of the process filesystem +contains an entry for each active process. +These processes are visible as a directory whose +name is the process' pid. +In addition, the special entry +.Pa curproc +references the current process. +.Pp +Each directory contains several files. +.Bl -tag -width status +.It Pa ctl +a writeonly file which supports a variety +of control operations. +Control commands are written as strings to the +.Pa ctl +file. +The control commands are: +.Bl -tag -width detach -compact +.It attach +stops the target process and arranges for the sending +process to become the debug control process. +.It detach +continue execution of the target process and +remove it from control by the debug process (which +need not be the sending process). +.It run +continue running the target process until +a signal is delivered, a breakpoint is hit, or the +target process exits. +.It step +single step the target process, with no signal delivery. +.It wait +wait for the target process to come to a steady +state ready for debugging. +The target process must be in this state before +any of the other commands are allowed. +.El +.Pp +The string can also be the name of a signal, lower case +and without the +.Dv SIG +prefix, +in which case that signal is delivered to the process +(see +.Xr sigaction 2 ). +.It Pa file +A reference to the vnode from which the process text was read. +This can be used to gain access to the process' symbol table, +or to start another copy of the process. +.It Pa mem +The complete virtual memory image of the process. +Only those address which exist in the process can be accessed. +Reads and writes to this file modify the process. +Writes to the text segment remain private to the process. +.It Pa note +Not implemented. +.It Pa notepg +Not implemented. +.It Pa regs +Allows read and write access to the process' register set. +This file contains a binary data structure +.Dv "struct regs" +defined in +.Pa . +.Pa regs +can only be written when the process is stopped. +.It Pa fpregs +The floating point registers as defined by +.Dv "struct fpregs" +in +.Pa . +.Pa fpregs +is only implemented on machines which have distinct general +purpose and floating point register sets. +.It Pa status +The process status. +This file is readonly and returns a single line containing +multiple space-separated fields as follows: +.Pp +.Bl -bullet -compact +.It +command name +.It +process id +.It +parent process id +.It +process group id +.It +session id +.It +.Ar major,minor +of the controlling terminal, or +.Dv -1,-1 +if there is no controlling terminal. +.It +a list of process flags: +.Dv ctty +if there is a controlling terminal, +.Dv sldr +if the process is a session leader, +.Dv noflags +if neither of the other two flags are set. +.It +the process start time in seconds and microseconds, +comma separated. +.It +the user time in seconds and microseconds, +comma separated. +.It +the system time in seconds and microseconds, +comma separated. +.It +the wait channel message +.It +the process credentials consisting of +the effective user id +and the list of groups (whose first member +is the effective group id) +all comma separated. +.El +.El +.Pp +In a normal debugging environment, +where the target is fork/exec'd by the debugger, +the debugger should fork and the child should stop +itself (with a self-inflicted +.Dv SIGSTOP +for example). +The parent should issue a +.Dv wait +and then an +.Dv attach +command via the appropriate +.Pa ctl +file. +The child process will receive a +.Dv SIGTRAP +immediately after the call to exec (see +.Xr execve 2 ). +.Sh FILES +.Bl -tag -width /proc/curproc -compact +.It Pa /proc/# +.It Pa /proc/curproc +.It Pa /proc/curproc/ctl +.It Pa /proc/curproc/file +.It Pa /proc/curproc/mem +.It Pa /proc/curproc/note +.It Pa /proc/curproc/notepg +.It Pa /proc/curproc/regs +.It Pa /proc/curproc/fpregs +.It Pa /proc/curproc/status +.El +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr mount 2 , +.Xr unmount 2 , +.Sh CAVEATS +No +.Pa . +and +.Pa .. +entries appear when listing the contents of the +.Pa /proc +directory. +This makes sense in the context of this filesystem, but is inconsistent +with usual filesystem conventions. +However, it is still possible to refer to both +.Pa . +and +.Pa .. +in a pathname. +.Pp +This filesystem may not be NFS-exported +since most of the functionality of +.Dv procfs +requires that state be maintained. +.Sh HISTORY +The +.Nm mount_procfs +utility first appeared in 4.4BSD. diff --git a/sbin/mount_procfs/mount_procfs.c b/sbin/mount_procfs/mount_procfs.c new file mode 100644 index 000000000000..79016c8377c1 --- /dev/null +++ b/sbin/mount_procfs/mount_procfs.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1990, 1992, 1993 Jan-Simon Pendry + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_procfs.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, mntflags; + + mntflags = 0; + while ((ch = getopt(argc, argv, "o:")) != EOF) + switch (ch) { + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (mount(MOUNT_PROCFS, argv[1], mntflags, NULL)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_procfs [-o options] /proc mount_point\n"); + exit(1); +} diff --git a/sbin/mount_umap/Makefile b/sbin/mount_umap/Makefile new file mode 100644 index 000000000000..af1bf2f0457d --- /dev/null +++ b/sbin/mount_umap/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_umap +SRCS= mount_umap.c getmntopts.c +MAN8= mount_umap.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_umap/mount_umap.8 b/sbin/mount_umap/mount_umap.8 new file mode 100644 index 000000000000..e90ce265b5c6 --- /dev/null +++ b/sbin/mount_umap/mount_umap.8 @@ -0,0 +1,131 @@ +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry and from John Heidemann of the UCLA Ficus project. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_umap.8 8.3 (Berkeley) 3/27/94 +.\" +.Dd "March 27, 1994" +.Dt MOUNT_UMAP 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_umap +.Nd sample file system layer +.Sh SYNOPSIS +.Nm mount_umap +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Ar uid-mapfile +.Ar gid-mapfile +.Sh DESCRIPTION +The +.Nm mount_umap +command is used to mount a sub-tree of an existing file system +that uses a different set of uids and gids than the local system. +Such a file system could be mounted from a remote site via NFS or +it could be a file system on removable media brought from some +foreign location that uses a different password file. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The +.Nm mount_umap +command uses a set of files provided by the user to make correspondences +between uids and gids in the sub-tree's original environment and +some other set of ids in the local environment. For instance, user +smith might have uid 1000 in the original environment, while having +uid 2000 in the local environment. The +.Nm mount_umap +command allows the subtree from smith's original environment to be +mapped in such a way that all files with owning uid 1000 look like +they are actually owned by uid 2000. +.Pp +.Em target +should be the current location of the sub-tree in the +local system's name space. +.Em mount-point +should be a directory +where the mapped subtree is to be placed. +.Em uid-mapfile +and +.Em gid-mapfile +describe the mappings to be made between identifiers. +Briefly, the format of these files is a count of the number of +mappings on the first line, with each subsequent line containing +a single mapping. Each of these mappings consists of an id from +the original environment and the corresponding id in the local environment, +separated by white space. +.Em uid-mapfile +should contain all uid +mappings, and +.Em gid-mapfile +should contain all gid mappings. +Any uids not mapped in +.Em uid-mapfile +will be treated as user NOBODY, +and any gids not mapped in +.Em gid-mapfile +will be treated as group +NULLGROUP. At most 64 uids can be mapped for a given subtree, and +at most 16 groups can be mapped by a given subtree. +.Pp +The mapfiles can be located anywhere in the file hierarchy, but they +must be owned by root, and they must be writable only by root. +.Nm mount_umap +will refuse to map the sub-tree if the ownership or permissions on +these files are improper. It will also balk if the count of mappings +in the first line of the map files is not correct. +.Pp +The layer created by the +.Nm mount_umap +command is meant to serve as a simple example of file system layering. +It is not meant for production use. The implementation is not very +sophisticated. +.Sh SEE ALSO +.Xr mount 8 , +.Xr mount_null 8 , +.Xr mount_lofs 8 +.Sh HISTORY +The +.Nm mount_umap +utility first appeared in 4.4BSD. diff --git a/sbin/mount_umap/mount_umap.c b/sbin/mount_umap/mount_umap.c new file mode 100644 index 000000000000..a069fe5f02b9 --- /dev/null +++ b/sbin/mount_umap/mount_umap.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_umap.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +#define ROOTUSER 0 +/* + * This define controls whether any user but the superuser can own and + * write mapfiles. If other users can, system security can be gravely + * compromised. If this is not a concern, undefine SECURITY. + */ +#define MAPSECURITY 1 + +/* + * This routine provides the user interface to mounting a umap layer. + * It takes 4 mandatory parameters. The mandatory arguments are the place + * where the next lower level is mounted, the place where the umap layer is to + * be mounted, the name of the user mapfile, and the name of the group + * mapfile. The routine checks the ownerships and permissions on the + * mapfiles, then opens and reads them. Then it calls mount(), which + * will, in turn, call the umap version of mount. + */ + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char not[] = "; not mounted."; + struct stat statbuf; + struct umap_args args; + FILE *fp, *gfp; + u_long gmapdata[GMAPFILEENTRIES][2], mapdata[MAPFILEENTRIES][2]; + int ch, count, gnentries, mntflags, nentries; + char *gmapfile, *mapfile, *source, *target, buf[20]; + + mntflags = 0; + mapfile = gmapfile = NULL; + while ((ch = getopt(argc, argv, "g:o:u:")) != EOF) + switch (ch) { + case 'g': + gmapfile = optarg; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'u': + mapfile = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2 || mapfile == NULL || gmapfile == NULL) + usage(); + + source = argv[0]; + target = argv[1]; + + /* Read in uid mapping data. */ + if ((fp = fopen(mapfile, "r")) == NULL) + err(1, "%s%s", mapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this mapfile, and that the mapfile belongs to root. + */ + if (fstat(fileno(fp), &statbuf)) + err(1, "%s%s", mapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + mapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", mapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &nentries)) != 1) + errx(1, "%s: nentries not found%s", mapfile, not); + if (nentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", MAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d entries\n", nentries); +#endif + for (count = 0; count < nentries; ++count) { + if ((fscanf(fp, "%lu %lu\n", + &(mapdata[count][0]), &(mapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", mapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + mapfile, not); + errx(1, "%s: illegal format (line %d)%s", + mapfile, count + 2, not); + } +#if 0 + /* Fix a security hole. */ + if (mapdata[count][1] == 0) + errx(1, "mapping id 0 not permitted (line %d)%s", + count + 2, not); +#endif + } + + /* Read in gid mapping data. */ + if ((gfp = fopen(gmapfile, "r")) == NULL) + err(1, "%s%s", gmapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this group mapfile, and that the file belongs to root. + */ + if (fstat(fileno(gfp), &statbuf)) + err(1, "%s%s", gmapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + gmapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", gmapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &gnentries)) != 1) + errx(1, "nentries not found%s", gmapfile, not); + if (gnentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", GMAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d group entries\n", gnentries); +#endif + + for (count = 0; count < gnentries; ++count) + if ((fscanf(fp, "%lu %lu\n", + &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", gmapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + gmapfile, not); + errx(1, "%s: illegal format (line %d)%s", + gmapfile, count + 2, not); + } + + + /* Setup mount call args. */ + args.target = source; + args.nentries = nentries; + args.mapdata = mapdata; + args.gnentries = gnentries; + args.gmapdata = gmapdata; + + if (mount(MOUNT_UMAP, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage: mount_umap [-o options] -u usermap -g groupmap target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_umap/sample.group.mapfile b/sbin/mount_umap/sample.group.mapfile new file mode 100644 index 000000000000..69d3d77c6363 --- /dev/null +++ b/sbin/mount_umap/sample.group.mapfile @@ -0,0 +1,2 @@ +1 +1200 1200 diff --git a/sbin/mount_umap/sample.user.mapfile b/sbin/mount_umap/sample.user.mapfile new file mode 100644 index 000000000000..03c59e9e7569 --- /dev/null +++ b/sbin/mount_umap/sample.user.mapfile @@ -0,0 +1,3 @@ +2 +5217 5217 +3 3 diff --git a/sbin/mount_umap/umap_manual b/sbin/mount_umap/umap_manual new file mode 100644 index 000000000000..059939f0f51a --- /dev/null +++ b/sbin/mount_umap/umap_manual @@ -0,0 +1,175 @@ + +\appendix +\section{The umap Layer} \label{sect:umap} + +\subsection{Introduction} + +Normally, the file system is expected to span a single administrative domain. +An administrative domain, for these purposes, is a machine or set of +machines that share common password file information, usually through +the yellow pages mechanism. File hierarchies that span more +than one domain leads to certain problems, since the same numerical +UID in one domain may correspond to a different user in another domain. +If the system administrator is very careful to ensure that both domains +contain identical user ID information, The umap layer can be used to +run between those domains without changes + +The umap layer is a file system layer that sits on top of the normal +file layer. The umap layer maps Unix-style UIDs from +one domain into the UIDs in the other domain. By setting up the mappings +properly, the same user with different UIDs in two domains can be seen +as the same user, from the system point of view, or, conversely, two +different users with the same UID in the two domains can be distinguished. + +First, we define some terms. ``User'' refers to the human (or daemon) that +has privileges to login, run programs, and access files. ``UID''refers to +the numerical identifier that uniquely identifies the user within a +single domain. ``Login name'' refers to the character string the user +types to log into the system. ``GID'' refers to the numerical group +identifier used by Unix systems to identify groups of users. ``Group +name'' is the character string name attached to a particular GID in the +local {\sf /etc/groups} file or the yellow pages groups file. + +In order for the umap layer to work properly, all users +in either domain must have password file entries in both domains. +They do not, however, have to have the same numerical UID, nor even the +same character string login name (the latter is highly recommended, +if possible, however). Any user not having a UID in one domain will be +treated as the special user NOBODY by the other domain, probably with +undesirable consequences. Any user not owning any files in the shared +sub-trees need not be given a UID in the other domain. + +Groups work similarly. The umap layer can translate group ID's between +domains in the same manner as UID's. Again, any group that wishes to +participate must have a group ID in both domains, +though it need not be the same GID in both. If a group in one domain is not +known in the other domain, that group will be treated as being NULLGROUP. +The umap layer has no provisions for enrolling UID's from other domains +as group members, but, since each user from each domain must have some +UID in every domain, the UID in the local domain can be used to enroll +the user in the local groups. + +NOBODY and NULLGROUP are special reserved UID's and GID's, respectively. +NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator +wants to have an appropriate text string appear when these UID's are +encountered by programs like {\sf ls -l}, he should add these values to +the password and {\sf /etc/groups} file, or to the appropriate yellow pages. +If these IDs are already in use in that domain, different values can be +used for NOBODY and NULLGROUP, but that will require a recompilation of +the umap layer code and, as a result, the entire kernel. These +values are defined in the {\sf umap\_info.h} file, kept with the rest of the +umap source code. + +When the umap layer is in use, one of the participating domains is declared +to be the master. All UID and GID information stored for participating files +will be stored in vnodes using its mappings, no matter what site the copies of +the files are stored at. The master domain therefore need not run a copy +of the umap layer, as it already has all of the correct mappings. All +other domains must run a umap layer on top of any other layers they use. + +\subsection{Setting Up a umap Layer} + +The system administrator of a system needing to use the umap layer +must take several actions. +First, he must create files containing the necessary UID +and GID mappings. There is a separate file for user and group IDs. The +format of the files is the same. The first line contains the total number +of entries in the file. Each subsequent line contains one mapping. A +mapping line consists of two numerical UIDs, separated by white space. +The first is the UID of a user on the local machine. The second is the +UID for the same user on the master machine. The maximum number of users +that can be mapped for a single shared sub-tree is 64. The maximum number of +groups that can be mapped for a single sub-tree is 16. These constants +are set in the {\sf umap\_info.h} file, and can be changed, but changing them +requires recompilation. Separate mapping files can be used for each shared +subtree, or the same mapping files can be shared by several sub-trees. + +Below is a sample UID mapping file. There are four entries. UID 5 is mapped +to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this +machine, the UID's for users 5, 521, and 7000 are the same as on the master, +but UID 2002 is for a user whose UID on the master machine is 604. All +files in the sub-tree belonging to that user have UID 604 in their inodes, +even on this machine, but the umap layer will ensure that anyone running +under UID 2002 will have all files in this sub-tree owned by 604 treated as if +they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree +will show the login name associated with UID 2002 as the owner. + +\noindent4\newline +5 5\newline +521 521\newline +2002 604\newline +7000 7000\newline + +The user and group mapping files should be owned by the root user, and +should be writable only by that user. If they are not owned by root, or +are writable by some other user, the umap mounting command will abort. + +Normally, the sub-treeis grafted directly into the place in +the file hierarchy where the it should appear to users.Using the umap +layer requires that the sub-tree be grafted somewhere else, and +the umap layer be mounted in the desired position in the file hierarchy. +Depending on the situation, the underlying sub-tree can be wherever is +convenient. + +\subsection{Troubleshooting umap Layer Problems} + +The umap layer code was not built with special convenience or +robustness in mind, as it is expected to be superseded with a better +user ID mapping strategy in the near future. As a result, it is not +very forgiving of errors in being set up. Here are some possible +problems, and what to do about them. + +\begin{itemize} + + +\item{Problem: A file belongs to NOBODY, or group NULLGROUP. + +Fixes: The mapping files don't know about this file's real user or group. +Either they are not in the mapping files, or the counts on the number of +entries in the mapping files are too low, so entries at the end (including +these) are being ignored. Add the entries or fix the counts, and either +unmount and remount the sub-tree, or reboot.} + +\item{Problem: A normal operation does not work. + +Fixes: Possibly, some mapping has not been set properly. Check to +see which files are used by the operation and who they appear to be +owned by. If they are owned by NOBODY or some other suspicious user, +there may be a problem in the mapping files. Be sure to check groups, +too. As above, if the counts of mappings in the mapping files are lower +than the actual numbers of pairs, pairs at the end of the file will be +ignored. If any changes are made in the mapping files, you will need to +either unmount and remount or reboot before they will take effect. + +Another possible problem can arise because not all Unix utilities +rely exclusively on numeric UID for identification. For instance, +SCCS saves the login name in files. If a user's login name on two machines +isn't the same, SCCS may veto an operation even though Unix file permissions, +as checked by the umap layer, may say it's OK. There's not much to be +done in such cases, unless the login name can be changed or one fiddles +improperly with SCCS information. There may be other, undiscovered cases +where similar problems arise, some of which may be even harder to handle.} + +\item{Problem: Someone has access permissions he should not have. + +Fixes: This is probably caused by a mistake in the mapping files. Check +both user and group mapping files. If any changes are made in the mapping +files, you will need to unmount and remount the sub-tree or reboot before they +will take effect.} + +\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file. + +Fixes: Probably a mistake in the mapping files. In particular, if +two local UIDs are mapped to a single master UID, stat calls will assign +ownership to the first local UID occurring in the file, which may or may +not be what was intended. (Generally speaking, mapping two local UIDs to +a single master UID is a bad idea, but the software will not prevent it. +Similarly, mapping a single local UID to two master UIDs is a bad idea, +but will not be prevented. In this case, only the first mapping of the +local UID will be done. The second, and all subsequent ones, will be +ignored.) If any changes are made in the mapping files, you will need to +unmount and remount the sub-tree or reboot before they will take effect.} + +\end{itemize} + +\end{document} diff --git a/sbin/mount_umapfs/Makefile b/sbin/mount_umapfs/Makefile new file mode 100644 index 000000000000..af1bf2f0457d --- /dev/null +++ b/sbin/mount_umapfs/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_umap +SRCS= mount_umap.c getmntopts.c +MAN8= mount_umap.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +.include diff --git a/sbin/mount_umapfs/mount_umapfs.8 b/sbin/mount_umapfs/mount_umapfs.8 new file mode 100644 index 000000000000..e90ce265b5c6 --- /dev/null +++ b/sbin/mount_umapfs/mount_umapfs.8 @@ -0,0 +1,131 @@ +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry and from John Heidemann of the UCLA Ficus project. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_umap.8 8.3 (Berkeley) 3/27/94 +.\" +.Dd "March 27, 1994" +.Dt MOUNT_UMAP 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_umap +.Nd sample file system layer +.Sh SYNOPSIS +.Nm mount_umap +.Op Fl o Ar options +.Ar target +.Ar mount-point +.Ar uid-mapfile +.Ar gid-mapfile +.Sh DESCRIPTION +The +.Nm mount_umap +command is used to mount a sub-tree of an existing file system +that uses a different set of uids and gids than the local system. +Such a file system could be mounted from a remote site via NFS or +it could be a file system on removable media brought from some +foreign location that uses a different password file. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.El +.Pp +The +.Nm mount_umap +command uses a set of files provided by the user to make correspondences +between uids and gids in the sub-tree's original environment and +some other set of ids in the local environment. For instance, user +smith might have uid 1000 in the original environment, while having +uid 2000 in the local environment. The +.Nm mount_umap +command allows the subtree from smith's original environment to be +mapped in such a way that all files with owning uid 1000 look like +they are actually owned by uid 2000. +.Pp +.Em target +should be the current location of the sub-tree in the +local system's name space. +.Em mount-point +should be a directory +where the mapped subtree is to be placed. +.Em uid-mapfile +and +.Em gid-mapfile +describe the mappings to be made between identifiers. +Briefly, the format of these files is a count of the number of +mappings on the first line, with each subsequent line containing +a single mapping. Each of these mappings consists of an id from +the original environment and the corresponding id in the local environment, +separated by white space. +.Em uid-mapfile +should contain all uid +mappings, and +.Em gid-mapfile +should contain all gid mappings. +Any uids not mapped in +.Em uid-mapfile +will be treated as user NOBODY, +and any gids not mapped in +.Em gid-mapfile +will be treated as group +NULLGROUP. At most 64 uids can be mapped for a given subtree, and +at most 16 groups can be mapped by a given subtree. +.Pp +The mapfiles can be located anywhere in the file hierarchy, but they +must be owned by root, and they must be writable only by root. +.Nm mount_umap +will refuse to map the sub-tree if the ownership or permissions on +these files are improper. It will also balk if the count of mappings +in the first line of the map files is not correct. +.Pp +The layer created by the +.Nm mount_umap +command is meant to serve as a simple example of file system layering. +It is not meant for production use. The implementation is not very +sophisticated. +.Sh SEE ALSO +.Xr mount 8 , +.Xr mount_null 8 , +.Xr mount_lofs 8 +.Sh HISTORY +The +.Nm mount_umap +utility first appeared in 4.4BSD. diff --git a/sbin/mount_umapfs/mount_umapfs.c b/sbin/mount_umapfs/mount_umapfs.c new file mode 100644 index 000000000000..a069fe5f02b9 --- /dev/null +++ b/sbin/mount_umapfs/mount_umapfs.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_umap.c 8.3 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +#define ROOTUSER 0 +/* + * This define controls whether any user but the superuser can own and + * write mapfiles. If other users can, system security can be gravely + * compromised. If this is not a concern, undefine SECURITY. + */ +#define MAPSECURITY 1 + +/* + * This routine provides the user interface to mounting a umap layer. + * It takes 4 mandatory parameters. The mandatory arguments are the place + * where the next lower level is mounted, the place where the umap layer is to + * be mounted, the name of the user mapfile, and the name of the group + * mapfile. The routine checks the ownerships and permissions on the + * mapfiles, then opens and reads them. Then it calls mount(), which + * will, in turn, call the umap version of mount. + */ + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char not[] = "; not mounted."; + struct stat statbuf; + struct umap_args args; + FILE *fp, *gfp; + u_long gmapdata[GMAPFILEENTRIES][2], mapdata[MAPFILEENTRIES][2]; + int ch, count, gnentries, mntflags, nentries; + char *gmapfile, *mapfile, *source, *target, buf[20]; + + mntflags = 0; + mapfile = gmapfile = NULL; + while ((ch = getopt(argc, argv, "g:o:u:")) != EOF) + switch (ch) { + case 'g': + gmapfile = optarg; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'u': + mapfile = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2 || mapfile == NULL || gmapfile == NULL) + usage(); + + source = argv[0]; + target = argv[1]; + + /* Read in uid mapping data. */ + if ((fp = fopen(mapfile, "r")) == NULL) + err(1, "%s%s", mapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this mapfile, and that the mapfile belongs to root. + */ + if (fstat(fileno(fp), &statbuf)) + err(1, "%s%s", mapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + mapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", mapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &nentries)) != 1) + errx(1, "%s: nentries not found%s", mapfile, not); + if (nentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", MAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d entries\n", nentries); +#endif + for (count = 0; count < nentries; ++count) { + if ((fscanf(fp, "%lu %lu\n", + &(mapdata[count][0]), &(mapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", mapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + mapfile, not); + errx(1, "%s: illegal format (line %d)%s", + mapfile, count + 2, not); + } +#if 0 + /* Fix a security hole. */ + if (mapdata[count][1] == 0) + errx(1, "mapping id 0 not permitted (line %d)%s", + count + 2, not); +#endif + } + + /* Read in gid mapping data. */ + if ((gfp = fopen(gmapfile, "r")) == NULL) + err(1, "%s%s", gmapfile, not); + +#ifdef MAPSECURITY + /* + * Check that group and other don't have write permissions on + * this group mapfile, and that the file belongs to root. + */ + if (fstat(fileno(gfp), &statbuf)) + err(1, "%s%s", gmapfile, not); + if (statbuf.st_mode & S_IWGRP || statbuf.st_mode & S_IWOTH) { + strmode(statbuf.st_mode, buf); + err(1, "%s: improper write permissions (%s)%s", + gmapfile, buf, not); + } + if (statbuf.st_uid != ROOTUSER) + errx(1, "%s does not belong to root%s", gmapfile, not); +#endif /* MAPSECURITY */ + + if ((fscanf(fp, "%d\n", &gnentries)) != 1) + errx(1, "nentries not found%s", gmapfile, not); + if (gnentries > MAPFILEENTRIES) + errx(1, + "maximum number of entries is %d%s", GMAPFILEENTRIES, not); +#if 0 + (void)printf("reading %d group entries\n", gnentries); +#endif + + for (count = 0; count < gnentries; ++count) + if ((fscanf(fp, "%lu %lu\n", + &(gmapdata[count][0]), &(gmapdata[count][1]))) != 2) { + if (ferror(fp)) + err(1, "%s%s", gmapfile, not); + if (feof(fp)) + errx(1, "%s: unexpected end-of-file%s", + gmapfile, not); + errx(1, "%s: illegal format (line %d)%s", + gmapfile, count + 2, not); + } + + + /* Setup mount call args. */ + args.target = source; + args.nentries = nentries; + args.mapdata = mapdata; + args.gnentries = gnentries; + args.gmapdata = gmapdata; + + if (mount(MOUNT_UMAP, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage: mount_umap [-o options] -u usermap -g groupmap target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_umapfs/sample.group.mapfile b/sbin/mount_umapfs/sample.group.mapfile new file mode 100644 index 000000000000..69d3d77c6363 --- /dev/null +++ b/sbin/mount_umapfs/sample.group.mapfile @@ -0,0 +1,2 @@ +1 +1200 1200 diff --git a/sbin/mount_umapfs/sample.user.mapfile b/sbin/mount_umapfs/sample.user.mapfile new file mode 100644 index 000000000000..03c59e9e7569 --- /dev/null +++ b/sbin/mount_umapfs/sample.user.mapfile @@ -0,0 +1,3 @@ +2 +5217 5217 +3 3 diff --git a/sbin/mount_umapfs/umap_manual b/sbin/mount_umapfs/umap_manual new file mode 100644 index 000000000000..059939f0f51a --- /dev/null +++ b/sbin/mount_umapfs/umap_manual @@ -0,0 +1,175 @@ + +\appendix +\section{The umap Layer} \label{sect:umap} + +\subsection{Introduction} + +Normally, the file system is expected to span a single administrative domain. +An administrative domain, for these purposes, is a machine or set of +machines that share common password file information, usually through +the yellow pages mechanism. File hierarchies that span more +than one domain leads to certain problems, since the same numerical +UID in one domain may correspond to a different user in another domain. +If the system administrator is very careful to ensure that both domains +contain identical user ID information, The umap layer can be used to +run between those domains without changes + +The umap layer is a file system layer that sits on top of the normal +file layer. The umap layer maps Unix-style UIDs from +one domain into the UIDs in the other domain. By setting up the mappings +properly, the same user with different UIDs in two domains can be seen +as the same user, from the system point of view, or, conversely, two +different users with the same UID in the two domains can be distinguished. + +First, we define some terms. ``User'' refers to the human (or daemon) that +has privileges to login, run programs, and access files. ``UID''refers to +the numerical identifier that uniquely identifies the user within a +single domain. ``Login name'' refers to the character string the user +types to log into the system. ``GID'' refers to the numerical group +identifier used by Unix systems to identify groups of users. ``Group +name'' is the character string name attached to a particular GID in the +local {\sf /etc/groups} file or the yellow pages groups file. + +In order for the umap layer to work properly, all users +in either domain must have password file entries in both domains. +They do not, however, have to have the same numerical UID, nor even the +same character string login name (the latter is highly recommended, +if possible, however). Any user not having a UID in one domain will be +treated as the special user NOBODY by the other domain, probably with +undesirable consequences. Any user not owning any files in the shared +sub-trees need not be given a UID in the other domain. + +Groups work similarly. The umap layer can translate group ID's between +domains in the same manner as UID's. Again, any group that wishes to +participate must have a group ID in both domains, +though it need not be the same GID in both. If a group in one domain is not +known in the other domain, that group will be treated as being NULLGROUP. +The umap layer has no provisions for enrolling UID's from other domains +as group members, but, since each user from each domain must have some +UID in every domain, the UID in the local domain can be used to enroll +the user in the local groups. + +NOBODY and NULLGROUP are special reserved UID's and GID's, respectively. +NOBODY is user 32767. NULLGROUP is group 65534. If the system administrator +wants to have an appropriate text string appear when these UID's are +encountered by programs like {\sf ls -l}, he should add these values to +the password and {\sf /etc/groups} file, or to the appropriate yellow pages. +If these IDs are already in use in that domain, different values can be +used for NOBODY and NULLGROUP, but that will require a recompilation of +the umap layer code and, as a result, the entire kernel. These +values are defined in the {\sf umap\_info.h} file, kept with the rest of the +umap source code. + +When the umap layer is in use, one of the participating domains is declared +to be the master. All UID and GID information stored for participating files +will be stored in vnodes using its mappings, no matter what site the copies of +the files are stored at. The master domain therefore need not run a copy +of the umap layer, as it already has all of the correct mappings. All +other domains must run a umap layer on top of any other layers they use. + +\subsection{Setting Up a umap Layer} + +The system administrator of a system needing to use the umap layer +must take several actions. +First, he must create files containing the necessary UID +and GID mappings. There is a separate file for user and group IDs. The +format of the files is the same. The first line contains the total number +of entries in the file. Each subsequent line contains one mapping. A +mapping line consists of two numerical UIDs, separated by white space. +The first is the UID of a user on the local machine. The second is the +UID for the same user on the master machine. The maximum number of users +that can be mapped for a single shared sub-tree is 64. The maximum number of +groups that can be mapped for a single sub-tree is 16. These constants +are set in the {\sf umap\_info.h} file, and can be changed, but changing them +requires recompilation. Separate mapping files can be used for each shared +subtree, or the same mapping files can be shared by several sub-trees. + +Below is a sample UID mapping file. There are four entries. UID 5 is mapped +to 5, 521 to 521, and 7000 to 7000. UID 2002 is mapped to 604. On this +machine, the UID's for users 5, 521, and 7000 are the same as on the master, +but UID 2002 is for a user whose UID on the master machine is 604. All +files in the sub-tree belonging to that user have UID 604 in their inodes, +even on this machine, but the umap layer will ensure that anyone running +under UID 2002 will have all files in this sub-tree owned by 604 treated as if +they were owned by 2002. An {\sf ls -l} on a file owned by 604 in this sub-tree +will show the login name associated with UID 2002 as the owner. + +\noindent4\newline +5 5\newline +521 521\newline +2002 604\newline +7000 7000\newline + +The user and group mapping files should be owned by the root user, and +should be writable only by that user. If they are not owned by root, or +are writable by some other user, the umap mounting command will abort. + +Normally, the sub-treeis grafted directly into the place in +the file hierarchy where the it should appear to users.Using the umap +layer requires that the sub-tree be grafted somewhere else, and +the umap layer be mounted in the desired position in the file hierarchy. +Depending on the situation, the underlying sub-tree can be wherever is +convenient. + +\subsection{Troubleshooting umap Layer Problems} + +The umap layer code was not built with special convenience or +robustness in mind, as it is expected to be superseded with a better +user ID mapping strategy in the near future. As a result, it is not +very forgiving of errors in being set up. Here are some possible +problems, and what to do about them. + +\begin{itemize} + + +\item{Problem: A file belongs to NOBODY, or group NULLGROUP. + +Fixes: The mapping files don't know about this file's real user or group. +Either they are not in the mapping files, or the counts on the number of +entries in the mapping files are too low, so entries at the end (including +these) are being ignored. Add the entries or fix the counts, and either +unmount and remount the sub-tree, or reboot.} + +\item{Problem: A normal operation does not work. + +Fixes: Possibly, some mapping has not been set properly. Check to +see which files are used by the operation and who they appear to be +owned by. If they are owned by NOBODY or some other suspicious user, +there may be a problem in the mapping files. Be sure to check groups, +too. As above, if the counts of mappings in the mapping files are lower +than the actual numbers of pairs, pairs at the end of the file will be +ignored. If any changes are made in the mapping files, you will need to +either unmount and remount or reboot before they will take effect. + +Another possible problem can arise because not all Unix utilities +rely exclusively on numeric UID for identification. For instance, +SCCS saves the login name in files. If a user's login name on two machines +isn't the same, SCCS may veto an operation even though Unix file permissions, +as checked by the umap layer, may say it's OK. There's not much to be +done in such cases, unless the login name can be changed or one fiddles +improperly with SCCS information. There may be other, undiscovered cases +where similar problems arise, some of which may be even harder to handle.} + +\item{Problem: Someone has access permissions he should not have. + +Fixes: This is probably caused by a mistake in the mapping files. Check +both user and group mapping files. If any changes are made in the mapping +files, you will need to unmount and remount the sub-tree or reboot before they +will take effect.} + +\item{Problem: {\sf ls -l} (or a similar program) shows the wrong user for a file. + +Fixes: Probably a mistake in the mapping files. In particular, if +two local UIDs are mapped to a single master UID, stat calls will assign +ownership to the first local UID occurring in the file, which may or may +not be what was intended. (Generally speaking, mapping two local UIDs to +a single master UID is a bad idea, but the software will not prevent it. +Similarly, mapping a single local UID to two master UIDs is a bad idea, +but will not be prevented. In this case, only the first mapping of the +local UID will be done. The second, and all subsequent ones, will be +ignored.) If any changes are made in the mapping files, you will need to +unmount and remount the sub-tree or reboot before they will take effect.} + +\end{itemize} + +\end{document} diff --git a/sbin/mount_union/Makefile b/sbin/mount_union/Makefile new file mode 100644 index 000000000000..a6b33af5ebf8 --- /dev/null +++ b/sbin/mount_union/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_union +SRCS= mount_union.c getmntopts.c +MAN8= mount_union.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +BINOWN= root +BINMODE=4555 + +.include diff --git a/sbin/mount_union/mount_union.8 b/sbin/mount_union/mount_union.8 new file mode 100644 index 000000000000..65a649722579 --- /dev/null +++ b/sbin/mount_union/mount_union.8 @@ -0,0 +1,204 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT_UNION 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_union +.Nd mount union filesystems +.Sh SYNOPSIS +.Nm mount_union +.Op Fl br +.Op Fl o Ar options +.Ar directory +.Ar uniondir +.Sh DESCRIPTION +The +.Nm mount_union +command +attaches +.Ar directory +above +.Ar uniondir +in such a way that the contents of both directory trees remain visible. +By default, +.Ar directory +becomes the +.Em upper +layer and +.Ar uniondir +becomes the +.Em lower +layer. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +Invert the default position, so that +.Ar directory +becomes the lower layer and +.Ar uniondir +becomes the upper layer. +However, +.Ar uniondir +remains the mount point. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl r +Hide the lower layer completely in the same way as mounting with +.Xr mount_lofs 8 +or +.Xr mount_null 8 . +.El +.Pp +To enforce filesystem security, the user mounting the filesystem +must be superuser or else have write permission on the mounted-on +directory. +.Pp +Filenames are looked up in the upper layer and then in the +lower layer. +If a directory is found in the lower layer, and there is no entry +in the upper layer, then a +.Em shadow +directory will be created in the upper layer. +It will be owned by the user who originally did the union mount, +with mode +.Dq rwxrwxrwx +(0777) modified by the umask in effect at that time. +.Pp +If a file exists in the upper layer then there is no way to access +a file with the same name in the lower layer. +If necessary, a combination of loopback and union mounts can be made +which will still allow the lower files to be accessed by a different +pathname. +.Pp +Except in the case of a directory, +access to an object is granted via the normal filesystem access checks. +For directories, the current user must have access to both the upper +and lower directories (should they both exist). +.Pp +Requests to create or modify objects in +.Ar uniondir +are passed to the upper layer with the exception of a few special cases. +An attempt to open for writing a file which exists in the lower layer +causes a copy of the +.Em entire +file to be made to the upper layer, and then for the upper layer copy +to be opened. +Similarly, an attempt to truncate a lower layer file to zero length +causes an empty file to be created in the upper layer. +Any other operation which would ultimately require modification to +the lower layer fails with +.Dv EROFS . +.Pp +The union filesystem manipulates the namespace, rather than +individual filesystems. +The union operation applies recursively down the directory tree +now rooted at +.Ar uniondir . +Thus any filesystems which are mounted under +.Ar uniondir +will take part in the union operation. +This differs from the +.Em union +option to +.Xr mount 8 +which only applies the union operation to the mount point itself, +and then only for lookups. +.Sh EXAMPLES +The commands +.Bd -literal -offset indent +mount -t cd9660 -o ro /dev/cd0a /usr/src +mount -t union -o /var/obj /usr/src +.Ed +.Pp +mount the CD-ROM drive +.Pa /dev/cd0a +on +.Pa /usr/src +and then attaches +.Pa /var/obj +on top. +For most purposes the effect of this is to make the +source tree appear writable +even though it is stored on a CD-ROM. +.Pp +The command +.Bd -literal -offset indent +mount -t union -o -b /sys $HOME/sys +.Ed +.Pp +attaches the system source tree below the +.Pa sys +directory in the user's home directory. +This allows individual users to make private changes +to the source, and build new kernels, without those +changes becoming visible to other users. +Note that the files in the lower layer remain +accessible via +.Pa /sys . +.Sh SEE ALSO +.Xr intro 2 , +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr mount_lofs 8 , +.Xr mount_null 8 +.Sh BUGS +Without whiteout support from the filesystem backing the upper layer, +there is no way that delete and rename operations on lower layer +objects can be done. +.Dv EROFS +is returned for this kind of operations along with any others +which would make modifications to the lower layer, such as +.Xr chmod 1 . +.Pp +Running +.Xr find 1 +over a union tree has the side-effect of creating +a tree of shadow directories in the upper layer. +.Sh HISTORY +The +.Nm mount_union +command first appeared in +.Bx 4.4 . diff --git a/sbin/mount_union/mount_union.c b/sbin/mount_union/mount_union.c new file mode 100644 index 000000000000..90d075b1dec5 --- /dev/null +++ b/sbin/mount_union/mount_union.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct union_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + args.mntflags = UNMNT_ABOVE; + while ((ch = getopt(argc, argv, "bo:r")) != EOF) + switch (ch) { + case 'b': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_BELOW; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'r': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_REPLACE; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_UNION, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_union [-br] [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile new file mode 100644 index 000000000000..a6b33af5ebf8 --- /dev/null +++ b/sbin/mount_unionfs/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.3 (Berkeley) 3/27/94 + +PROG= mount_union +SRCS= mount_union.c getmntopts.c +MAN8= mount_union.0 + +MOUNT= ${.CURDIR}/../mount +CFLAGS+= -I/sys -I${MOUNT} +.PATH: ${MOUNT} + +BINOWN= root +BINMODE=4555 + +.include diff --git a/sbin/mount_unionfs/mount_unionfs.8 b/sbin/mount_unionfs/mount_unionfs.8 new file mode 100644 index 000000000000..65a649722579 --- /dev/null +++ b/sbin/mount_unionfs/mount_unionfs.8 @@ -0,0 +1,204 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software donated to Berkeley by +.\" Jan-Simon Pendry. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94 +.\" +.Dd March 27, 1994 +.Dt MOUNT_UNION 8 +.Os BSD 4.4 +.Sh NAME +.Nm mount_union +.Nd mount union filesystems +.Sh SYNOPSIS +.Nm mount_union +.Op Fl br +.Op Fl o Ar options +.Ar directory +.Ar uniondir +.Sh DESCRIPTION +The +.Nm mount_union +command +attaches +.Ar directory +above +.Ar uniondir +in such a way that the contents of both directory trees remain visible. +By default, +.Ar directory +becomes the +.Em upper +layer and +.Ar uniondir +becomes the +.Em lower +layer. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +Invert the default position, so that +.Ar directory +becomes the lower layer and +.Ar uniondir +becomes the upper layer. +However, +.Ar uniondir +remains the mount point. +.It Fl o +Options are specified with a +.Fl o +flag followed by a comma separated string of options. +See the +.Xr mount 8 +man page for possible options and their meanings. +.It Fl r +Hide the lower layer completely in the same way as mounting with +.Xr mount_lofs 8 +or +.Xr mount_null 8 . +.El +.Pp +To enforce filesystem security, the user mounting the filesystem +must be superuser or else have write permission on the mounted-on +directory. +.Pp +Filenames are looked up in the upper layer and then in the +lower layer. +If a directory is found in the lower layer, and there is no entry +in the upper layer, then a +.Em shadow +directory will be created in the upper layer. +It will be owned by the user who originally did the union mount, +with mode +.Dq rwxrwxrwx +(0777) modified by the umask in effect at that time. +.Pp +If a file exists in the upper layer then there is no way to access +a file with the same name in the lower layer. +If necessary, a combination of loopback and union mounts can be made +which will still allow the lower files to be accessed by a different +pathname. +.Pp +Except in the case of a directory, +access to an object is granted via the normal filesystem access checks. +For directories, the current user must have access to both the upper +and lower directories (should they both exist). +.Pp +Requests to create or modify objects in +.Ar uniondir +are passed to the upper layer with the exception of a few special cases. +An attempt to open for writing a file which exists in the lower layer +causes a copy of the +.Em entire +file to be made to the upper layer, and then for the upper layer copy +to be opened. +Similarly, an attempt to truncate a lower layer file to zero length +causes an empty file to be created in the upper layer. +Any other operation which would ultimately require modification to +the lower layer fails with +.Dv EROFS . +.Pp +The union filesystem manipulates the namespace, rather than +individual filesystems. +The union operation applies recursively down the directory tree +now rooted at +.Ar uniondir . +Thus any filesystems which are mounted under +.Ar uniondir +will take part in the union operation. +This differs from the +.Em union +option to +.Xr mount 8 +which only applies the union operation to the mount point itself, +and then only for lookups. +.Sh EXAMPLES +The commands +.Bd -literal -offset indent +mount -t cd9660 -o ro /dev/cd0a /usr/src +mount -t union -o /var/obj /usr/src +.Ed +.Pp +mount the CD-ROM drive +.Pa /dev/cd0a +on +.Pa /usr/src +and then attaches +.Pa /var/obj +on top. +For most purposes the effect of this is to make the +source tree appear writable +even though it is stored on a CD-ROM. +.Pp +The command +.Bd -literal -offset indent +mount -t union -o -b /sys $HOME/sys +.Ed +.Pp +attaches the system source tree below the +.Pa sys +directory in the user's home directory. +This allows individual users to make private changes +to the source, and build new kernels, without those +changes becoming visible to other users. +Note that the files in the lower layer remain +accessible via +.Pa /sys . +.Sh SEE ALSO +.Xr intro 2 , +.Xr mount 2 , +.Xr unmount 2 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr mount_lofs 8 , +.Xr mount_null 8 +.Sh BUGS +Without whiteout support from the filesystem backing the upper layer, +there is no way that delete and rename operations on lower layer +objects can be done. +.Dv EROFS +is returned for this kind of operations along with any others +which would make modifications to the lower layer, such as +.Xr chmod 1 . +.Pp +Running +.Xr find 1 +over a union tree has the side-effect of creating +a tree of shadow directories in the upper layer. +.Sh HISTORY +The +.Nm mount_union +command first appeared in +.Bx 4.4 . diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c new file mode 100644 index 000000000000..90d075b1dec5 --- /dev/null +++ b/sbin/mount_unionfs/mount_unionfs.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; +#endif /* not lint */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mntopts.h" + +struct mntopt mopts[] = { + MOPT_STDOPTS, + { NULL } +}; + +int subdir __P((const char *, const char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct union_args args; + int ch, mntflags; + char target[MAXPATHLEN]; + + mntflags = 0; + args.mntflags = UNMNT_ABOVE; + while ((ch = getopt(argc, argv, "bo:r")) != EOF) + switch (ch) { + case 'b': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_BELOW; + break; + case 'o': + getmntopts(optarg, mopts, &mntflags); + break; + case 'r': + args.mntflags &= ~UNMNT_OPMASK; + args.mntflags |= UNMNT_REPLACE; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if (realpath(argv[0], target) == 0) + err(1, "%s", target); + + if (subdir(target, argv[1]) || subdir(argv[1], target)) + errx(1, "%s (%s) and %s are not distinct paths", + argv[0], target, argv[1]); + + args.target = target; + + if (mount(MOUNT_UNION, argv[1], mntflags, &args)) + err(1, NULL); + exit(0); +} + +int +subdir(p, dir) + const char *p; + const char *dir; +{ + int l; + + l = strlen(dir); + if (l <= 1) + return (1); + + if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) + return (1); + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: mount_union [-br] [-o options] target_fs mount_point\n"); + exit(1); +} diff --git a/sbin/mountd/Makefile b/sbin/mountd/Makefile new file mode 100644 index 000000000000..36e2a0572406 --- /dev/null +++ b/sbin/mountd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.3 (Berkeley) 1/25/94 + +PROG= mountd +CFLAGS+=-DNFS -DMFS -DCD9660 +MAN5= exports.0 netgroup.0 +MAN8= mountd.0 +DPADD= ${LIBRPC} +LDADD= -lrpc + +.include diff --git a/sbin/mountd/exports.5 b/sbin/mountd/exports.5 new file mode 100644 index 000000000000..d32527f71766 --- /dev/null +++ b/sbin/mountd/exports.5 @@ -0,0 +1,250 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)exports.5 8.2 (Berkeley) 1/28/94 +.\" +.Dd January 28, 1994 +.Dt EXPORTS 5 +.Os +.Sh NAME +.Nm exports +.Nd define remote mount points for +.Tn NFS +mount requests +.Sh SYNOPSIS +.Nm exports +.Sh DESCRIPTION +The +.Nm exports +file specifies remote mount points for the +.Tn NFS +mount protocol per the +.Tn NFS +server specification; see +.%T "Network File System Protocol Specification \\*(tNRFC\\*(sP 1094, Appendix A" . +.Pp +Each line in the file +(other than comment lines that begin with a #) +specifies the mount point(s) and export flags within one local server +filesystem for one or more hosts. +A host may be specified only once for each local filesystem on the +server and there may be only one default entry for each server +filesystem that applies to all other hosts. +The latter exports the filesystem to the ``world'' and should +be used only when the filesystem contains public information. +.Pp +In a mount entry, +the first field(s) specify the directory path(s) within a server filesystem +that can be mounted on by the corresponding client(s). +There are two forms of this specification. +The first is to list all mount points as absolute +directory paths separated by whitespace. +The second is to specify the pathname of the root of the filesystem +followed by the +.Fl alldirs +flag; +this form allows the host(s) to mount any directory within the filesystem. +The pathnames must not have any symbolic links in them and should not have +any "." or ".." components. +Mount points for a filesystem may appear on multiple lines each with +different sets of hosts and export options. +.Pp +The second component of a line specifies how the filesystem is to be +exported to the host set. +The option flags specify whether the filesystem +is exported read-only or read-write and how the client uid is mapped to +user credentials on the server. +.Pp +Export options are specified as follows: +.Pp +.Sm off +.Fl maproot No = Sy user +.Sm on +The credential of the specified user is used for remote access by root. +The credential includes all the groups to which the user is a member +on the local machine (see +.Xr id 1 ). +The user may be specified by name or number. +.Pp +.Sm off +.Fl maproot No = Sy user:group1:group2:... +.Sm on +The colon separated list is used to specify the precise credential +to be used for remote access by root. +The elements of the list may be either names or numbers. +Note that user: should be used to distinguish a credential containing +no groups from a complete credential for that user. +.Pp +.Sm off +.Fl mapall No = Sy user +.Sm on +or +.Sm off +.Fl mapall No = Sy user:group1:group2:... +.Sm on +specifies a mapping for all client uids (including root) +using the same semantics as +.Fl maproot . +.Pp +The option +.Fl r +is a synonym for +.Fl maproot +in an effort to be backward compatible with older export file formats. +.Pp +In the absence of +.Fl maproot +and +.Fl mapall +options, remote accesses by root will result in using a credential of -2:-2. +All other users will be mapped to their remote credential. +If a +.Fl maproot +option is given, +remote access by root will be mapped to that credential instead of -2:-2. +If a +.Fl mapall +option is given, +all users (including root) will be mapped to that credential in +place of their own. +.Pp +The +.Fl kerb +option specifies that the Kerberos authentication server should be +used to authenticate and map client credentials. +(Note that this is NOT Sun NFS compatible and +is supported for TCP transport only.) +.Pp +The +.Fl ro +option specifies that the filesystem should be exported read-only +(default read/write). +The option +.Fl o +is a synonym for +.Fl ro +in an effort to be backward compatible with older export file formats. +.Pp +The third component of a line specifies the host set to which the line applies. +The set may be specified in three ways. +The first way is to list the host name(s) separated by white space. +(Standard internet ``dot'' addresses may be used in place of names.) +The second way is to specify a ``netgroup'' as defined in the netgroup file (see +.Xr netgroup 5 ). +The third way is to specify an internet subnetwork using a network and +network mask that is defined as the set of all hosts with addresses within +the subnetwork. +This latter approach requires less overhead within the +kernel and is recommended for cases where the export line refers to a +large number of clients within an administrative subnet. +.Pp +The first two cases are specified by simply listing the name(s) separated +by whitespace. +All names are checked to see if they are ``netgroup'' names +first and are assumed to be hostnames otherwise. +Using the full domain specification for a hostname can normally +circumvent the problem of a host that has the same name as a netgroup. +The third case is specified by the flag +.Sm off +.Fl network No = Sy netname +.Sm on +and optionally +.Sm off +.Fl mask No = Sy netmask . +.Sm on +If the mask is not specified, it will default to the mask for that network +class (A, B or C; see +.Xr inet 5 ). +.Pp +For example: +.Bd -literal -offset indent +/usr /usr/local -maproot=0:10 friends +/usr -maproot=daemon grumpy.cis.uoguelph.ca 131.104.48.16 +/usr -ro -mapall=nobody +/u -maproot=bin: -network 131.104.48 -mask 255.255.255.0 +/u2 -maproot=root friends +/u2 -alldirs -kerb -network cis-net -mask cis-mask +.Ed +.Pp +Given that +.Sy /usr , +.Sy /u +and +.Sy /u2 +are +local filesystem mount points, the above example specifies the following: +.Sy /usr +is exported to hosts +.Em friends +where friends is specified in the netgroup file +with users mapped to their remote credentials and +root mapped to uid 0 and group 10. +It is exported read-write and the hosts in ``friends'' can mount either /usr +or /usr/local. +It is exported to +.Em 131.104.48.16 +and +.Em grumpy.cis.uoguelph.ca +with users mapped to their remote credentials and +root mapped to the user and groups associated with ``daemon''; +it is exported to the rest of the world as read-only with +all users mapped to the user and groups associated with ``nobody''. +.Pp +.Sy /u +is exported to all hosts on the subnetwork +.Em 131.104.48 +with root mapped to the uid for ``bin'' and with no group access. +.Pp +.Sy /u2 +is exported to the hosts in ``friends'' with root mapped to uid and groups +associated with ``root''; +it is exported to all hosts on network ``cis-net'' allowing mounts at any +directory within /u2 and mapping all uids to credentials for the principal +that is authenticated by a Kerberos ticket. +.Sh FILES +.Bl -tag -width /etc/exports -compact +.It Pa /etc/exports +The default remote mount-point file. +.El +.Sh SEE ALSO +.Xr netgroup 5 , +.Xr mountd 8 , +.Xr nfsd 8 , +.Xr showmount 8 +.Sh BUGS +The export options are tied to the local mount points in the kernel and +must be non-contradictory for any exported subdirectory of the local +server mount point. +It is recommended that all exported directories within the same server +filesystem be specified on adjacent lines going down the tree. +You cannot specify a hostname that is also the name of a netgroup. +Specifying the full domain specification for a hostname can normally +circumvent the problem. diff --git a/sbin/mountd/mountd.8 b/sbin/mountd/mountd.8 new file mode 100644 index 000000000000..47a6cfc5ebea --- /dev/null +++ b/sbin/mountd/mountd.8 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mountd.8 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt MOUNTD 8 +.Os +.Sh NAME +.Nm mountd +.Nd service remote +.Tn NFS +mount requests +.Sh SYNOPSIS +.Nm /sbin/mountd +.Op Fl n +.Op Ar exportsfile +.Sh DESCRIPTION +.Xr Mountd +is the server for +.Tn NFS +mount requests from other client machines. +.Xr Mountd +listens for service requests at the port indicated in the +.Tn NFS +server specification; see +.%T "Network File System Protocol Specification" , +RFC1094. +.Pp +Options and operands available for +.Nm mountd : +.Bl -tag -width Ds +.It Fl n +The +.Fl n +option allows non-root mount requests to be served. +This should only be specified if there are clients such as PC's, +that require it. +.It Ar exportsfile +The +.Ar exportsfile +argument specifies an alternate location +for the exports file. +.El +.Pp +When mountd is started, +it loads the export host addresses and options into the kernel +using the mount(2) system call. +After changing the exports file, +a hangup signal should be sent to the mountd daemon +to get it to reload the export information. +After sending the SIGHUP +(kill -HUP `cat /var/run/mountd.pid`), +check the syslog output to see if mountd logged any parsing +errors in the exports file. +.Sh FILES +.Bl -tag -width /var/run/mountd.pid -compact +.It Pa /etc/exports +the list of exported filesystems +.It Pa /var/run/mountd.pid +the pid of the currently running mountd +.El +.Sh SEE ALSO +.Xr nfsstat 1 , +.Xr exports 5 , +.Xr nfsd 8 , +.Xr portmap 8 , +.Xr showmount 8 +.Sh HISTORY +The +.Nm mountd +utility first appeared in 4.4BSD. diff --git a/sbin/mountd/mountd.c b/sbin/mountd/mountd.c new file mode 100644 index 000000000000..d7c31dfb4ea1 --- /dev/null +++ b/sbin/mountd/mountd.c @@ -0,0 +1,2005 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Herb Hasler and Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)mountd.c 8.8 (Berkeley) 2/20/94"; +#endif not lint + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef ISO +#include +#endif +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#ifdef DEBUG +#include +#endif + +/* + * Structures for keeping the mount list and export list + */ +struct mountlist { + struct mountlist *ml_next; + char ml_host[RPCMNT_NAMELEN+1]; + char ml_dirp[RPCMNT_PATHLEN+1]; +}; + +struct dirlist { + struct dirlist *dp_left; + struct dirlist *dp_right; + int dp_flag; + struct hostlist *dp_hosts; /* List of hosts this dir exported to */ + char dp_dirp[1]; /* Actually malloc'd to size of dir */ +}; +/* dp_flag bits */ +#define DP_DEFSET 0x1 + +struct exportlist { + struct exportlist *ex_next; + struct dirlist *ex_dirl; + struct dirlist *ex_defdir; + int ex_flag; + fsid_t ex_fs; + char *ex_fsdir; +}; +/* ex_flag bits */ +#define EX_LINKED 0x1 + +struct netmsk { + u_long nt_net; + u_long nt_mask; + char *nt_name; +}; + +union grouptypes { + struct hostent *gt_hostent; + struct netmsk gt_net; +#ifdef ISO + struct sockaddr_iso *gt_isoaddr; +#endif +}; + +struct grouplist { + int gr_type; + union grouptypes gr_ptr; + struct grouplist *gr_next; +}; +/* Group types */ +#define GT_NULL 0x0 +#define GT_HOST 0x1 +#define GT_NET 0x2 +#define GT_ISO 0x4 + +struct hostlist { + struct grouplist *ht_grp; + struct hostlist *ht_next; +}; + +/* Global defs */ +char *add_expdir __P((struct dirlist **, char *, int)); +void add_dlist __P((struct dirlist **, struct dirlist *, + struct grouplist *)); +void add_mlist __P((char *, char *)); +int check_dirpath __P((char *)); +int check_options __P((struct dirlist *)); +int chk_host __P((struct dirlist *, u_long, int *)); +void del_mlist __P((char *, char *)); +struct dirlist *dirp_search __P((struct dirlist *, char *)); +int do_mount __P((struct exportlist *, struct grouplist *, int, + struct ucred *, char *, int, struct statfs *)); +int do_opt __P((char **, char **, struct exportlist *, struct grouplist *, + int *, int *, struct ucred *)); +struct exportlist *ex_search __P((fsid_t *)); +struct exportlist *get_exp __P((void)); +void free_dir __P((struct dirlist *)); +void free_exp __P((struct exportlist *)); +void free_grp __P((struct grouplist *)); +void free_host __P((struct hostlist *)); +void get_exportlist __P((void)); +int get_host __P((char *, struct grouplist *)); +struct hostlist *get_ht __P((void)); +int get_line __P((void)); +void get_mountlist __P((void)); +int get_net __P((char *, struct netmsk *, int)); +void getexp_err __P((struct exportlist *, struct grouplist *)); +struct grouplist *get_grp __P((void)); +void hang_dirp __P((struct dirlist *, struct grouplist *, + struct exportlist *, int)); +void mntsrv __P((struct svc_req *, SVCXPRT *)); +void nextfield __P((char **, char **)); +void out_of_mem __P((void)); +void parsecred __P((char *, struct ucred *)); +int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); +int scan_tree __P((struct dirlist *, u_long)); +void send_umntall __P((void)); +int umntall_each __P((caddr_t, struct sockaddr_in *)); +int xdr_dir __P((XDR *, char *)); +int xdr_explist __P((XDR *, caddr_t)); +int xdr_fhs __P((XDR *, nfsv2fh_t *)); +int xdr_mlist __P((XDR *, caddr_t)); + +/* C library */ +int getnetgrent(); +void endnetgrent(); +void setnetgrent(); + +#ifdef ISO +struct iso_addr *iso_addr(); +#endif + +struct exportlist *exphead; +struct mountlist *mlhead; +struct grouplist *grphead; +char exname[MAXPATHLEN]; +struct ucred def_anon = { + 1, + (uid_t) -2, + 1, + { (gid_t) -2 } +}; +int root_only = 1; +int opt_flags; +/* Bits for above */ +#define OP_MAPROOT 0x01 +#define OP_MAPALL 0x02 +#define OP_KERB 0x04 +#define OP_MASK 0x08 +#define OP_NET 0x10 +#define OP_ISO 0x20 +#define OP_ALLDIRS 0x40 + +#ifdef DEBUG +int debug = 1; +void SYSLOG __P((int, const char *, ...)); +#define syslog SYSLOG +#else +int debug = 0; +#endif + +/* + * Mountd server for NFS mount protocol as described in: + * NFS: Network File System Protocol Specification, RFC1094, Appendix A + * The optional arguments are the exports file name + * default: _PATH_EXPORTS + * and "-n" to allow nonroot mount. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + SVCXPRT *transp; + int c; + + while ((c = getopt(argc, argv, "n")) != EOF) + switch (c) { + case 'n': + root_only = 0; + break; + default: + fprintf(stderr, "Usage: mountd [-n] [export_file]\n"); + exit(1); + }; + argc -= optind; + argv += optind; + grphead = (struct grouplist *)NULL; + exphead = (struct exportlist *)NULL; + mlhead = (struct mountlist *)NULL; + if (argc == 1) { + strncpy(exname, *argv, MAXPATHLEN-1); + exname[MAXPATHLEN-1] = '\0'; + } else + strcpy(exname, _PATH_EXPORTS); + openlog("mountd", LOG_PID, LOG_DAEMON); + if (debug) + fprintf(stderr,"Getting export list.\n"); + get_exportlist(); + if (debug) + fprintf(stderr,"Getting mount list.\n"); + get_mountlist(); + if (debug) + fprintf(stderr,"Here we go.\n"); + if (debug == 0) { + daemon(0, 0); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + } + signal(SIGHUP, (void (*) __P((int))) get_exportlist); + signal(SIGTERM, (void (*) __P((int))) send_umntall); + { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); + if (pidfile != NULL) { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); + } + } + if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) { + syslog(LOG_ERR, "Can't create socket"); + exit(1); + } + pmap_unset(RPCPROG_MNT, RPCMNT_VER1); + if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, + IPPROTO_UDP)) { + syslog(LOG_ERR, "Can't register mount"); + exit(1); + } + svc_run(); + syslog(LOG_ERR, "Mountd died"); + exit(1); +} + +/* + * The mount rpc service + */ +void +mntsrv(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + struct exportlist *ep; + struct dirlist *dp; + nfsv2fh_t nfh; + struct authunix_parms *ucr; + struct stat stb; + struct statfs fsb; + struct hostent *hp; + u_long saddr; + char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN]; + int bad = ENOENT, omask, defset; + uid_t uid = -2; + + /* Get authorization */ + switch (rqstp->rq_cred.oa_flavor) { + case AUTH_UNIX: + ucr = (struct authunix_parms *)rqstp->rq_clntcred; + uid = ucr->aup_uid; + break; + case AUTH_NULL: + default: + break; + } + + saddr = transp->xp_raddr.sin_addr.s_addr; + hp = (struct hostent *)NULL; + switch (rqstp->rq_proc) { + case NULLPROC: + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCMNT_MOUNT: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_getargs(transp, xdr_dir, rpcpath)) { + svcerr_decode(transp); + return; + } + + /* + * Get the real pathname and make sure it is a directory + * that exists. + */ + if (realpath(rpcpath, dirpath) == 0 || + stat(dirpath, &stb) < 0 || + (stb.st_mode & S_IFMT) != S_IFDIR || + statfs(dirpath, &fsb) < 0) { + chdir("/"); /* Just in case realpath doesn't */ + if (debug) + fprintf(stderr, "stat failed on %s\n", dirpath); + if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + return; + } + + /* Check in the exports list */ + omask = sigblock(sigmask(SIGHUP)); + ep = ex_search(&fsb.f_fsid); + defset = 0; + if (ep && (chk_host(ep->ex_defdir, saddr, &defset) || + ((dp = dirp_search(ep->ex_dirl, dirpath)) && + chk_host(dp, saddr, &defset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { + /* Get the file handle */ + bzero((caddr_t)&nfh, sizeof(nfh)); + if (getfh(dirpath, (fhandle_t *)&nfh) < 0) { + bad = errno; + syslog(LOG_ERR, "Can't get fh for %s", dirpath); + if (!svc_sendreply(transp, xdr_long, + (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + sigsetmask(omask); + return; + } + if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh)) + syslog(LOG_ERR, "Can't send reply"); + if (hp == NULL) + hp = gethostbyaddr((caddr_t)&saddr, + sizeof(saddr), AF_INET); + if (hp) + add_mlist(hp->h_name, dirpath); + else + add_mlist(inet_ntoa(transp->xp_raddr.sin_addr), + dirpath); + if (debug) + fprintf(stderr,"Mount successfull.\n"); + } else { + bad = EACCES; + if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + } + sigsetmask(omask); + return; + case RPCMNT_DUMP: + if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCMNT_UMOUNT: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_getargs(transp, xdr_dir, dirpath)) { + svcerr_decode(transp); + return; + } + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); + if (hp) + del_mlist(hp->h_name, dirpath); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath); + return; + case RPCMNT_UMNTALL: + if ((uid != 0 && root_only) || uid == -2) { + svcerr_weakauth(transp); + return; + } + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); + if (hp) + del_mlist(hp->h_name, (char *)NULL); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL); + return; + case RPCMNT_EXPORT: + if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + default: + svcerr_noproc(transp); + return; + } +} + +/* + * Xdr conversion for a dirpath string + */ +int +xdr_dir(xdrsp, dirp) + XDR *xdrsp; + char *dirp; +{ + return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); +} + +/* + * Xdr routine to generate fhstatus + */ +int +xdr_fhs(xdrsp, nfh) + XDR *xdrsp; + nfsv2fh_t *nfh; +{ + int ok = 0; + + if (!xdr_long(xdrsp, &ok)) + return (0); + return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH)); +} + +int +xdr_mlist(xdrsp, cp) + XDR *xdrsp; + caddr_t cp; +{ + struct mountlist *mlp; + int true = 1; + int false = 0; + char *strp; + + mlp = mlhead; + while (mlp) { + if (!xdr_bool(xdrsp, &true)) + return (0); + strp = &mlp->ml_host[0]; + if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) + return (0); + strp = &mlp->ml_dirp[0]; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (0); + mlp = mlp->ml_next; + } + if (!xdr_bool(xdrsp, &false)) + return (0); + return (1); +} + +/* + * Xdr conversion for export list + */ +int +xdr_explist(xdrsp, cp) + XDR *xdrsp; + caddr_t cp; +{ + struct exportlist *ep; + int false = 0; + int omask, putdef; + + omask = sigblock(sigmask(SIGHUP)); + ep = exphead; + while (ep) { + putdef = 0; + if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) + goto errout; + if (ep->ex_defdir && putdef == 0 && + put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, + &putdef)) + goto errout; + ep = ep->ex_next; + } + sigsetmask(omask); + if (!xdr_bool(xdrsp, &false)) + return (0); + return (1); +errout: + sigsetmask(omask); + return (0); +} + +/* + * Called from xdr_explist() to traverse the tree and export the + * directory paths. + */ +int +put_exlist(dp, xdrsp, adp, putdefp) + struct dirlist *dp; + XDR *xdrsp; + struct dirlist *adp; + int *putdefp; +{ + struct grouplist *grp; + struct hostlist *hp; + int true = 1; + int false = 0; + int gotalldir = 0; + char *strp; + + if (dp) { + if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) + return (1); + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = dp->dp_dirp; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (1); + if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { + gotalldir = 1; + *putdefp = 1; + } + if ((dp->dp_flag & DP_DEFSET) == 0 && + (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + if (grp->gr_type == GT_HOST) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_hostent->h_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } else if (grp->gr_type == GT_NET) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_net.nt_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } + hp = hp->ht_next; + if (gotalldir && hp == (struct hostlist *)NULL) { + hp = adp->dp_hosts; + gotalldir = 0; + } + } + } + if (!xdr_bool(xdrsp, &false)) + return (1); + if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) + return (1); + } + return (0); +} + +#define LINESIZ 10240 +char line[LINESIZ]; +FILE *exp_file; + +/* + * Get the export list + */ +void +get_exportlist() +{ + struct exportlist *ep, *ep2; + struct grouplist *grp, *tgrp; + struct exportlist **epp; + struct dirlist *dirhead; + struct statfs fsb, *fsp; + struct hostent *hpe; + struct ucred anon; + char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; + int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; + + /* + * First, get rid of the old list + */ + ep = exphead; + while (ep) { + ep2 = ep; + ep = ep->ex_next; + free_exp(ep2); + } + exphead = (struct exportlist *)NULL; + + grp = grphead; + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } + grphead = (struct grouplist *)NULL; + + /* + * And delete exports that are in the kernel for all local + * file systems. + * XXX: Should know how to handle all local exportable file systems + * instead of just MOUNT_UFS. + */ + num = getmntinfo(&fsp, MNT_NOWAIT); + for (i = 0; i < num; i++) { + union { + struct ufs_args ua; + struct iso_args ia; + struct mfs_args ma; + } targs; + + switch (fsp->f_type) { + case MOUNT_MFS: + case MOUNT_UFS: + case MOUNT_CD9660: + targs.ua.fspec = NULL; + targs.ua.export.ex_flags = MNT_DELEXPORT; + if (mount(fsp->f_type, fsp->f_mntonname, + fsp->f_flags | MNT_UPDATE, + (caddr_t)&targs) < 0) + syslog(LOG_ERR, "Can't delete exports for %s", + fsp->f_mntonname); + } + fsp++; + } + + /* + * Read in the exports file and build the list, calling + * mount() as we go along to push the export rules into the kernel. + */ + if ((exp_file = fopen(exname, "r")) == NULL) { + syslog(LOG_ERR, "Can't open %s", exname); + exit(2); + } + dirhead = (struct dirlist *)NULL; + while (get_line()) { + if (debug) + fprintf(stderr,"Got line %s\n",line); + cp = line; + nextfield(&cp, &endcp); + if (*cp == '#') + goto nextline; + + /* + * Set defaults. + */ + has_host = FALSE; + anon = def_anon; + exflags = MNT_EXPORTED; + got_nondir = 0; + opt_flags = 0; + ep = (struct exportlist *)NULL; + + /* + * Create new exports list entry + */ + len = endcp-cp; + tgrp = grp = get_grp(); + while (len > 0) { + if (len > RPCMNT_NAMELEN) { + getexp_err(ep, tgrp); + goto nextline; + } + if (*cp == '-') { + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + if (debug) + fprintf(stderr, "doing opt %s\n", cp); + got_nondir = 1; + if (do_opt(&cp, &endcp, ep, grp, &has_host, + &exflags, &anon)) { + getexp_err(ep, tgrp); + goto nextline; + } + } else if (*cp == '/') { + savedc = *endcp; + *endcp = '\0'; + if (check_dirpath(cp) && + statfs(cp, &fsb) >= 0) { + if (got_nondir) { + syslog(LOG_ERR, "Dirs must be first"); + getexp_err(ep, tgrp); + goto nextline; + } + if (ep) { + if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || + ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { + getexp_err(ep, tgrp); + goto nextline; + } + } else { + /* + * See if this directory is already + * in the list. + */ + ep = ex_search(&fsb.f_fsid); + if (ep == (struct exportlist *)NULL) { + ep = get_exp(); + ep->ex_fs = fsb.f_fsid; + ep->ex_fsdir = (char *) + malloc(strlen(fsb.f_mntonname) + 1); + if (ep->ex_fsdir) + strcpy(ep->ex_fsdir, + fsb.f_mntonname); + else + out_of_mem(); + if (debug) + fprintf(stderr, + "Making new ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } else if (debug) + fprintf(stderr, + "Found ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } + + /* + * Add dirpath to export mount point. + */ + dirp = add_expdir(&dirhead, cp, len); + dirplen = len; + } else { + getexp_err(ep, tgrp); + goto nextline; + } + *endcp = savedc; + } else { + savedc = *endcp; + *endcp = '\0'; + got_nondir = 1; + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Get the host or netgroup. + */ + setnetgrent(cp); + netgrp = getnetgrent(&hst, &usr, &dom); + do { + if (has_host) { + grp->gr_next = get_grp(); + grp = grp->gr_next; + } + if (netgrp) { + if (get_host(hst, grp)) { + syslog(LOG_ERR, "Bad netgroup %s", cp); + getexp_err(ep, tgrp); + goto nextline; + } + } else if (get_host(cp, grp)) { + getexp_err(ep, tgrp); + goto nextline; + } + has_host = TRUE; + } while (netgrp && getnetgrent(&hst, &usr, &dom)); + endnetgrent(); + *endcp = savedc; + } + cp = endcp; + nextfield(&cp, &endcp); + len = endcp - cp; + } + if (check_options(dirhead)) { + getexp_err(ep, tgrp); + goto nextline; + } + if (!has_host) { + grp->gr_type = GT_HOST; + if (debug) + fprintf(stderr,"Adding a default entry\n"); + /* add a default group and make the grp list NULL */ + hpe = (struct hostent *)malloc(sizeof(struct hostent)); + if (hpe == (struct hostent *)NULL) + out_of_mem(); + hpe->h_name = "Default"; + hpe->h_addrtype = AF_INET; + hpe->h_length = sizeof (u_long); + hpe->h_addr_list = (char **)NULL; + grp->gr_ptr.gt_hostent = hpe; + + /* + * Don't allow a network export coincide with a list of + * host(s) on the same line. + */ + } else if ((opt_flags & OP_NET) && tgrp->gr_next) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Loop through hosts, pushing the exports into the kernel. + * After loop, tgrp points to the start of the list and + * grp points to the last entry in the list. + */ + grp = tgrp; + do { + if (do_mount(ep, grp, exflags, &anon, dirp, + dirplen, &fsb)) { + getexp_err(ep, tgrp); + goto nextline; + } + } while (grp->gr_next && (grp = grp->gr_next)); + + /* + * Success. Update the data structures. + */ + if (has_host) { + hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS)); + grp->gr_next = grphead; + grphead = tgrp; + } else { + hang_dirp(dirhead, (struct grouplist *)NULL, ep, + (opt_flags & OP_ALLDIRS)); + free_grp(grp); + } + dirhead = (struct dirlist *)NULL; + if ((ep->ex_flag & EX_LINKED) == 0) { + ep2 = exphead; + epp = &exphead; + + /* + * Insert in the list in alphabetical order. + */ + while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { + epp = &ep2->ex_next; + ep2 = ep2->ex_next; + } + if (ep2) + ep->ex_next = ep2; + *epp = ep; + ep->ex_flag |= EX_LINKED; + } +nextline: + if (dirhead) { + free_dir(dirhead); + dirhead = (struct dirlist *)NULL; + } + } + fclose(exp_file); +} + +/* + * Allocate an export list element + */ +struct exportlist * +get_exp() +{ + struct exportlist *ep; + + ep = (struct exportlist *)malloc(sizeof (struct exportlist)); + if (ep == (struct exportlist *)NULL) + out_of_mem(); + bzero((caddr_t)ep, sizeof (struct exportlist)); + return (ep); +} + +/* + * Allocate a group list element + */ +struct grouplist * +get_grp() +{ + struct grouplist *gp; + + gp = (struct grouplist *)malloc(sizeof (struct grouplist)); + if (gp == (struct grouplist *)NULL) + out_of_mem(); + bzero((caddr_t)gp, sizeof (struct grouplist)); + return (gp); +} + +/* + * Clean up upon an error in get_exportlist(). + */ +void +getexp_err(ep, grp) + struct exportlist *ep; + struct grouplist *grp; +{ + struct grouplist *tgrp; + + syslog(LOG_ERR, "Bad exports list line %s", line); + if (ep && (ep->ex_flag & EX_LINKED) == 0) + free_exp(ep); + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } +} + +/* + * Search the export list for a matching fs. + */ +struct exportlist * +ex_search(fsid) + fsid_t *fsid; +{ + struct exportlist *ep; + + ep = exphead; + while (ep) { + if (ep->ex_fs.val[0] == fsid->val[0] && + ep->ex_fs.val[1] == fsid->val[1]) + return (ep); + ep = ep->ex_next; + } + return (ep); +} + +/* + * Add a directory path to the list. + */ +char * +add_expdir(dpp, cp, len) + struct dirlist **dpp; + char *cp; + int len; +{ + struct dirlist *dp; + + dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); + dp->dp_left = *dpp; + dp->dp_right = (struct dirlist *)NULL; + dp->dp_flag = 0; + dp->dp_hosts = (struct hostlist *)NULL; + strcpy(dp->dp_dirp, cp); + *dpp = dp; + return (dp->dp_dirp); +} + +/* + * Hang the dir list element off the dirpath binary tree as required + * and update the entry for host. + */ +void +hang_dirp(dp, grp, ep, alldirs) + struct dirlist *dp; + struct grouplist *grp; + struct exportlist *ep; + int alldirs; +{ + struct hostlist *hp; + struct dirlist *dp2; + + if (alldirs) { + if (ep->ex_defdir) + free((caddr_t)dp); + else + ep->ex_defdir = dp; + if (grp == (struct grouplist *)NULL) + ep->ex_defdir->dp_flag |= DP_DEFSET; + else while (grp) { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = ep->ex_defdir->dp_hosts; + ep->ex_defdir->dp_hosts = hp; + grp = grp->gr_next; + } + } else { + + /* + * Loop throught the directories adding them to the tree. + */ + while (dp) { + dp2 = dp->dp_left; + add_dlist(&ep->ex_dirl, dp, grp); + dp = dp2; + } + } +} + +/* + * Traverse the binary tree either updating a node that is already there + * for the new directory or adding the new node. + */ +void +add_dlist(dpp, newdp, grp) + struct dirlist **dpp; + struct dirlist *newdp; + struct grouplist *grp; +{ + struct dirlist *dp; + struct hostlist *hp; + int cmp; + + dp = *dpp; + if (dp) { + cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); + if (cmp > 0) { + add_dlist(&dp->dp_left, newdp, grp); + return; + } else if (cmp < 0) { + add_dlist(&dp->dp_right, newdp, grp); + return; + } else + free((caddr_t)newdp); + } else { + dp = newdp; + dp->dp_left = (struct dirlist *)NULL; + *dpp = dp; + } + if (grp) { + + /* + * Hang all of the host(s) off of the directory point. + */ + do { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = dp->dp_hosts; + dp->dp_hosts = hp; + grp = grp->gr_next; + } while (grp); + } else + dp->dp_flag |= DP_DEFSET; +} + +/* + * Search for a dirpath on the export point. + */ +struct dirlist * +dirp_search(dp, dirpath) + struct dirlist *dp; + char *dirpath; +{ + int cmp; + + if (dp) { + cmp = strcmp(dp->dp_dirp, dirpath); + if (cmp > 0) + return (dirp_search(dp->dp_left, dirpath)); + else if (cmp < 0) + return (dirp_search(dp->dp_right, dirpath)); + else + return (dp); + } + return (dp); +} + +/* + * Scan for a host match in a directory tree. + */ +int +chk_host(dp, saddr, defsetp) + struct dirlist *dp; + u_long saddr; + int *defsetp; +{ + struct hostlist *hp; + struct grouplist *grp; + u_long **addrp; + + if (dp) { + if (dp->dp_flag & DP_DEFSET) + *defsetp = 1; + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + switch (grp->gr_type) { + case GT_HOST: + addrp = (u_long **) + grp->gr_ptr.gt_hostent->h_addr_list; + while (*addrp) { + if (**addrp == saddr) + return (1); + addrp++; + } + break; + case GT_NET: + if ((saddr & grp->gr_ptr.gt_net.nt_mask) == + grp->gr_ptr.gt_net.nt_net) + return (1); + break; + }; + hp = hp->ht_next; + } + } + return (0); +} + +/* + * Scan tree for a host that matches the address. + */ +int +scan_tree(dp, saddr) + struct dirlist *dp; + u_long saddr; +{ + int defset; + + if (dp) { + if (scan_tree(dp->dp_left, saddr)) + return (1); + if (chk_host(dp, saddr, &defset)) + return (1); + if (scan_tree(dp->dp_right, saddr)) + return (1); + } + return (0); +} + +/* + * Traverse the dirlist tree and free it up. + */ +void +free_dir(dp) + struct dirlist *dp; +{ + + if (dp) { + free_dir(dp->dp_left); + free_dir(dp->dp_right); + free_host(dp->dp_hosts); + free((caddr_t)dp); + } +} + +/* + * Parse the option string and update fields. + * Option arguments may either be -