1b923897th/*
2b923897th * CDDL HEADER START
3b923897th *
4b923897th * The contents of this file are subject to the terms of the
5b923897th * Common Development and Distribution License (the "License").
6b923897th * You may not use this file except in compliance with the License.
7b923897th *
8b923897th * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b923897th * or http://www.opensolaris.org/os/licensing.
10b923897th * See the License for the specific language governing permissions
11b923897th * and limitations under the License.
12b923897th *
13b923897th * When distributing Covered Code, include this CDDL HEADER in each
14b923897th * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b923897th * If applicable, add the following below this CDDL HEADER, with the
16b923897th * fields enclosed by brackets "[]" replaced with your own identifying
17b923897th * information: Portions Copyright [yyyy] [name of copyright owner]
18b923897th *
19b923897th * CDDL HEADER END
20b923897th */
21b923897th
22b923897th/*
23546a399Thomas Haynes * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24b923897th * Use is subject to license terms.
25b923897th */
26b923897th
27b923897th/*
28b923897th * Support for ephemeral mounts, e.g. mirror-mounts. These mounts are
29b923897th * triggered from a "stub" rnode via a special set of vnodeops.
30b923897th */
31b923897th
32b923897th#include <sys/param.h>
33b923897th#include <sys/types.h>
34b923897th#include <sys/systm.h>
35b923897th#include <sys/cred.h>
36b923897th#include <sys/time.h>
37b923897th#include <sys/vnode.h>
38b923897th#include <sys/vfs.h>
39b923897th#include <sys/vfs_opreg.h>
40b923897th#include <sys/file.h>
41b923897th#include <sys/filio.h>
42b923897th#include <sys/uio.h>
43b923897th#include <sys/buf.h>
44b923897th#include <sys/mman.h>
45b923897th#include <sys/pathname.h>
46b923897th#include <sys/dirent.h>
47b923897th#include <sys/debug.h>
48b923897th#include <sys/vmsystm.h>
49b923897th#include <sys/fcntl.h>
50b923897th#include <sys/flock.h>
51b923897th#include <sys/swap.h>
52b923897th#include <sys/errno.h>
53b923897th#include <sys/strsubr.h>
54b923897th#include <sys/sysmacros.h>
55b923897th#include <sys/kmem.h>
56b923897th#include <sys/mount.h>
57b923897th#include <sys/cmn_err.h>
58b923897th#include <sys/pathconf.h>
59b923897th#include <sys/utsname.h>
60b923897th#include <sys/dnlc.h>
61b923897th#include <sys/acl.h>
62b923897th#include <sys/systeminfo.h>
63b923897th#include <sys/policy.h>
64b923897th#include <sys/sdt.h>
65b923897th#include <sys/list.h>
66b923897th#include <sys/stat.h>
67b923897th#include <sys/mntent.h>
682f172c5Robert Thurlow#include <sys/priv.h>
69b923897th
70b923897th#include <rpc/types.h>
71b923897th#include <rpc/auth.h>
72b923897th#include <rpc/clnt.h>
73b923897th
74b923897th#include <nfs/nfs.h>
75b923897th#include <nfs/nfs_clnt.h>
76b923897th#include <nfs/nfs_acl.h>
77b923897th#include <nfs/lm.h>
78b923897th#include <nfs/nfs4.h>
79b923897th#include <nfs/nfs4_kprot.h>
80b923897th#include <nfs/rnode4.h>
81b923897th#include <nfs/nfs4_clnt.h>
822f172c5Robert Thurlow#include <nfs/nfsid_map.h>
832f172c5Robert Thurlow#include <nfs/nfs4_idmap_impl.h>
84b923897th
85b923897th#include <vm/hat.h>
86b923897th#include <vm/as.h>
87b923897th#include <vm/page.h>
88b923897th#include <vm/pvn.h>
89b923897th#include <vm/seg.h>
90b923897th#include <vm/seg_map.h>
91b923897th#include <vm/seg_kpm.h>
92b923897th#include <vm/seg_vn.h>
93b923897th
94b923897th#include <fs/fs_subr.h>
95b923897th
96b923897th#include <sys/ddi.h>
97b923897th#include <sys/int_fmtio.h>
98b923897th
99f39b878th#include <sys/sunddi.h>
100b923897th
101546a399Thomas Haynes#include <sys/priv_names.h>
102546a399Thomas Haynes
1032f172c5Robert Thurlowextern zone_key_t	nfs4clnt_zone_key;
1042f172c5Robert Thurlowextern zone_key_t	nfsidmap_zone_key;
1052f172c5Robert Thurlow
106b923897th/*
107b923897th * The automatic unmounter thread stuff!
108b923897th */
109b923897thstatic int nfs4_trigger_thread_timer = 20;	/* in seconds */
110b923897th
111b923897th/*
112b923897th * Just a default....
113b923897th */
114b923897thstatic uint_t nfs4_trigger_mount_to = 240;
115b923897th
116b923897thtypedef struct nfs4_trigger_globals {
117b923897th	kmutex_t		ntg_forest_lock;
118b923897th	uint_t			ntg_mount_to;
119b923897th	int			ntg_thread_started;
120b923897th	nfs4_ephemeral_tree_t	*ntg_forest;
121b923897th} nfs4_trigger_globals_t;
122b923897th
123b923897thkmutex_t	nfs4_ephemeral_thread_lock;
124b923897th
125b923897thzone_key_t	nfs4_ephemeral_key = ZONE_KEY_UNINITIALIZED;
126b923897th
127b923897thstatic void	nfs4_ephemeral_start_harvester(nfs4_trigger_globals_t *);
128b923897th
129b923897th/*
130b923897th * Used for ephemeral mounts; contains data either duplicated from
131b923897th * servinfo4_t, or hand-crafted, depending on type of ephemeral mount.
132b923897th *
133b923897th * It's intended that this structure is used solely for ephemeral
134b923897th * mount-type specific data, for passing this data to
135b923897th * nfs4_trigger_nargs_create().
136b923897th */
137b923897thtypedef struct ephemeral_servinfo {
138b923897th	char			*esi_hostname;
139b923897th	char			*esi_netname;
140b923897th	char			*esi_path;
141b923897th	int			esi_path_len;
142b923897th	int			esi_mount_flags;
143b923897th	struct netbuf		*esi_addr;
144b923897th	struct netbuf		*esi_syncaddr;
145b923897th	struct knetconfig	*esi_knconf;
146b923897th} ephemeral_servinfo_t;
147b923897th
148b923897th/*
149b923897th * Collect together the mount-type specific and generic data args.
150b923897th */
151b923897thtypedef struct domount_args {
152b923897th	ephemeral_servinfo_t	*dma_esi;
153b923897th	char			*dma_hostlist; /* comma-sep. for RO failover */
154b923897th	struct nfs_args		*dma_nargs;
155b923897th} domount_args_t;
156b923897th
157b923897th
158b923897th/*
159b923897th * The vnode ops functions for a trigger stub vnode
160b923897th */
161da6c28aamwstatic int nfs4_trigger_open(vnode_t **, int, cred_t *, caller_context_t *);
162da6c28aamwstatic int nfs4_trigger_getattr(vnode_t *, struct vattr *, int, cred_t *,
163da6c28aamw    caller_context_t *);
164da6c28aamwstatic int nfs4_trigger_setattr(vnode_t *, struct vattr *, int, cred_t *,
165da6c28aamw    caller_context_t *);
166da6c28aamwstatic int nfs4_trigger_access(vnode_t *, int, int, cred_t *,
167da6c28aamw    caller_context_t *);
168da6c28aamwstatic int nfs4_trigger_readlink(vnode_t *, struct uio *, cred_t *,
169da6c28aamw    caller_context_t *);
170da6c28aamwstatic int nfs4_trigger_lookup(vnode_t *, char *, vnode_t **,
171da6c28aamw    struct pathname *, int, vnode_t *, cred_t *, caller_context_t *,
172da6c28aamw    int *, pathname_t *);
173da6c28aamwstatic int nfs4_trigger_create(vnode_t *, char *, struct vattr *,
174da6c28aamw    enum vcexcl, int, vnode_t **, cred_t *, int, caller_context_t *,
175da6c28aamw    vsecattr_t *);
176da6c28aamwstatic int nfs4_trigger_remove(vnode_t *, char *, cred_t *, caller_context_t *,
177da6c28aamw    int);
178da6c28aamwstatic int nfs4_trigger_link(vnode_t *, vnode_t *, char *, cred_t *,
179da6c28aamw    caller_context_t *, int);
180da6c28aamwstatic int nfs4_trigger_rename(vnode_t *, char *, vnode_t *, char *,
181da6c28aamw    cred_t *, caller_context_t *, int);
182da6c28aamwstatic int nfs4_trigger_mkdir(vnode_t *, char *, struct vattr *,
183da6c28aamw    vnode_t **, cred_t *, caller_context_t *, int, vsecattr_t *vsecp);
184da6c28aamwstatic int nfs4_trigger_rmdir(vnode_t *, char *, vnode_t *, cred_t *,
185da6c28aamw    caller_context_t *, int);
186da6c28aamwstatic int nfs4_trigger_symlink(vnode_t *, char *, struct vattr *, char *,
187da6c28aamw    cred_t *, caller_context_t *, int);
188da6c28aamwstatic int nfs4_trigger_cmp(vnode_t *, vnode_t *, caller_context_t *);
189b923897th
190b923897th/*
191b923897th * Regular NFSv4 vnodeops that we need to reference directly
192b923897th */
193da6c28aamwextern int	nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *,
194da6c28aamw		    caller_context_t *);
195da6c28aamwextern void	nfs4_inactive(vnode_t *, cred_t *, caller_context_t *);
196b923897thextern int	nfs4_rwlock(vnode_t *, int, caller_context_t *);
197b923897thextern void	nfs4_rwunlock(vnode_t *, int, caller_context_t *);
198b923897thextern int	nfs4_lookup(vnode_t *, char *, vnode_t **,
199da6c28aamw		    struct pathname *, int, vnode_t *, cred_t *,
200da6c28aamw		    caller_context_t *, int *, pathname_t *);
201da6c28aamwextern int	nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *,
202da6c28aamw		    caller_context_t *);
203da6c28aamwextern int	nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *,
204da6c28aamw		    caller_context_t *);
205da6c28aamwextern int	nfs4_fid(vnode_t *, fid_t *, caller_context_t *);
206da6c28aamwextern int	nfs4_realvp(vnode_t *, vnode_t **, caller_context_t *);
207b923897th
208546a399Thomas Haynesstatic int	nfs4_trigger_mount(vnode_t *, cred_t *, vnode_t **);
209b923897thstatic int	nfs4_trigger_domount(vnode_t *, domount_args_t *, vfs_t **,
2106962f5bThomas Haynes    cred_t *, vnode_t **);
211d16da32Simon Klinkertstatic int 	nfs4_trigger_domount_args_create(vnode_t *, cred_t *,
212d16da32Simon Klinkert    domount_args_t **dmap);
213b923897thstatic void	nfs4_trigger_domount_args_destroy(domount_args_t *dma,
214b923897th    vnode_t *vp);
2152f172c5Robert Thurlowstatic ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *,
2162f172c5Robert Thurlow    cred_t *);
217b923897thstatic void	nfs4_trigger_esi_destroy(ephemeral_servinfo_t *, vnode_t *);
218b923897thstatic ephemeral_servinfo_t *nfs4_trigger_esi_create_mirrormount(vnode_t *,
219b923897th    servinfo4_t *);
2202f172c5Robert Thurlowstatic ephemeral_servinfo_t *nfs4_trigger_esi_create_referral(vnode_t *,
2212f172c5Robert Thurlow    cred_t *);
222b923897thstatic struct nfs_args 	*nfs4_trigger_nargs_create(mntinfo4_t *, servinfo4_t *,
223b923897th    ephemeral_servinfo_t *);
224b923897thstatic void	nfs4_trigger_nargs_destroy(struct nfs_args *);
225b923897thstatic char	*nfs4_trigger_create_mntopts(vfs_t *);
226b923897thstatic void	nfs4_trigger_destroy_mntopts(char *);
227b923897thstatic int 	nfs4_trigger_add_mntopt(char *, char *, vfs_t *);
228b923897thstatic enum clnt_stat nfs4_trigger_ping_server(servinfo4_t *, int);
2292f172c5Robert Thurlowstatic enum clnt_stat nfs4_ping_server_common(struct knetconfig *,
2302f172c5Robert Thurlow    struct netbuf *, int);
231b923897th
232b923897thextern int	umount2_engine(vfs_t *, int, cred_t *, int);
233b923897th
234b923897thvnodeops_t *nfs4_trigger_vnodeops;
235b923897th
236b923897th/*
237b923897th * These are the vnodeops that we must define for stub vnodes.
238b923897th *
239b923897th *
240b923897th * Many of the VOPs defined for NFSv4 do not need to be defined here,
241b923897th * for various reasons. This will result in the VFS default function being
242b923897th * used:
243b923897th *
244b923897th * - These VOPs require a previous VOP_OPEN to have occurred. That will have
245b923897th *   lost the reference to the stub vnode, meaning these should not be called:
246b923897th *       close, read, write, ioctl, readdir, seek.
247b923897th *
248b923897th * - These VOPs are meaningless for vnodes without data pages. Since the
249b923897th *   stub vnode is of type VDIR, these should not be called:
250b923897th *       space, getpage, putpage, map, addmap, delmap, pageio, fsync.
251b923897th *
252b923897th * - These VOPs are otherwise not applicable, and should not be called:
253b923897th *       dump, setsecattr.
254b923897th *
255b923897th *
256b923897th * These VOPs we do not want to define, but nor do we want the VFS default
257b923897th * action. Instead, we specify the VFS error function, with fs_error(), but
258b923897th * note that fs_error() is not actually called. Instead it results in the
259b923897th * use of the error function defined for the particular VOP, in vn_ops_table[]:
260b923897th *
261b923897th * -   frlock, dispose, shrlock.
262b923897th *
263b923897th *
264b923897th * These VOPs we define to use the corresponding regular NFSv4 vnodeop.
265b923897th * NOTE: if any of these ops involve an OTW call with the stub FH, then
266b923897th * that call must be wrapped with save_mnt_secinfo()/check_mnt_secinfo()
267b923897th * to protect the security data in the servinfo4_t for the "parent"
268b923897th * filesystem that contains the stub.
269b923897th *
270b923897th * - These VOPs should not trigger a mount, so that "ls -l" does not:
271b923897th *       pathconf, getsecattr.
272b923897th *
273b923897th * - These VOPs would not make sense to trigger:
274b923897th *       inactive, rwlock, rwunlock, fid, realvp.
275b923897th */
276b923897thconst fs_operation_def_t nfs4_trigger_vnodeops_template[] = {
277b923897th	VOPNAME_OPEN,		{ .vop_open = nfs4_trigger_open },
278b923897th	VOPNAME_GETATTR,	{ .vop_getattr = nfs4_trigger_getattr },
279b923897th	VOPNAME_SETATTR,	{ .vop_setattr = nfs4_trigger_setattr },
280b923897th	VOPNAME_ACCESS,		{ .vop_access = nfs4_trigger_access },
281b923897th	VOPNAME_LOOKUP,		{ .vop_lookup = nfs4_trigger_lookup },
282b923897th	VOPNAME_CREATE,		{ .vop_create = nfs4_trigger_create },
283b923897th	VOPNAME_REMOVE,		{ .vop_remove = nfs4_trigger_remove },
284b923897th	VOPNAME_LINK,		{ .vop_link = nfs4_trigger_link },
285b923897th	VOPNAME_RENAME,		{ .vop_rename = nfs4_trigger_rename },
286b923897th	VOPNAME_MKDIR,		{ .vop_mkdir = nfs4_trigger_mkdir },
287b923897th	VOPNAME_RMDIR,		{ .vop_rmdir = nfs4_trigger_rmdir },
288b923897th	VOPNAME_SYMLINK,	{ .vop_symlink = nfs4_trigger_symlink },
289b923897th	VOPNAME_READLINK,	{ .vop_readlink = nfs4_trigger_readlink },
290b923897th	VOPNAME_INACTIVE, 	{ .vop_inactive = nfs4_inactive },
291b923897th	VOPNAME_FID,		{ .vop_fid = nfs4_fid },
292b923897th	VOPNAME_RWLOCK,		{ .vop_rwlock = nfs4_rwlock },
293b923897th	VOPNAME_RWUNLOCK,	{ .vop_rwunlock = nfs4_rwunlock },
294b923897th	VOPNAME_REALVP,		{ .vop_realvp = nfs4_realvp },
295b923897th	VOPNAME_GETSECATTR,	{ .vop_getsecattr = nfs4_getsecattr },
296b923897th	VOPNAME_PATHCONF,	{ .vop_pathconf = nfs4_pathconf },
297b923897th	VOPNAME_FRLOCK,		{ .error = fs_error },
298b923897th	VOPNAME_DISPOSE,	{ .error = fs_error },
299b923897th	VOPNAME_SHRLOCK,	{ .error = fs_error },
300b923897th	VOPNAME_VNEVENT,	{ .vop_vnevent = fs_vnevent_support },
301b923897th	NULL, NULL
302b923897th};
303b923897th
304d3a1459Thomas Haynesstatic void
305d708af7Thomas Haynesnfs4_ephemeral_tree_incr(nfs4_ephemeral_tree_t *net)
306d3a1459Thomas Haynes{
307d708af7Thomas Haynes	ASSERT(mutex_owned(&net->net_cnt_lock));
308d3a1459Thomas Haynes	net->net_refcnt++;
309d3a1459Thomas Haynes	ASSERT(net->net_refcnt != 0);
310d708af7Thomas Haynes}
311d708af7Thomas Haynes
312d708af7Thomas Haynesstatic void
313d708af7Thomas Haynesnfs4_ephemeral_tree_hold(nfs4_ephemeral_tree_t *net)
314d708af7Thomas Haynes{
315d708af7Thomas Haynes	mutex_enter(&net->net_cnt_lock);
316d708af7Thomas Haynes	nfs4_ephemeral_tree_incr(net);
317d3a1459Thomas Haynes	mutex_exit(&net->net_cnt_lock);
318d3a1459Thomas Haynes}
319d3a1459Thomas Haynes
320d3a1459Thomas Haynes/*
321d3a1459Thomas Haynes * We need a safe way to decrement the refcnt whilst the
322d3a1459Thomas Haynes * lock is being held.
323d3a1459Thomas Haynes */
324d3a1459Thomas Haynesstatic void
325d3a1459Thomas Haynesnfs4_ephemeral_tree_decr(nfs4_ephemeral_tree_t *net)
326d3a1459Thomas Haynes{
327d3a1459Thomas Haynes	ASSERT(mutex_owned(&net->net_cnt_lock));
328d3a1459Thomas Haynes	ASSERT(net->net_refcnt != 0);
329d3a1459Thomas Haynes	net->net_refcnt--;
330d3a1459Thomas Haynes}
331d3a1459Thomas Haynes
332d3a1459Thomas Haynesstatic void
333d3a1459Thomas Haynesnfs4_ephemeral_tree_rele(nfs4_ephemeral_tree_t *net)
334d3a1459Thomas Haynes{
335d3a1459Thomas Haynes	mutex_enter(&net->net_cnt_lock);
336d3a1459Thomas Haynes	nfs4_ephemeral_tree_decr(net);
337d3a1459Thomas Haynes	mutex_exit(&net->net_cnt_lock);
338d3a1459Thomas Haynes}
339d3a1459Thomas Haynes
340b923897th/*
341b923897th * Trigger ops for stub vnodes; for mirror mounts, etc.
342b923897th *
343b923897th * The general idea is that a "triggering" op will first call
344b923897th * nfs4_trigger_mount(), which will find out whether a mount has already
345b923897th * been triggered.
346b923897th *
347b923897th * If it has, then nfs4_trigger_mount() sets newvp to the root vnode
348b923897th * of the covering vfs.
349b923897th *
350b923897th * If a mount has not yet been triggered, nfs4_trigger_mount() will do so,
351b923897th * and again set newvp, as above.
352b923897th *
353b923897th * The triggering op may then re-issue the VOP by calling it on newvp.
354b923897th *
355b923897th * Note that some ops may perform custom action, and may or may not need
356b923897th * to trigger a mount.
357b923897th *
358b923897th * Some ops need to call the regular NFSv4 vnodeop for a stub vnode. We
359b923897th * obviously can't do this with VOP_<whatever>, since it's a stub vnode
360b923897th * and that would just recurse. Instead, we call the v4 op directly,
361b923897th * by name.  This is OK, since we know that the vnode is for NFSv4,
362b923897th * otherwise it couldn't be a stub.
363b923897th *
364b923897th */
365b923897th
366b923897thstatic int
367da6c28aamwnfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
368b923897th{
369b923897th	int error;
370b923897th	vnode_t *newvp;
371b923897th
372546a399Thomas Haynes	error = nfs4_trigger_mount(*vpp, cr, &newvp);
373b923897th	if (error)
374b923897th		return (error);
375b923897th
376b923897th	/* Release the stub vnode, as we're losing the reference to it */
377b923897th	VN_RELE(*vpp);
378b923897th
379b923897th	/* Give the caller the root vnode of the newly-mounted fs */
380b923897th	*vpp = newvp;
381b923897th
382b923897th	/* return with VN_HELD(newvp) */
383da6c28aamw	return (VOP_OPEN(vpp, flag, cr, ct));
384b923897th}
385b923897th
3862f172c5Robert Thurlowvoid
3872f172c5Robert Thurlownfs4_fake_attrs(vnode_t *vp, struct vattr *vap)
3882f172c5Robert Thurlow{
3892f172c5Robert Thurlow	uint_t mask;
3902f172c5Robert Thurlow	timespec_t now;
3912f172c5Robert Thurlow
3922f172c5Robert Thurlow	/*
3932f172c5Robert Thurlow	 * Set some attributes here for referrals.
3942f172c5Robert Thurlow	 */
3952f172c5Robert Thurlow	mask = vap->va_mask;
3962f172c5Robert Thurlow	bzero(vap, sizeof (struct vattr));
3972f172c5Robert Thurlow	vap->va_mask	= mask;
3982f172c5Robert Thurlow	vap->va_uid	= 0;
3992f172c5Robert Thurlow	vap->va_gid	= 0;
4002f172c5Robert Thurlow	vap->va_nlink	= 1;
4012f172c5Robert Thurlow	vap->va_size	= 1;
4022f172c5Robert Thurlow	gethrestime(&now);
4032f172c5Robert Thurlow	vap->va_atime	= now;
4042f172c5Robert Thurlow	vap->va_mtime	= now;
4052f172c5Robert Thurlow	vap->va_ctime	= now;
4062f172c5Robert Thurlow	vap->va_type	= VDIR;
4072f172c5Robert Thurlow	vap->va_mode	= 0555;
4082f172c5Robert Thurlow	vap->va_fsid	= vp->v_vfsp->vfs_dev;
4092f172c5Robert Thurlow	vap->va_rdev	= 0;
4102f172c5Robert Thurlow	vap->va_blksize	= MAXBSIZE;
4112f172c5Robert Thurlow	vap->va_nblocks	= 1;
4122f172c5Robert Thurlow	vap->va_seq	= 0;
4132f172c5Robert Thurlow}
4142f172c5Robert Thurlow
415b923897th/*
416b923897th * For the majority of cases, nfs4_trigger_getattr() will not trigger
417b923897th * a mount. However, if ATTR_TRIGGER is set, we are being informed
418b923897th * that we need to force the mount before we attempt to determine
419b923897th * the attributes. The intent is an atomic operation for security
420b923897th * testing.
4212f172c5Robert Thurlow *
4222f172c5Robert Thurlow * If we're not triggering a mount, we can still inquire about the
4232f172c5Robert Thurlow * actual attributes from the server in the mirror mount case,
4242f172c5Robert Thurlow * and will return manufactured attributes for a referral (see
4252f172c5Robert Thurlow * the 'create' branch of find_referral_stubvp()).
426b923897th */
427b923897thstatic int
428da6c28aamwnfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
429da6c28aamw    caller_context_t *ct)
430b923897th{
431b923897th	int error;
432b923897th
433b923897th	if (flags & ATTR_TRIGGER) {
434b923897th		vnode_t	*newvp;
435b923897th
436546a399Thomas Haynes		error = nfs4_trigger_mount(vp, cr, &newvp);
437b923897th		if (error)
438b923897th			return (error);
439b923897th
440da6c28aamw		error = VOP_GETATTR(newvp, vap, flags, cr, ct);
441b923897th		VN_RELE(newvp);
4422f172c5Robert Thurlow
4432f172c5Robert Thurlow	} else if (RP_ISSTUB_MIRRORMOUNT(VTOR4(vp))) {
4442f172c5Robert Thurlow
445da6c28aamw		error = nfs4_getattr(vp, vap, flags, cr, ct);
4462f172c5Robert Thurlow
4472f172c5Robert Thurlow	} else if (RP_ISSTUB_REFERRAL(VTOR4(vp))) {
4482f172c5Robert Thurlow
4492f172c5Robert Thurlow		nfs4_fake_attrs(vp, vap);
4502f172c5Robert Thurlow		error = 0;
451b923897th	}
452b923897th
453b923897th	return (error);
454b923897th}
455b923897th
456b923897thstatic int
457b923897thnfs4_trigger_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
458a17ce84Marcel Telka    caller_context_t *ct)
459b923897th{
460b923897th	int error;
461b923897th	vnode_t *newvp;
462b923897th
463546a399Thomas Haynes	error = nfs4_trigger_mount(vp, cr, &newvp);
464b923897th	if (error)
465b923897th		return (error);
466b923897th
467b923897th	error = VOP_SETATTR(newvp, vap, flags, cr, ct);
468b923897th	VN_RELE(newvp);
469b923897th
470b923897th	return (error);
471b923897th}
472b923897th
473b923897thstatic int
474da6c28aamwnfs4_trigger_access(vnode_t *vp, int mode, int flags, cred_t *cr,
475da6c28aamw    caller_context_t *ct)
476b923897th{
477b923897th	int error;
478b923897th	vnode_t *newvp;
479b923897th
480546a399Thomas Haynes	error = nfs4_trigger_mount(vp, cr, &newvp);
481b923897th	if (error)
482b923897th		return (error);
483b923897th
484da6c28aamw	error = VOP_ACCESS(newvp, mode, flags, cr, ct);
485b923897th	VN_RELE(newvp);
486b923897th
487b923897th	return (error);
488b923897th}
489b923897th
490b923897thstatic int
491da6c28aamwnfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp,
492da6c28aamw    struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr,
493da6c28aamw    caller_context_t *ct, int *deflags, pathname_t *rpnp)
494b923897th{
495b923897th	int error;
496b923897th	vnode_t *newdvp;
497b923897th	rnode4_t *drp = VTOR4(dvp);
498b923897th
499b923897th	ASSERT(RP_ISSTUB(drp));
500b923897th
501b923897th	/*
502b923897th	 * It's not legal to lookup ".." for an fs root, so we mustn't pass
503b923897th	 * that up. Instead, pass onto the regular op, regardless of whether
504b923897th	 * we've triggered a mount.
505b923897th	 */
506b923897th	if (strcmp(nm, "..") == 0)
5072f172c5Robert Thurlow		if (RP_ISSTUB_MIRRORMOUNT(drp)) {
5082f172c5Robert Thurlow			return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr,
5092f172c5Robert Thurlow			    ct, deflags, rpnp));
5102f172c5Robert Thurlow		} else if (RP_ISSTUB_REFERRAL(drp)) {
5112f172c5Robert Thurlow			/* Return the parent vnode */
5122f172c5Robert Thurlow			return (vtodv(dvp, vpp, cr, TRUE));
5132f172c5Robert Thurlow		}
514b923897th
515546a399Thomas Haynes	error = nfs4_trigger_mount(dvp, cr, &newdvp);
516b923897th	if (error)
517b923897th		return (error);
518b923897th
519da6c28aamw	error = VOP_LOOKUP(newdvp, nm, vpp, pnp, flags, rdir, cr, ct,
520da6c28aamw	    deflags, rpnp);
521b923897th	VN_RELE(newdvp);
522b923897th
523b923897th	return (error);
524b923897th}
525b923897th
526b923897thstatic int
527b923897thnfs4_trigger_create(vnode_t *dvp, char *nm, struct vattr *va,
528da6c28aamw    enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr,
529da6c28aamw    int flags, caller_context_t *ct, vsecattr_t *vsecp)
530b923897th{
531b923897th	int error;
532b923897th	vnode_t *newdvp;
533b923897th
534546a399Thomas Haynes	error = nfs4_trigger_mount(dvp, cr, &newdvp);
535b923897th	if (error)
536b923897th		return (error);
537b923897th
538da6c28aamw	error = VOP_CREATE(newdvp, nm, va, exclusive, mode, vpp, cr,
539da6c28aamw	    flags, ct, vsecp);
540b923897th	VN_RELE(newdvp);
541b923897th
542b923897th	return (error);
543b923897th}
544b923897th
545b923897thstatic int
546da6c28aamwnfs4_trigger_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
547da6c28aamw    int flags)
548b923897th{
549b923897th	int error;
550b923897th	vnode_t *newdvp;
551b923897th
552546a399Thomas Haynes	error = nfs4_trigger_mount(dvp, cr, &newdvp);
553b923897th	if (error)
554b923897th		return (error);
555b923897th
556da6c28aamw	error = VOP_REMOVE(newdvp, nm, cr, ct, flags);
557b923897th	VN_RELE(newdvp);
558b923897th
559b923897th	return (error);
560b923897th}
561b923897th
562b923897thstatic int
563da6c28aamwnfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr,
564da6c28aamw    caller_context_t *ct, int flags)
565b923897th{
566b923897th	int error;
567b923897th	vnode_t *newtdvp;
568b923897th
569546a399Thomas Haynes	error = nfs4_trigger_mount(tdvp, cr, &newtdvp);
570b923897th	if (error)
571b923897th		return (error);
572b923897th
573b923897th	/*
574b923897th	 * We don't check whether svp is a stub. Let the NFSv4 code
575b923897th	 * detect that error, and return accordingly.
576b923897th	 */
577da6c28aamw	error = VOP_LINK(newtdvp, svp, tnm, cr, ct, flags);
578b923897th	VN_RELE(newtdvp);
579b923897th
580b923897th	return (error);
581b923897th}
582b923897th
583b923897thstatic int
584b923897thnfs4_trigger_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
585da6c28aamw    cred_t *cr, caller_context_t *ct, int flags)
586b923897th{
587b923897th	int error;
588b923897th	vnode_t *newsdvp;
589b923897th	rnode4_t *tdrp = VTOR4(tdvp);
590b923897th
591b923897th	/*
592b923897th	 * We know that sdvp is a stub, otherwise we would not be here.
593b923897th	 *
594b923897th	 * If tdvp is also be a stub, there are two possibilities: it
595b923897th	 * is either the same stub as sdvp [i.e. VN_CMP(sdvp, tdvp)]
596b923897th	 * or it is a different stub [!VN_CMP(sdvp, tdvp)].
597b923897th	 *
598b923897th	 * In the former case, just trigger sdvp, and treat tdvp as
599b923897th	 * though it were not a stub.
600b923897th	 *
601b923897th	 * In the latter case, it might be a different stub for the
602b923897th	 * same server fs as sdvp, or for a different server fs.
603b923897th	 * Regardless, from the client perspective this would still
604b923897th	 * be a cross-filesystem rename, and should not be allowed,
605b923897th	 * so return EXDEV, without triggering either mount.
606b923897th	 */
607b923897th	if (RP_ISSTUB(tdrp) && !VN_CMP(sdvp, tdvp))
608b923897th		return (EXDEV);
609b923897th
610546a399Thomas Haynes	error = nfs4_trigger_mount(sdvp, cr, &newsdvp);
611b923897th	if (error)
612b923897th		return (error);
613b923897th
614da6c28aamw	error = VOP_RENAME(newsdvp, snm, tdvp, tnm, cr, ct, flags);
615b923897th
616b923897th	VN_RELE(newsdvp);
617b923897th
618b923897th	return (error);
619b923897th}
620b923897th
621da6c28aamw/* ARGSUSED */
622b923897thstatic int
623b923897thnfs4_trigger_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
624da6c28aamw    cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
625b923897th{
626b923897th	int error;
627b923897th	vnode_t *newdvp;
628b923897th
629546a399Thomas Haynes	error = nfs4_trigger_mount(dvp, cr, &newdvp);
630b923897th	if (error)
631b923897th		return (error);
632b923897th
633da6c28aamw	error = VOP_MKDIR(newdvp, nm, va, vpp, cr, ct, flags, vsecp);
634b923897th	VN_RELE(newdvp);
635b923897th
636b923897th	return (error);
637b923897th}
638b923897th
639b923897thstatic int
640da6c28aamwnfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
641