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