コード例 #1
0
ファイル: devfs_vnops.c プロジェクト: mihaicarabas/dragonfly
static int
devfs_vop_nsymlink(struct vop_nsymlink_args *ap)
{
	struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp);
	struct devfs_node *node;
	size_t targetlen;

	if (!devfs_node_is_accessible(dnode))
		return ENOENT;

	ap->a_vap->va_type = VLNK;

	if ((dnode->node_type != Nroot) && (dnode->node_type != Ndir))
		goto out;

	lockmgr(&devfs_lock, LK_EXCLUSIVE);
	devfs_allocvp(ap->a_dvp->v_mount, ap->a_vpp, Nlink,
		      ap->a_nch->ncp->nc_name, dnode, NULL);

	targetlen = strlen(ap->a_target);
	if (*ap->a_vpp) {
		node = DEVFS_NODE(*ap->a_vpp);
		node->flags |= DEVFS_USER_CREATED;
		node->symlink_namelen = targetlen;
		node->symlink_name = kmalloc(targetlen + 1, M_DEVFS, M_WAITOK);
		memcpy(node->symlink_name, ap->a_target, targetlen);
		node->symlink_name[targetlen] = '\0';
		cache_setunresolved(ap->a_nch);
		cache_setvp(ap->a_nch, *ap->a_vpp);
	}
	lockmgr(&devfs_lock, LK_RELEASE);
out:
	return ((*ap->a_vpp == NULL) ? ENOTDIR : 0);
}
コード例 #2
0
ファイル: devfs_vnops.c プロジェクト: mihaicarabas/dragonfly
static int
devfs_vop_nmkdir(struct vop_nmkdir_args *ap)
{
	struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp);
	struct devfs_node *node;

	if (!devfs_node_is_accessible(dnode))
		return ENOENT;

	if ((dnode->node_type != Nroot) && (dnode->node_type != Ndir))
		goto out;

	lockmgr(&devfs_lock, LK_EXCLUSIVE);
	devfs_allocvp(ap->a_dvp->v_mount, ap->a_vpp, Ndir,
		      ap->a_nch->ncp->nc_name, dnode, NULL);

	if (*ap->a_vpp) {
		node = DEVFS_NODE(*ap->a_vpp);
		node->flags |= DEVFS_USER_CREATED;
		cache_setunresolved(ap->a_nch);
		cache_setvp(ap->a_nch, *ap->a_vpp);
	}
	lockmgr(&devfs_lock, LK_RELEASE);
out:
	return ((*ap->a_vpp == NULL) ? ENOTDIR : 0);
}
コード例 #3
0
ファイル: puffs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
puffs_vnop_mknod(struct vop_nmknod_args *ap)
{
    PUFFS_MSG_VARS(vn, mknod);
    struct vnode *dvp = ap->a_dvp;
    struct vattr *vap = ap->a_vap;
    struct puffs_node *dpn = VPTOPP(dvp);
    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, MKNOD))
        return EOPNOTSUPP;

    DPRINTF(("puffs_mknod: dvp %p, name: %s\n",
             dvp, ncp->nc_name));

    if (vap->va_type != VFIFO)
        return EINVAL;

    if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
        DPRINTF(("puffs_vnop_mknod: EAGAIN on ncp %p %s\n",
                 ncp, ncp->nc_name));
        return EAGAIN;
    }

    PUFFS_MSG_ALLOC(vn, mknod);
    puffs_makecn(&mknod_msg->pvnr_cn, &mknod_msg->pvnr_cn_cred,
                 ncp, cred);
    mknod_msg->pvnr_va = *ap->a_vap;
    puffs_msg_setinfo(park_mknod, PUFFSOP_VN,
                      PUFFS_VN_MKNOD, VPTOPNC(dvp));

    PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error);

    error = checkerr(pmp, error, __func__);
    if (error)
        goto out;

    error = puffs_newnode(mp, dvp, ap->a_vpp,
                          mknod_msg->pvnr_newnode, vap->va_type);
    if (error)
        puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie,
                          mknod_msg->pvnr_newnode, ncp, cred);

out:
    vput(dvp);
    if (!error) {
        cache_setunresolved(nch);
        cache_setvp(nch, *ap->a_vpp);
    }
    PUFFS_MSG_RELEASE(mknod);
    return error;
}
コード例 #4
0
ファイル: puffs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
puffs_vnop_link(struct vop_nlink_args *ap)
{
    PUFFS_MSG_VARS(vn, link);
    struct vnode *dvp = ap->a_dvp;
    struct vnode *vp = ap->a_vp;
    struct puffs_node *dpn = VPTOPP(dvp);
    struct puffs_node *pn = VPTOPP(vp);
    struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
    struct nchandle *nch = ap->a_nch;
    struct namecache *ncp = nch->ncp;
    struct ucred *cred = ap->a_cred;
    int error;

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

    if (vp->v_mount != dvp->v_mount)
        return EXDEV;

    if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
        DPRINTF(("puffs_vnop_link: EAGAIN on ncp %p %s\n",
                 ncp, ncp->nc_name));
        return EAGAIN;
    }

    PUFFS_MSG_ALLOC(vn, link);
    link_msg->pvnr_cookie_targ = VPTOPNC(vp);
    puffs_makecn(&link_msg->pvnr_cn, &link_msg->pvnr_cn_cred,
                 ncp, cred);
    puffs_msg_setinfo(park_link, PUFFSOP_VN,
                      PUFFS_VN_LINK, VPTOPNC(dvp));

    puffs_msg_enqueue(pmp, park_link);
    error = puffs_msg_wait2(pmp, park_link, dpn, pn);

    PUFFS_MSG_RELEASE(link);

    error = checkerr(pmp, error, __func__);

    /*
     * XXX: stay in touch with the cache.  I don't like this, but
     * don't have a better solution either.  See also puffs_rename().
     */
    if (error == 0) {
        puffs_updatenode(pn, PUFFS_UPDATECTIME);
    }

    vput(dvp);
    if (error == 0) {
        cache_setunresolved(nch);
        cache_setvp(nch, vp);
    }
    return error;
}
コード例 #5
0
ファイル: puffs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
puffs_vnop_symlink(struct vop_nsymlink_args *ap)
{
    PUFFS_MSG_VARS(vn, symlink);
    struct vnode *dvp = ap->a_dvp;
    struct puffs_node *dpn = VPTOPP(dvp);
    struct mount *mp = dvp->v_mount;
    struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
    struct nchandle *nch = ap->a_nch;
    struct namecache *ncp = nch->ncp;
    struct ucred *cred = ap->a_cred;
    int error;

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

    if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
        DPRINTF(("puffs_vnop_symlink: EAGAIN on ncp %p %s\n",
                 ncp, ncp->nc_name));
        return EAGAIN;
    }

    *ap->a_vpp = NULL;

    PUFFS_MSG_ALLOC(vn, symlink);
    puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred,
                 ncp, cred);
    symlink_msg->pvnr_va = *ap->a_vap;
    (void)strlcpy(symlink_msg->pvnr_link, ap->a_target,
                  sizeof(symlink_msg->pvnr_link));
    puffs_msg_setinfo(park_symlink, PUFFSOP_VN,
                      PUFFS_VN_SYMLINK, VPTOPNC(dvp));

    PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error);

    error = checkerr(pmp, error, __func__);
    if (error)
        goto out;

    error = puffs_newnode(mp, dvp, ap->a_vpp,
                          symlink_msg->pvnr_newnode, VLNK);
    if (error)
        puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie,
                          symlink_msg->pvnr_newnode, ncp, cred);

out:
    vput(dvp);
    PUFFS_MSG_RELEASE(symlink);
    if (!error) {
        cache_setunresolved(nch);
        cache_setvp(nch, *ap->a_vpp);
    }
    return error;
}
コード例 #6
0
ファイル: tmpfs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
tmpfs_nsymlink(struct vop_nsymlink_args *v)
{
	struct vnode *dvp = v->a_dvp;
	struct vnode **vpp = v->a_vpp;
	struct namecache *ncp = v->a_nch->ncp;
	struct vattr *vap = v->a_vap;
	struct ucred *cred = v->a_cred;
	char *target = v->a_target;
	int error;

	vap->va_type = VLNK;
	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, target);
	if (error == 0) {
		tmpfs_knote(*vpp, NOTE_WRITE);
		cache_setunresolved(v->a_nch);
		cache_setvp(v->a_nch, *vpp);
	}
	return error;
}
コード例 #7
0
static int
tmpfs_ncreate(struct vop_ncreate_args *ap)
{
	struct vnode *dvp = ap->a_dvp;
	struct vnode **vpp = ap->a_vpp;
	struct namecache *ncp = ap->a_nch->ncp;
	struct vattr *vap = ap->a_vap;
	struct ucred *cred = ap->a_cred;
	int error;

	KKASSERT(vap->va_type == VREG || vap->va_type == VSOCK);

	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
	if (error == 0) {
		cache_setunresolved(ap->a_nch);
		cache_setvp(ap->a_nch, *vpp);
		tmpfs_knote(dvp, NOTE_WRITE);
	}
	return (error);
}
コード例 #8
0
ファイル: tmpfs_vnops.c プロジェクト: varialus/DragonFlyX
static int
tmpfs_nmkdir(struct vop_nmkdir_args *v)
{
	struct vnode *dvp = v->a_dvp;
	struct vnode **vpp = v->a_vpp;
	struct namecache *ncp = v->a_nch->ncp;
	struct vattr *vap = v->a_vap;
	struct ucred *cred = v->a_cred;
	int error;

	KKASSERT(vap->va_type == VDIR);

	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
	if (error == 0) {
		cache_setunresolved(v->a_nch);
		cache_setvp(v->a_nch, *vpp);
		tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
	}

	return error;
}
コード例 #9
0
ファイル: tmpfs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
tmpfs_nmknod(struct vop_nmknod_args *v)
{
	struct vnode *dvp = v->a_dvp;
	struct vnode **vpp = v->a_vpp;
	struct namecache *ncp = v->a_nch->ncp;
	struct vattr *vap = v->a_vap;
	struct ucred *cred = v->a_cred;
	int error;

	if (vap->va_type != VBLK && vap->va_type != VCHR &&
	    vap->va_type != VFIFO) {
		return (EINVAL);
	}

	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
	if (error == 0) {
		cache_setunresolved(v->a_nch);
		cache_setvp(v->a_nch, *vpp);
		tmpfs_knote(dvp, NOTE_WRITE);
	}
	return error;
}
コード例 #10
0
ファイル: tmpfs_vnops.c プロジェクト: wan721/DragonFlyBSD
static int
tmpfs_nlink(struct vop_nlink_args *v)
{
	struct vnode *dvp = v->a_dvp;
	struct vnode *vp = v->a_vp;
	struct namecache *ncp = v->a_nch->ncp;
	struct tmpfs_dirent *de;
	struct tmpfs_node *node;
	struct tmpfs_node *dnode;
	struct mount *mp;
	int error;

	mp = dvp->v_mount;

	KKASSERT(dvp != vp); /* XXX When can this be false? */

	node = VP_TO_TMPFS_NODE(vp);
	dnode = VP_TO_TMPFS_NODE(dvp);
	TMPFS_NODE_LOCK(dnode);

	/* XXX: Why aren't the following two tests done by the caller? */

	/* Hard links of directories are forbidden. */
	if (vp->v_type == VDIR) {
		error = EPERM;
		goto out;
	}

	/* Cannot create cross-device links. */
	if (dvp->v_mount != vp->v_mount) {
		error = EXDEV;
		goto out;
	}

	/* Ensure that we do not overflow the maximum number of links imposed
	 * by the system. */
	KKASSERT(node->tn_links <= LINK_MAX);
	if (node->tn_links >= LINK_MAX) {
		error = EMLINK;
		goto out;
	}

	/* We cannot create links of files marked immutable or append-only. */
	if (node->tn_flags & (IMMUTABLE | APPEND)) {
		error = EPERM;
		goto out;
	}

	/* Allocate a new directory entry to represent the node. */
	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
				   ncp->nc_name, ncp->nc_nlen, &de);
	if (error != 0)
		goto out;

	/* Insert the new directory entry into the appropriate directory. */
	tmpfs_dir_attach(dnode, de);

	/* vp link count has changed, so update node times. */

	TMPFS_NODE_LOCK(node);
	node->tn_status |= TMPFS_NODE_CHANGED;
	TMPFS_NODE_UNLOCK(node);
	tmpfs_update(vp);

	tmpfs_knote(vp, NOTE_LINK);
	cache_setunresolved(v->a_nch);
	cache_setvp(v->a_nch, vp);
	error = 0;

out:
	TMPFS_NODE_UNLOCK(dnode);
	if (error == 0)
		tmpfs_knote(dvp, NOTE_WRITE);
	return error;
}
コード例 #11
0
/*
 * 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);
}
コード例 #12
0
/*
 * vop_compat_nwhiteout { struct nchandle *a_nch, 	XXX STOPGAP FUNCTION
 *			  struct vnode *a_dvp,
 *			  struct ucred *a_cred,
 *			  int a_flags }
 *
 * Issie a whiteout operation (create, lookup, or delete).  Compatibility 
 * requires us to issue the appropriate VOP_OLD_LOOKUP before we issue 
 * VOP_OLD_WHITEOUT in order to setup the directory inode's i_offset and i_count
 * (e.g. in UFS) for the NAMEI_CREATE and NAMEI_DELETE ops.  For NAMEI_LOOKUP
 * no lookup is necessary.
 */
int
vop_compat_nwhiteout(struct vop_nwhiteout_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 create the entry in the
	 * directory inode.  We expect a return code of EJUSTRETURN for
	 * the CREATE case.  The cnp must simulated a saved-name situation.
	 */
	bzero(&cnp, sizeof(cnp));
	cnp.cn_nameiop = ap->a_flags;
	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;

	vp = NULL;

	/*
	 * EJUSTRETURN should be returned for the CREATE or DELETE cases.
	 * The VFS has setup the directory inode for the create.  The dvp we
	 * passed in is expected to remain in a locked state.
	 *
	 * If the VOP_OLD_WHITEOUT is successful we are responsible for updating
	 * the cache state of the locked ncp that was passed to us.
	 */
	switch(ap->a_flags) {
	case NAMEI_DELETE:
		cnp.cn_flags |= CNP_DOWHITEOUT;
		/* fall through */
	case NAMEI_CREATE:
		error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
		if (error == EJUSTRETURN) {
			KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
			error = VOP_OLD_WHITEOUT(dvp, &cnp, ap->a_flags);
			if (error == 0)
				cache_setunresolved(nch);
		} else {
			if (error == 0) {
				vput(vp);
				vp = NULL;
				error = EEXIST;
			}
			KKASSERT(vp == NULL);
		}
		break;
	case NAMEI_LOOKUP:
		error = VOP_OLD_WHITEOUT(dvp, NULL, ap->a_flags);
		break;
	default:
		error = EINVAL;
		break;
	}
	if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
		vn_unlock(dvp);
	vrele(dvp);
	return (error);
}
コード例 #13
0
/*
 * vop_compat_nlink { struct nchandle *a_nch, 	XXX STOPGAP FUNCTION
 *			struct vnode *a_dvp,
 *			struct vnode *a_vp,
 *			struct ucred *a_cred }
 *
 * The passed vp is locked and represents the source.  The passed ncp is
 * locked and represents the target to create.
 */
int
vop_compat_nlink(struct vop_nlink_args *ap)
{
	struct thread *td = curthread;
	struct componentname cnp;
	struct nchandle *nch;
	struct namecache *ncp;
	struct vnode *dvp;
	struct vnode *tvp;
	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 create the entry in the
	 * directory inode.  We expect a return code of EJUSTRETURN for
	 * the CREATE case.  The cnp must simulated a saved-name situation.
	 */
	bzero(&cnp, sizeof(cnp));
	cnp.cn_nameiop = NAMEI_CREATE;
	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;

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

	/*
	 * EJUSTRETURN should be returned for this case, which means that
	 * the VFS has setup the directory inode for the create.  The dvp we
	 * passed in is expected to remain in a locked state.
	 *
	 * If the VOP_OLD_LINK is successful we are responsible for updating
	 * the cache state of the locked ncp that was passed to us.
	 */
	if (error == EJUSTRETURN) {
		KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
		error = VOP_OLD_LINK(dvp, ap->a_vp, &cnp);
		if (error == 0) {
			cache_setunresolved(nch);
			cache_setvp(nch, ap->a_vp);
		}
	} else {
		if (error == 0) {
			vput(tvp);
			error = EEXIST;
		}
	}
	if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
		vn_unlock(dvp);
	vrele(dvp);
	return (error);
}
コード例 #14
0
ファイル: vfs_vnops.c プロジェクト: AhmadTux/DragonFlyBSD
/*
 * Common code for vnode open operations.  Check permissions, and call
 * the VOP_NOPEN or VOP_NCREATE routine.
 *
 * The caller is responsible for setting up nd with nlookup_init() and
 * for cleaning it up with nlookup_done(), whether we return an error
 * or not.
 *
 * On success nd->nl_open_vp will hold a referenced and, if requested,
 * locked vnode.  A locked vnode is requested via NLC_LOCKVP.  If fp
 * is non-NULL the vnode will be installed in the file pointer.
 *
 * NOTE: The vnode is referenced just once on return whether or not it
 * is also installed in the file pointer.
 */
int
vn_open(struct nlookupdata *nd, struct file *fp, int fmode, int cmode)
{
	struct vnode *vp;
	struct ucred *cred = nd->nl_cred;
	struct vattr vat;
	struct vattr *vap = &vat;
	int error;
	u_int flags;
	uint64_t osize;
	struct mount *mp;

	/*
	 * Certain combinations are illegal
	 */
	if ((fmode & (FWRITE | O_TRUNC)) == O_TRUNC)
		return(EACCES);

	/*
	 * Lookup the path and create or obtain the vnode.  After a
	 * successful lookup a locked nd->nl_nch will be returned.
	 *
	 * The result of this section should be a locked vnode.
	 *
	 * XXX with only a little work we should be able to avoid locking
	 * the vnode if FWRITE, O_CREAT, and O_TRUNC are *not* set.
	 */
	nd->nl_flags |= NLC_OPEN;
	if (fmode & O_APPEND)
		nd->nl_flags |= NLC_APPEND;
	if (fmode & O_TRUNC)
		nd->nl_flags |= NLC_TRUNCATE;
	if (fmode & FREAD)
		nd->nl_flags |= NLC_READ;
	if (fmode & FWRITE)
		nd->nl_flags |= NLC_WRITE;
	if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
		nd->nl_flags |= NLC_FOLLOW;

	if (fmode & O_CREAT) {
		/*
		 * CONDITIONAL CREATE FILE CASE
		 *
		 * Setting NLC_CREATE causes a negative hit to store
		 * the negative hit ncp and not return an error.  Then
		 * nc_error or nc_vp may be checked to see if the ncp 
		 * represents a negative hit.  NLC_CREATE also requires
		 * write permission on the governing directory or EPERM
		 * is returned.
		 */
		nd->nl_flags |= NLC_CREATE;
		nd->nl_flags |= NLC_REFDVP;
		bwillinode(1);
		error = nlookup(nd);
	} else {
		/*
		 * NORMAL OPEN FILE CASE
		 */
		error = nlookup(nd);
	}

	if (error)
		return (error);

	/*
	 * split case to allow us to re-resolve and retry the ncp in case
	 * we get ESTALE.
	 */
again:
	if (fmode & O_CREAT) {
		if (nd->nl_nch.ncp->nc_vp == NULL) {
			if ((error = ncp_writechk(&nd->nl_nch)) != 0)
				return (error);
			VATTR_NULL(vap);
			vap->va_type = VREG;
			vap->va_mode = cmode;
			if (fmode & O_EXCL)
				vap->va_vaflags |= VA_EXCLUSIVE;
			error = VOP_NCREATE(&nd->nl_nch, nd->nl_dvp, &vp,
					    nd->nl_cred, vap);
			if (error)
				return (error);
			fmode &= ~O_TRUNC;
			/* locked vnode is returned */
		} else {
			if (fmode & O_EXCL) {
				error = EEXIST;
			} else {
				error = cache_vget(&nd->nl_nch, cred, 
						    LK_EXCLUSIVE, &vp);
			}
			if (error)
				return (error);
			fmode &= ~O_CREAT;
		}
	} else {
		error = cache_vget(&nd->nl_nch, cred, LK_EXCLUSIVE, &vp);
		if (error)
			return (error);
	}

	/*
	 * We have a locked vnode and ncp now.  Note that the ncp will
	 * be cleaned up by the caller if nd->nl_nch is left intact.
	 */
	if (vp->v_type == VLNK) {
		error = EMLINK;
		goto bad;
	}
	if (vp->v_type == VSOCK) {
		error = EOPNOTSUPP;
		goto bad;
	}
	if ((fmode & O_CREAT) == 0) {
		if (fmode & (FWRITE | O_TRUNC)) {
			if (vp->v_type == VDIR) {
				error = EISDIR;
				goto bad;
			}
			error = vn_writechk(vp, &nd->nl_nch);
			if (error) {
				/*
				 * Special stale handling, re-resolve the
				 * vnode.
				 */
				if (error == ESTALE) {
					vput(vp);
					vp = NULL;
					cache_setunresolved(&nd->nl_nch);
					error = cache_resolve(&nd->nl_nch, cred);
					if (error == 0)
						goto again;
				}
				goto bad;
			}
		}
	}
	if (fmode & O_TRUNC) {
		vn_unlock(vp);				/* XXX */
		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);	/* XXX */
		osize = vp->v_filesize;
		VATTR_NULL(vap);
		vap->va_size = 0;
		error = VOP_SETATTR(vp, vap, cred);
		if (error)
			goto bad;
		error = VOP_GETATTR(vp, vap);
		if (error)
			goto bad;
		mp = vq_vptomp(vp);
		VFS_ACCOUNT(mp, vap->va_uid, vap->va_gid, -osize);
	}

	/*
	 * Set or clear VNSWAPCACHE on the vp based on nd->nl_nch.ncp->nc_flag.
	 * These particular bits a tracked all the way from the root.
	 *
	 * NOTE: Might not work properly on NFS servers due to the
	 * disconnected namecache.
	 */
	flags = nd->nl_nch.ncp->nc_flag;
	if ((flags & (NCF_UF_CACHE | NCF_UF_PCACHE)) &&
	    (flags & (NCF_SF_NOCACHE | NCF_SF_PNOCACHE)) == 0) {
		vsetflags(vp, VSWAPCACHE);
	} else {
		vclrflags(vp, VSWAPCACHE);
	}

	/*
	 * Setup the fp so VOP_OPEN can override it.  No descriptor has been
	 * associated with the fp yet so we own it clean.  
	 *
	 * f_nchandle inherits nl_nch.  This used to be necessary only for
	 * directories but now we do it unconditionally so f*() ops
	 * such as fchmod() can access the actual namespace that was
	 * used to open the file.
	 */
	if (fp) {
		if (nd->nl_flags & NLC_APPENDONLY)
			fmode |= FAPPENDONLY;
		fp->f_nchandle = nd->nl_nch;
		cache_zero(&nd->nl_nch);
		cache_unlock(&fp->f_nchandle);
	}

	/*
	 * Get rid of nl_nch.  vn_open does not return it (it returns the
	 * vnode or the file pointer).  Note: we can't leave nl_nch locked
	 * through the VOP_OPEN anyway since the VOP_OPEN may block, e.g.
	 * on /dev/ttyd0
	 */
	if (nd->nl_nch.ncp)
		cache_put(&nd->nl_nch);

	error = VOP_OPEN(vp, fmode, cred, fp);
	if (error) {
		/*
		 * setting f_ops to &badfileops will prevent the descriptor
		 * code from trying to close and release the vnode, since
		 * the open failed we do not want to call close.
		 */
		if (fp) {
			fp->f_data = NULL;
			fp->f_ops = &badfileops;
		}
		goto bad;
	}

#if 0
	/*
	 * Assert that VREG files have been setup for vmio.
	 */
	KASSERT(vp->v_type != VREG || vp->v_object != NULL,
		("vn_open: regular file was not VMIO enabled!"));
#endif

	/*
	 * Return the vnode.  XXX needs some cleaning up.  The vnode is
	 * only returned in the fp == NULL case.
	 */
	if (fp == NULL) {
		nd->nl_open_vp = vp;
		nd->nl_vp_fmode = fmode;
		if ((nd->nl_flags & NLC_LOCKVP) == 0)
			vn_unlock(vp);
	} else {
		vput(vp);
	}
	return (0);
bad:
	if (vp)
		vput(vp);
	return (error);
}
コード例 #15
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;

	/*
	 * 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);

	if (vp->v_type == VDIR) {
		error = EISDIR;
		goto out;
	}

	dnode = VP_TO_TMPFS_DIR(dvp);
	node = VP_TO_TMPFS_NODE(vp);
	tmp = VFS_TO_TMPFS(vp->v_mount);
	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_setunresolved(v->a_nch);
	cache_setvp(v->a_nch, NULL);
	tmpfs_knote(vp, NOTE_DELETE);
	/*cache_inval_vp(vp, CINV_DESTROY);*/
	tmpfs_knote(dvp, NOTE_WRITE);
	error = 0;

out:
	vrele(vp);

	return error;
}
コード例 #16
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;
	int error;

	/*
	 * 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. */
	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;
		goto out;
	}


	/* Detach the directory entry from the directory (dnode). */
	tmpfs_dir_detach(dnode, de);

	/* No vnode should be allocated for this entry from this point */
	TMPFS_NODE_LOCK(node);
	TMPFS_ASSERT_ELOCKED(node);
	TMPFS_NODE_LOCK(dnode);
	TMPFS_ASSERT_ELOCKED(dnode);

#if 0
	/* handled by tmpfs_free_node */
	KKASSERT(node->tn_links > 0);
	node->tn_links--;
	node->tn_dir.tn_parent = NULL;
#endif
	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
	    TMPFS_NODE_MODIFIED;

#if 0
	/* handled by tmpfs_free_node */
	KKASSERT(dnode->tn_links > 0);
	dnode->tn_links--;
#endif
	dnode->tn_status |= TMPFS_NODE_ACCESSED | \
	    TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;

	TMPFS_NODE_UNLOCK(dnode);
	TMPFS_NODE_UNLOCK(node);

	/* 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_setunresolved(v->a_nch);
	cache_setvp(v->a_nch, NULL);
	/*cache_inval_vp(vp, CINV_DESTROY);*/
	tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
	error = 0;

out:
	vrele(vp);

	return error;
}