예제 #1
0
/*
 * smb_vop_rename()
 *
 * The rename is for files in the same tree (identical TID) only.
 */
int
smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp,
    char *to_name, int flags, cred_t *cr)
{
	int error;
	int option_flags = 0;
	char *from, *to, *fbuf, *tbuf;

	if (flags & SMB_IGNORE_CASE)
		option_flags = FIGNORECASE;

	if (flags & SMB_CATIA) {
		tbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
		to = smb_vop_catia_v5tov4(to_name, tbuf, MAXNAMELEN);
		if (strchr(to, '/') != NULL) {
			kmem_free(tbuf, MAXNAMELEN);
			return (EILSEQ);
		}

		fbuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
		from = smb_vop_catia_v5tov4(from_name, fbuf, MAXNAMELEN);

		error = VOP_RENAME(from_dvp, from, to_dvp, to, cr,
		    &smb_ct, option_flags);

		kmem_free(tbuf, MAXNAMELEN);
		kmem_free(fbuf, MAXNAMELEN);
		return (error);
	}

	error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr,
	    &smb_ct, option_flags);

	return (error);
}
예제 #2
0
STATIC int
linvfs_rename(
	struct inode	*odir,
	struct dentry	*odentry,
	struct inode	*ndir,
	struct dentry	*ndentry)
{
	struct inode	*new_inode = ndentry->d_inode;
	vnode_t		*fvp;	/* from directory */
	vnode_t		*tvp;	/* target directory */
	int		error;

	fvp = LINVFS_GET_VP(odir);
	tvp = LINVFS_GET_VP(ndir);

	VOP_RENAME(fvp, odentry, tvp, ndentry, NULL, error);
	if (error)
		return -error;

	if (new_inode)
		validate_fields(new_inode);

	validate_fields(odir);
	if (ndir != odir)
		validate_fields(ndir);
	return 0;
}
예제 #3
0
static int
zfs_replay_rename(void *arg1, char *arg2, boolean_t byteswap)
{
	zfsvfs_t *zfsvfs = (zfsvfs_t *)arg1;
	lr_rename_t *lr = (lr_rename_t *)arg2;
	char *sname = (char *)(lr + 1);	/* sname and tname follow lr_rename_t */
	char *tname = sname + strlen(sname) + 1;
	znode_t *sdzp, *tdzp;
	int error;
	int vflg = 0;

	if (byteswap)
		byteswap_uint64_array(lr, sizeof (*lr));

	if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0)
		return (error);

	if ((error = zfs_zget(zfsvfs, lr->lr_tdoid, &tdzp)) != 0) {
		VN_RELE(ZTOV(sdzp));
		return (error);
	}

	if (lr->lr_common.lrc_txtype & TX_CI)
		vflg |= FIGNORECASE;

	error = VOP_RENAME(ZTOV(sdzp), sname, ZTOV(tdzp), tname, kcred,
	    NULL, vflg);

	VN_RELE(ZTOV(tdzp));
	VN_RELE(ZTOV(sdzp));

	return (error);
}
예제 #4
0
파일: vfspath.c 프로젝트: Mr-Grieves/os161
/* Does most of the work for rename(). */
int
vfs_rename(char *oldpath, char *newpath)
{
	struct vnode *olddir;
	char oldname[NAME_MAX+1];
	struct vnode *newdir;
	char newname[NAME_MAX+1];
	int result;
	
	result = vfs_lookparent(oldpath, &olddir, oldname, sizeof(oldname));
	if (result) {
		return result;
	}
	result = vfs_lookparent(newpath, &newdir, newname, sizeof(newname));
	if (result) {
		VOP_DECREF(olddir);
		return result;
	}

	if (olddir->vn_fs==NULL || newdir->vn_fs==NULL || 
	    olddir->vn_fs != newdir->vn_fs) {
		VOP_DECREF(newdir);
		VOP_DECREF(olddir);
		return EXDEV;
	}

	result = VOP_RENAME(olddir, oldname, newdir, newname);

	VOP_DECREF(newdir);
	VOP_DECREF(olddir);

	return result;
}
예제 #5
0
static int zfsfuse_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname)
{
	if(strlen(name) >= MAXNAMELEN)
		return ENAMETOOLONG;
	if(strlen(newname) >= MAXNAMELEN)
		return ENAMETOOLONG;

	vfs_t *vfs = (vfs_t *) fuse_req_userdata(req);
	zfsvfs_t *zfsvfs = vfs->vfs_data;

	ZFS_ENTER(zfsvfs);

	znode_t *p_znode, *np_znode;

	int error = zfs_zget(zfsvfs, parent, &p_znode, B_FALSE);
	if(error) {
		ZFS_EXIT(zfsvfs);
		/* If the inode we are trying to get was recently deleted
		   dnode_hold_impl will return EEXIST instead of ENOENT */
		return error == EEXIST ? ENOENT : error;
	}

	ASSERT(p_znode != NULL);

	error = zfs_zget(zfsvfs, newparent, &np_znode, B_FALSE);
	if(error) {
		VN_RELE(ZTOV(p_znode));
		ZFS_EXIT(zfsvfs);
		/* If the inode we are trying to get was recently deleted
		   dnode_hold_impl will return EEXIST instead of ENOENT */
		return error == EEXIST ? ENOENT : error;
	}

	ASSERT(np_znode != NULL);

	vnode_t *p_vp = ZTOV(p_znode);
	vnode_t *np_vp = ZTOV(np_znode);
	ASSERT(p_vp != NULL);
	ASSERT(np_vp != NULL);

	cred_t cred;
	zfsfuse_getcred(req, &cred);

	error = VOP_RENAME(p_vp, (char *) name, np_vp, (char *) newname, &cred, NULL, 0);

	VN_RELE(p_vp);
	VN_RELE(np_vp);

	ZFS_EXIT(zfsvfs);

	return error;
}
예제 #6
0
int
RUMP_VOP_RENAME(struct vnode *fdvp,
    struct vnode *fvp,
    struct componentname *fcnp,
    struct vnode *tdvp,
    struct vnode *tvp,
    struct componentname *tcnp)
{
	int error;

	rump_schedule();
	error = VOP_RENAME(fdvp, fvp, fcnp, tdvp, tvp, tcnp);
	rump_unschedule();

	return error;
}
예제 #7
0
파일: xattr.c 프로젝트: pcd1193182/openzfs
static int
xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
    cred_t *cr, caller_context_t *ct, int flags)
{
	vnode_t *spvp, *tpvp;
	int error;

	if (is_sattr_name(snm) || is_sattr_name(tnm))
		return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
	/*
	 * We know that sdvp is a GFS dir, or we wouldn't be here.
	 * Get the real unnamed directory.
	 */
	error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
	if (error) {
		return (error);
	}

	if (sdvp == tdvp) {
		/*
		 * If the source and target are the same GFS directory, the
		 * underlying unnamed source and target dir will be the same.
		 */
		tpvp = spvp;
	} else if (tdvp->v_flag & V_SYSATTR) {
		/*
		 * If the target dir is a different GFS directory,
		 * find its underlying unnamed dir.
		 */
		error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
		if (error) {
			return (error);
		}
	} else {
		/*
		 * Target dir is outside of GFS, pass it on through.
		 */
		tpvp = tdvp;
	}

	error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);

	return (error);
}
예제 #8
0
static int
zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
{
	char *sname = (char *)(lr + 1);	/* sname and tname follow lr_rename_t */
	char *tname = sname + strlen(sname) + 1;
	znode_t *sdzp, *tdzp;
	struct componentname scn, tcn;
	vnode_t *svp, *tvp;
	kthread_t *td = curthread;
	int error;
	int vflg = 0;

	if (byteswap)
		byteswap_uint64_array(lr, sizeof (*lr));

	if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0)
		return (error);

	if ((error = zfs_zget(zfsvfs, lr->lr_tdoid, &tdzp)) != 0) {
		VN_RELE(ZTOV(sdzp));
		return (error);
	}

	if (lr->lr_common.lrc_txtype & TX_CI)
		vflg |= FIGNORECASE;
	svp = tvp = NULL;

	scn.cn_nameptr = sname;
	scn.cn_namelen = strlen(sname);
	scn.cn_nameiop = DELETE;
	scn.cn_flags = ISLASTCN | SAVENAME;
	scn.cn_lkflags = LK_EXCLUSIVE | LK_RETRY;
	scn.cn_cred = kcred;
	scn.cn_thread = td;
	vn_lock(ZTOV(sdzp), LK_EXCLUSIVE | LK_RETRY);
	error = VOP_LOOKUP(ZTOV(sdzp), &svp, &scn);
	VOP_UNLOCK(ZTOV(sdzp), 0);
	if (error != 0)
		goto fail;
	VOP_UNLOCK(svp, 0);

	tcn.cn_nameptr = tname;
	tcn.cn_namelen = strlen(tname);
	tcn.cn_nameiop = RENAME;
	tcn.cn_flags = ISLASTCN | SAVENAME;
	tcn.cn_lkflags = LK_EXCLUSIVE | LK_RETRY;
	tcn.cn_cred = kcred;
	tcn.cn_thread = td;
	vn_lock(ZTOV(tdzp), LK_EXCLUSIVE | LK_RETRY);
	error = VOP_LOOKUP(ZTOV(tdzp), &tvp, &tcn);
	if (error == EJUSTRETURN)
		tvp = NULL;
	else if (error != 0) {
		VOP_UNLOCK(ZTOV(tdzp), 0);
		goto fail;
	}

	error = VOP_RENAME(ZTOV(sdzp), svp, &scn, ZTOV(tdzp), tvp, &tcn /*,vflg*/);
	return (error);
fail:
	if (svp != NULL)
		vrele(svp);
	if (tvp != NULL)
		vrele(tvp);
	VN_RELE(ZTOV(tdzp));
	VN_RELE(ZTOV(sdzp));

	return (error);
}
예제 #9
0
static int
unionfs_rename(void *v)
{
	struct vop_rename_args *ap = v;
	int		error;
	struct vnode   *fdvp;
	struct vnode   *fvp;
	struct componentname *fcnp;
	struct vnode   *tdvp;
	struct vnode   *tvp;
	struct componentname *tcnp;
	struct vnode   *ltdvp;
	struct vnode   *ltvp;

	/* rename target vnodes */
	struct vnode   *rfdvp;
	struct vnode   *rfvp;
	struct vnode   *rtdvp;
	struct vnode   *rtvp;

	int		needrelookup;
	struct unionfs_mount *ump;
	struct unionfs_node *unp;

	UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n");

	error = 0;
	fdvp = ap->a_fdvp;
	fvp = ap->a_fvp;
	fcnp = ap->a_fcnp;
	tdvp = ap->a_tdvp;
	tvp = ap->a_tvp;
	tcnp = ap->a_tcnp;
	ltdvp = NULLVP;
	ltvp = NULLVP;
	rfdvp = fdvp;
	rfvp = fvp;
	rtdvp = tdvp;
	rtvp = tvp;
	needrelookup = 0;

	/* check for cross device rename */
	if (fvp->v_mount != tdvp->v_mount ||
	    (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) {
		error = EXDEV;
		goto unionfs_rename_abort;
	}

	/* Renaming a file to itself has no effect. */
	if (fvp == tvp)
		goto unionfs_rename_abort;

	/*
	 * from/to vnode is unionfs node.
	 */

	unp = VTOUNIONFS(fdvp);
#ifdef UNIONFS_IDBG_RENAME
	UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n", fdvp, unp->un_uppervp, unp->un_lowervp);
#endif
	if (unp->un_uppervp == NULLVP) {
		error = ENODEV;
		goto unionfs_rename_abort;
	}
	rfdvp = unp->un_uppervp;
	vref(rfdvp);

	unp = VTOUNIONFS(fvp);
#ifdef UNIONFS_IDBG_RENAME
	UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n", fvp, unp->un_uppervp, unp->un_lowervp);
#endif
	ump = MOUNTTOUNIONFSMOUNT(fvp->v_mount);
	if (unp->un_uppervp == NULLVP) {
		switch (fvp->v_type) {
		case VREG:
			if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
				goto unionfs_rename_abort;
			error = unionfs_copyfile(unp, 1, fcnp->cn_cred);
			VOP_UNLOCK(fvp);
			if (error != 0)
				goto unionfs_rename_abort;
			break;
		case VDIR:
			if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
				goto unionfs_rename_abort;
			error = unionfs_mkshadowdir(ump, rfdvp, unp, fcnp);
			VOP_UNLOCK(fvp);
			if (error != 0)
				goto unionfs_rename_abort;
			break;
		default:
			error = ENODEV;
			goto unionfs_rename_abort;
		}

		needrelookup = 1;
	}

	if (unp->un_lowervp != NULLVP)
		fcnp->cn_flags |= DOWHITEOUT;
	rfvp = unp->un_uppervp;
	vref(rfvp);

	unp = VTOUNIONFS(tdvp);
#ifdef UNIONFS_IDBG_RENAME
	UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n", tdvp, unp->un_uppervp, unp->un_lowervp);
#endif
	if (unp->un_uppervp == NULLVP) {
		error = ENODEV;
		goto unionfs_rename_abort;
	}
	rtdvp = unp->un_uppervp;
	ltdvp = unp->un_lowervp;
	vref(rtdvp);

	if (tdvp == tvp) {
		rtvp = rtdvp;
		vref(rtvp);
	} else if (tvp != NULLVP) {
		unp = VTOUNIONFS(tvp);
#ifdef UNIONFS_IDBG_RENAME
		UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n", tvp, unp->un_uppervp, unp->un_lowervp);
#endif
		if (unp->un_uppervp == NULLVP)
			rtvp = NULLVP;
		else {
			if (tvp->v_type == VDIR) {
				error = EINVAL;
				goto unionfs_rename_abort;
			}
			rtvp = unp->un_uppervp;
			ltvp = unp->un_lowervp;
			vref(rtvp);
		}
	}

	if (needrelookup != 0) {
		if ((error = vn_lock(fdvp, LK_EXCLUSIVE)) != 0)
			goto unionfs_rename_abort;
		error = unionfs_relookup_for_delete(fdvp, fcnp);
		VOP_UNLOCK(fdvp);
		if (error != 0)
			goto unionfs_rename_abort;

		/* Locke of tvp is canceled in order to avoid recursive lock. */
		if (tvp != NULLVP && tvp != tdvp)
			VOP_UNLOCK(tvp);
		error = unionfs_relookup_for_rename(tdvp, tcnp);
		if (tvp != NULLVP && tvp != tdvp)
			vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
		if (error != 0)
			goto unionfs_rename_abort;
	}

	error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp);

	if (error == 0) {
		if (rtvp != NULLVP && rtvp->v_type == VDIR)
			cache_purge(tdvp);
		if (fvp->v_type == VDIR && fdvp != tdvp)
			cache_purge(fdvp);
	}

	if (fdvp != rfdvp)
		vrele(fdvp);
	if (fvp != rfvp)
		vrele(fvp);
	if (ltdvp != NULLVP)
		VOP_UNLOCK(ltdvp);
	if (tdvp != rtdvp)
		vrele(tdvp);
	if (ltvp != NULLVP)
		VOP_UNLOCK(ltvp);
	if (tvp != rtvp && tvp != NULLVP) {
		if (rtvp == NULLVP)
			vput(tvp);
		else
			vrele(tvp);
	}

	UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);

	return (error);

unionfs_rename_abort:
	if (fdvp != rfdvp)
		vrele(rfdvp);
	if (fvp != rfvp)
		vrele(rfvp);
	if (tdvp != rtdvp)
		vrele(rtdvp);
	vput(tdvp);
	if (tvp != rtvp && rtvp != NULLVP)
		vrele(rtvp);
	if (tvp != NULLVP) {
		if (tdvp != tvp)
			vput(tvp);
		else
			vrele(tvp);
	}
	vrele(fdvp);
	vrele(fvp);

	UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);

	return (error);
}
예제 #10
0
static int
auto_rename(
	vnode_t *odvp,
	char *onm,
	vnode_t *ndvp,
	char *nnm,
	cred_t *cr,
	caller_context_t *ct,
	int flags)
{
	vnode_t *o_newvp, *n_newvp;
	int error;

	AUTOFS_DPRINT((4, "auto_rename odvp %p onm %s to ndvp %p nnm %s\n",
	    (void *)odvp, onm, (void *)ndvp, nnm));

	/*
	 * we know odvp is an autonode, otherwise this function
	 * could not have ever been called.
	 */
	ASSERT(vn_matchops(odvp, auto_vnodeops));

	if (error = auto_trigger_mount(odvp, cr, &o_newvp))
		goto done;

	if (o_newvp == NULL) {
		/*
		 * can't rename an autonode
		 */
		error = ENOSYS;
		goto done;
	}

	if (vn_matchops(ndvp, auto_vnodeops)) {
		/*
		 * directory is AUTOFS, need to trigger the
		 * mount of the real filesystem.
		 */
		if (error = auto_trigger_mount(ndvp, cr, &n_newvp)) {
			VN_RELE(o_newvp);
			goto done;
		}

		if (n_newvp == NULL) {
			/*
			 * target can't be an autonode
			 */
			error = ENOSYS;
			VN_RELE(o_newvp);
			goto done;
		}
	} else {
		/*
		 * destination directory mount had been
		 * triggered prior to the call to this function.
		 */
		n_newvp = ndvp;
	}

	ASSERT(!vn_matchops(n_newvp, auto_vnodeops));

	if (vn_is_readonly(n_newvp)) {
		error = EROFS;
		VN_RELE(o_newvp);
		if (n_newvp != ndvp)
			VN_RELE(n_newvp);
		goto done;
	}

	error = VOP_RENAME(o_newvp, onm, n_newvp, nnm, cr, ct, flags);
	VN_RELE(o_newvp);
	if (n_newvp != ndvp)
		VN_RELE(n_newvp);

done:
	AUTOFS_DPRINT((5, "auto_rename error=%d\n", error));
	return (error);
}
예제 #11
0
/*
 * union_rename(struct vnode *a_fdvp, struct vnode *a_fvp,
 *		struct componentname *a_fcnp, struct vnode *a_tdvp,
 *		struct vnode *a_tvp, struct componentname *a_tcnp)
 */
static int
union_rename(struct vop_old_rename_args *ap)
{
	int error;
	struct vnode *fdvp = ap->a_fdvp;
	struct vnode *fvp = ap->a_fvp;
	struct vnode *tdvp = ap->a_tdvp;
	struct vnode *tvp = ap->a_tvp;

	/*
	 * Figure out what fdvp to pass to our upper or lower vnode.  If we
	 * replace the fdvp, release the original one and ref the new one.
	 */

	if (fdvp->v_tag == VT_UNION) {	/* always true */
		struct union_node *un = VTOUNION(fdvp);
		if (un->un_uppervp == NULLVP) {
			/*
			 * this should never happen in normal
			 * operation but might if there was
			 * a problem creating the top-level shadow
			 * directory.
			 */
			error = EXDEV;
			goto bad;
		}
		fdvp = un->un_uppervp;
		vref(fdvp);
		vrele(ap->a_fdvp);
	}

	/*
	 * Figure out what fvp to pass to our upper or lower vnode.  If we
	 * replace the fvp, release the original one and ref the new one.
	 */

	if (fvp->v_tag == VT_UNION) {	/* always true */
		struct union_node *un = VTOUNION(fvp);
#if 0
		struct union_mount *um = MOUNTTOUNIONMOUNT(fvp->v_mount);
#endif

		if (un->un_uppervp == NULLVP) {
			switch(fvp->v_type) {
			case VREG:
				vn_lock(un->un_vnode, LK_EXCLUSIVE | LK_RETRY);
				error = union_copyup(un, 1, ap->a_fcnp->cn_cred, ap->a_fcnp->cn_td);
				vn_unlock(un->un_vnode);
				if (error)
					goto bad;
				break;
			case VDIR:
				/*
				 * XXX not yet.
				 *
				 * There is only one way to rename a directory
				 * based in the lowervp, and that is to copy
				 * the entire directory hierarchy.  Otherwise
				 * it would not last across a reboot.
				 */
#if 0
				vrele(fvp);
				fvp = NULL;
				vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
				error = union_mkshadow(um, fdvp, 
					    ap->a_fcnp, &un->un_uppervp);
				vn_unlock(fdvp);
				if (un->un_uppervp)
					vn_unlock(un->un_uppervp);
				if (error)
					goto bad;
				break;
#endif
			default:
				error = EXDEV;
				goto bad;
			}
		}

		if (un->un_lowervp != NULLVP)
			ap->a_fcnp->cn_flags |= CNP_DOWHITEOUT;
		fvp = un->un_uppervp;
		vref(fvp);
		vrele(ap->a_fvp);
	}

	/*
	 * Figure out what tdvp (destination directory) to pass to the
	 * lower level.  If we replace it with uppervp, we need to vput the 
	 * old one.  The exclusive lock is transfered to what we will pass
	 * down in the VOP_RENAME and we replace uppervp with a simple
	 * reference.
	 */

	if (tdvp->v_tag == VT_UNION) {
		struct union_node *un = VTOUNION(tdvp);

		if (un->un_uppervp == NULLVP) {
			/*
			 * this should never happen in normal
			 * operation but might if there was
			 * a problem creating the top-level shadow
			 * directory.
			 */
			error = EXDEV;
			goto bad;
		}

		/*
		 * new tdvp is a lock and reference on uppervp, put away
		 * the old tdvp.
		 */
		tdvp = union_lock_upper(un, ap->a_tcnp->cn_td);
		vput(ap->a_tdvp);
	}

	/*
	 * Figure out what tvp (destination file) to pass to the
	 * lower level.
	 *
	 * If the uppervp file does not exist put away the (wrong)
	 * file and change tvp to NULL.
	 */

	if (tvp != NULLVP && tvp->v_tag == VT_UNION) {
		struct union_node *un = VTOUNION(tvp);

		tvp = union_lock_upper(un, ap->a_tcnp->cn_td);
		vput(ap->a_tvp);
		/* note: tvp may be NULL */
	}

	/*
	 * VOP_RENAME releases/vputs prior to returning, so we have no
	 * cleanup to do.
	 */

	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));

	/*
	 * Error.  We still have to release / vput the various elements.
	 */

bad:
	vrele(fdvp);
	if (fvp)
		vrele(fvp);
	vput(tdvp);
	if (tvp != NULLVP) {
		if (tvp != tdvp)
			vput(tvp);
		else
			vrele(tvp);
	}
	return (error);
}
/*
 * Creates the fs cache directory.
 * The directory name is the ascii version of the fsid.
 * Also makes a symlink to the directory using the specified name.
 */
int
fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp)
{
	int error;
	vnode_t *fscdirvp = NULL;
	vnode_t *infovp = NULL;
	vnode_t *attrvp = NULL;
	struct vattr *attrp = (struct vattr *)NULL;
	char name[CFS_FRONTFILE_NAME_SIZE];
	int files;
	int blocks = 0;
	cfs_cid_t cid;
	ino64_t fsid;

	ASSERT(MUTEX_HELD(&cachep->c_fslistlock));
	ASSERT(fscp->fs_infovp == NULL);
	ASSERT(fscp->fs_fscdirvp == NULL);
	ASSERT(fscp->fs_fsattrdir == NULL);

	/* directory, symlink and options file + attrcache dir */
	files = 0;
	while (files < 4) {
		error = cachefs_allocfile(cachep);
		if (error)
			goto out;
		files++;
	}
	error = cachefs_allocblocks(cachep, 4, CACHEFS_RL_NONE);
	if (error)
		goto out;
	blocks = 4;

	attrp = cachefs_kmem_alloc(sizeof (struct vattr), KM_SLEEP);
	attrp->va_mode = S_IFDIR | 0777;
	attrp->va_uid = 0;
	attrp->va_gid = 0;
	attrp->va_type = VDIR;
	attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
	error = VOP_MKDIR(cachep->c_dirvp, namep, attrp, &fscdirvp, kcred,
	    NULL, 0, NULL);
	if (error) {
		cmn_err(CE_WARN, "Can't create fs cache directory");
		goto out;
	}

	/*
	 * Created the directory. Get the fileno. That'll be the cachefs_fsid.
	 */
	attrp->va_mask = AT_NODEID;
	error = VOP_GETATTR(fscdirvp, attrp, 0, kcred, NULL);
	if (error) {
		goto out;
	}
	fsid = attrp->va_nodeid;
	attrp->va_mode = S_IFREG | 0666;
	attrp->va_uid = 0;
	attrp->va_gid = 0;
	attrp->va_type = VREG;
	attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
	error = VOP_CREATE(fscdirvp, CACHEFS_FSINFO, attrp, EXCL,
			0600, &infovp, kcred, 0, NULL, NULL);
	if (error) {
		cmn_err(CE_WARN, "Can't create fs option file");
		goto out;
	}
	attrp->va_size = MAXBSIZE;
	attrp->va_mask = AT_SIZE;
	error = VOP_SETATTR(infovp, attrp, 0, kcred, NULL);
	if (error) {
		cmn_err(CE_WARN, "Can't set size of fsinfo file");
		goto out;
	}

	/* write out the info file */
	fscp->fs_flags |= CFS_FS_DIRTYINFO;
	error = fscache_info_sync(fscp);
	if (error)
		goto out;

	/*
	 * Install the symlink from cachefs_fsid -> directory.
	 */
	cid.cid_flags = 0;
	cid.cid_fileno = fsid;
	make_ascii_name(&cid, name);
	error = VOP_RENAME(cachep->c_dirvp, namep, cachep->c_dirvp,
		name, kcred, NULL, 0);
	if (error) {
		cmn_err(CE_WARN, "Can't rename cache directory");
		goto out;
	}
	attrp->va_mask = AT_MODE | AT_TYPE;
	attrp->va_mode = 0777;
	attrp->va_type = VLNK;
	error = VOP_SYMLINK(cachep->c_dirvp, namep, attrp, name, kcred, NULL,
	    0);
	if (error) {
		cmn_err(CE_WARN, "Can't create cache directory symlink");
		goto out;
	}

	/*
	 * Finally, make the attrcache directory
	 */
	attrp->va_mode = S_IFDIR | 0777;
	attrp->va_uid = 0;
	attrp->va_gid = 0;
	attrp->va_type = VDIR;
	attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
	error = VOP_MKDIR(fscdirvp, ATTRCACHE_NAME, attrp, &attrvp, kcred, NULL,
	    0, NULL);
	if (error) {
		cmn_err(CE_WARN, "Can't create attrcache dir for fscache");
		goto out;
	}

	mutex_enter(&fscp->fs_fslock);
	fscp->fs_cfsid = fsid;
	fscp->fs_fscdirvp = fscdirvp;
	fscp->fs_fsattrdir = attrvp;
	fscp->fs_infovp = infovp;
	mutex_exit(&fscp->fs_fslock);

out:

	if (error) {
		while (files-- > 0)
			cachefs_freefile(cachep);
		if (fscdirvp)
			VN_RELE(fscdirvp);
		if (blocks)
			cachefs_freeblocks(cachep, blocks, CACHEFS_RL_NONE);
		if (attrvp)
			VN_RELE(attrvp);
		if (infovp)
			VN_RELE(infovp);
	}
	if (attrp)
		cachefs_kmem_free(attrp, sizeof (struct vattr));
	return (error);
}