diff options
author | mpp <mpp@FreeBSD.org> | 1995-07-31 08:52:02 +0000 |
---|---|---|
committer | mpp <mpp@FreeBSD.org> | 1995-07-31 08:52:02 +0000 |
commit | b772633f528f59e50fa9f278e06e5e7634e1d28e (patch) | |
tree | 26b31480ab27b28e066df786f2a51a10d9d61f30 /sys | |
parent | 1214917908a1e5a327d11ab75cf2bf387d14abb9 (diff) | |
download | FreeBSD-src-b772633f528f59e50fa9f278e06e5e7634e1d28e.zip FreeBSD-src-b772633f528f59e50fa9f278e06e5e7634e1d28e.tar.gz |
Fix various kernfs file system problems, which can result in
umountable file systems, hung processes, or system panics:
- Some operations could return without decrementing the vnode
reference count.
- Some operations could leave the vnode locked.
- Generalize the /kern/rootdev & rrootdev files so that they
are no longer special cased in kernfs_lookup().
Note: procfs, fdescfs, and most of the other miscfs/* file systems
also suffer from the same type of problems and I will work on
fixing them one at a time.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/miscfs/kernfs/kernfs_vnops.c | 103 |
1 files changed, 61 insertions, 42 deletions
diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c index a9bf9e7..7dd0ec9 100644 --- a/sys/miscfs/kernfs/kernfs_vnops.c +++ b/sys/miscfs/kernfs/kernfs_vnops.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)kernfs_vnops.c 8.6 (Berkeley) 2/10/94 - * $Id: kernfs_vnops.c,v 1.7 1994/11/15 20:30:56 jkh Exp $ + * $Id: kernfs_vnops.c,v 1.8 1995/05/25 01:35:16 davidg Exp $ */ /* @@ -78,26 +78,27 @@ struct kern_target { int kt_tag; int kt_rw; int kt_vtype; + struct vnode **kt_vp; } kern_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ - /* name data tag ro/rw */ - { ".", 0, KTT_NULL, VREAD, VDIR }, - { "..", 0, KTT_NULL, VREAD, VDIR }, - { "boottime", &boottime.tv_sec, KTT_INT, VREAD, VREG }, - { "copyright", copyright, KTT_STRING, VREAD, VREG }, - { "hostname", 0, KTT_HOSTNAME, VREAD|VWRITE, VREG }, - { "bootfile", 0, KTT_BOOTFILE, VREAD, VREG }, - { "hz", &hz, KTT_INT, VREAD, VREG }, - { "loadavg", 0, KTT_AVENRUN, VREAD, VREG }, - { "pagesize", &cnt.v_page_size, KTT_INT, VREAD, VREG }, - { "physmem", &physmem, KTT_INT, VREAD, VREG }, + /* name data tag ro/rw type vnodep*/ + { ".", 0, KTT_NULL, VREAD, VDIR, NULL }, + { "..", 0, KTT_NULL, VREAD, VDIR, NULL }, + { "boottime", &boottime.tv_sec, KTT_INT, VREAD, VREG, NULL }, + { "copyright", copyright, KTT_STRING, VREAD, VREG, NULL }, + { "hostname", 0, KTT_HOSTNAME,VREAD|VWRITE,VREG, NULL }, + { "bootfile", 0, KTT_BOOTFILE, VREAD, VREG, NULL }, + { "hz", &hz, KTT_INT, VREAD, VREG, NULL }, + { "loadavg", 0, KTT_AVENRUN, VREAD, VREG, NULL }, + { "pagesize", &cnt.v_page_size, KTT_INT, VREAD, VREG, NULL }, + { "physmem", &physmem, KTT_INT, VREAD, VREG, NULL }, #if 0 - { "root", 0, KTT_NULL, VREAD, VDIR }, + { "root", 0, KTT_NULL, VREAD, VDIR, &rootdir}, #endif - { "rootdev", 0, KTT_NULL, VREAD, VBLK }, - { "rrootdev", 0, KTT_NULL, VREAD, VCHR }, - { "time", 0, KTT_TIME, VREAD, VREG }, - { "version", version, KTT_STRING, VREAD, VREG }, + { "rootdev", 0, KTT_NULL, VREAD, VBLK, &rootvp }, + { "rrootdev", 0, KTT_NULL, VREAD, VCHR, &rrootvp}, + { "time", 0, KTT_TIME, VREAD, VREG, NULL }, + { "version", version, KTT_STRING, VREAD, VREG, NULL }, }; static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); @@ -214,6 +215,7 @@ kernfs_lookup(ap) struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct vnode *fvp; + int nameiop = cnp->cn_nameiop; int error, i; char *pname; @@ -241,30 +243,6 @@ kernfs_lookup(ap) } #endif - /* - * /kern/rootdev is the root device - */ - if (cnp->cn_namelen == 7 && bcmp(pname, "rootdev", 7) == 0) { - *vpp = rootvp; - VREF(rootvp); - VOP_LOCK(rootvp); - return (0); - } - - /* - * /kern/rrootdev is the raw root device - */ - if (cnp->cn_namelen == 8 && bcmp(pname, "rrootdev", 8) == 0) { - if (rrootvp) { - *vpp = rrootvp; - VREF(rrootvp); - VOP_LOCK(rrootvp); - return (0); - } - error = ENXIO; - goto bad; - } - error = ENOENT; for (i = 0; i < nkern_targets; i++) { @@ -280,8 +258,49 @@ kernfs_lookup(ap) printf("kernfs_lookup: i = %d, error = %d\n", i, error); #endif - if (error) + /* + * If the name wasn't found, and this is not a LOOKUP + * request, we return EOPNOTSUPP so that the initial namei() + * fails and the higher level routines will not try to call + * our VOP_* functions. + */ + if (error) { + if (nameiop != LOOKUP) + error = EOPNOTSUPP; goto bad; + } + + /* + * DELETE requests are not supported. + */ + if (nameiop == DELETE) { + error = EOPNOTSUPP; + goto bad; + } + + /* + * Allow CREATE requests if the name in question can + * be written to. This allows open(name, O_RDWR | O_CREAT) + * to work. Otherwise CREATE requests are not supported. + */ + if (nameiop == CREATE && (kern_targets[i].kt_rw & VWRITE == 0)) { + error = EOPNOTSUPP; + goto bad; + } + + /* + * Check if this name has already has a vnode associated with it. + */ + if (kern_targets[i].kt_vp) { + if (*kern_targets[i].kt_vp) { + *vpp = *kern_targets[i].kt_vp; + VREF(*vpp); + VOP_LOCK(*vpp); + return (0); + } + error = ENXIO; + goto bad; + } #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: allocate new vnode\n"); |