static int devfs_vop_nrmdir(struct vop_nrmdir_args *ap) { struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); struct devfs_node *node; struct namecache *ncp; int error = ENOENT; ncp = ap->a_nch->ncp; if (!devfs_node_is_accessible(dnode)) return ENOENT; lockmgr(&devfs_lock, LK_EXCLUSIVE); if ((dnode->node_type != Nroot) && (dnode->node_type != Ndir)) goto out; TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(dnode), link) { if (ncp->nc_nlen != node->d_dir.d_namlen) continue; if (memcmp(ncp->nc_name, node->d_dir.d_name, ncp->nc_nlen)) continue; /* * only allow removal of user created dirs */ if ((node->flags & DEVFS_USER_CREATED) == 0) { error = EPERM; goto out; } else if (node->node_type != Ndir) { error = ENOTDIR; goto out; } else if (node->nchildren > 2) { error = ENOTEMPTY; goto out; } else { if (node->v_node) cache_inval_vp(node->v_node, CINV_DESTROY); devfs_unlinkp(node); error = 0; break; } } cache_unlink(ap->a_nch); out: lockmgr(&devfs_lock, LK_RELEASE); return error; }
/* * vop_compat_nrmdir { struct nchandle *a_nch, XXX STOPGAP FUNCTION * struct vnode *dvp, * struct ucred *a_cred } */ int vop_compat_nrmdir(struct vop_nrmdir_args *ap) { struct thread *td = curthread; struct componentname cnp; struct nchandle *nch; struct namecache *ncp; struct vnode *dvp; struct vnode *vp; int error; /* * Sanity checks, get a locked directory vnode. */ nch = ap->a_nch; /* locked namecache node */ ncp = nch->ncp; dvp = ap->a_dvp; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name); return(EAGAIN); } /* * Setup the cnp for a traditional vop_old_lookup() call. The lookup * caches all information required to delete the entry in the * directory inode. We expect a return code of 0 for the DELETE * case (meaning that a vp has been found). The cnp must simulated * a saved-name situation. */ bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = NAMEI_DELETE; cnp.cn_flags = CNP_LOCKPARENT; cnp.cn_nameptr = ncp->nc_name; cnp.cn_namelen = ncp->nc_nlen; cnp.cn_cred = ap->a_cred; cnp.cn_td = td; /* * The vnode must be a directory and must not represent the * current directory. */ vp = NULL; error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp); if (error == 0 && vp->v_type != VDIR) error = ENOTDIR; if (error == 0 && vp == dvp) error = EINVAL; if (error == 0 && (vp->v_flag & VROOT)) error = EBUSY; if (error == 0) { KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0); error = VOP_OLD_RMDIR(dvp, vp, &cnp); /* * Note that this invalidation will cause any process * currently CD'd into the directory being removed to be * disconnected from the topology and not be able to ".." * back out. */ if (error == 0) { cache_inval(nch, CINV_DESTROY); cache_inval_vp(vp, CINV_DESTROY); } } if (vp) { if (dvp == vp) vrele(vp); else vput(vp); } if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0) vn_unlock(dvp); vrele(dvp); return (error); }
/* * vop_compat_nremove { struct nchandle *a_nch, XXX STOPGAP FUNCTION * struct vnode *a_dvp, * struct ucred *a_cred } */ int vop_compat_nremove(struct vop_nremove_args *ap) { struct thread *td = curthread; struct componentname cnp; struct nchandle *nch; struct namecache *ncp; struct vnode *dvp; struct vnode *vp; int error; /* * Sanity checks, get a locked directory vnode. */ nch = ap->a_nch; /* locked namecache node */ ncp = nch->ncp; dvp = ap->a_dvp; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name); return(EAGAIN); } /* * Setup the cnp for a traditional vop_old_lookup() call. The lookup * caches all information required to delete the entry in the * directory inode. We expect a return code of 0 for the DELETE * case (meaning that a vp has been found). The cnp must simulated * a saved-name situation. */ bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = NAMEI_DELETE; cnp.cn_flags = CNP_LOCKPARENT; cnp.cn_nameptr = ncp->nc_name; cnp.cn_namelen = ncp->nc_nlen; cnp.cn_cred = ap->a_cred; cnp.cn_td = td; /* * The vnode must be a directory and must not represent the * current directory. */ vp = NULL; error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp); if (error == 0 && vp->v_type == VDIR) error = EPERM; if (error == 0) { KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0); error = VOP_OLD_REMOVE(dvp, vp, &cnp); if (error == 0) { cache_setunresolved(nch); cache_setvp(nch, NULL); cache_inval_vp(vp, CINV_DESTROY); } } if (vp) { if (dvp == vp) vrele(vp); else vput(vp); } if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0) vn_unlock(dvp); vrele(dvp); return (error); }