Example #1
0
/*
 * 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;
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}