Exemplo n.º 1
0
/*
 * vop_compat_nrename { struct nchandle *a_fnch, 	XXX STOPGAP FUNCTION
 *			struct nchandle *a_tnch,
 *			struct ucred *a_cred }
 *
 * This is a fairly difficult procedure.  The old VOP_OLD_RENAME requires that
 * the source directory and vnode be unlocked and the target directory and
 * vnode (if it exists) be locked.  All arguments will be vrele'd and 
 * the targets will also be unlocked regardless of the return code.
 */
int
vop_compat_nrename(struct vop_nrename_args *ap)
{
	struct thread *td = curthread;
	struct componentname fcnp;
	struct componentname tcnp;
	struct nchandle *fnch;
	struct nchandle *tnch;
	struct namecache *fncp;
	struct namecache *tncp;
	struct vnode *fdvp, *fvp;
	struct vnode *tdvp, *tvp;
	int error;

	/*
	 * Sanity checks, get referenced vnodes representing the source.
	 */
	fnch = ap->a_fnch;		/* locked namecache node */
	fncp = fnch->ncp;
	fdvp = ap->a_fdvp;

	/*
	 * Temporarily lock the source directory and lookup in DELETE mode to
	 * check permissions.  XXX delete permissions should have been
	 * checked by nlookup(), we need to add NLC_DELETE for delete
	 * checking.  It is unclear whether VFS's require the directory setup
	 * info NAMEI_DELETE causes to be stored in the fdvp's inode, but
	 * since it isn't locked and since UFS always does a relookup of
	 * the source, it is believed that the only side effect that matters
	 * is the permissions check.
	 */
	if ((error = vget(fdvp, LK_EXCLUSIVE)) != 0) {
		kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
			fncp, fncp->nc_name);
		return(EAGAIN);
	}

	bzero(&fcnp, sizeof(fcnp));
	fcnp.cn_nameiop = NAMEI_DELETE;
	fcnp.cn_flags = CNP_LOCKPARENT;
	fcnp.cn_nameptr = fncp->nc_name;
	fcnp.cn_namelen = fncp->nc_nlen;
	fcnp.cn_cred = ap->a_cred;
	fcnp.cn_td = td;

	/*
	 * note: vop_old_lookup (i.e. VOP_OLD_LOOKUP) always returns a locked
	 * fvp.
	 */
	fvp = NULL;
	error = vop_old_lookup(ap->a_head.a_ops, fdvp, &fvp, &fcnp);
	if (error == 0 && (fvp->v_flag & VROOT)) {
		vput(fvp);	/* as if vop_old_lookup had failed */
		error = EBUSY;
	}
	if ((fcnp.cn_flags & CNP_PDIRUNLOCK) == 0) {
		fcnp.cn_flags |= CNP_PDIRUNLOCK;
		vn_unlock(fdvp);
	}
	if (error) {
		vrele(fdvp);
		return (error);
	}
	vn_unlock(fvp);

	/*
	 * fdvp and fvp are now referenced and unlocked.
	 *
	 * Get a locked directory vnode for the target and lookup the target
	 * in CREATE mode so it places the required information in the
	 * directory inode.
	 */
	tnch = ap->a_tnch;		/* locked namecache node */
	tncp = tnch->ncp;
	tdvp = ap->a_tdvp;
	if (error) {
		vrele(fdvp);
		vrele(fvp);
		return (error);
	}
	if ((error = vget(tdvp, LK_EXCLUSIVE)) != 0) {
		kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
			tncp, tncp->nc_name);
		vrele(fdvp);
		vrele(fvp);
		return(EAGAIN);
	}

	/*
	 * Setup the cnp for a traditional vop_old_lookup() call.  The lookup
	 * caches all information required to create the entry in the
	 * target directory inode.
	 */
	bzero(&tcnp, sizeof(tcnp));
	tcnp.cn_nameiop = NAMEI_RENAME;
	tcnp.cn_flags = CNP_LOCKPARENT;
	tcnp.cn_nameptr = tncp->nc_name;
	tcnp.cn_namelen = tncp->nc_nlen;
	tcnp.cn_cred = ap->a_cred;
	tcnp.cn_td = td;

	tvp = NULL;
	error = vop_old_lookup(ap->a_head.a_ops, tdvp, &tvp, &tcnp);

	if (error == EJUSTRETURN) {
		/*
		 * Target does not exist.  tvp should be NULL.
		 */
		KKASSERT(tvp == NULL);
		KKASSERT((tcnp.cn_flags & CNP_PDIRUNLOCK) == 0);
		error = VOP_OLD_RENAME(fdvp, fvp, &fcnp, tdvp, tvp, &tcnp);
		if (error == 0)
			cache_rename(fnch, tnch);
	} else if (error == 0) {
		/*
		 * Target exists.  VOP_OLD_RENAME should correctly delete the
		 * target.
		 */
		KKASSERT((tcnp.cn_flags & CNP_PDIRUNLOCK) == 0);
		error = VOP_OLD_RENAME(fdvp, fvp, &fcnp, tdvp, tvp, &tcnp);
		if (error == 0)
			cache_rename(fnch, tnch);
	} else {
		vrele(fdvp);
		vrele(fvp);
		if (tcnp.cn_flags & CNP_PDIRUNLOCK)
			vrele(tdvp);
		else
			vput(tdvp);
	}
	return (error);
}
Exemplo n.º 2
0
static int
tmpfs_nrename(struct vop_nrename_args *v)
{
	struct vnode *fdvp = v->a_fdvp;
	struct namecache *fncp = v->a_fnch->ncp;
	struct vnode *fvp = fncp->nc_vp;
	struct vnode *tdvp = v->a_tdvp;
	struct namecache *tncp = v->a_tnch->ncp;
	struct vnode *tvp;
	struct tmpfs_dirent *de, *tde;
	struct tmpfs_mount *tmp;
	struct tmpfs_node *fdnode;
	struct tmpfs_node *fnode;
	struct tmpfs_node *tnode;
	struct tmpfs_node *tdnode;
	struct mount *mp;
	char *newname;
	char *oldname;
	int error;

	mp = fdvp->v_mount;
	KKASSERT(fdvp->v_mount == fvp->v_mount);

	/*
	 * Because tvp can get overwritten we have to vget it instead of
	 * just vref or use it, otherwise it's VINACTIVE flag may not get
	 * cleared and the node won't get destroyed.
	 */
	error = cache_vget(v->a_tnch, v->a_cred, LK_SHARED, &tvp);
	if (error == 0) {
		tnode = VP_TO_TMPFS_NODE(tvp);
		vn_unlock(tvp);
	} else {
		tnode = NULL;
	}

	/* Disallow cross-device renames.
	 * XXX Why isn't this done by the caller? */
	if (fvp->v_mount != tdvp->v_mount ||
	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
		error = EXDEV;
		goto out;
	}

	tmp = VFS_TO_TMPFS(tdvp->v_mount);
	tdnode = VP_TO_TMPFS_DIR(tdvp);

	/* If source and target are the same file, there is nothing to do. */
	if (fvp == tvp) {
		error = 0;
		goto out;
	}

	fdnode = VP_TO_TMPFS_DIR(fdvp);
	fnode = VP_TO_TMPFS_NODE(fvp);
	TMPFS_NODE_LOCK(fdnode);
	de = tmpfs_dir_lookup(fdnode, fnode, fncp);
	TMPFS_NODE_UNLOCK(fdnode);	/* XXX depend on namecache lock */

	/* Avoid manipulating '.' and '..' entries. */
	if (de == NULL) {
		error = ENOENT;
		goto out_locked;
	}
	KKASSERT(de->td_node == fnode);

	/*
	 * If replacing an entry in the target directory and that entry
	 * is a directory, it must be empty.
	 *
	 * Kern_rename gurantees the destination to be a directory
	 * if the source is one (it does?).
	 */
	if (tvp != NULL) {
		KKASSERT(tnode != NULL);

		if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
		    (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
			error = EPERM;
			goto out_locked;
		}

		if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
			if (tnode->tn_size > 0) {
				error = ENOTEMPTY;
				goto out_locked;
			}
		} else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
			error = ENOTDIR;
			goto out_locked;
		} else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
			error = EISDIR;
			goto out_locked;
		} else {
			KKASSERT(fnode->tn_type != VDIR &&
				tnode->tn_type != VDIR);
		}
	}

	if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
	    (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
		error = EPERM;
		goto out_locked;
	}

	/*
	 * Ensure that we have enough memory to hold the new name, if it
	 * has to be changed.
	 */
	if (fncp->nc_nlen != tncp->nc_nlen ||
	    bcmp(fncp->nc_name, tncp->nc_name, fncp->nc_nlen) != 0) {
		newname = kmalloc(tncp->nc_nlen + 1, tmp->tm_name_zone, 
				  M_WAITOK | M_NULLOK);
		if (newname == NULL) {
			error = ENOSPC;
			goto out_locked;
		}
		bcopy(tncp->nc_name, newname, tncp->nc_nlen);
		newname[tncp->nc_nlen] = '\0';
	} else {
		newname = NULL;
	}

	/*
	 * Unlink entry from source directory.  Note that the kernel has
	 * already checked for illegal recursion cases (renaming a directory
	 * into a subdirectory of itself).
	 */
	if (fdnode != tdnode) {
		tmpfs_dir_detach(fdnode, de);
	} else {
		/* XXX depend on namecache lock */
		TMPFS_NODE_LOCK(fdnode);
		KKASSERT(de == tmpfs_dir_lookup(fdnode, fnode, fncp));
		RB_REMOVE(tmpfs_dirtree, &fdnode->tn_dir.tn_dirtree, de);
		RB_REMOVE(tmpfs_dirtree_cookie,
			  &fdnode->tn_dir.tn_cookietree, de);
		TMPFS_NODE_UNLOCK(fdnode);
	}

	/*
	 * Handle any name change.  Swap with newname, we will
	 * deallocate it at the end.
	 */
	if (newname != NULL) {
#if 0
		TMPFS_NODE_LOCK(fnode);
		fnode->tn_status |= TMPFS_NODE_CHANGED;
		TMPFS_NODE_UNLOCK(fnode);
#endif
		oldname = de->td_name;
		de->td_name = newname;
		de->td_namelen = (uint16_t)tncp->nc_nlen;
		newname = oldname;
	}

	/*
	 * If we are overwriting an entry, we have to remove the old one
	 * from the target directory.
	 */
	if (tvp != NULL) {
		/* Remove the old entry from the target directory. */
		TMPFS_NODE_LOCK(tdnode);
		tde = tmpfs_dir_lookup(tdnode, tnode, tncp);
		tmpfs_dir_detach(tdnode, tde);
		TMPFS_NODE_UNLOCK(tdnode);
		tmpfs_knote(tdnode->tn_vnode, NOTE_DELETE);

		/*
		 * 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(VFS_TO_TMPFS(tvp->v_mount), tde);
		/*cache_inval_vp(tvp, CINV_DESTROY);*/
	}

	/*
	 * Link entry to target directory.  If the entry
	 * represents a directory move the parent linkage
	 * as well.
	 */
	if (fdnode != tdnode) {
		if (de->td_node->tn_type == VDIR) {
			TMPFS_VALIDATE_DIR(fnode);
		}
		tmpfs_dir_attach(tdnode, de);
	} else {
		TMPFS_NODE_LOCK(tdnode);
		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
		RB_INSERT(tmpfs_dirtree, &tdnode->tn_dir.tn_dirtree, de);
		RB_INSERT(tmpfs_dirtree_cookie,
			  &tdnode->tn_dir.tn_cookietree, de);
		TMPFS_NODE_UNLOCK(tdnode);
	}

	/*
	 * Finish up
	 */
	if (newname) {
		kfree(newname, tmp->tm_name_zone);
		newname = NULL;
	}
	cache_rename(v->a_fnch, v->a_tnch);
	tmpfs_knote(v->a_fdvp, NOTE_WRITE);
	tmpfs_knote(v->a_tdvp, NOTE_WRITE);
	if (fnode->tn_vnode)
		tmpfs_knote(fnode->tn_vnode, NOTE_RENAME);
	error = 0;

out_locked:
	;
out:
	if (tvp)
		vrele(tvp);
	return error;
}
Exemplo n.º 3
0
static int
puffs_vnop_rename(struct vop_nrename_args *ap)
{
    PUFFS_MSG_VARS(vn, rename);
    struct nchandle *fnch = ap->a_fnch;
    struct nchandle *tnch = ap->a_tnch;
    struct vnode *fdvp = ap->a_fdvp;
    struct vnode *fvp = fnch->ncp->nc_vp;
    struct vnode *tdvp = ap->a_tdvp;
    struct vnode *tvp = tnch->ncp->nc_vp;
    struct ucred *cred = ap->a_cred;
    struct puffs_mount *pmp = MPTOPUFFSMP(fdvp->v_mount);
    int error;

    if (!EXISTSOP(pmp, RENAME))
        return EOPNOTSUPP;

    error = vget(tdvp, LK_EXCLUSIVE);
    if (error != 0) {
        DPRINTF(("puffs_vnop_rename: EAGAIN on tdvp vnode %p %s\n",
                 tdvp, tnch->ncp->nc_name));
        return EAGAIN;
    }
    if (tvp != NULL) {
        error = vget(tvp, LK_EXCLUSIVE);
        if (error != 0) {
            DPRINTF(("puffs_vnop_rename: EAGAIN on tvp vnode %p %s\n",
                     tvp, tnch->ncp->nc_name));
            vput(tdvp);
            return EAGAIN;
        }
    }

    if ((fvp->v_mount != tdvp->v_mount) ||
            (tvp && (fvp->v_mount != tvp->v_mount))) {
        error = EXDEV;
        goto out;
    }

    if (tvp) {
        if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
            error = ENOTDIR;
            goto out;
        } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
            error = EISDIR;
            goto out;
        }
    }

    PUFFS_MSG_ALLOC(vn, rename);
    rename_msg->pvnr_cookie_src = VPTOPNC(fvp);
    rename_msg->pvnr_cookie_targdir = VPTOPNC(tdvp);
    if (tvp)
        rename_msg->pvnr_cookie_targ = VPTOPNC(tvp);
    else
        rename_msg->pvnr_cookie_targ = NULL;
    puffs_makecn(&rename_msg->pvnr_cn_src, &rename_msg->pvnr_cn_src_cred,
                 fnch->ncp, cred);
    puffs_makecn(&rename_msg->pvnr_cn_targ, &rename_msg->pvnr_cn_targ_cred,
                 tnch->ncp, cred);
    puffs_msg_setinfo(park_rename, PUFFSOP_VN,
                      PUFFS_VN_RENAME, VPTOPNC(fdvp));

    PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rename, fdvp->v_data, NULL, error);
    PUFFS_MSG_RELEASE(rename);
    error = checkerr(pmp, error, __func__);

    if (error == 0)
        puffs_updatenode(VPTOPP(fvp), PUFFS_UPDATECTIME);

out:
    if (tvp != NULL)
        vn_unlock(tvp);
    if (tdvp != tvp)
        vn_unlock(tdvp);
    if (error == 0)
        cache_rename(fnch, tnch);
    if (tvp != NULL)
        vrele(tvp);
    vrele(tdvp);

    return error;
}