17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
58f71b825Sowenr  * Common Development and Distribution License (the "License").
68f71b825Sowenr  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22d7334e51Srm  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*5ffe8443SJerry Jelinek  * Copyright 2015 Joyent, Inc.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/param.h>
287c478bd9Sstevel@tonic-gate #include <sys/systm.h>
297c478bd9Sstevel@tonic-gate #include <sys/errno.h>
307c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
317c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
32aa59c4cbSrsb #include <sys/vfs_opreg.h>
337c478bd9Sstevel@tonic-gate #include <sys/uio.h>
347c478bd9Sstevel@tonic-gate #include <sys/cred.h>
357c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
367c478bd9Sstevel@tonic-gate #include <sys/debug.h>
377c478bd9Sstevel@tonic-gate #include <sys/fs/lofs_node.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/lofs_info.h>
397c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
407c478bd9Sstevel@tonic-gate #include <vm/as.h>
417c478bd9Sstevel@tonic-gate #include <vm/seg.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate  * These are the vnode ops routines which implement the vnode interface to
457c478bd9Sstevel@tonic-gate  * the looped-back file system.  These routines just take their parameters,
467c478bd9Sstevel@tonic-gate  * and then calling the appropriate real vnode routine(s) to do the work.
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate static int
lo_open(vnode_t ** vpp,int flag,struct cred * cr,caller_context_t * ct)50da6c28aaSamw lo_open(vnode_t **vpp, int flag, struct cred *cr, caller_context_t *ct)
517c478bd9Sstevel@tonic-gate {
527c478bd9Sstevel@tonic-gate 	vnode_t *vp = *vpp;
537c478bd9Sstevel@tonic-gate 	vnode_t *rvp;
547c478bd9Sstevel@tonic-gate 	vnode_t *oldvp;
557c478bd9Sstevel@tonic-gate 	int error;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate #ifdef LODEBUG
587c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_open vp %p cnt=%d realvp %p cnt=%d\n",
59d7334e51Srm 	    vp, vp->v_count, realvp(vp), realvp(vp)->v_count);
607c478bd9Sstevel@tonic-gate #endif
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	oldvp = vp;
637c478bd9Sstevel@tonic-gate 	vp = rvp = realvp(vp);
647c478bd9Sstevel@tonic-gate 	/*
657c478bd9Sstevel@tonic-gate 	 * Need to hold new reference to vp since VOP_OPEN() may
667c478bd9Sstevel@tonic-gate 	 * decide to release it.
677c478bd9Sstevel@tonic-gate 	 */
687c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
69da6c28aaSamw 	error = VOP_OPEN(&rvp, flag, cr, ct);
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate 	if (!error && rvp != vp) {
727c478bd9Sstevel@tonic-gate 		/*
737c478bd9Sstevel@tonic-gate 		 * the FS which we called should have released the
747c478bd9Sstevel@tonic-gate 		 * new reference on vp
757c478bd9Sstevel@tonic-gate 		 */
76b431137cSowenr 		*vpp = makelonode(rvp, vtoli(oldvp->v_vfsp), 0);
778f71b825Sowenr 		if ((*vpp)->v_type == VDIR) {
788f71b825Sowenr 			/*
798f71b825Sowenr 			 * Copy over any looping flags to the new lnode.
808f71b825Sowenr 			 */
818f71b825Sowenr 			(vtol(*vpp))->lo_looping |= (vtol(oldvp))->lo_looping;
828f71b825Sowenr 		}
837c478bd9Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
847c478bd9Sstevel@tonic-gate 			vnode_t *svp;
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
877c478bd9Sstevel@tonic-gate 			VN_RELE(*vpp);
887c478bd9Sstevel@tonic-gate 			if (svp == NULL)
897c478bd9Sstevel@tonic-gate 				error = ENOSYS;
907c478bd9Sstevel@tonic-gate 			else
917c478bd9Sstevel@tonic-gate 				*vpp = svp;
927c478bd9Sstevel@tonic-gate 		}
937c478bd9Sstevel@tonic-gate 		VN_RELE(oldvp);
947c478bd9Sstevel@tonic-gate 	} else {
957c478bd9Sstevel@tonic-gate 		ASSERT(rvp->v_count > 1);
967c478bd9Sstevel@tonic-gate 		VN_RELE(rvp);
977c478bd9Sstevel@tonic-gate 	}
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	return (error);
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate static int
lo_close(vnode_t * vp,int flag,int count,offset_t offset,struct cred * cr,caller_context_t * ct)1037c478bd9Sstevel@tonic-gate lo_close(
1047c478bd9Sstevel@tonic-gate 	vnode_t *vp,
1057c478bd9Sstevel@tonic-gate 	int flag,
1067c478bd9Sstevel@tonic-gate 	int count,
1077c478bd9Sstevel@tonic-gate 	offset_t offset,
108da6c28aaSamw 	struct cred *cr,
109da6c28aaSamw 	caller_context_t *ct)
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1127c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_close vp %p realvp %p\n", vp, realvp(vp));
1137c478bd9Sstevel@tonic-gate #endif
1147c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
115da6c28aaSamw 	return (VOP_CLOSE(vp, flag, count, offset, cr, ct));
1167c478bd9Sstevel@tonic-gate }
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate static int
lo_read(vnode_t * vp,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)1197c478bd9Sstevel@tonic-gate lo_read(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
1207c478bd9Sstevel@tonic-gate 	caller_context_t *ct)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1237c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_read vp %p realvp %p\n", vp, realvp(vp));
1247c478bd9Sstevel@tonic-gate #endif
1257c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
1267c478bd9Sstevel@tonic-gate 	return (VOP_READ(vp, uiop, ioflag, cr, ct));
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate static int
lo_write(vnode_t * vp,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)1307c478bd9Sstevel@tonic-gate lo_write(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
1317c478bd9Sstevel@tonic-gate 	caller_context_t *ct)
1327c478bd9Sstevel@tonic-gate {
1337c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1347c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_write vp %p realvp %p\n", vp, realvp(vp));
1357c478bd9Sstevel@tonic-gate #endif
1367c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
1377c478bd9Sstevel@tonic-gate 	return (VOP_WRITE(vp, uiop, ioflag, cr, ct));
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate static int
lo_ioctl(vnode_t * vp,int cmd,intptr_t arg,int flag,struct cred * cr,int * rvalp,caller_context_t * ct)1417c478bd9Sstevel@tonic-gate lo_ioctl(
1427c478bd9Sstevel@tonic-gate 	vnode_t *vp,
1437c478bd9Sstevel@tonic-gate 	int cmd,
1447c478bd9Sstevel@tonic-gate 	intptr_t arg,
1457c478bd9Sstevel@tonic-gate 	int flag,
1467c478bd9Sstevel@tonic-gate 	struct cred *cr,
147da6c28aaSamw 	int *rvalp,
148da6c28aaSamw 	caller_context_t *ct)
1497c478bd9Sstevel@tonic-gate {
1507c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1517c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_ioctl vp %p realvp %p\n", vp, realvp(vp));
1527c478bd9Sstevel@tonic-gate #endif
1537c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
154da6c28aaSamw 	return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp, ct));
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate static int
lo_setfl(vnode_t * vp,int oflags,int nflags,cred_t * cr,caller_context_t * ct)158da6c28aaSamw lo_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct)
1597c478bd9Sstevel@tonic-gate {
1607c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
161da6c28aaSamw 	return (VOP_SETFL(vp, oflags, nflags, cr, ct));
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate static int
lo_getattr(vnode_t * vp,struct vattr * vap,int flags,struct cred * cr,caller_context_t * ct)1657c478bd9Sstevel@tonic-gate lo_getattr(
1667c478bd9Sstevel@tonic-gate 	vnode_t *vp,
1677c478bd9Sstevel@tonic-gate 	struct vattr *vap,
1687c478bd9Sstevel@tonic-gate 	int flags,
169da6c28aaSamw 	struct cred *cr,
170da6c28aaSamw 	caller_context_t *ct)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	int error;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1757c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_getattr vp %p realvp %p\n", vp, realvp(vp));
1767c478bd9Sstevel@tonic-gate #endif
177da6c28aaSamw 	if (error = VOP_GETATTR(realvp(vp), vap, flags, cr, ct))
1787c478bd9Sstevel@tonic-gate 		return (error);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	return (0);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate static int
lo_setattr(vnode_t * vp,struct vattr * vap,int flags,struct cred * cr,caller_context_t * ct)1847c478bd9Sstevel@tonic-gate lo_setattr(
1857c478bd9Sstevel@tonic-gate 	vnode_t *vp,
1867c478bd9Sstevel@tonic-gate 	struct vattr *vap,
1877c478bd9Sstevel@tonic-gate 	int flags,
1887c478bd9Sstevel@tonic-gate 	struct cred *cr,
1897c478bd9Sstevel@tonic-gate 	caller_context_t *ct)
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate #ifdef LODEBUG
1927c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_setattr vp %p realvp %p\n", vp, realvp(vp));
1937c478bd9Sstevel@tonic-gate #endif
1947c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
1957c478bd9Sstevel@tonic-gate 	return (VOP_SETATTR(vp, vap, flags, cr, ct));
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate static int
lo_access(vnode_t * vp,int mode,int flags,struct cred * cr,caller_context_t * ct)199da6c28aaSamw lo_access(
200da6c28aaSamw 	vnode_t *vp,
201da6c28aaSamw 	int mode,
202da6c28aaSamw 	int flags,
203da6c28aaSamw 	struct cred *cr,
204da6c28aaSamw 	caller_context_t *ct)
2057c478bd9Sstevel@tonic-gate {
2067c478bd9Sstevel@tonic-gate #ifdef LODEBUG
2077c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_access vp %p realvp %p\n", vp, realvp(vp));
2087c478bd9Sstevel@tonic-gate #endif
2097c478bd9Sstevel@tonic-gate 	if (mode & VWRITE) {
2107c478bd9Sstevel@tonic-gate 		if (vp->v_type == VREG && vn_is_readonly(vp))
2117c478bd9Sstevel@tonic-gate 			return (EROFS);
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
214da6c28aaSamw 	return (VOP_ACCESS(vp, mode, flags, cr, ct));
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static int
lo_fsync(vnode_t * vp,int syncflag,struct cred * cr,caller_context_t * ct)218da6c28aaSamw lo_fsync(vnode_t *vp, int syncflag, struct cred *cr, caller_context_t *ct)
2197c478bd9Sstevel@tonic-gate {
2207c478bd9Sstevel@tonic-gate #ifdef LODEBUG
2217c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_fsync vp %p realvp %p\n", vp, realvp(vp));
2227c478bd9Sstevel@tonic-gate #endif
2237c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
224da6c28aaSamw 	return (VOP_FSYNC(vp, syncflag, cr, ct));
2257c478bd9Sstevel@tonic-gate }
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2287c478bd9Sstevel@tonic-gate static void
lo_inactive(vnode_t * vp,struct cred * cr,caller_context_t * ct)229da6c28aaSamw lo_inactive(vnode_t *vp, struct cred *cr, caller_context_t *ct)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate #ifdef LODEBUG
2327c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_inactive %p, realvp %p\n", vp, realvp(vp));
2337c478bd9Sstevel@tonic-gate #endif
2347c478bd9Sstevel@tonic-gate 	freelonode(vtol(vp));
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate /* ARGSUSED */
2387c478bd9Sstevel@tonic-gate static int
lo_fid(vnode_t * vp,struct fid * fidp,caller_context_t * ct)239da6c28aaSamw lo_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate #ifdef LODEBUG
2427c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_fid %p, realvp %p\n", vp, realvp(vp));
2437c478bd9Sstevel@tonic-gate #endif
2447c478bd9Sstevel@tonic-gate 	vp = realvp(vp);
245da6c28aaSamw 	return (VOP_FID(vp, fidp, ct));
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate /*
2497c478bd9Sstevel@tonic-gate  * Given a vnode of lofs type, lookup nm name and
2507c478bd9Sstevel@tonic-gate  * return a shadow vnode (of lofs type) of the
2517c478bd9Sstevel@tonic-gate  * real vnode found.
2527c478bd9Sstevel@tonic-gate  *
2537c478bd9Sstevel@tonic-gate  * Due to the nature of lofs, there is a potential
2547c478bd9Sstevel@tonic-gate  * looping in path traversal.
2557c478bd9Sstevel@tonic-gate  *
2567c478bd9Sstevel@tonic-gate  * starting from the mount point of an lofs;
2577c478bd9Sstevel@tonic-gate  * a loop is defined to be a traversal path
2587c478bd9Sstevel@tonic-gate  * where the mount point or the real vnode of
2597c478bd9Sstevel@tonic-gate  * the root of this lofs is encountered twice.
2607c478bd9Sstevel@tonic-gate  * Once at the start of traversal and second
2617c478bd9Sstevel@tonic-gate  * when the looping is found.
2627c478bd9Sstevel@tonic-gate  *
2637c478bd9Sstevel@tonic-gate  * When a loop is encountered, a shadow of the
2647c478bd9Sstevel@tonic-gate  * covered vnode is returned to stop the looping.
2657c478bd9Sstevel@tonic-gate  *
2667c478bd9Sstevel@tonic-gate  * This normally works, but with the advent of
2677c478bd9Sstevel@tonic-gate  * the new automounter, returning the shadow of the
2687c478bd9Sstevel@tonic-gate  * covered vnode (autonode, in this case) does not
2697c478bd9Sstevel@tonic-gate  * stop the loop.  Because further lookup on this
2707c478bd9Sstevel@tonic-gate  * lonode will cause the autonode to call lo_lookup()
2717c478bd9Sstevel@tonic-gate  * on the lonode covering it.
2727c478bd9Sstevel@tonic-gate  *
2737c478bd9Sstevel@tonic-gate  * example "/net/jurassic/net/jurassic" is a loop.
2747c478bd9Sstevel@tonic-gate  * returning the shadow of the autonode corresponding to
2757c478bd9Sstevel@tonic-gate  * "/net/jurassic/net/jurassic" will not terminate the
2767c478bd9Sstevel@tonic-gate  * loop.   To solve this problem we allow the loop to go
277b431137cSowenr  * through one more level component lookup.  Whichever
278b431137cSowenr  * directory is then looked up in "/net/jurassic/net/jurassic"
279b431137cSowenr  * the vnode returned is the vnode covered by the autonode
280b431137cSowenr  * "net" and this will terminate the loop.
2817c478bd9Sstevel@tonic-gate  *
2827c478bd9Sstevel@tonic-gate  * Lookup for dot dot has to be dealt with separately.
2837c478bd9Sstevel@tonic-gate  * It will be nice to have a "one size fits all" kind
2847c478bd9Sstevel@tonic-gate  * of solution, so that we don't have so many ifs statement
2857c478bd9Sstevel@tonic-gate  * in the lo_lookup() to handle dotdot.  But, since
2867c478bd9Sstevel@tonic-gate  * there are so many special cases to handle different
2877c478bd9Sstevel@tonic-gate  * kinds looping above, we need special codes to handle
2887c478bd9Sstevel@tonic-gate  * dotdot lookup as well.
2897c478bd9Sstevel@tonic-gate  */
2907c478bd9Sstevel@tonic-gate static int
lo_lookup(vnode_t * dvp,char * nm,vnode_t ** vpp,struct pathname * pnp,int flags,vnode_t * rdir,struct cred * cr,caller_context_t * ct,int * direntflags,pathname_t * realpnp)2917c478bd9Sstevel@tonic-gate lo_lookup(
2927c478bd9Sstevel@tonic-gate 	vnode_t *dvp,
2937c478bd9Sstevel@tonic-gate 	char *nm,
2947c478bd9Sstevel@tonic-gate 	vnode_t **vpp,
2957c478bd9Sstevel@tonic-gate 	struct pathname *pnp,
2967c478bd9Sstevel@tonic-gate 	int flags,
2977c478bd9Sstevel@tonic-gate 	vnode_t *rdir,
298da6c28aaSamw 	struct cred *cr,
299da6c28aaSamw 	caller_context_t *ct,
300da6c28aaSamw 	int *direntflags,
301da6c28aaSamw 	pathname_t *realpnp)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL, *tvp = NULL, *nonlovp;
3047c478bd9Sstevel@tonic-gate 	int error, is_indirectloop;
3057c478bd9Sstevel@tonic-gate 	vnode_t *realdvp = realvp(dvp);
3067c478bd9Sstevel@tonic-gate 	struct loinfo *li = vtoli(dvp->v_vfsp);
3077c478bd9Sstevel@tonic-gate 	int looping = 0;
308b431137cSowenr 	int autoloop = 0;
3097c478bd9Sstevel@tonic-gate 	int doingdotdot = 0;
3107c478bd9Sstevel@tonic-gate 	int nosub = 0;
311b431137cSowenr 	int mkflag = 0;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	/*
3147c478bd9Sstevel@tonic-gate 	 * If name is empty and no XATTR flags are set, then return
3157c478bd9Sstevel@tonic-gate 	 * dvp (empty name == lookup ".").  If an XATTR flag is set
3167c478bd9Sstevel@tonic-gate 	 * then we need to call VOP_LOOKUP to get the xattr dir.
3177c478bd9Sstevel@tonic-gate 	 */
3187c478bd9Sstevel@tonic-gate 	if (nm[0] == '\0' && ! (flags & (CREATE_XATTR_DIR|LOOKUP_XATTR))) {
3197c478bd9Sstevel@tonic-gate 		VN_HOLD(dvp);
3207c478bd9Sstevel@tonic-gate 		*vpp = dvp;
3217c478bd9Sstevel@tonic-gate 		return (0);
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
3257c478bd9Sstevel@tonic-gate 		doingdotdot++;
3267c478bd9Sstevel@tonic-gate 		/*
3277c478bd9Sstevel@tonic-gate 		 * Handle ".." out of mounted filesystem
3287c478bd9Sstevel@tonic-gate 		 */
3297c478bd9Sstevel@tonic-gate 		while ((realdvp->v_flag & VROOT) && realdvp != rootdir) {
3307c478bd9Sstevel@tonic-gate 			realdvp = realdvp->v_vfsp->vfs_vnodecovered;
3317c478bd9Sstevel@tonic-gate 			ASSERT(realdvp != NULL);
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	*vpp = NULL;	/* default(error) case */
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	/*
3387c478bd9Sstevel@tonic-gate 	 * Do the normal lookup
3397c478bd9Sstevel@tonic-gate 	 */
340da6c28aaSamw 	if (error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr,
341da6c28aaSamw 	    ct, direntflags, realpnp)) {
342d13f2f50Smarks 		vp = NULL;
3437c478bd9Sstevel@tonic-gate 		goto out;
344d13f2f50Smarks 	}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	/*
3477c478bd9Sstevel@tonic-gate 	 * We do this check here to avoid returning a stale file handle to the
3487c478bd9Sstevel@tonic-gate 	 * caller.
3497c478bd9Sstevel@tonic-gate 	 */
3507c478bd9Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == '\0') {
3517c478bd9Sstevel@tonic-gate 		ASSERT(vp == realdvp);
3527c478bd9Sstevel@tonic-gate 		VN_HOLD(dvp);
3537c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
3547c478bd9Sstevel@tonic-gate 		*vpp = dvp;
3557c478bd9Sstevel@tonic-gate 		return (0);
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	if (doingdotdot) {
359b431137cSowenr 		if ((vtol(dvp))->lo_looping & LO_LOOPING) {
3607c478bd9Sstevel@tonic-gate 			vfs_t *vfsp;
3617c478bd9Sstevel@tonic-gate 
36295b97885Snr 			error = vn_vfsrlock_wait(realdvp);
3637c478bd9Sstevel@tonic-gate 			if (error)
3647c478bd9Sstevel@tonic-gate 				goto out;
3657c478bd9Sstevel@tonic-gate 			vfsp = vn_mountedvfs(realdvp);
366b431137cSowenr 			/*
367b431137cSowenr 			 * In the standard case if the looping flag is set and
368b431137cSowenr 			 * performing dotdot we would be returning from a
369b431137cSowenr 			 * covered vnode, implying vfsp could not be null. The
370b431137cSowenr 			 * exceptions being if we have looping and overlay
371b431137cSowenr 			 * mounts or looping and covered file systems.
372b431137cSowenr 			 */
373b431137cSowenr 			if (vfsp == NULL) {
3747c478bd9Sstevel@tonic-gate 				/*
375b431137cSowenr 				 * Overlay mount or covered file system,
376b431137cSowenr 				 * so just make the shadow node.
3777c478bd9Sstevel@tonic-gate 				 */
3787c478bd9Sstevel@tonic-gate 				vn_vfsunlock(realdvp);
379b431137cSowenr 				*vpp = makelonode(vp, li, 0);
380b431137cSowenr 				(vtol(*vpp))->lo_looping |= LO_LOOPING;
381b431137cSowenr 				return (0);
382b431137cSowenr 			}
383b431137cSowenr 			/*
384b431137cSowenr 			 * When looping get the actual found vnode
385b431137cSowenr 			 * instead of the vnode covered.
386b431137cSowenr 			 * Here we have to hold the lock for realdvp
387b431137cSowenr 			 * since an unmount during the traversal to the
388b431137cSowenr 			 * root vnode would turn *vfsp into garbage
389b431137cSowenr 			 * which would be fatal.
390b431137cSowenr 			 */
391b431137cSowenr 			error = VFS_ROOT(vfsp, &tvp);
39295b97885Snr 			vn_vfsunlock(realdvp);
393b431137cSowenr 
394b431137cSowenr 			if (error)
395b431137cSowenr 				goto out;
39695b97885Snr 
397b431137cSowenr 			if ((tvp == li->li_rootvp) && (vp == realvp(tvp))) {
3987c478bd9Sstevel@tonic-gate 				/*
399b431137cSowenr 				 * we're back at the real vnode
400b431137cSowenr 				 * of the rootvp
4017c478bd9Sstevel@tonic-gate 				 *
402b431137cSowenr 				 * return the rootvp
403b431137cSowenr 				 * Ex: /mnt/mnt/..
404b431137cSowenr 				 * where / has been lofs-mounted
405b431137cSowenr 				 * onto /mnt.  Return the lofs
406b431137cSowenr 				 * node mounted at /mnt.
407b431137cSowenr 				 */
408b431137cSowenr 				*vpp = tvp;
409b431137cSowenr 				VN_RELE(vp);
410b431137cSowenr 				return (0);
411b431137cSowenr 			} else {
412b431137cSowenr 				/*
413b431137cSowenr 				 * We are returning from a covered
414b431137cSowenr 				 * node whose vfs_mountedhere is
415b431137cSowenr 				 * not pointing to vfs of the current
416b431137cSowenr 				 * root vnode.
417b431137cSowenr 				 * This is a condn where in we
418b431137cSowenr 				 * returned a covered node say Zc
419b431137cSowenr 				 * but Zc is not the cover of current
420b431137cSowenr 				 * root.
421b431137cSowenr 				 * i.e.., if X is the root vnode
422b431137cSowenr 				 * lookup(Zc,"..") is taking us to
423b431137cSowenr 				 * X.
424b431137cSowenr 				 * Ex: /net/X/net/X/Y
4257c478bd9Sstevel@tonic-gate 				 *
426b431137cSowenr 				 * If LO_AUTOLOOP (autofs/lofs looping detected)
427b431137cSowenr 				 * has been set then we are encountering the
428b431137cSowenr 				 * cover of Y (Y being any directory vnode
429b431137cSowenr 				 * under /net/X/net/X/).
430b431137cSowenr 				 * When performing a dotdot set the
431b431137cSowenr 				 * returned vp to the vnode covered
432b431137cSowenr 				 * by the mounted lofs, ie /net/X/net/X
4337c478bd9Sstevel@tonic-gate 				 */
434b431137cSowenr 				VN_RELE(tvp);
435b431137cSowenr 				if ((vtol(dvp))->lo_looping & LO_AUTOLOOP) {
436b431137cSowenr 					VN_RELE(vp);
437b431137cSowenr 					vp = li->li_rootvp;
438b431137cSowenr 					vp = vp->v_vfsp->vfs_vnodecovered;
4397c478bd9Sstevel@tonic-gate 					VN_HOLD(vp);
440b431137cSowenr 					*vpp = makelonode(vp, li, 0);
441b431137cSowenr 					(vtol(*vpp))->lo_looping |= LO_LOOPING;
442b431137cSowenr 					return (0);
4437c478bd9Sstevel@tonic-gate 				}
4447c478bd9Sstevel@tonic-gate 			}
4457c478bd9Sstevel@tonic-gate 		} else {
4467c478bd9Sstevel@tonic-gate 			/*
4477c478bd9Sstevel@tonic-gate 			 * No frills just make the shadow node.
4487c478bd9Sstevel@tonic-gate 			 */
449b431137cSowenr 			*vpp = makelonode(vp, li, 0);
4507c478bd9Sstevel@tonic-gate 			return (0);
4517c478bd9Sstevel@tonic-gate 		}
4527c478bd9Sstevel@tonic-gate 	}
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	nosub = (vtoli(dvp->v_vfsp)->li_flag & LO_NOSUB);
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	/*
4577c478bd9Sstevel@tonic-gate 	 * If this vnode is mounted on, then we
4587c478bd9Sstevel@tonic-gate 	 * traverse to the vnode which is the root of
4597c478bd9Sstevel@tonic-gate 	 * the mounted file system.
4607c478bd9Sstevel@tonic-gate 	 */
4617c478bd9Sstevel@tonic-gate 	if (!nosub && (error = traverse(&vp)))
4627c478bd9Sstevel@tonic-gate 		goto out;
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	/*
4657c478bd9Sstevel@tonic-gate 	 * Make a lnode for the real vnode.
4667c478bd9Sstevel@tonic-gate 	 */
4677c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDIR || nosub) {
468b431137cSowenr 		*vpp = makelonode(vp, li, 0);
4697c478bd9Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
4707c478bd9Sstevel@tonic-gate 			vnode_t *svp;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
4737c478bd9Sstevel@tonic-gate 			VN_RELE(*vpp);
4747c478bd9Sstevel@tonic-gate 			if (svp == NULL)
4757c478bd9Sstevel@tonic-gate 				error = ENOSYS;
4767c478bd9Sstevel@tonic-gate 			else
4777c478bd9Sstevel@tonic-gate 				*vpp = svp;
4787c478bd9Sstevel@tonic-gate 		}
4797c478bd9Sstevel@tonic-gate 		return (error);
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	/*
4837c478bd9Sstevel@tonic-gate 	 * if the found vnode (vp) is not of type lofs
4847c478bd9Sstevel@tonic-gate 	 * then we're just going to make a shadow of that
4857c478bd9Sstevel@tonic-gate 	 * vp and get out.
4867c478bd9Sstevel@tonic-gate 	 *
4877c478bd9Sstevel@tonic-gate 	 * If the found vnode (vp) is of lofs type, and
4887c478bd9Sstevel@tonic-gate 	 * we're not doing dotdot, check if we are
4897c478bd9Sstevel@tonic-gate 	 * looping.
4907c478bd9Sstevel@tonic-gate 	 */
4917c478bd9Sstevel@tonic-gate 	if (!doingdotdot && vfs_matchops(vp->v_vfsp, lo_vfsops)) {
4927c478bd9Sstevel@tonic-gate 		/*
4937c478bd9Sstevel@tonic-gate 		 * Check if we're looping, i.e.
4947c478bd9Sstevel@tonic-gate 		 * vp equals the root vp of the lofs, directly
4957c478bd9Sstevel@tonic-gate 		 * or indirectly, return the covered node.
4967c478bd9Sstevel@tonic-gate 		 */
4977c478bd9Sstevel@tonic-gate 
498b431137cSowenr 		if (!((vtol(dvp))->lo_looping & LO_LOOPING)) {
4997c478bd9Sstevel@tonic-gate 			if (vp == li->li_rootvp) {
5007c478bd9Sstevel@tonic-gate 				/*
5017c478bd9Sstevel@tonic-gate 				 * Direct looping condn.
5027c478bd9Sstevel@tonic-gate 				 * Ex:- X is / mounted directory so lookup of
5037c478bd9Sstevel@tonic-gate 				 * /X/X is a direct looping condn.
5047c478bd9Sstevel@tonic-gate 				 */
5057c478bd9Sstevel@tonic-gate 				tvp = vp;
5067c478bd9Sstevel@tonic-gate 				vp = vp->v_vfsp->vfs_vnodecovered;
5077c478bd9Sstevel@tonic-gate 				VN_HOLD(vp);
5087c478bd9Sstevel@tonic-gate 				VN_RELE(tvp);
5097c478bd9Sstevel@tonic-gate 				looping++;
5107c478bd9Sstevel@tonic-gate 			} else {
5117c478bd9Sstevel@tonic-gate 				/*
5127c478bd9Sstevel@tonic-gate 				 * Indirect looping can be defined as
5137c478bd9Sstevel@tonic-gate 				 * real lookup returning rootvp of the current
5147c478bd9Sstevel@tonic-gate 				 * tree in any level of recursion.
5157c478bd9Sstevel@tonic-gate 				 *
5167c478bd9Sstevel@tonic-gate 				 * This check is useful if there are multiple
5177c478bd9Sstevel@tonic-gate 				 * levels of lofs indirections. Suppose vnode X
5187c478bd9Sstevel@tonic-gate 				 * in the current lookup has as its real vnode
5197c478bd9Sstevel@tonic-gate 				 * another lofs node. Y = realvp(X) Y should be
5207c478bd9Sstevel@tonic-gate 				 * a lofs node for the check to continue or Y
5217c478bd9Sstevel@tonic-gate 				 * is not the rootvp of X.
5227c478bd9Sstevel@tonic-gate 				 * Ex:- say X and Y are two vnodes
5237c478bd9Sstevel@tonic-gate 				 * say real(Y) is X and real(X) is Z
5247c478bd9Sstevel@tonic-gate 				 * parent vnode for X and Y is Z
5257c478bd9Sstevel@tonic-gate 				 * lookup(Y,"path") say we are looking for Y
5267c478bd9Sstevel@tonic-gate 				 * again under Y and we have to return Yc.
5277c478bd9Sstevel@tonic-gate 				 * but the lookup of Y under Y doesnot return
5287c478bd9Sstevel@tonic-gate 				 * Y the root vnode again here is why.
5297c478bd9Sstevel@tonic-gate 				 * 1. lookup(Y,"path of Y") will go to
5307c478bd9Sstevel@tonic-gate 				 * 2. lookup(real(Y),"path of Y") and then to
5317c478bd9Sstevel@tonic-gate 				 * 3. lookup(real(X),"path of Y").
5327c478bd9Sstevel@tonic-gate 				 * and now what lookup level 1 sees is the
5337c478bd9Sstevel@tonic-gate 				 * outcome of 2 but the vnode Y is due to
5347c478bd9Sstevel@tonic-gate 				 * lookup(Z,"path of Y") so we have to skip
5357c478bd9Sstevel@tonic-gate 				 * intermediate levels to find if in any level
5367c478bd9Sstevel@tonic-gate 				 * there is a looping.
5377c478bd9Sstevel@tonic-gate 				 */
5387c478bd9Sstevel@tonic-gate 				is_indirectloop = 0;
5397c478bd9Sstevel@tonic-gate 				nonlovp = vp;
5407c478bd9Sstevel@tonic-gate 				while (
5417c478bd9Sstevel@tonic-gate 				    vfs_matchops(nonlovp->v_vfsp, lo_vfsops) &&
5427c478bd9Sstevel@tonic-gate 				    !(is_indirectloop)) {
5437c478bd9Sstevel@tonic-gate 					if (li->li_rootvp  == nonlovp) {
5447c478bd9Sstevel@tonic-gate 						is_indirectloop++;
5457c478bd9Sstevel@tonic-gate 						break;
5467c478bd9Sstevel@tonic-gate 					}
5477c478bd9Sstevel@tonic-gate 					nonlovp = realvp(nonlovp);
5487c478bd9Sstevel@tonic-gate 				}
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 				if (is_indirectloop) {
5517c478bd9Sstevel@tonic-gate 					VN_RELE(vp);
5527c478bd9Sstevel@tonic-gate 					vp = nonlovp;
5537c478bd9Sstevel@tonic-gate 					vp = vp->v_vfsp->vfs_vnodecovered;
5547c478bd9Sstevel@tonic-gate 					VN_HOLD(vp);
5557c478bd9Sstevel@tonic-gate 					looping++;
5567c478bd9Sstevel@tonic-gate 				}
5577c478bd9Sstevel@tonic-gate 			}
5587c478bd9Sstevel@tonic-gate 		} else {
5597c478bd9Sstevel@tonic-gate 			/*
5607c478bd9Sstevel@tonic-gate 			 * come here only because of the interaction between
5617c478bd9Sstevel@tonic-gate 			 * the autofs and lofs.
5627c478bd9Sstevel@tonic-gate 			 *
5637c478bd9Sstevel@tonic-gate 			 * Lookup of "/net/X/net/X" will return a shadow of
5647c478bd9Sstevel@tonic-gate 			 * an autonode X_a which we call X_l.
5657c478bd9Sstevel@tonic-gate 			 *
5667c478bd9Sstevel@tonic-gate 			 * Lookup of anything under X_l, will trigger a call to
5677c478bd9Sstevel@tonic-gate 			 * auto_lookup(X_a,nm) which will eventually call
5687c478bd9Sstevel@tonic-gate 			 * lo_lookup(X_lr,nm) where X_lr is the root vnode of
5697c478bd9Sstevel@tonic-gate 			 * the current lofs.
5707c478bd9Sstevel@tonic-gate 			 *
5717c478bd9Sstevel@tonic-gate 			 * We come here only when we are called with X_l as dvp
5727c478bd9Sstevel@tonic-gate 			 * and look for something underneath.
5737c478bd9Sstevel@tonic-gate 			 *
574b431137cSowenr 			 * Now that an autofs/lofs looping condition has been
575b431137cSowenr 			 * identified any directory vnode contained within
576b431137cSowenr 			 * dvp will be set to the vnode covered by the
577b431137cSowenr 			 * mounted autofs. Thus all directories within dvp
578b431137cSowenr 			 * will appear empty hence teminating the looping.
579b431137cSowenr 			 * The LO_AUTOLOOP flag is set on the returned lonode
580b431137cSowenr 			 * to indicate the termination of the autofs/lofs
581b431137cSowenr 			 * looping. This is required for the correct behaviour
582b431137cSowenr 			 * when performing a dotdot.
5837c478bd9Sstevel@tonic-gate 			 */
5847c478bd9Sstevel@tonic-gate 			realdvp = realvp(dvp);
5857c478bd9Sstevel@tonic-gate 			while (vfs_matchops(realdvp->v_vfsp, lo_vfsops)) {
5867c478bd9Sstevel@tonic-gate 				realdvp = realvp(realdvp);
5877c478bd9Sstevel@tonic-gate 			}
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 			error = VFS_ROOT(realdvp->v_vfsp, &tvp);
5907c478bd9Sstevel@tonic-gate 			if (error)
5917c478bd9Sstevel@tonic-gate 				goto out;
5927c478bd9Sstevel@tonic-gate 			/*
5937c478bd9Sstevel@tonic-gate 			 * tvp now contains the rootvp of the vfs of the
594b431137cSowenr 			 * real vnode of dvp. The directory vnode vp is set
595b431137cSowenr 			 * to the covered vnode to terminate looping. No
596b431137cSowenr 			 * distinction is made between any vp as all directory
597b431137cSowenr 			 * vnodes contained in dvp are returned as the covered
598b431137cSowenr 			 * vnode.
5997c478bd9Sstevel@tonic-gate 			 */
600b431137cSowenr 			VN_RELE(vp);
6018f71b825Sowenr 			vp = tvp;	/* possibly is an autonode */
6027c478bd9Sstevel@tonic-gate 
603b431137cSowenr 			/*
604b431137cSowenr 			 * Need to find the covered vnode
605b431137cSowenr 			 */
6068f71b825Sowenr 			if (vp->v_vfsp->vfs_vnodecovered == NULL) {
6078f71b825Sowenr 				/*
6088f71b825Sowenr 				 * We don't have a covered vnode so this isn't
6098f71b825Sowenr 				 * an autonode. To find the autonode simply
6108f71b825Sowenr 				 * find the vnode covered by the lofs rootvp.
6118f71b825Sowenr 				 */
6128f71b825Sowenr 				vp = li->li_rootvp;
6138f71b825Sowenr 				vp = vp->v_vfsp->vfs_vnodecovered;
6148f71b825Sowenr 				VN_RELE(tvp);
6158f71b825Sowenr 				error = VFS_ROOT(vp->v_vfsp, &tvp);
6168f71b825Sowenr 				if (error)
6178f71b825Sowenr 					goto out;
6188f71b825Sowenr 				vp = tvp;	/* now this is an autonode */
6198f71b825Sowenr 				if (vp->v_vfsp->vfs_vnodecovered == NULL) {
6208f71b825Sowenr 					/*
6218f71b825Sowenr 					 * Still can't find a covered vnode.
6228f71b825Sowenr 					 * Fail the lookup, or we'd loop.
6238f71b825Sowenr 					 */
6248f71b825Sowenr 					error = ENOENT;
6258f71b825Sowenr 					goto out;
6268f71b825Sowenr 				}
6278f71b825Sowenr 			}
628b431137cSowenr 			vp = vp->v_vfsp->vfs_vnodecovered;
629b431137cSowenr 			VN_HOLD(vp);
630b431137cSowenr 			VN_RELE(tvp);
631b431137cSowenr 			/*
632b431137cSowenr 			 * Force the creation of a new lnode even if the hash
633b431137cSowenr 			 * table contains a lnode that references this vnode.
634b431137cSowenr 			 */
635b431137cSowenr 			mkflag = LOF_FORCE;
636b431137cSowenr 			autoloop++;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 	}
639b431137cSowenr 	*vpp = makelonode(vp, li, mkflag);
640b431137cSowenr 
641b431137cSowenr 	if ((looping) ||
642b431137cSowenr 	    (((vtol(dvp))->lo_looping & LO_LOOPING) && !doingdotdot)) {
643b431137cSowenr 		(vtol(*vpp))->lo_looping |= LO_LOOPING;
644b431137cSowenr 	}
6457c478bd9Sstevel@tonic-gate 
646b431137cSowenr 	if (autoloop) {
647b431137cSowenr 		(vtol(*vpp))->lo_looping |= LO_AUTOLOOP;
6487c478bd9Sstevel@tonic-gate 	}
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate out:
6517c478bd9Sstevel@tonic-gate 	if (error != 0 && vp != NULL)
6527c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
6537c478bd9Sstevel@tonic-gate #ifdef LODEBUG
6547c478bd9Sstevel@tonic-gate 	lo_dprint(4,
6557c478bd9Sstevel@tonic-gate 	"lo_lookup dvp %x realdvp %x nm '%s' newvp %x real vp %x error %d\n",
656d7334e51Srm 	    dvp, realvp(dvp), nm, *vpp, vp, error);
6577c478bd9Sstevel@tonic-gate #endif
6587c478bd9Sstevel@tonic-gate 	return (error);
6597c478bd9Sstevel@tonic-gate }
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6627c478bd9Sstevel@tonic-gate static int
lo_create(vnode_t * dvp,char * nm,struct vattr * va,enum vcexcl exclusive,int mode,vnode_t ** vpp,struct cred * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)6637c478bd9Sstevel@tonic-gate lo_create(
6647c478bd9Sstevel@tonic-gate 	vnode_t *dvp,
6657c478bd9Sstevel@tonic-gate 	char *nm,
6667c478bd9Sstevel@tonic-gate 	struct vattr *va,
6677c478bd9Sstevel@tonic-gate 	enum vcexcl exclusive,
6687c478bd9Sstevel@tonic-gate 	int mode,
6697c478bd9Sstevel@tonic-gate 	vnode_t **vpp,
6707c478bd9Sstevel@tonic-gate 	struct cred *cr,
671da6c28aaSamw 	int flag,
672da6c28aaSamw 	caller_context_t *ct,
673da6c28aaSamw 	vsecattr_t *vsecp)
6747c478bd9Sstevel@tonic-gate {
6757c478bd9Sstevel@tonic-gate 	int error;
6767c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL;
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate #ifdef LODEBUG
6797c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_create vp %p realvp %p\n", dvp, realvp(dvp));
6807c478bd9Sstevel@tonic-gate #endif
6817c478bd9Sstevel@tonic-gate 	if (*nm == '\0') {
6827c478bd9Sstevel@tonic-gate 		ASSERT(vpp && dvp == *vpp);
6837c478bd9Sstevel@tonic-gate 		vp = realvp(*vpp);
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
686da6c28aaSamw 	error = VOP_CREATE(realvp(dvp), nm, va, exclusive, mode, &vp, cr, flag,
687d7334e51Srm 	    ct, vsecp);
6887c478bd9Sstevel@tonic-gate 	if (!error) {
689b431137cSowenr 		*vpp = makelonode(vp, vtoli(dvp->v_vfsp), 0);
6907c478bd9Sstevel@tonic-gate 		if (IS_DEVVP(*vpp)) {
6917c478bd9Sstevel@tonic-gate 			vnode_t *svp;
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
6947c478bd9Sstevel@tonic-gate 			VN_RELE(*vpp);
6957c478bd9Sstevel@tonic-gate 			if (svp == NULL)
6967c478bd9Sstevel@tonic-gate 				error = ENOSYS;
6977c478bd9Sstevel@tonic-gate 			else
6987c478bd9Sstevel@tonic-gate 				*vpp = svp;
6997c478bd9Sstevel@tonic-gate 		}
700*5ffe8443SJerry Jelinek 	} else if (error == ENOSYS && exclusive == NONEXCL &&
701*5ffe8443SJerry Jelinek 	    dvp == vtoli(dvp->v_vfsp)->li_rootvp &&
702*5ffe8443SJerry Jelinek 	    realvp(dvp)->v_type == VREG) {
703*5ffe8443SJerry Jelinek 		/*
704*5ffe8443SJerry Jelinek 		 * We have a single regular file lofs mounted, thus the file is
705*5ffe8443SJerry Jelinek 		 * the root vnode (the directory vp is the file vp). Some
706*5ffe8443SJerry Jelinek 		 * underlying file systems (e.g. tmpfs or ufs) properly handle
707*5ffe8443SJerry Jelinek 		 * this style of create but at least zfs won't support create
708*5ffe8443SJerry Jelinek 		 * this way (see zfs_fvnodeops_template which has fs_nosys for
709*5ffe8443SJerry Jelinek 		 * the vop_create entry because zfs_create doesn't work
710*5ffe8443SJerry Jelinek 		 * properly for this case).
711*5ffe8443SJerry Jelinek 		 */
712*5ffe8443SJerry Jelinek 		if ((error = VOP_ACCESS(dvp, mode, 0, cr, NULL)) == 0) {
713*5ffe8443SJerry Jelinek 			/*
714*5ffe8443SJerry Jelinek 			 * Since we already know the vnode for the existing
715*5ffe8443SJerry Jelinek 			 * file we can handle create as a no-op, as expected,
716*5ffe8443SJerry Jelinek 			 * truncating the file if necessary.
717*5ffe8443SJerry Jelinek 			 */
718*5ffe8443SJerry Jelinek 			struct vattr vattr;
719*5ffe8443SJerry Jelinek 
720*5ffe8443SJerry Jelinek 			vattr.va_size = 0;
721*5ffe8443SJerry Jelinek 			vattr.va_mask = AT_SIZE;
722*5ffe8443SJerry Jelinek 
723*5ffe8443SJerry Jelinek 			if ((va->va_mask & AT_SIZE) != 0 && va->va_size == 0 &&
724*5ffe8443SJerry Jelinek 			    VOP_SETATTR(dvp, &vattr, 0, CRED(), NULL) != 0)
725*5ffe8443SJerry Jelinek 				return (error);
726*5ffe8443SJerry Jelinek 
727*5ffe8443SJerry Jelinek 			/*
728*5ffe8443SJerry Jelinek 			 * vn_createat will do a vn_rele on the file if it is
729*5ffe8443SJerry Jelinek 			 * pre-existing, which it is in the case of a single
730*5ffe8443SJerry Jelinek 			 * file mounted as the root. Thus, when we eventually
731*5ffe8443SJerry Jelinek 			 * close the file the count will already be 1 so the
732*5ffe8443SJerry Jelinek 			 * vnode would be freed. To prevent that, we add an
733*5ffe8443SJerry Jelinek 			 * extra hold here.
734*5ffe8443SJerry Jelinek 			 */
735*5ffe8443SJerry Jelinek 			VN_HOLD(dvp);
736*5ffe8443SJerry Jelinek 			*vpp = dvp;
737*5ffe8443SJerry Jelinek 			error = 0;
738*5ffe8443SJerry Jelinek 		}
7397c478bd9Sstevel@tonic-gate 	}
740*5ffe8443SJerry Jelinek 
7417c478bd9Sstevel@tonic-gate 	return (error);
7427c478bd9Sstevel@tonic-gate }
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate static int
lo_remove(vnode_t * dvp,char * nm,struct cred * cr,caller_context_t * ct,int flags)745da6c28aaSamw lo_remove(
746da6c28aaSamw 	vnode_t *dvp,
747da6c28aaSamw 	char *nm,
748da6c28aaSamw 	struct cred *cr,
749da6c28aaSamw 	caller_context_t *ct,
750da6c28aaSamw 	int flags)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate #ifdef LODEBUG
7537c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_remove vp %p realvp %p\n", dvp, realvp(dvp));
7547c478bd9Sstevel@tonic-gate #endif
7557c478bd9Sstevel@tonic-gate 	dvp = realvp(dvp);
756da6c28aaSamw 	return (VOP_REMOVE(dvp, nm, cr, ct, flags));
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate static int
lo_link(vnode_t * tdvp,vnode_t * vp,char * tnm,struct cred * cr,caller_context_t * ct,int flags)760da6c28aaSamw lo_link(
761da6c28aaSamw 	vnode_t *tdvp,
762da6c28aaSamw 	vnode_t *vp,
763da6c28aaSamw 	char *tnm,
764da6c28aaSamw 	struct cred *cr,
765da6c28aaSamw 	caller_context_t *ct,
766da6c28aaSamw 	int flags)
7677c478bd9Sstevel@tonic-gate {
768fa871852Sbatschul 	vnode_t *realvp;
769fa871852Sbatschul 
7707c478bd9Sstevel@tonic-gate #ifdef LODEBUG
7717c478bd9Sstevel@tonic-gate 	lo_dprint(4, "lo_link vp %p realvp %p\n", vp, realvp(vp));
7727c478bd9Sstevel@tonic-gate #endif
77359cbbcadSsjelinek 
77459cbbcadSsjelinek 	/*
77559cbbcadSsjelinek 	 * The source and destination vnodes may be in different lofs
77659cbbcadSsjelinek 	 * filesystems sharing the same underlying filesystem, so we need to
77759cbbcadSsjelinek 	 * make sure that the filesystem containing the source vnode is not
77859cbbcadSsjelinek 	 * mounted read-only (vn_link() has already checked the target vnode).
77959cbbcadSsjelinek 	 *
78059cbbcadSsjelinek 	 * In a situation such as:
78159cbbcadSsjelinek 	 *
78259cbbcadSsjelinek 	 * /data	- regular filesystem
78359cbbcadSsjelinek 	 * /foo		- lofs mount of /data/foo
78459cbbcadSsjelinek 	 * /bar		- read-only lofs mount of /data/bar
78559cbbcadSsjelinek 	 *
78659cbbcadSsjelinek 	 * This disallows a link from /bar/somefile to /foo/somefile,
78759cbbcadSsjelinek 	 * which would otherwise allow changes to somefile on the read-only
78859cbbcadSsjelinek 	 * mounted /bar.
78959cbbcadSsjelinek 	 */
79059cbbcadSsjelinek 
79159cbbcadSsjelinek 	if (vn_is_readonly(vp)) {
79259cbbcadSsjelinek 		return (EROFS);
79359cbbcadSsjelinek 	}
7947c478bd9Sstevel@tonic-gate 	while (vn_matchops(vp, lo_vnodeops)) {
7957c478bd9Sstevel@tonic-gate 		vp = realvp(vp);
7967c478bd9Sstevel@tonic-gate 	}
797fa871852Sbatschul 
798fa871852Sbatschul 	/*
799fa871852Sbatschul 	 * In the case where the source vnode is on another stacking
800fa871852Sbatschul 	 * filesystem (such as specfs), the loop above will
801fa871852Sbatschul 	 * terminate before finding the true underlying vnode.
802fa871852Sbatschul 	 *
803fa871852Sbatschul 	 * We use VOP_REALVP here to continue the search.
804fa871852Sbatschul 	 */
805da6c28aaSamw 	if (VOP_REALVP(vp, &realvp, ct) == 0)
806fa871852Sbatschul 		vp = realvp;
807fa871852Sbatschul 
8087c478bd9Sstevel@tonic-gate 	while (vn_matchops(tdvp, lo_vnodeops)) {
8097c478bd9Sstevel@tonic-gate 		tdvp = realvp(tdvp);
8107c478bd9Sstevel@tonic-gate 	}
8117c478bd9Sstevel@tonic-gate 	if (vp->v_vfsp != tdvp->v_vfsp)
8127c478bd9Sstevel@tonic-gate 		return (EXDEV);
813