xref: /onnv-gate/usr/src/uts/common/fs/lofs/lofs_vnops.c (revision 1085:21d75ca00d40)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23324Sowenr  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/systm.h>
310Sstevel@tonic-gate #include <sys/errno.h>
320Sstevel@tonic-gate #include <sys/vnode.h>
330Sstevel@tonic-gate #include <sys/vfs.h>
340Sstevel@tonic-gate #include <sys/uio.h>
350Sstevel@tonic-gate #include <sys/cred.h>
360Sstevel@tonic-gate #include <sys/pathname.h>
370Sstevel@tonic-gate #include <sys/debug.h>
380Sstevel@tonic-gate #include <sys/fs/lofs_node.h>
390Sstevel@tonic-gate #include <sys/fs/lofs_info.h>
400Sstevel@tonic-gate #include <fs/fs_subr.h>
410Sstevel@tonic-gate #include <vm/as.h>
420Sstevel@tonic-gate #include <vm/seg.h>
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #define	IS_ZONEDEVFS(vp) \
450Sstevel@tonic-gate 	(vtoli((vp)->v_vfsp)->li_flag & LO_ZONEDEVFS)
460Sstevel@tonic-gate 
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate  * These are the vnode ops routines which implement the vnode interface to
490Sstevel@tonic-gate  * the looped-back file system.  These routines just take their parameters,
500Sstevel@tonic-gate  * and then calling the appropriate real vnode routine(s) to do the work.
510Sstevel@tonic-gate  */
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static int
540Sstevel@tonic-gate lo_open(vnode_t **vpp, int flag, struct cred *cr)
550Sstevel@tonic-gate {
560Sstevel@tonic-gate 	vnode_t *vp = *vpp;
570Sstevel@tonic-gate 	vnode_t *rvp;
580Sstevel@tonic-gate 	vnode_t *oldvp;
590Sstevel@tonic-gate 	int error;
600Sstevel@tonic-gate 
610Sstevel@tonic-gate #ifdef LODEBUG
620Sstevel@tonic-gate 	lo_dprint(4, "lo_open vp %p cnt=%d realvp %p cnt=%d\n",
630Sstevel@tonic-gate 		vp, vp->v_count, realvp(vp), realvp(vp)->v_count);
640Sstevel@tonic-gate #endif
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 	oldvp = vp;
670Sstevel@tonic-gate 	vp = rvp = realvp(vp);
680Sstevel@tonic-gate 	/*
690Sstevel@tonic-gate 	 * Need to hold new reference to vp since VOP_OPEN() may
700Sstevel@tonic-gate 	 * decide to release it.
710Sstevel@tonic-gate 	 */
720Sstevel@tonic-gate 	VN_HOLD(vp);
730Sstevel@tonic-gate 	error = VOP_OPEN(&rvp, flag, cr);
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 	if (!error && rvp != vp) {
760Sstevel@tonic-gate 		/*
770Sstevel@tonic-gate 		 * the FS which we called should have released the
780Sstevel@tonic-gate 		 * new reference on vp
790Sstevel@tonic-gate 		 */
80324Sowenr 		*vpp = makelonode(rvp, vtoli(oldvp->v_vfsp), 0);
810Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
820Sstevel@tonic-gate 			vnode_t *svp;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
850Sstevel@tonic-gate 			VN_RELE(*vpp);
860Sstevel@tonic-gate 			if (svp == NULL)
870Sstevel@tonic-gate 				error = ENOSYS;
880Sstevel@tonic-gate 			else
890Sstevel@tonic-gate 				*vpp = svp;
900Sstevel@tonic-gate 		}
910Sstevel@tonic-gate 		VN_RELE(oldvp);
920Sstevel@tonic-gate 	} else {
930Sstevel@tonic-gate 		ASSERT(rvp->v_count > 1);
940Sstevel@tonic-gate 		VN_RELE(rvp);
950Sstevel@tonic-gate 	}
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	return (error);
980Sstevel@tonic-gate }
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate static int
1010Sstevel@tonic-gate lo_close(
1020Sstevel@tonic-gate 	vnode_t *vp,
1030Sstevel@tonic-gate 	int flag,
1040Sstevel@tonic-gate 	int count,
1050Sstevel@tonic-gate 	offset_t offset,
1060Sstevel@tonic-gate 	struct cred *cr)
1070Sstevel@tonic-gate {
1080Sstevel@tonic-gate #ifdef LODEBUG
1090Sstevel@tonic-gate 	lo_dprint(4, "lo_close vp %p realvp %p\n", vp, realvp(vp));
1100Sstevel@tonic-gate #endif
1110Sstevel@tonic-gate 	vp = realvp(vp);
1120Sstevel@tonic-gate 	return (VOP_CLOSE(vp, flag, count, offset, cr));
1130Sstevel@tonic-gate }
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate static int
1160Sstevel@tonic-gate lo_read(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
1170Sstevel@tonic-gate 	caller_context_t *ct)
1180Sstevel@tonic-gate {
1190Sstevel@tonic-gate #ifdef LODEBUG
1200Sstevel@tonic-gate 	lo_dprint(4, "lo_read vp %p realvp %p\n", vp, realvp(vp));
1210Sstevel@tonic-gate #endif
1220Sstevel@tonic-gate 	vp = realvp(vp);
1230Sstevel@tonic-gate 	return (VOP_READ(vp, uiop, ioflag, cr, ct));
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate static int
1270Sstevel@tonic-gate lo_write(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
1280Sstevel@tonic-gate 	caller_context_t *ct)
1290Sstevel@tonic-gate {
1300Sstevel@tonic-gate #ifdef LODEBUG
1310Sstevel@tonic-gate 	lo_dprint(4, "lo_write vp %p realvp %p\n", vp, realvp(vp));
1320Sstevel@tonic-gate #endif
1330Sstevel@tonic-gate 	vp = realvp(vp);
1340Sstevel@tonic-gate 	return (VOP_WRITE(vp, uiop, ioflag, cr, ct));
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate static int
1380Sstevel@tonic-gate lo_ioctl(
1390Sstevel@tonic-gate 	vnode_t *vp,
1400Sstevel@tonic-gate 	int cmd,
1410Sstevel@tonic-gate 	intptr_t arg,
1420Sstevel@tonic-gate 	int flag,
1430Sstevel@tonic-gate 	struct cred *cr,
1440Sstevel@tonic-gate 	int *rvalp)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate #ifdef LODEBUG
1470Sstevel@tonic-gate 	lo_dprint(4, "lo_ioctl vp %p realvp %p\n", vp, realvp(vp));
1480Sstevel@tonic-gate #endif
1490Sstevel@tonic-gate 	vp = realvp(vp);
1500Sstevel@tonic-gate 	return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp));
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate static int
1540Sstevel@tonic-gate lo_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr)
1550Sstevel@tonic-gate {
1560Sstevel@tonic-gate 	vp = realvp(vp);
1570Sstevel@tonic-gate 	return (VOP_SETFL(vp, oflags, nflags, cr));
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate static int
1610Sstevel@tonic-gate lo_getattr(
1620Sstevel@tonic-gate 	vnode_t *vp,
1630Sstevel@tonic-gate 	struct vattr *vap,
1640Sstevel@tonic-gate 	int flags,
1650Sstevel@tonic-gate 	struct cred *cr)
1660Sstevel@tonic-gate {
1670Sstevel@tonic-gate 	int error;
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate #ifdef LODEBUG
1700Sstevel@tonic-gate 	lo_dprint(4, "lo_getattr vp %p realvp %p\n", vp, realvp(vp));
1710Sstevel@tonic-gate #endif
1720Sstevel@tonic-gate 	if (error = VOP_GETATTR(realvp(vp), vap, flags, cr))
1730Sstevel@tonic-gate 		return (error);
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	/*
1760Sstevel@tonic-gate 	 * In zonedevfs mode, we pull a nasty trick; we make sure that
1770Sstevel@tonic-gate 	 * the dev_t does *not* reflect the underlying device, so that
1780Sstevel@tonic-gate 	 * no renames can occur to or from the /dev hierarchy.
1790Sstevel@tonic-gate 	 */
1800Sstevel@tonic-gate 	if (IS_ZONEDEVFS(vp)) {
1810Sstevel@tonic-gate 		vap->va_fsid = expldev(vp->v_vfsp->vfs_fsid.val[0]);
1820Sstevel@tonic-gate 	}
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	return (0);
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate static int
1880Sstevel@tonic-gate lo_setattr(
1890Sstevel@tonic-gate 	vnode_t *vp,
1900Sstevel@tonic-gate 	struct vattr *vap,
1910Sstevel@tonic-gate 	int flags,
1920Sstevel@tonic-gate 	struct cred *cr,
1930Sstevel@tonic-gate 	caller_context_t *ct)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate #ifdef LODEBUG
1960Sstevel@tonic-gate 	lo_dprint(4, "lo_setattr vp %p realvp %p\n", vp, realvp(vp));
1970Sstevel@tonic-gate #endif
1980Sstevel@tonic-gate 	if (IS_ZONEDEVFS(vp) && !IS_DEVVP(vp)) {
1990Sstevel@tonic-gate 		return (EACCES);
2000Sstevel@tonic-gate 	}
2010Sstevel@tonic-gate 	vp = realvp(vp);
2020Sstevel@tonic-gate 	return (VOP_SETATTR(vp, vap, flags, cr, ct));
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate static int
2060Sstevel@tonic-gate lo_access(vnode_t *vp, int mode, int flags, struct cred *cr)
2070Sstevel@tonic-gate {
2080Sstevel@tonic-gate #ifdef LODEBUG
2090Sstevel@tonic-gate 	lo_dprint(4, "lo_access vp %p realvp %p\n", vp, realvp(vp));
2100Sstevel@tonic-gate #endif
2110Sstevel@tonic-gate 	if (mode & VWRITE) {
2120Sstevel@tonic-gate 		if (vp->v_type == VREG && vn_is_readonly(vp))
2130Sstevel@tonic-gate 			return (EROFS);
2140Sstevel@tonic-gate 		if (IS_ZONEDEVFS(vp) && !IS_DEVVP(vp))
2150Sstevel@tonic-gate 			return (EACCES);
2160Sstevel@tonic-gate 	}
2170Sstevel@tonic-gate 	vp = realvp(vp);
2180Sstevel@tonic-gate 	return (VOP_ACCESS(vp, mode, flags, cr));
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate static int
2220Sstevel@tonic-gate lo_fsync(vnode_t *vp, int syncflag, struct cred *cr)
2230Sstevel@tonic-gate {
2240Sstevel@tonic-gate #ifdef LODEBUG
2250Sstevel@tonic-gate 	lo_dprint(4, "lo_fsync vp %p realvp %p\n", vp, realvp(vp));
2260Sstevel@tonic-gate #endif
2270Sstevel@tonic-gate 	vp = realvp(vp);
2280Sstevel@tonic-gate 	return (VOP_FSYNC(vp, syncflag, cr));
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate /*ARGSUSED*/
2320Sstevel@tonic-gate static void
2330Sstevel@tonic-gate lo_inactive(vnode_t *vp, struct cred *cr)
2340Sstevel@tonic-gate {
2350Sstevel@tonic-gate #ifdef LODEBUG
2360Sstevel@tonic-gate 	lo_dprint(4, "lo_inactive %p, realvp %p\n", vp, realvp(vp));
2370Sstevel@tonic-gate #endif
2380Sstevel@tonic-gate 	freelonode(vtol(vp));
2390Sstevel@tonic-gate }
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate /* ARGSUSED */
2420Sstevel@tonic-gate static int
2430Sstevel@tonic-gate lo_fid(vnode_t *vp, struct fid *fidp)
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate #ifdef LODEBUG
2460Sstevel@tonic-gate 	lo_dprint(4, "lo_fid %p, realvp %p\n", vp, realvp(vp));
2470Sstevel@tonic-gate #endif
2480Sstevel@tonic-gate 	vp = realvp(vp);
2490Sstevel@tonic-gate 	return (VOP_FID(vp, fidp));
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate /*
2530Sstevel@tonic-gate  * Given a vnode of lofs type, lookup nm name and
2540Sstevel@tonic-gate  * return a shadow vnode (of lofs type) of the
2550Sstevel@tonic-gate  * real vnode found.
2560Sstevel@tonic-gate  *
2570Sstevel@tonic-gate  * Due to the nature of lofs, there is a potential
2580Sstevel@tonic-gate  * looping in path traversal.
2590Sstevel@tonic-gate  *
2600Sstevel@tonic-gate  * starting from the mount point of an lofs;
2610Sstevel@tonic-gate  * a loop is defined to be a traversal path
2620Sstevel@tonic-gate  * where the mount point or the real vnode of
2630Sstevel@tonic-gate  * the root of this lofs is encountered twice.
2640Sstevel@tonic-gate  * Once at the start of traversal and second
2650Sstevel@tonic-gate  * when the looping is found.
2660Sstevel@tonic-gate  *
2670Sstevel@tonic-gate  * When a loop is encountered, a shadow of the
2680Sstevel@tonic-gate  * covered vnode is returned to stop the looping.
2690Sstevel@tonic-gate  *
2700Sstevel@tonic-gate  * This normally works, but with the advent of
2710Sstevel@tonic-gate  * the new automounter, returning the shadow of the
2720Sstevel@tonic-gate  * covered vnode (autonode, in this case) does not
2730Sstevel@tonic-gate  * stop the loop.  Because further lookup on this
2740Sstevel@tonic-gate  * lonode will cause the autonode to call lo_lookup()
2750Sstevel@tonic-gate  * on the lonode covering it.
2760Sstevel@tonic-gate  *
2770Sstevel@tonic-gate  * example "/net/jurassic/net/jurassic" is a loop.
2780Sstevel@tonic-gate  * returning the shadow of the autonode corresponding to
2790Sstevel@tonic-gate  * "/net/jurassic/net/jurassic" will not terminate the
2800Sstevel@tonic-gate  * loop.   To solve this problem we allow the loop to go
281324Sowenr  * through one more level component lookup.  Whichever
282324Sowenr  * directory is then looked up in "/net/jurassic/net/jurassic"
283324Sowenr  * the vnode returned is the vnode covered by the autonode
284324Sowenr  * "net" and this will terminate the loop.
2850Sstevel@tonic-gate  *
2860Sstevel@tonic-gate  * Lookup for dot dot has to be dealt with separately.
2870Sstevel@tonic-gate  * It will be nice to have a "one size fits all" kind
2880Sstevel@tonic-gate  * of solution, so that we don't have so many ifs statement
2890Sstevel@tonic-gate  * in the lo_lookup() to handle dotdot.  But, since
2900Sstevel@tonic-gate  * there are so many special cases to handle different
2910Sstevel@tonic-gate  * kinds looping above, we need special codes to handle
2920Sstevel@tonic-gate  * dotdot lookup as well.
2930Sstevel@tonic-gate  */
2940Sstevel@tonic-gate static int
2950Sstevel@tonic-gate lo_lookup(
2960Sstevel@tonic-gate 	vnode_t *dvp,
2970Sstevel@tonic-gate 	char *nm,
2980Sstevel@tonic-gate 	vnode_t **vpp,
2990Sstevel@tonic-gate 	struct pathname *pnp,
3000Sstevel@tonic-gate 	int flags,
3010Sstevel@tonic-gate 	vnode_t *rdir,
3020Sstevel@tonic-gate 	struct cred *cr)
3030Sstevel@tonic-gate {
3040Sstevel@tonic-gate 	vnode_t *vp = NULL, *tvp = NULL, *nonlovp;
3050Sstevel@tonic-gate 	int error, is_indirectloop;
3060Sstevel@tonic-gate 	vnode_t *realdvp = realvp(dvp);
3070Sstevel@tonic-gate 	struct loinfo *li = vtoli(dvp->v_vfsp);
3080Sstevel@tonic-gate 	int looping = 0;
309324Sowenr 	int autoloop = 0;
3100Sstevel@tonic-gate 	int doingdotdot = 0;
3110Sstevel@tonic-gate 	int nosub = 0;
312324Sowenr 	int mkflag = 0;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	/*
3150Sstevel@tonic-gate 	 * If name is empty and no XATTR flags are set, then return
3160Sstevel@tonic-gate 	 * dvp (empty name == lookup ".").  If an XATTR flag is set
3170Sstevel@tonic-gate 	 * then we need to call VOP_LOOKUP to get the xattr dir.
3180Sstevel@tonic-gate 	 */
3190Sstevel@tonic-gate 	if (nm[0] == '\0' && ! (flags & (CREATE_XATTR_DIR|LOOKUP_XATTR))) {
3200Sstevel@tonic-gate 		VN_HOLD(dvp);
3210Sstevel@tonic-gate 		*vpp = dvp;
3220Sstevel@tonic-gate 		return (0);
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
3260Sstevel@tonic-gate 		doingdotdot++;
3270Sstevel@tonic-gate 		/*
3280Sstevel@tonic-gate 		 * Handle ".." out of mounted filesystem
3290Sstevel@tonic-gate 		 */
3300Sstevel@tonic-gate 		while ((realdvp->v_flag & VROOT) && realdvp != rootdir) {
3310Sstevel@tonic-gate 			realdvp = realdvp->v_vfsp->vfs_vnodecovered;
3320Sstevel@tonic-gate 			ASSERT(realdvp != NULL);
3330Sstevel@tonic-gate 		}
3340Sstevel@tonic-gate 	}
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	*vpp = NULL;	/* default(error) case */
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	/*
3390Sstevel@tonic-gate 	 * Do the normal lookup
3400Sstevel@tonic-gate 	 */
341*1085Smarks 	if (error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr)) {
342*1085Smarks 		vp = NULL;
3430Sstevel@tonic-gate 		goto out;
344*1085Smarks 	}
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	/*
3470Sstevel@tonic-gate 	 * We do this check here to avoid returning a stale file handle to the
3480Sstevel@tonic-gate 	 * caller.
3490Sstevel@tonic-gate 	 */
3500Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == '\0') {
3510Sstevel@tonic-gate 		ASSERT(vp == realdvp);
3520Sstevel@tonic-gate 		VN_HOLD(dvp);
3530Sstevel@tonic-gate 		VN_RELE(vp);
3540Sstevel@tonic-gate 		*vpp = dvp;
3550Sstevel@tonic-gate 		return (0);
3560Sstevel@tonic-gate 	}
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	if (doingdotdot) {
359324Sowenr 		if ((vtol(dvp))->lo_looping & LO_LOOPING) {
3600Sstevel@tonic-gate 			vfs_t *vfsp;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 			error = vn_vfswlock_wait(realdvp);
3630Sstevel@tonic-gate 			if (error)
3640Sstevel@tonic-gate 				goto out;
3650Sstevel@tonic-gate 			vfsp = vn_mountedvfs(realdvp);
366324Sowenr 			/*
367324Sowenr 			 * In the standard case if the looping flag is set and
368324Sowenr 			 * performing dotdot we would be returning from a
369324Sowenr 			 * covered vnode, implying vfsp could not be null. The
370324Sowenr 			 * exceptions being if we have looping and overlay
371324Sowenr 			 * mounts or looping and covered file systems.
372324Sowenr 			 */
373324Sowenr 			if (vfsp == NULL) {
3740Sstevel@tonic-gate 				/*
375324Sowenr 				 * Overlay mount or covered file system,
376324Sowenr 				 * so just make the shadow node.
3770Sstevel@tonic-gate 				 */
3780Sstevel@tonic-gate 				vn_vfsunlock(realdvp);
379324Sowenr 				*vpp = makelonode(vp, li, 0);
380324Sowenr 				(vtol(*vpp))->lo_looping |= LO_LOOPING;
381324Sowenr 				return (0);
382324Sowenr 			}
383324Sowenr 			/*
384324Sowenr 			 * When looping get the actual found vnode
385324Sowenr 			 * instead of the vnode covered.
386324Sowenr 			 * Here we have to hold the lock for realdvp
387324Sowenr 			 * since an unmount during the traversal to the
388324Sowenr 			 * root vnode would turn *vfsp into garbage
389324Sowenr 			 * which would be fatal.
390324Sowenr 			 */
391324Sowenr 			vfs_lock_wait(vfsp);
392324Sowenr 			vn_vfsunlock(realdvp);
3930Sstevel@tonic-gate 
394324Sowenr 			error = VFS_ROOT(vfsp, &tvp);
395324Sowenr 
396324Sowenr 			vfs_unlock(vfsp);
397324Sowenr 			if (error)
398324Sowenr 				goto out;
399324Sowenr 			if ((tvp == li->li_rootvp) && (vp == realvp(tvp))) {
400324Sowenr 				/*
401324Sowenr 				 * we're back at the real vnode
402324Sowenr 				 * of the rootvp
403324Sowenr 				 *
404324Sowenr 				 * return the rootvp
405324Sowenr 				 * Ex: /mnt/mnt/..
406324Sowenr 				 * where / has been lofs-mounted
407324Sowenr 				 * onto /mnt.  Return the lofs
408324Sowenr 				 * node mounted at /mnt.
409324Sowenr 				 */
410324Sowenr 				*vpp = tvp;
411324Sowenr 				VN_RELE(vp);
412324Sowenr 				return (0);
4130Sstevel@tonic-gate 			} else {
4140Sstevel@tonic-gate 				/*
415324Sowenr 				 * We are returning from a covered
416324Sowenr 				 * node whose vfs_mountedhere is
417324Sowenr 				 * not pointing to vfs of the current
418324Sowenr 				 * root vnode.
419324Sowenr 				 * This is a condn where in we
420324Sowenr 				 * returned a covered node say Zc
421324Sowenr 				 * but Zc is not the cover of current
422324Sowenr 				 * root.
423324Sowenr 				 * i.e.., if X is the root vnode
424324Sowenr 				 * lookup(Zc,"..") is taking us to
425324Sowenr 				 * X.
426324Sowenr 				 * Ex: /net/X/net/X/Y
4270Sstevel@tonic-gate 				 *
428324Sowenr 				 * If LO_AUTOLOOP (autofs/lofs looping detected)
429324Sowenr 				 * has been set then we are encountering the
430324Sowenr 				 * cover of Y (Y being any directory vnode
431324Sowenr 				 * under /net/X/net/X/).
432324Sowenr 				 * When performing a dotdot set the
433324Sowenr 				 * returned vp to the vnode covered
434324Sowenr 				 * by the mounted lofs, ie /net/X/net/X
4350Sstevel@tonic-gate 				 */
436324Sowenr 				VN_RELE(tvp);
437324Sowenr 				if ((vtol(dvp))->lo_looping & LO_AUTOLOOP) {
438324Sowenr 					VN_RELE(vp);
439324Sowenr 					vp = li->li_rootvp;
440324Sowenr 					vp = vp->v_vfsp->vfs_vnodecovered;
4410Sstevel@tonic-gate 					VN_HOLD(vp);
442324Sowenr 					*vpp = makelonode(vp, li, 0);
443324Sowenr 					(vtol(*vpp))->lo_looping |= LO_LOOPING;
444324Sowenr 					return (0);
4450Sstevel@tonic-gate 				}
4460Sstevel@tonic-gate 			}
4470Sstevel@tonic-gate 		} else {
4480Sstevel@tonic-gate 			/*
4490Sstevel@tonic-gate 			 * No frills just make the shadow node.
4500Sstevel@tonic-gate 			 */
451324Sowenr 			*vpp = makelonode(vp, li, 0);
4520Sstevel@tonic-gate 			return (0);
4530Sstevel@tonic-gate 		}
4540Sstevel@tonic-gate 	}
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	nosub = (vtoli(dvp->v_vfsp)->li_flag & LO_NOSUB);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	/*
4590Sstevel@tonic-gate 	 * If this vnode is mounted on, then we
4600Sstevel@tonic-gate 	 * traverse to the vnode which is the root of
4610Sstevel@tonic-gate 	 * the mounted file system.
4620Sstevel@tonic-gate 	 */
4630Sstevel@tonic-gate 	if (!nosub && (error = traverse(&vp)))
4640Sstevel@tonic-gate 		goto out;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	/*
4670Sstevel@tonic-gate 	 * Make a lnode for the real vnode.
4680Sstevel@tonic-gate 	 */
4690Sstevel@tonic-gate 	if (vp->v_type != VDIR || nosub) {
470324Sowenr 		*vpp = makelonode(vp, li, 0);
4710Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
4720Sstevel@tonic-gate 			vnode_t *svp;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
4750Sstevel@tonic-gate 			VN_RELE(*vpp);
4760Sstevel@tonic-gate 			if (svp == NULL)
4770Sstevel@tonic-gate 				error = ENOSYS;
4780Sstevel@tonic-gate 			else
4790Sstevel@tonic-gate 				*vpp = svp;
4800Sstevel@tonic-gate 		}
4810Sstevel@tonic-gate 		return (error);
4820Sstevel@tonic-gate 	}
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 	/*
4850Sstevel@tonic-gate 	 * if the found vnode (vp) is not of type lofs
4860Sstevel@tonic-gate 	 * then we're just going to make a shadow of that
4870Sstevel@tonic-gate 	 * vp and get out.
4880Sstevel@tonic-gate 	 *
4890Sstevel@tonic-gate 	 * If the found vnode (vp) is of lofs type, and
4900Sstevel@tonic-gate 	 * we're not doing dotdot, check if we are
4910Sstevel@tonic-gate 	 * looping.
4920Sstevel@tonic-gate 	 */
4930Sstevel@tonic-gate 	if (!doingdotdot && vfs_matchops(vp->v_vfsp, lo_vfsops)) {
4940Sstevel@tonic-gate 		/*
4950Sstevel@tonic-gate 		 * Check if we're looping, i.e.
4960Sstevel@tonic-gate 		 * vp equals the root vp of the lofs, directly
4970Sstevel@tonic-gate 		 * or indirectly, return the covered node.
4980Sstevel@tonic-gate 		 */
4990Sstevel@tonic-gate 
500324Sowenr 		if (!((vtol(dvp))->lo_looping & LO_LOOPING)) {
5010Sstevel@tonic-gate 			if (vp == li->li_rootvp) {
5020Sstevel@tonic-gate 				/*
5030Sstevel@tonic-gate 				 * Direct looping condn.
5040Sstevel@tonic-gate 				 * Ex:- X is / mounted directory so lookup of
5050Sstevel@tonic-gate 				 * /X/X is a direct looping condn.
5060Sstevel@tonic-gate 				 */
5070Sstevel@tonic-gate 				tvp = vp;
5080Sstevel@tonic-gate 				vp = vp->v_vfsp->vfs_vnodecovered;
5090Sstevel@tonic-gate 				VN_HOLD(vp);
5100Sstevel@tonic-gate 				VN_RELE(tvp);
5110Sstevel@tonic-gate 				looping++;
5120Sstevel@tonic-gate 			} else {
5130Sstevel@tonic-gate 				/*
5140Sstevel@tonic-gate 				 * Indirect looping can be defined as
5150Sstevel@tonic-gate 				 * real lookup returning rootvp of the current
5160Sstevel@tonic-gate 				 * tree in any level of recursion.
5170Sstevel@tonic-gate 				 *
5180Sstevel@tonic-gate 				 * This check is useful if there are multiple
5190Sstevel@tonic-gate 				 * levels of lofs indirections. Suppose vnode X
5200Sstevel@tonic-gate 				 * in the current lookup has as its real vnode
5210Sstevel@tonic-gate 				 * another lofs node. Y = realvp(X) Y should be
5220Sstevel@tonic-gate 				 * a lofs node for the check to continue or Y
5230Sstevel@tonic-gate 				 * is not the rootvp of X.
5240Sstevel@tonic-gate 				 * Ex:- say X and Y are two vnodes
5250Sstevel@tonic-gate 				 * say real(Y) is X and real(X) is Z
5260Sstevel@tonic-gate 				 * parent vnode for X and Y is Z
5270Sstevel@tonic-gate 				 * lookup(Y,"path") say we are looking for Y
5280Sstevel@tonic-gate 				 * again under Y and we have to return Yc.
5290Sstevel@tonic-gate 				 * but the lookup of Y under Y doesnot return
5300Sstevel@tonic-gate 				 * Y the root vnode again here is why.
5310Sstevel@tonic-gate 				 * 1. lookup(Y,"path of Y") will go to
5320Sstevel@tonic-gate 				 * 2. lookup(real(Y),"path of Y") and then to
5330Sstevel@tonic-gate 				 * 3. lookup(real(X),"path of Y").
5340Sstevel@tonic-gate 				 * and now what lookup level 1 sees is the
5350Sstevel@tonic-gate 				 * outcome of 2 but the vnode Y is due to
5360Sstevel@tonic-gate 				 * lookup(Z,"path of Y") so we have to skip
5370Sstevel@tonic-gate 				 * intermediate levels to find if in any level
5380Sstevel@tonic-gate 				 * there is a looping.
5390Sstevel@tonic-gate 				 */
5400Sstevel@tonic-gate 				is_indirectloop = 0;
5410Sstevel@tonic-gate 				nonlovp = vp;
5420Sstevel@tonic-gate 				while (
5430Sstevel@tonic-gate 				    vfs_matchops(nonlovp->v_vfsp, lo_vfsops) &&
5440Sstevel@tonic-gate 				    !(is_indirectloop)) {
5450Sstevel@tonic-gate 					if (li->li_rootvp  == nonlovp) {
5460Sstevel@tonic-gate 						is_indirectloop++;
5470Sstevel@tonic-gate 						break;
5480Sstevel@tonic-gate 					}
5490Sstevel@tonic-gate 					nonlovp = realvp(nonlovp);
5500Sstevel@tonic-gate 				}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 				if (is_indirectloop) {
5530Sstevel@tonic-gate 					VN_RELE(vp);
5540Sstevel@tonic-gate 					vp = nonlovp;
5550Sstevel@tonic-gate 					vp = vp->v_vfsp->vfs_vnodecovered;
5560Sstevel@tonic-gate 					VN_HOLD(vp);
5570Sstevel@tonic-gate 					looping++;
5580Sstevel@tonic-gate 				}
5590Sstevel@tonic-gate 			}
5600Sstevel@tonic-gate 		} else {
5610Sstevel@tonic-gate 			/*
5620Sstevel@tonic-gate 			 * come here only because of the interaction between
5630Sstevel@tonic-gate 			 * the autofs and lofs.
5640Sstevel@tonic-gate 			 *
5650Sstevel@tonic-gate 			 * Lookup of "/net/X/net/X" will return a shadow of
5660Sstevel@tonic-gate 			 * an autonode X_a which we call X_l.
5670Sstevel@tonic-gate 			 *
5680Sstevel@tonic-gate 			 * Lookup of anything under X_l, will trigger a call to
5690Sstevel@tonic-gate 			 * auto_lookup(X_a,nm) which will eventually call
5700Sstevel@tonic-gate 			 * lo_lookup(X_lr,nm) where X_lr is the root vnode of
5710Sstevel@tonic-gate 			 * the current lofs.
5720Sstevel@tonic-gate 			 *
5730Sstevel@tonic-gate 			 * We come here only when we are called with X_l as dvp
5740Sstevel@tonic-gate 			 * and look for something underneath.
5750Sstevel@tonic-gate 			 *
576324Sowenr 			 * Now that an autofs/lofs looping condition has been
577324Sowenr 			 * identified any directory vnode contained within
578324Sowenr 			 * dvp will be set to the vnode covered by the
579324Sowenr 			 * mounted autofs. Thus all directories within dvp
580324Sowenr 			 * will appear empty hence teminating the looping.
581324Sowenr 			 * The LO_AUTOLOOP flag is set on the returned lonode
582324Sowenr 			 * to indicate the termination of the autofs/lofs
583324Sowenr 			 * looping. This is required for the correct behaviour
584324Sowenr 			 * when performing a dotdot.
5850Sstevel@tonic-gate 			 */
5860Sstevel@tonic-gate 			realdvp = realvp(dvp);
5870Sstevel@tonic-gate 			while (vfs_matchops(realdvp->v_vfsp, lo_vfsops)) {
5880Sstevel@tonic-gate 				realdvp = realvp(realdvp);
5890Sstevel@tonic-gate 			}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 			error = VFS_ROOT(realdvp->v_vfsp, &tvp);
5920Sstevel@tonic-gate 			if (error)
5930Sstevel@tonic-gate 				goto out;
5940Sstevel@tonic-gate 			/*
5950Sstevel@tonic-gate 			 * tvp now contains the rootvp of the vfs of the
596324Sowenr 			 * real vnode of dvp. The directory vnode vp is set
597324Sowenr 			 * to the covered vnode to terminate looping. No
598324Sowenr 			 * distinction is made between any vp as all directory
599324Sowenr 			 * vnodes contained in dvp are returned as the covered
600324Sowenr 			 * vnode.
6010Sstevel@tonic-gate 			 */
602324Sowenr 			VN_RELE(vp);
603324Sowenr 			vp = tvp;	/* this is an autonode */
6040Sstevel@tonic-gate 
605324Sowenr 			/*
606324Sowenr 			 * Need to find the covered vnode
607324Sowenr 			 */
608324Sowenr 			vp = vp->v_vfsp->vfs_vnodecovered;
609324Sowenr 			ASSERT(vp);
610324Sowenr 			VN_HOLD(vp);
611324Sowenr 			VN_RELE(tvp);
612324Sowenr 			/*
613324Sowenr 			 * Force the creation of a new lnode even if the hash
614324Sowenr 			 * table contains a lnode that references this vnode.
615324Sowenr 			 */
616324Sowenr 			mkflag = LOF_FORCE;
617324Sowenr 			autoloop++;
6180Sstevel@tonic-gate 		}
6190Sstevel@tonic-gate 	}
620324Sowenr 	*vpp = makelonode(vp, li, mkflag);
6210Sstevel@tonic-gate 
622324Sowenr 	if ((looping) ||
623324Sowenr 	    (((vtol(dvp))->lo_looping & LO_LOOPING) && !doingdotdot)) {
624324Sowenr 		(vtol(*vpp))->lo_looping |= LO_LOOPING;
625324Sowenr 	}
626324Sowenr 
627324Sowenr 	if (autoloop) {
628324Sowenr 		(vtol(*vpp))->lo_looping |= LO_AUTOLOOP;
6290Sstevel@tonic-gate 	}
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate out:
6320Sstevel@tonic-gate 	if (error != 0 && vp != NULL)
6330Sstevel@tonic-gate 		VN_RELE(vp);
6340Sstevel@tonic-gate #ifdef LODEBUG
6350Sstevel@tonic-gate 	lo_dprint(4,
6360Sstevel@tonic-gate 	"lo_lookup dvp %x realdvp %x nm '%s' newvp %x real vp %x error %d\n",
6370Sstevel@tonic-gate 		dvp, realvp(dvp), nm, *vpp, vp, error);
6380Sstevel@tonic-gate #endif
6390Sstevel@tonic-gate 	return (error);
6400Sstevel@tonic-gate }
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate /*ARGSUSED*/
6430Sstevel@tonic-gate static int
6440Sstevel@tonic-gate lo_create(
6450Sstevel@tonic-gate 	vnode_t *dvp,
6460Sstevel@tonic-gate 	char *nm,
6470Sstevel@tonic-gate 	struct vattr *va,
6480Sstevel@tonic-gate 	enum vcexcl exclusive,
6490Sstevel@tonic-gate 	int mode,
6500Sstevel@tonic-gate 	vnode_t **vpp,
6510Sstevel@tonic-gate 	struct cred *cr,
6520Sstevel@tonic-gate 	int flag)
6530Sstevel@tonic-gate {
6540Sstevel@tonic-gate 	int error;
6550Sstevel@tonic-gate 	vnode_t *vp = NULL;
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate #ifdef LODEBUG
6580Sstevel@tonic-gate 	lo_dprint(4, "lo_create vp %p realvp %p\n", dvp, realvp(dvp));
6590Sstevel@tonic-gate #endif
6600Sstevel@tonic-gate 	if (*nm == '\0') {
6610Sstevel@tonic-gate 		ASSERT(vpp && dvp == *vpp);
6620Sstevel@tonic-gate 		vp = realvp(*vpp);
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	if (IS_ZONEDEVFS(dvp)) {
6660Sstevel@tonic-gate 		/* Is this truly a create?  If so, fail */
6670Sstevel@tonic-gate 		if (*vpp == NULL)
6680Sstevel@tonic-gate 			return (EACCES);
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 		/* Is this an open of a non-special for writing?  If so, fail */
6710Sstevel@tonic-gate 		if (*vpp != NULL && (mode & VWRITE) && !IS_DEVVP(*vpp))
6720Sstevel@tonic-gate 			return (EACCES);
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	error = VOP_CREATE(realvp(dvp), nm, va, exclusive, mode, &vp, cr, flag);
6760Sstevel@tonic-gate 	if (!error) {
677324Sowenr 		*vpp = makelonode(vp, vtoli(dvp->v_vfsp), 0);
6780Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
6790Sstevel@tonic-gate 			vnode_t *svp;
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
6820Sstevel@tonic-gate 			VN_RELE(*vpp);
6830Sstevel@tonic-gate 			if (svp == NULL)
6840Sstevel@tonic-gate 				error = ENOSYS;
6850Sstevel@tonic-gate 			else
6860Sstevel@tonic-gate 				*vpp = svp;
6870Sstevel@tonic-gate 		}
6880Sstevel@tonic-gate 	}
6890Sstevel@tonic-gate 	return (error);
6900Sstevel@tonic-gate }
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate static int
6930Sstevel@tonic-gate lo_remove(vnode_t *dvp, char *nm, struct cred *cr)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate #ifdef LODEBUG
6960Sstevel@tonic-gate 	lo_dprint(4, "lo_remove vp %p realvp %p\n", dvp, realvp(dvp));
6970Sstevel@tonic-gate #endif
6980Sstevel@tonic-gate 	if (IS_ZONEDEVFS(dvp))
6990Sstevel@tonic-gate 		return (EACCES);
7000Sstevel@tonic-gate 	dvp = realvp(dvp);
7010Sstevel@tonic-gate 	return (VOP_REMOVE(dvp, nm, cr));
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate static int
7050Sstevel@tonic-gate lo_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr)
7060Sstevel@tonic-gate {
7070Sstevel@tonic-gate #ifdef LODEBUG
7080Sstevel@tonic-gate 	lo_dprint(4, "lo_link vp %p realvp %p\n", vp, realvp(vp));
7090Sstevel@tonic-gate #endif
7100Sstevel@tonic-gate 	while (vn_matchops(vp, lo_vnodeops)) {
7110Sstevel@tonic-gate 		if (IS_ZONEDEVFS(vp))
7120Sstevel@tonic-gate 			return (EACCES);
7130Sstevel@tonic-gate 		vp = realvp(vp);
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 	while (vn_matchops(tdvp, lo_vnodeops)) {
7160Sstevel@tonic-gate 		if (IS_ZONEDEVFS(tdvp))
7170Sstevel@tonic-gate 			return (EACCES);
7180Sstevel@tonic-gate 		tdvp = realvp(tdvp);
7190Sstevel@tonic-gate 	}
7200Sstevel@tonic-gate 	if (vp->v_vfsp != tdvp->v_vfsp)
7210Sstevel@tonic-gate 		return (EXDEV);
7220Sstevel@tonic-gate 	return (VOP_LINK(tdvp, vp, tnm, cr));
7230Sstevel@tonic-gate }
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate static int
7260Sstevel@tonic-gate lo_rename(
7270Sstevel@tonic-gate 	vnode_t *odvp,
7280Sstevel@tonic-gate 	char *onm,
7290Sstevel@tonic-gate 	vnode_t *ndvp,
7300Sstevel@tonic-gate 	char *nnm,
7310Sstevel@tonic-gate 	struct cred *cr)
7320Sstevel@tonic-gate {
7330Sstevel@tonic-gate 	vnode_t *tnvp;
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate #ifdef LODEBUG
7360Sstevel@tonic-gate 	lo_dprint(4, "lo_rename vp %p realvp %p\n", odvp, realvp(odvp));
7370Sstevel@tonic-gate #endif
7380Sstevel@tonic-gate 	if (IS_ZONEDEVFS(odvp))
7390Sstevel@tonic-gate 		return (EACCES);
7400Sstevel@tonic-gate 	/*
7410Sstevel@tonic-gate 	 * We need to make sure we're not trying to remove a mount point for a
7420Sstevel@tonic-gate 	 * filesystem mounted on top of lofs, which only we know about.
7430Sstevel@tonic-gate 	 */
7440Sstevel@tonic-gate 	if (vn_matchops(ndvp, lo_vnodeops))	/* Not our problem. */
7450Sstevel@tonic-gate 		goto rename;
7460Sstevel@tonic-gate 	if (VOP_LOOKUP(ndvp, nnm, &tnvp, NULL, 0, NULL, cr) != 0)
7470Sstevel@tonic-gate 		goto rename;
7480Sstevel@tonic-gate 	if (tnvp->v_type != VDIR) {
7490Sstevel@tonic-gate 		VN_RELE(tnvp);
7500Sstevel@tonic-gate 		goto rename;
7510Sstevel@tonic-gate 	}
7520Sstevel@tonic-gate 	if (vn_mountedvfs(tnvp)) {
7530Sstevel@tonic-gate 		VN_RELE(tnvp);
7540Sstevel@tonic-gate 		return (EBUSY);
7550Sstevel@tonic-gate 	}
7560Sstevel@tonic-gate 	VN_RELE(tnvp);
7570Sstevel@tonic-gate rename:
7580Sstevel@tonic-gate 	/*
7590Sstevel@tonic-gate 	 * Since the case we're dealing with above can happen at any layer in
7600Sstevel@tonic-gate 	 * the stack of lofs filesystems, we need to recurse down the stack,
7610Sstevel@tonic-gate 	 * checking to see if there are any instances of a filesystem mounted on
7620Sstevel@tonic-gate 	 * top of lofs. In order to keep on using the lofs version of
7630Sstevel@tonic-gate 	 * VOP_RENAME(), we make sure that while the target directory is of type
7640Sstevel@tonic-gate 	 * lofs, the source directory (the one used for getting the fs-specific
7650Sstevel@tonic-gate 	 * version of VOP_RENAME()) is also of type lofs.
7660Sstevel@tonic-gate 	 */
7670Sstevel@tonic-gate 	if (vn_matchops(ndvp, lo_vnodeops)) {
7680Sstevel@tonic-gate 		if (IS_ZONEDEVFS(ndvp))
7690Sstevel@tonic-gate 			return (EACCES);
7700Sstevel@tonic-gate 		ndvp = realvp(ndvp);	/* Check the next layer */
7710Sstevel@tonic-gate 	} else {
7720Sstevel@tonic-gate 		/*
7730Sstevel@tonic-gate 		 * We can go fast here
7740Sstevel@tonic-gate 		 */
7750Sstevel@tonic-gate 		while (vn_matchops(odvp, lo_vnodeops)) {
7760Sstevel@tonic-gate 			if (IS_ZONEDEVFS(odvp))
7770Sstevel@tonic-gate 				return (EACCES);
7780Sstevel@tonic-gate 			odvp = realvp(odvp);
7790Sstevel@tonic-gate 		}
7800Sstevel@tonic-gate 		if (odvp->v_vfsp != ndvp->v_vfsp)
7810Sstevel@tonic-gate 			return (EXDEV);
7820Sstevel@tonic-gate 	}
7830Sstevel@tonic-gate 	return (VOP_RENAME(odvp, onm, ndvp, nnm, cr));
7840Sstevel@tonic-gate }
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate static int
7870Sstevel@tonic-gate lo_mkdir(
7880Sstevel@tonic-gate 	vnode_t *dvp,
7890Sstevel@tonic-gate 	char *nm,
7900Sstevel@tonic-gate 	struct vattr *va,
7910Sstevel@tonic-gate 	vnode_t **vpp,
7920Sstevel@tonic-gate 	struct cred *cr)
7930Sstevel@tonic-gate {
7940Sstevel@tonic-gate 	int error;
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate #ifdef LODEBUG
7970Sstevel@tonic-gate 	lo_dprint(4, "lo_mkdir vp %p realvp %p\n", dvp, realvp(dvp));
7980Sstevel@tonic-gate #endif
7990Sstevel@tonic-gate 	if (IS_ZONEDEVFS(dvp))
8000Sstevel@tonic-gate 		return (EACCES);
8010Sstevel@tonic-gate 	error = VOP_MKDIR(realvp(dvp), nm, va, vpp, cr);
8020Sstevel@tonic-gate 	if (!error)
803324Sowenr 		*vpp = makelonode(*vpp, vtoli(dvp->v_vfsp), 0);
8040Sstevel@tonic-gate 	return (error);
8050Sstevel@tonic-gate }
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate static int
8080Sstevel@tonic-gate lo_realvp(vnode_t *vp, vnode_t **vpp)
8090Sstevel@tonic-gate {
8100Sstevel@tonic-gate #ifdef LODEBUG
8110Sstevel@tonic-gate 	lo_dprint(4, "lo_realvp %p\n", vp);
8120Sstevel@tonic-gate #endif
8130Sstevel@tonic-gate 	while (vn_matchops(vp, lo_vnodeops))
8140Sstevel@tonic-gate 		vp = realvp(vp);
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	if (VOP_REALVP(vp, vpp) != 0)
8170Sstevel@tonic-gate 		*vpp = vp;
8180Sstevel@tonic-gate 	return (0);
8190Sstevel@tonic-gate }
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate static int
8220Sstevel@tonic-gate lo_rmdir(
8230Sstevel@tonic-gate 	vnode_t *dvp,
8240Sstevel@tonic-gate 	char *nm,
8250Sstevel@tonic-gate 	vnode_t *cdir,
8260Sstevel@tonic-gate 	struct cred *cr)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	vnode_t *rvp = cdir;
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate #ifdef LODEBUG
8310Sstevel@tonic-gate 	lo_dprint(4, "lo_rmdir vp %p realvp %p\n", dvp, realvp(dvp));
8320Sstevel@tonic-gate #endif
8330Sstevel@tonic-gate 	if (IS_ZONEDEVFS(dvp))
8340Sstevel@tonic-gate 		return (EACCES);
8350Sstevel@tonic-gate 	/* if cdir is lofs vnode ptr get its real vnode ptr */
8360Sstevel@tonic-gate 	if (vn_matchops(dvp, vn_getops(rvp)))
8370Sstevel@tonic-gate 		(void) lo_realvp(cdir, &rvp);
8380Sstevel@tonic-gate 	dvp = realvp(dvp);
8390Sstevel@tonic-gate 	return (VOP_RMDIR(dvp, nm, rvp, cr));
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate static int
8430Sstevel@tonic-gate lo_symlink(
8440Sstevel@tonic-gate 	vnode_t *dvp,
8450Sstevel@tonic-gate 	char *lnm,
8460Sstevel@tonic-gate 	struct vattr *tva,
8470Sstevel@tonic-gate 	char *tnm,
8480Sstevel@tonic-gate 	struct cred *cr)
8490Sstevel@tonic-gate {
8500Sstevel@tonic-gate #ifdef LODEBUG
8510Sstevel@tonic-gate 	lo_dprint(4, "lo_symlink vp %p realvp %p\n", dvp, realvp(dvp));
8520Sstevel@tonic-gate #endif
8530Sstevel@tonic-gate 	if (IS_ZONEDEVFS(dvp))
8540Sstevel@tonic-gate 		return (EACCES);
8550Sstevel@tonic-gate 	dvp = realvp(dvp);
8560Sstevel@tonic-gate 	return (VOP_SYMLINK(dvp, lnm, tva, tnm, cr));
8570Sstevel@tonic-gate }
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate static int
8600Sstevel@tonic-gate lo_readlink(vnode_t *vp, struct uio *uiop, struct cred *cr)
8610Sstevel@tonic-gate {
8620Sstevel@tonic-gate 	vp = realvp(vp);
8630Sstevel@tonic-gate 	return (VOP_READLINK(vp, uiop, cr));
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate static int
8670Sstevel@tonic-gate lo_readdir(vnode_t *vp, struct uio *uiop, struct cred *cr, int *eofp)
8680Sstevel@tonic-gate {
8690Sstevel@tonic-gate #ifdef LODEBUG
8700Sstevel@tonic-gate 	lo_dprint(4, "lo_readdir vp %p realvp %p\n", vp, realvp(vp));
8710Sstevel@tonic-gate #endif
8720Sstevel@tonic-gate 	vp = realvp(vp);
8730Sstevel@tonic-gate 	return (VOP_READDIR(vp, uiop, cr, eofp));
8740Sstevel@tonic-gate }
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate static int
8770Sstevel@tonic-gate lo_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
8780Sstevel@tonic-gate {
8790Sstevel@tonic-gate 	vp = realvp(vp);
8800Sstevel@tonic-gate 	return (VOP_RWLOCK(vp, write_lock, ct));
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate static void
8840Sstevel@tonic-gate lo_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
8850Sstevel@tonic-gate {
8860Sstevel@tonic-gate 	vp = realvp(vp);
8870Sstevel@tonic-gate 	VOP_RWUNLOCK(vp, write_lock, ct);
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate static int
8910Sstevel@tonic-gate lo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp)
8920Sstevel@tonic-gate {
8930Sstevel@tonic-gate 	vp = realvp(vp);
8940Sstevel@tonic-gate 	return (VOP_SEEK(vp, ooff, noffp));
8950Sstevel@tonic-gate }
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate static int
8980Sstevel@tonic-gate lo_cmp(vnode_t *vp1, vnode_t *vp2)
8990Sstevel@tonic-gate {
9000Sstevel@tonic-gate 	while (vn_matchops(vp1, lo_vnodeops))
9010Sstevel@tonic-gate 		vp1 = realvp(vp1);
9020Sstevel@tonic-gate 	while (vn_matchops(vp2, lo_vnodeops))
9030Sstevel@tonic-gate 		vp2 = realvp(vp2);
9040Sstevel@tonic-gate 	return (VOP_CMP(vp1, vp2));
9050Sstevel@tonic-gate }
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate static int
9080Sstevel@tonic-gate lo_frlock(
9090Sstevel@tonic-gate 	vnode_t *vp,
9100Sstevel@tonic-gate 	int cmd,
9110Sstevel@tonic-gate 	struct flock64 *bfp,
9120Sstevel@tonic-gate 	int flag,
9130Sstevel@tonic-gate 	offset_t offset,
9140Sstevel@tonic-gate 	struct flk_callback *flk_cbp,
9150Sstevel@tonic-gate 	cred_t *cr)
9160Sstevel@tonic-gate {
9170Sstevel@tonic-gate 	vp = realvp(vp);
9180Sstevel@tonic-gate 	return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr));
9190Sstevel@tonic-gate }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate static int
9220Sstevel@tonic-gate lo_space(
9230Sstevel@tonic-gate 	vnode_t *vp,
9240Sstevel@tonic-gate 	int cmd,
9250Sstevel@tonic-gate 	struct flock64 *bfp,
9260Sstevel@tonic-gate 	int flag,
9270Sstevel@tonic-gate 	offset_t offset,
9280Sstevel@tonic-gate 	struct cred *cr,
9290Sstevel@tonic-gate 	caller_context_t *ct)
9300Sstevel@tonic-gate {
9310Sstevel@tonic-gate 	vp = realvp(vp);
9320Sstevel@tonic-gate 	return (VOP_SPACE(vp, cmd, bfp, flag, offset, cr, ct));
9330Sstevel@tonic-gate }
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate static int
9360Sstevel@tonic-gate lo_getpage(
9370Sstevel@tonic-gate 	vnode_t *vp,
9380Sstevel@tonic-gate 	offset_t off,
9390Sstevel@tonic-gate 	size_t len,
9400Sstevel@tonic-gate 	uint_t *prot,
9410Sstevel@tonic-gate 	struct page *parr[],
9420Sstevel@tonic-gate 	size_t psz,
9430Sstevel@tonic-gate 	struct seg *seg,
9440Sstevel@tonic-gate 	caddr_t addr,
9450Sstevel@tonic-gate 	enum seg_rw rw,
9460Sstevel@tonic-gate 	struct cred *cr)
9470Sstevel@tonic-gate {
9480Sstevel@tonic-gate 	vp = realvp(vp);
9490Sstevel@tonic-gate 	return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr));
9500Sstevel@tonic-gate }
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate static int
9530Sstevel@tonic-gate lo_putpage(vnode_t *vp, offset_t off, size_t len, int flags, struct cred *cr)
9540Sstevel@tonic-gate {
9550Sstevel@tonic-gate 	vp = realvp(vp);
9560Sstevel@tonic-gate 	return (VOP_PUTPAGE(vp, off, len, flags, cr));
9570Sstevel@tonic-gate }
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate static int
9600Sstevel@tonic-gate lo_map(
9610Sstevel@tonic-gate 	vnode_t *vp,
9620Sstevel@tonic-gate 	offset_t off,
9630Sstevel@tonic-gate 	struct as *as,
9640Sstevel@tonic-gate 	caddr_t *addrp,
9650Sstevel@tonic-gate 	size_t len,
9660Sstevel@tonic-gate 	uchar_t prot,
9670Sstevel@tonic-gate 	uchar_t maxprot,
9680Sstevel@tonic-gate 	uint_t flags,
9690Sstevel@tonic-gate 	struct cred *cr)
9700Sstevel@tonic-gate {
9710Sstevel@tonic-gate 	vp = realvp(vp);
9720Sstevel@tonic-gate 	return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr));
9730Sstevel@tonic-gate }
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate static int
9760Sstevel@tonic-gate lo_addmap(
9770Sstevel@tonic-gate 	vnode_t *vp,
9780Sstevel@tonic-gate 	offset_t off,
9790Sstevel@tonic-gate 	struct as *as,
9800Sstevel@tonic-gate 	caddr_t addr,
9810Sstevel@tonic-gate 	size_t len,
9820Sstevel@tonic-gate 	uchar_t prot,
9830Sstevel@tonic-gate 	uchar_t maxprot,
9840Sstevel@tonic-gate 	uint_t flags,
9850Sstevel@tonic-gate 	struct cred *cr)
9860Sstevel@tonic-gate {
9870Sstevel@tonic-gate 	vp = realvp(vp);
9880Sstevel@tonic-gate 	return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr));
9890Sstevel@tonic-gate }
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate static int
9920Sstevel@tonic-gate lo_delmap(
9930Sstevel@tonic-gate 	vnode_t *vp,
9940Sstevel@tonic-gate 	offset_t off,
9950Sstevel@tonic-gate 	struct as *as,
9960Sstevel@tonic-gate 	caddr_t addr,
9970Sstevel@tonic-gate 	size_t len,
9980Sstevel@tonic-gate 	uint_t prot,
9990Sstevel@tonic-gate 	uint_t maxprot,
10000Sstevel@tonic-gate 	uint_t flags,
10010Sstevel@tonic-gate 	struct cred *cr)
10020Sstevel@tonic-gate {
10030Sstevel@tonic-gate 	vp = realvp(vp);
10040Sstevel@tonic-gate 	return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr));
10050Sstevel@tonic-gate }
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate static int
10080Sstevel@tonic-gate lo_poll(
10090Sstevel@tonic-gate 	vnode_t *vp,
10100Sstevel@tonic-gate 	short events,
10110Sstevel@tonic-gate 	int anyyet,
10120Sstevel@tonic-gate 	short *reventsp,
10130Sstevel@tonic-gate 	struct pollhead **phpp)
10140Sstevel@tonic-gate {
10150Sstevel@tonic-gate 	vp = realvp(vp);
10160Sstevel@tonic-gate 	return (VOP_POLL(vp, events, anyyet, reventsp, phpp));
10170Sstevel@tonic-gate }
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate static int
10200Sstevel@tonic-gate lo_dump(vnode_t *vp, caddr_t addr, int bn, int count)
10210Sstevel@tonic-gate {
10220Sstevel@tonic-gate 	vp = realvp(vp);
10230Sstevel@tonic-gate 	return (VOP_DUMP(vp, addr, bn, count));
10240Sstevel@tonic-gate }
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate static int
10270Sstevel@tonic-gate lo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, struct cred *cr)
10280Sstevel@tonic-gate {
10290Sstevel@tonic-gate 	vp = realvp(vp);
10300Sstevel@tonic-gate 	return (VOP_PATHCONF(vp, cmd, valp, cr));
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate static int
10340Sstevel@tonic-gate lo_pageio(
10350Sstevel@tonic-gate 	vnode_t *vp,
10360Sstevel@tonic-gate 	struct page *pp,
10370Sstevel@tonic-gate 	u_offset_t io_off,
10380Sstevel@tonic-gate 	size_t io_len,
10390Sstevel@tonic-gate 	int flags,
10400Sstevel@tonic-gate 	cred_t *cr)
10410Sstevel@tonic-gate {
10420Sstevel@tonic-gate 	vp = realvp(vp);
10430Sstevel@tonic-gate 	return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr));
10440Sstevel@tonic-gate }
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate static void
10470Sstevel@tonic-gate lo_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr)
10480Sstevel@tonic-gate {
10490Sstevel@tonic-gate 	vp = realvp(vp);
10500Sstevel@tonic-gate 	if (vp != NULL && vp != &kvp)
10510Sstevel@tonic-gate 		VOP_DISPOSE(vp, pp, fl, dn, cr);
10520Sstevel@tonic-gate }
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate static int
10550Sstevel@tonic-gate lo_setsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr)
10560Sstevel@tonic-gate {
10570Sstevel@tonic-gate 	if (vn_is_readonly(vp))
10580Sstevel@tonic-gate 		return (EROFS);
10590Sstevel@tonic-gate 	vp = realvp(vp);
10600Sstevel@tonic-gate 	return (VOP_SETSECATTR(vp, secattr, flags, cr));
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate static int
10640Sstevel@tonic-gate lo_getsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr)
10650Sstevel@tonic-gate {
10660Sstevel@tonic-gate 	vp = realvp(vp);
10670Sstevel@tonic-gate 	return (VOP_GETSECATTR(vp, secattr, flags, cr));
10680Sstevel@tonic-gate }
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate static int
10710Sstevel@tonic-gate lo_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr)
10720Sstevel@tonic-gate {
10730Sstevel@tonic-gate 	vp = realvp(vp);
10740Sstevel@tonic-gate 	return (VOP_SHRLOCK(vp, cmd, shr, flag, cr));
10750Sstevel@tonic-gate }
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate /*
10780Sstevel@tonic-gate  * Loopback vnode operations vector.
10790Sstevel@tonic-gate  */
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate struct vnodeops *lo_vnodeops;
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate const fs_operation_def_t lo_vnodeops_template[] = {
10840Sstevel@tonic-gate 	VOPNAME_OPEN, lo_open,
10850Sstevel@tonic-gate 	VOPNAME_CLOSE, lo_close,
10860Sstevel@tonic-gate 	VOPNAME_READ, lo_read,
10870Sstevel@tonic-gate 	VOPNAME_WRITE, lo_write,
10880Sstevel@tonic-gate 	VOPNAME_IOCTL, lo_ioctl,
10890Sstevel@tonic-gate 	VOPNAME_SETFL, lo_setfl,
10900Sstevel@tonic-gate 	VOPNAME_GETATTR, lo_getattr,
10910Sstevel@tonic-gate 	VOPNAME_SETATTR, lo_setattr,
10920Sstevel@tonic-gate 	VOPNAME_ACCESS, lo_access,
10930Sstevel@tonic-gate 	VOPNAME_LOOKUP, lo_lookup,
10940Sstevel@tonic-gate 	VOPNAME_CREATE, lo_create,
10950Sstevel@tonic-gate 	VOPNAME_REMOVE, lo_remove,
10960Sstevel@tonic-gate 	VOPNAME_LINK, lo_link,
10970Sstevel@tonic-gate 	VOPNAME_RENAME, lo_rename,
10980Sstevel@tonic-gate 	VOPNAME_MKDIR, lo_mkdir,
10990Sstevel@tonic-gate 	VOPNAME_RMDIR, lo_rmdir,
11000Sstevel@tonic-gate 	VOPNAME_READDIR, lo_readdir,
11010Sstevel@tonic-gate 	VOPNAME_SYMLINK, lo_symlink,
11020Sstevel@tonic-gate 	VOPNAME_READLINK, lo_readlink,
11030Sstevel@tonic-gate 	VOPNAME_FSYNC, lo_fsync,
11040Sstevel@tonic-gate 	VOPNAME_INACTIVE, (fs_generic_func_p) lo_inactive,
11050Sstevel@tonic-gate 	VOPNAME_FID, lo_fid,
11060Sstevel@tonic-gate 	VOPNAME_RWLOCK, lo_rwlock,
11070Sstevel@tonic-gate 	VOPNAME_RWUNLOCK, (fs_generic_func_p) lo_rwunlock,
11080Sstevel@tonic-gate 	VOPNAME_SEEK, lo_seek,
11090Sstevel@tonic-gate 	VOPNAME_CMP, lo_cmp,
11100Sstevel@tonic-gate 	VOPNAME_FRLOCK, lo_frlock,
11110Sstevel@tonic-gate 	VOPNAME_SPACE, lo_space,
11120Sstevel@tonic-gate 	VOPNAME_REALVP, lo_realvp,
11130Sstevel@tonic-gate 	VOPNAME_GETPAGE, lo_getpage,
11140Sstevel@tonic-gate 	VOPNAME_PUTPAGE, lo_putpage,
11150Sstevel@tonic-gate 	VOPNAME_MAP, (fs_generic_func_p) lo_map,
11160Sstevel@tonic-gate 	VOPNAME_ADDMAP, (fs_generic_func_p) lo_addmap,
11170Sstevel@tonic-gate 	VOPNAME_DELMAP, lo_delmap,
11180Sstevel@tonic-gate 	VOPNAME_POLL, (fs_generic_func_p) lo_poll,
11190Sstevel@tonic-gate 	VOPNAME_DUMP, lo_dump,
11200Sstevel@tonic-gate 	VOPNAME_DUMPCTL, fs_error,		/* XXX - why? */
11210Sstevel@tonic-gate 	VOPNAME_PATHCONF, lo_pathconf,
11220Sstevel@tonic-gate 	VOPNAME_PAGEIO, lo_pageio,
11230Sstevel@tonic-gate 	VOPNAME_DISPOSE, (fs_generic_func_p) lo_dispose,
11240Sstevel@tonic-gate 	VOPNAME_SETSECATTR, lo_setsecattr,
11250Sstevel@tonic-gate 	VOPNAME_GETSECATTR, lo_getsecattr,
11260Sstevel@tonic-gate 	VOPNAME_SHRLOCK, lo_shrlock,
11270Sstevel@tonic-gate 	NULL, NULL
11280Sstevel@tonic-gate };
1129