/* * XXX: can't use callremove now because can't catch setbacks with * it due to lack of a pnode argument. */ static int puffs_vnop_remove(struct vop_nremove_args *ap) { PUFFS_MSG_VARS(vn, remove); struct vnode *dvp = ap->a_dvp; struct vnode *vp; struct puffs_node *dpn = VPTOPP(dvp); struct puffs_node *pn; struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(mp); int error; if (!EXISTSOP(pmp, REMOVE)) return EOPNOTSUPP; error = vget(dvp, LK_EXCLUSIVE); if (error != 0) { DPRINTF(("puffs_vnop_remove: EAGAIN on parent vnode %p %s\n", dvp, ncp->nc_name)); return EAGAIN; } error = cache_vget(nch, cred, LK_EXCLUSIVE, &vp); if (error != 0) { DPRINTF(("puffs_vnop_remove: cache_vget error: %p %s\n", dvp, ncp->nc_name)); return EAGAIN; } if (vp->v_type == VDIR) { error = EISDIR; goto out; } pn = VPTOPP(vp); PUFFS_MSG_ALLOC(vn, remove); remove_msg->pvnr_cookie_targ = VPTOPNC(vp); puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_remove, PUFFSOP_VN, PUFFS_VN_REMOVE, VPTOPNC(dvp)); puffs_msg_enqueue(pmp, park_remove); error = puffs_msg_wait2(pmp, park_remove, dpn, pn); PUFFS_MSG_RELEASE(remove); error = checkerr(pmp, error, __func__); out: vput(dvp); vn_unlock(vp); if (error == 0) cache_unlink(nch); vrele(vp); return error; }
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; }
static int tmpfs_nremove(struct vop_nremove_args *v) { struct vnode *dvp = v->a_dvp; struct namecache *ncp = v->a_nch->ncp; struct vnode *vp; int error; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; struct mount *mp; mp = dvp->v_mount; /* * We have to acquire the vp from v->a_nch because we will likely * unresolve the namecache entry, and a vrele/vput is needed to * trigger the tmpfs_inactive/tmpfs_reclaim sequence. * * We have to use vget to clear any inactive state on the vnode, * otherwise the vnode may remain inactive and thus tmpfs_inactive * will not get called when we release it. */ error = cache_vget(v->a_nch, v->a_cred, LK_SHARED, &vp); KKASSERT(vp->v_mount == dvp->v_mount); KKASSERT(error == 0); vn_unlock(vp); if (vp->v_type == VDIR) { error = EISDIR; goto out2; } dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_NODE(vp); tmp = VFS_TO_TMPFS(vp->v_mount); TMPFS_NODE_LOCK(dnode); de = tmpfs_dir_lookup(dnode, node, ncp); if (de == NULL) { error = ENOENT; goto out; } /* Files marked as immutable or append-only cannot be deleted. */ if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) || (dnode->tn_flags & APPEND)) { error = EPERM; goto out; } /* Remove the entry from the directory; as it is a file, we do not * have to change the number of hard links of the directory. */ tmpfs_dir_detach(dnode, de); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de); if (node->tn_links > 0) { TMPFS_NODE_LOCK(node); node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(node); } cache_unlink(v->a_nch); tmpfs_knote(vp, NOTE_DELETE); error = 0; out: TMPFS_NODE_UNLOCK(dnode); if (error == 0) tmpfs_knote(dvp, NOTE_WRITE); out2: vrele(vp); return error; }
static int tmpfs_nrmdir(struct vop_nrmdir_args *v) { struct vnode *dvp = v->a_dvp; struct namecache *ncp = v->a_nch->ncp; struct vnode *vp; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; struct mount *mp; int error; mp = dvp->v_mount; /* * We have to acquire the vp from v->a_nch because we will likely * unresolve the namecache entry, and a vrele/vput is needed to * trigger the tmpfs_inactive/tmpfs_reclaim sequence. * * We have to use vget to clear any inactive state on the vnode, * otherwise the vnode may remain inactive and thus tmpfs_inactive * will not get called when we release it. */ error = cache_vget(v->a_nch, v->a_cred, LK_SHARED, &vp); KKASSERT(error == 0); vn_unlock(vp); /* * Prevalidate so we don't hit an assertion later */ if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } tmp = VFS_TO_TMPFS(dvp->v_mount); dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_DIR(vp); /* * Directories with more than two entries ('.' and '..') cannot * be removed. */ if (node->tn_size > 0) { error = ENOTEMPTY; goto out; } if ((dnode->tn_flags & APPEND) || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* * This invariant holds only if we are not trying to * remove "..". We checked for that above so this is safe now. */ KKASSERT(node->tn_dir.tn_parent == dnode); /* * Get the directory entry associated with node (vp). This * was filled by tmpfs_lookup while looking up the entry. */ TMPFS_NODE_LOCK(dnode); de = tmpfs_dir_lookup(dnode, node, ncp); KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen)); /* Check flags to see if we are allowed to remove the directory. */ if ((dnode->tn_flags & APPEND) || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { error = EPERM; TMPFS_NODE_UNLOCK(dnode); goto out; } /* Detach the directory entry from the directory (dnode). */ tmpfs_dir_detach(dnode, de); TMPFS_NODE_UNLOCK(dnode); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); /* * Must set parent linkage to NULL (tested by ncreate to disallow * the creation of new files/dirs in a deleted directory) */ node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(node); TMPFS_NODE_UNLOCK(dnode); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de); /* Release the deleted vnode (will destroy the node, notify * interested parties and clean it from the cache). */ TMPFS_NODE_LOCK(dnode); dnode->tn_status |= TMPFS_NODE_CHANGED; TMPFS_NODE_UNLOCK(dnode); tmpfs_update(dvp); cache_unlink(v->a_nch); tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK); error = 0; out: vrele(vp); return error; }