]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_vfsops.c
xnu-6153.101.6.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_vfsops.c
CommitLineData
1c79356b 1/*
cb323159 2 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1989, 1993, 1995
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_vfsops.c 8.12 (Berkeley) 5/20/95
65 * FreeBSD-Id: nfs_vfsops.c,v 1.52 1997/11/12 05:42:21 julian Exp $
1c79356b 66 */
ea3f0419
A
67
68#include <nfs/nfs_conf.h>
69#if CONFIG_NFS_CLIENT
70
2d21ac55
A
71/*
72 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
73 * support for mandatory and extensible security protections. This notice
74 * is included in support of clause 2.2 (b) of the Apple Public License,
75 * Version 2.0.
76 */
1c79356b
A
77
78#include <sys/param.h>
79#include <sys/systm.h>
80#include <sys/conf.h>
81#include <sys/ioctl.h>
82#include <sys/signal.h>
91447636
A
83#include <sys/proc_internal.h> /* for fs rooting to update rootdir in fdp */
84#include <sys/kauth.h>
85#include <sys/vnode_internal.h>
1c79356b
A
86#include <sys/malloc.h>
87#include <sys/kernel.h>
88#include <sys/sysctl.h>
91447636
A
89#include <sys/mount_internal.h>
90#include <sys/kpi_mbuf.h>
1c79356b 91#include <sys/socket.h>
cb323159 92#include <sys/un.h>
1c79356b 93#include <sys/socketvar.h>
91447636 94#include <sys/fcntl.h>
2d21ac55 95#include <sys/quota.h>
6d2010ae 96#include <sys/priv.h>
91447636 97#include <libkern/OSAtomic.h>
1c79356b
A
98
99#include <sys/vm.h>
100#include <sys/vmparam.h>
101
102#if !defined(NO_MOUNT_PRIVATE)
103#include <sys/filedesc.h>
104#endif /* NO_MOUNT_PRIVATE */
105
106#include <net/if.h>
107#include <net/route.h>
108#include <netinet/in.h>
109
110#include <nfs/rpcv2.h>
2d21ac55 111#include <nfs/krpc.h>
1c79356b
A
112#include <nfs/nfsproto.h>
113#include <nfs/nfs.h>
114#include <nfs/nfsnode.h>
2d21ac55 115#include <nfs/nfs_gss.h>
1c79356b
A
116#include <nfs/nfsmount.h>
117#include <nfs/xdr_subs.h>
118#include <nfs/nfsm_subs.h>
119#include <nfs/nfsdiskless.h>
91447636 120#include <nfs/nfs_lock.h>
2d21ac55
A
121#if CONFIG_MACF
122#include <security/mac_framework.h>
1c79356b 123#endif
1c79356b 124
2d21ac55 125#include <pexpert/pexpert.h>
1c79356b 126
fe8ab488
A
127#define NFS_VFS_DBG(...) NFS_DBG(NFS_FAC_VFS, 7, ## __VA_ARGS__)
128
2d21ac55
A
129/*
130 * NFS client globals
131 */
132
133int nfs_ticks;
b0d623f7
A
134static lck_grp_t *nfs_global_grp, *nfs_mount_grp;
135lck_mtx_t *nfs_global_mutex;
2d21ac55
A
136uint32_t nfs_fs_attr_bitmap[NFS_ATTR_BITMAP_LEN];
137uint32_t nfs_object_attr_bitmap[NFS_ATTR_BITMAP_LEN];
138uint32_t nfs_getattr_bitmap[NFS_ATTR_BITMAP_LEN];
b0d623f7 139struct nfsclientidlist nfsclientids;
2d21ac55
A
140
141/* NFS requests */
142struct nfs_reqqhead nfs_reqq;
143lck_grp_t *nfs_request_grp;
144lck_mtx_t *nfs_request_mutex;
145thread_call_t nfs_request_timer_call;
146int nfs_request_timer_on;
b0d623f7 147u_int32_t nfs_xid = 0;
0a7de745 148u_int32_t nfs_xidwrap = 0; /* to build a (non-wrapping) 64 bit xid */
2d21ac55
A
149
150thread_call_t nfs_buf_timer_call;
151
b0d623f7
A
152/* NFSv4 */
153lck_grp_t *nfs_open_grp;
154uint32_t nfs_open_owner_seqnum = 0;
155uint32_t nfs_lock_owner_seqnum = 0;
156thread_call_t nfs4_callback_timer_call;
157int nfs4_callback_timer_on = 0;
5ba3f43e 158char nfs4_default_domain[MAXPATHLEN];
b0d623f7 159
2d21ac55
A
160/* nfsiod */
161lck_grp_t *nfsiod_lck_grp;
162lck_mtx_t *nfsiod_mutex;
163struct nfsiodlist nfsiodfree, nfsiodwork;
164struct nfsiodmountlist nfsiodmounts;
165int nfsiod_thread_count = 0;
166int nfsiod_thread_max = NFS_DEFASYNCTHREAD;
167int nfs_max_async_writes = NFS_DEFMAXASYNCWRITES;
168
169int nfs_iosize = NFS_IOSIZE;
170int nfs_access_cache_timeout = NFS_MAXATTRTIMO;
6d2010ae
A
171int nfs_access_delete = 1; /* too many servers get this wrong - workaround on by default */
172int nfs_access_dotzfs = 1;
173int nfs_access_for_getattr = 0;
2d21ac55
A
174int nfs_allow_async = 0;
175int nfs_statfs_rate_limit = NFS_DEFSTATFSRATELIMIT;
176int nfs_lockd_mounts = 0;
177int nfs_lockd_request_sent = 0;
6d2010ae
A
178int nfs_idmap_ctrl = NFS_IDMAP_CTRL_USE_IDMAP_SERVICE;
179int nfs_callback_port = 0;
2d21ac55
A
180
181int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
182int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
183
184
0a7de745 185int mountnfs(char *, mount_t, vfs_context_t, vnode_t *);
cb323159 186#if CONFIG_NETBOOT
0a7de745 187static int nfs_mount_diskless(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
2d21ac55 188#if !defined(NO_MOUNT_PRIVATE)
0a7de745 189static int nfs_mount_diskless_private(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
2d21ac55 190#endif /* NO_MOUNT_PRIVATE */
cb323159 191#endif
0a7de745
A
192int nfs_mount_connect(struct nfsmount *);
193void nfs_mount_drain_and_cleanup(struct nfsmount *);
194void nfs_mount_cleanup(struct nfsmount *);
195int nfs_mountinfo_assemble(struct nfsmount *, struct xdrbuf *);
196int nfs4_mount_update_path_with_symlink(struct nfsmount *, struct nfs_fs_path *, uint32_t, fhandle_t *, int *, fhandle_t *, vfs_context_t);
1c79356b
A
197
198/*
2d21ac55 199 * NFS VFS operations.
1c79356b 200 */
0a7de745
A
201int nfs_vfs_mount(mount_t, vnode_t, user_addr_t, vfs_context_t);
202int nfs_vfs_start(mount_t, int, vfs_context_t);
203int nfs_vfs_unmount(mount_t, int, vfs_context_t);
204int nfs_vfs_root(mount_t, vnode_t *, vfs_context_t);
205int nfs_vfs_quotactl(mount_t, int, uid_t, caddr_t, vfs_context_t);
206int nfs_vfs_getattr(mount_t, struct vfs_attr *, vfs_context_t);
207int nfs_vfs_sync(mount_t, int, vfs_context_t);
208int nfs_vfs_vget(mount_t, ino64_t, vnode_t *, vfs_context_t);
209int nfs_vfs_vptofh(vnode_t, int *, unsigned char *, vfs_context_t);
210int nfs_vfs_fhtovp(mount_t, int, unsigned char *, vnode_t *, vfs_context_t);
211int nfs_vfs_init(struct vfsconf *);
212int nfs_vfs_sysctl(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t);
2d21ac55 213
5c9f4661 214const struct vfsops nfs_vfsops = {
39037602
A
215 .vfs_mount = nfs_vfs_mount,
216 .vfs_start = nfs_vfs_start,
217 .vfs_unmount = nfs_vfs_unmount,
218 .vfs_root = nfs_vfs_root,
219 .vfs_quotactl = nfs_vfs_quotactl,
220 .vfs_getattr = nfs_vfs_getattr,
221 .vfs_sync = nfs_vfs_sync,
222 .vfs_vget = nfs_vfs_vget,
223 .vfs_fhtovp = nfs_vfs_fhtovp,
224 .vfs_vptofh = nfs_vfs_vptofh,
225 .vfs_init = nfs_vfs_init,
226 .vfs_sysctl = nfs_vfs_sysctl,
227 // We do not support the remaining VFS ops
1c79356b 228};
1c79356b 229
1c79356b 230
2d21ac55
A
231/*
232 * version-specific NFS functions
233 */
6d2010ae
A
234int nfs3_mount(struct nfsmount *, vfs_context_t, nfsnode_t *);
235int nfs4_mount(struct nfsmount *, vfs_context_t, nfsnode_t *);
b0d623f7
A
236int nfs3_fsinfo(struct nfsmount *, nfsnode_t, vfs_context_t);
237int nfs3_update_statfs(struct nfsmount *, vfs_context_t);
238int nfs4_update_statfs(struct nfsmount *, vfs_context_t);
2d21ac55 239#if !QUOTA
0a7de745
A
240#define nfs3_getquota NULL
241#define nfs4_getquota NULL
2d21ac55 242#else
b0d623f7
A
243int nfs3_getquota(struct nfsmount *, vfs_context_t, uid_t, int, struct dqblk *);
244int nfs4_getquota(struct nfsmount *, vfs_context_t, uid_t, int, struct dqblk *);
2d21ac55 245#endif
1c79356b 246
5c9f4661 247const struct nfs_funcs nfs3_funcs = {
cb323159
A
248 .nf_mount = nfs3_mount,
249 .nf_update_statfs = nfs3_update_statfs,
250 .nf_getquota = nfs3_getquota,
251 .nf_access_rpc = nfs3_access_rpc,
252 .nf_getattr_rpc = nfs3_getattr_rpc,
253 .nf_setattr_rpc = nfs3_setattr_rpc,
254 .nf_read_rpc_async = nfs3_read_rpc_async,
255 .nf_read_rpc_async_finish = nfs3_read_rpc_async_finish,
256 .nf_readlink_rpc = nfs3_readlink_rpc,
257 .nf_write_rpc_async = nfs3_write_rpc_async,
258 .nf_write_rpc_async_finish = nfs3_write_rpc_async_finish,
259 .nf_commit_rpc = nfs3_commit_rpc,
260 .nf_lookup_rpc_async = nfs3_lookup_rpc_async,
261 .nf_lookup_rpc_async_finish = nfs3_lookup_rpc_async_finish,
262 .nf_remove_rpc = nfs3_remove_rpc,
263 .nf_rename_rpc = nfs3_rename_rpc,
264 .nf_setlock_rpc = nfs3_setlock_rpc,
265 .nf_unlock_rpc = nfs3_unlock_rpc,
266 .nf_getlock_rpc = nfs3_getlock_rpc
0a7de745 267};
cb323159 268#if CONFIG_NFS4
5c9f4661 269const struct nfs_funcs nfs4_funcs = {
cb323159
A
270 .nf_mount = nfs4_mount,
271 .nf_update_statfs = nfs4_update_statfs,
272 .nf_getquota = nfs4_getquota,
273 .nf_access_rpc = nfs4_access_rpc,
274 .nf_getattr_rpc = nfs4_getattr_rpc,
275 .nf_setattr_rpc = nfs4_setattr_rpc,
276 .nf_read_rpc_async = nfs4_read_rpc_async,
277 .nf_read_rpc_async_finish = nfs4_read_rpc_async_finish,
278 .nf_readlink_rpc = nfs4_readlink_rpc,
279 .nf_write_rpc_async = nfs4_write_rpc_async,
280 .nf_write_rpc_async_finish = nfs4_write_rpc_async_finish,
281 .nf_commit_rpc = nfs4_commit_rpc,
282 .nf_lookup_rpc_async = nfs4_lookup_rpc_async,
283 .nf_lookup_rpc_async_finish = nfs4_lookup_rpc_async_finish,
284 .nf_remove_rpc = nfs4_remove_rpc,
285 .nf_rename_rpc = nfs4_rename_rpc,
286 .nf_setlock_rpc = nfs4_setlock_rpc,
287 .nf_unlock_rpc = nfs4_unlock_rpc,
288 .nf_getlock_rpc = nfs4_getlock_rpc
0a7de745 289};
cb323159 290#endif
2d21ac55
A
291
292/*
293 * Called once to initialize data structures...
294 */
b0d623f7
A
295int
296nfs_vfs_init(__unused struct vfsconf *vfsp)
1c79356b 297{
cb323159 298#if CONFIG_NFS4
2d21ac55 299 int i;
cb323159 300#endif
1c79356b 301 /*
2d21ac55 302 * Check to see if major data structures haven't bloated.
1c79356b 303 */
0a7de745 304 if (sizeof(struct nfsnode) > NFS_NODEALLOC) {
2d21ac55
A
305 printf("struct nfsnode bloated (> %dbytes)\n", NFS_NODEALLOC);
306 printf("Try reducing NFS_SMALLFH\n");
307 }
0a7de745 308 if (sizeof(struct nfsmount) > NFS_MNTALLOC) {
2d21ac55 309 printf("struct nfsmount bloated (> %dbytes)\n", NFS_MNTALLOC);
0a7de745 310 }
2d21ac55
A
311
312 nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
0a7de745 313 if (nfs_ticks < 1) {
2d21ac55 314 nfs_ticks = 1;
0a7de745 315 }
2d21ac55
A
316
317 /* init async I/O thread pool state */
318 TAILQ_INIT(&nfsiodfree);
319 TAILQ_INIT(&nfsiodwork);
320 TAILQ_INIT(&nfsiodmounts);
321 nfsiod_lck_grp = lck_grp_alloc_init("nfsiod", LCK_GRP_ATTR_NULL);
322 nfsiod_mutex = lck_mtx_alloc_init(nfsiod_lck_grp, LCK_ATTR_NULL);
323
b0d623f7 324 /* init lock groups, etc. */
2d21ac55 325 nfs_mount_grp = lck_grp_alloc_init("nfs_mount", LCK_GRP_ATTR_NULL);
b0d623f7
A
326 nfs_open_grp = lck_grp_alloc_init("nfs_open", LCK_GRP_ATTR_NULL);
327 nfs_global_grp = lck_grp_alloc_init("nfs_global", LCK_GRP_ATTR_NULL);
328
329 nfs_global_mutex = lck_mtx_alloc_init(nfs_global_grp, LCK_ATTR_NULL);
2d21ac55
A
330
331 /* init request list mutex */
332 nfs_request_grp = lck_grp_alloc_init("nfs_request", LCK_GRP_ATTR_NULL);
333 nfs_request_mutex = lck_mtx_alloc_init(nfs_request_grp, LCK_ATTR_NULL);
334
335 /* initialize NFS request list */
336 TAILQ_INIT(&nfs_reqq);
337
0a7de745
A
338 nfs_nbinit(); /* Init the nfsbuf table */
339 nfs_nhinit(); /* Init the nfsnode table */
340 nfs_lockinit(); /* Init the nfs lock state */
cb323159 341#if CONFIG_NFS_GSS
0a7de745 342 nfs_gss_init(); /* Init RPCSEC_GSS security */
cb323159 343#endif
2d21ac55 344
cb323159 345#if CONFIG_NFS4
2d21ac55
A
346 /* NFSv4 stuff */
347 NFS4_PER_FS_ATTRIBUTES(nfs_fs_attr_bitmap);
348 NFS4_PER_OBJECT_ATTRIBUTES(nfs_object_attr_bitmap);
349 NFS4_DEFAULT_ATTRIBUTES(nfs_getattr_bitmap);
0a7de745 350 for (i = 0; i < NFS_ATTR_BITMAP_LEN; i++) {
2d21ac55 351 nfs_getattr_bitmap[i] &= nfs_object_attr_bitmap[i];
0a7de745 352 }
b0d623f7 353 TAILQ_INIT(&nfsclientids);
cb323159 354#endif
2d21ac55
A
355
356 /* initialize NFS timer callouts */
357 nfs_request_timer_call = thread_call_allocate(nfs_request_timer, NULL);
358 nfs_buf_timer_call = thread_call_allocate(nfs_buf_timer, NULL);
cb323159 359#if CONFIG_NFS4
b0d623f7 360 nfs4_callback_timer_call = thread_call_allocate(nfs4_callback_timer, NULL);
cb323159 361#endif
0a7de745 362 return 0;
1c79356b
A
363}
364
cb323159 365
1c79356b
A
366/*
367 * nfs statfs call
368 */
b0d623f7 369int
2d21ac55 370nfs3_update_statfs(struct nfsmount *nmp, vfs_context_t ctx)
1c79356b 371{
2d21ac55
A
372 nfsnode_t np;
373 int error = 0, lockerror, status, nfsvers;
fa4905b1 374 u_int64_t xid;
2d21ac55 375 struct nfsm_chain nmreq, nmrep;
b0d623f7 376 uint32_t val = 0;
1c79356b 377
2d21ac55
A
378 nfsvers = nmp->nm_vers;
379 np = nmp->nm_dnp;
0a7de745
A
380 if (!np) {
381 return ENXIO;
382 }
383 if ((error = vnode_get(NFSTOV(np)))) {
384 return error;
385 }
91447636 386
2d21ac55
A
387 nfsm_chain_null(&nmreq);
388 nfsm_chain_null(&nmrep);
389
390 nfsm_chain_build_alloc_init(error, &nmreq, NFSX_FH(nfsvers));
391 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
392 nfsm_chain_build_done(error, &nmreq);
393 nfsmout_if(error);
fe8ab488 394 error = nfs_request2(np, NULL, &nmreq, NFSPROC_FSSTAT, vfs_context_thread(ctx),
0a7de745
A
395 vfs_context_ucred(ctx), NULL, R_SOFT, &nmrep, &xid, &status);
396 if (error == ETIMEDOUT) {
fe8ab488 397 goto nfsmout;
0a7de745
A
398 }
399 if ((lockerror = nfs_node_lock(np))) {
2d21ac55 400 error = lockerror;
0a7de745
A
401 }
402 if (nfsvers == NFS_VER3) {
2d21ac55 403 nfsm_chain_postop_attr_update(error, &nmrep, np, &xid);
0a7de745
A
404 }
405 if (!lockerror) {
b0d623f7 406 nfs_node_unlock(np);
0a7de745
A
407 }
408 if (!error) {
2d21ac55 409 error = status;
0a7de745 410 }
2d21ac55
A
411 nfsm_assert(error, NFSTONMP(np), ENXIO);
412 nfsmout_if(error);
413 lck_mtx_lock(&nmp->nm_lock);
414 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL);
415 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE);
416 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_AVAIL);
417 if (nfsvers == NFS_VER3) {
418 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_AVAIL);
419 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_TOTAL);
420 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_FREE);
421 nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
422 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_total);
423 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_free);
424 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_avail);
425 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_total);
426 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_free);
427 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_avail);
428 // skip invarsec
429 } else {
430 nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED); // skip tsize?
431 nfsm_chain_get_32(error, &nmrep, nmp->nm_fsattr.nfsa_bsize);
432 nfsm_chain_get_32(error, &nmrep, val);
433 nfsmout_if(error);
0a7de745 434 if (nmp->nm_fsattr.nfsa_bsize <= 0) {
2d21ac55 435 nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
0a7de745 436 }
2d21ac55
A
437 nmp->nm_fsattr.nfsa_space_total = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
438 nfsm_chain_get_32(error, &nmrep, val);
439 nfsmout_if(error);
440 nmp->nm_fsattr.nfsa_space_free = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
441 nfsm_chain_get_32(error, &nmrep, val);
442 nfsmout_if(error);
443 nmp->nm_fsattr.nfsa_space_avail = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
91447636 444 }
2d21ac55
A
445 lck_mtx_unlock(&nmp->nm_lock);
446nfsmout:
447 nfsm_chain_cleanup(&nmreq);
448 nfsm_chain_cleanup(&nmrep);
449 vnode_put(NFSTOV(np));
0a7de745 450 return error;
2d21ac55 451}
e5568f75 452
cb323159 453#if CONFIG_NFS4
b0d623f7 454int
2d21ac55
A
455nfs4_update_statfs(struct nfsmount *nmp, vfs_context_t ctx)
456{
457 nfsnode_t np;
458 int error = 0, lockerror, status, nfsvers, numops;
459 u_int64_t xid;
460 struct nfsm_chain nmreq, nmrep;
461 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
462 struct nfs_vattr nvattr;
6d2010ae 463 struct nfsreq_secinfo_args si;
e5568f75 464
2d21ac55
A
465 nfsvers = nmp->nm_vers;
466 np = nmp->nm_dnp;
0a7de745
A
467 if (!np) {
468 return ENXIO;
469 }
470 if ((error = vnode_get(NFSTOV(np)))) {
471 return error;
472 }
e5568f75 473
6d2010ae
A
474 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
475 NVATTR_INIT(&nvattr);
2d21ac55
A
476 nfsm_chain_null(&nmreq);
477 nfsm_chain_null(&nmrep);
478
479 // PUTFH + GETATTR
480 numops = 2;
481 nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
3e170ce0 482 nfsm_chain_add_compound_header(error, &nmreq, "statfs", nmp->nm_minor_vers, numops);
2d21ac55
A
483 numops--;
484 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
485 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
486 numops--;
487 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
488 NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
489 NFS4_STATFS_ATTRIBUTES(bitmap);
6d2010ae 490 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, np);
2d21ac55
A
491 nfsm_chain_build_done(error, &nmreq);
492 nfsm_assert(error, (numops == 0), EPROTO);
493 nfsmout_if(error);
fe8ab488 494 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND,
0a7de745
A
495 vfs_context_thread(ctx), vfs_context_ucred(ctx),
496 NULL, R_SOFT, &nmrep, &xid, &status);
2d21ac55
A
497 nfsm_chain_skip_tag(error, &nmrep);
498 nfsm_chain_get_32(error, &nmrep, numops);
499 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
500 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
501 nfsm_assert(error, NFSTONMP(np), ENXIO);
502 nfsmout_if(error);
503 lck_mtx_lock(&nmp->nm_lock);
6d2010ae 504 error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL, NULL);
2d21ac55
A
505 lck_mtx_unlock(&nmp->nm_lock);
506 nfsmout_if(error);
0a7de745 507 if ((lockerror = nfs_node_lock(np))) {
2d21ac55 508 error = lockerror;
0a7de745
A
509 }
510 if (!error) {
2d21ac55 511 nfs_loadattrcache(np, &nvattr, &xid, 0);
0a7de745
A
512 }
513 if (!lockerror) {
b0d623f7 514 nfs_node_unlock(np);
0a7de745 515 }
2d21ac55
A
516 nfsm_assert(error, NFSTONMP(np), ENXIO);
517 nfsmout_if(error);
518 nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
519nfsmout:
6d2010ae 520 NVATTR_CLEANUP(&nvattr);
2d21ac55
A
521 nfsm_chain_cleanup(&nmreq);
522 nfsm_chain_cleanup(&nmrep);
523 vnode_put(NFSTOV(np));
0a7de745 524 return error;
91447636 525}
cb323159
A
526#endif /* CONFIG_NFS4 */
527
91447636 528
5ba3f43e
A
529/*
530 * Return an NFS volume name from the mntfrom name.
531 */
532static void
c6bf4f31 533nfs_get_volname(struct mount *mp, char *volname, size_t len, __unused vfs_context_t ctx)
5ba3f43e
A
534{
535 const char *ptr, *cptr;
536 const char *mntfrom = mp->mnt_vfsstat.f_mntfromname;
cb323159
A
537 size_t mflen;
538
539
540 mflen = strnlen(mntfrom, MAXPATHLEN + 1);
5ba3f43e
A
541
542 if (mflen > MAXPATHLEN || mflen == 0) {
543 strlcpy(volname, "Bad volname", len);
544 return;
545 }
546
547 /* Move back over trailing slashes */
0a7de745 548 for (ptr = &mntfrom[mflen - 1]; ptr != mntfrom && *ptr == '/'; ptr--) {
5ba3f43e
A
549 mflen--;
550 }
551
552 /* Find first character after the last slash */
553 cptr = ptr = NULL;
0a7de745
A
554 for (size_t i = 0; i < mflen; i++) {
555 if (mntfrom[i] == '/') {
556 ptr = &mntfrom[i + 1];
557 }
5ba3f43e 558 /* And the first character after the first colon */
0a7de745
A
559 else if (cptr == NULL && mntfrom[i] == ':') {
560 cptr = &mntfrom[i + 1];
561 }
5ba3f43e
A
562 }
563
564 /*
565 * No slash or nothing after the last slash
566 * use everything past the first colon
567 */
0a7de745 568 if (ptr == NULL || *ptr == '\0') {
5ba3f43e 569 ptr = cptr;
0a7de745 570 }
5ba3f43e 571 /* Otherwise use the mntfrom name */
0a7de745 572 if (ptr == NULL) {
5ba3f43e 573 ptr = mntfrom;
0a7de745 574 }
5ba3f43e
A
575
576 mflen = &mntfrom[mflen] - ptr;
0a7de745 577 len = mflen + 1 < len ? mflen + 1 : len;
5ba3f43e
A
578
579 strlcpy(volname, ptr, len);
580}
2d21ac55 581
cb323159 582
91447636 583/*
2d21ac55
A
584 * The NFS VFS_GETATTR function: "statfs"-type information is retrieved
585 * using the nf_update_statfs() function, and other attributes are cobbled
586 * together from whatever sources we can (getattr, fsinfo, pathconf).
91447636 587 */
b0d623f7 588int
2d21ac55 589nfs_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t ctx)
91447636 590{
2d21ac55
A
591 struct nfsmount *nmp;
592 uint32_t bsize;
593 int error = 0, nfsvers;
594
fe8ab488 595 nmp = VFSTONFS(mp);
0a7de745
A
596 if (nfs_mount_gone(nmp)) {
597 return ENXIO;
598 }
2d21ac55
A
599 nfsvers = nmp->nm_vers;
600
0a7de745 601 if (VFSATTR_IS_ACTIVE(fsap, f_bsize) ||
91447636
A
602 VFSATTR_IS_ACTIVE(fsap, f_iosize) ||
603 VFSATTR_IS_ACTIVE(fsap, f_blocks) ||
0a7de745 604 VFSATTR_IS_ACTIVE(fsap, f_bfree) ||
91447636 605 VFSATTR_IS_ACTIVE(fsap, f_bavail) ||
0a7de745
A
606 VFSATTR_IS_ACTIVE(fsap, f_bused) ||
607 VFSATTR_IS_ACTIVE(fsap, f_files) ||
91447636 608 VFSATTR_IS_ACTIVE(fsap, f_ffree)) {
2d21ac55
A
609 int statfsrate = nfs_statfs_rate_limit;
610 int refresh = 1;
91447636 611
2d21ac55
A
612 /*
613 * Are we rate-limiting statfs RPCs?
614 * (Treat values less than 1 or greater than 1,000,000 as no limit.)
615 */
616 if ((statfsrate > 0) && (statfsrate < 1000000)) {
617 struct timeval now;
618 uint32_t stamp;
619
620 microuptime(&now);
621 lck_mtx_lock(&nmp->nm_lock);
0a7de745 622 stamp = (now.tv_sec * statfsrate) + (now.tv_usec / (1000000 / statfsrate));
2d21ac55
A
623 if (stamp != nmp->nm_fsattrstamp) {
624 refresh = 1;
625 nmp->nm_fsattrstamp = stamp;
626 } else {
627 refresh = 0;
628 }
629 lck_mtx_unlock(&nmp->nm_lock);
91447636 630 }
2d21ac55 631
0a7de745 632 if (refresh && !nfs_use_cache(nmp)) {
2d21ac55 633 error = nmp->nm_funcs->nf_update_statfs(nmp, ctx);
0a7de745
A
634 }
635 if ((error == ESTALE) || (error == ETIMEDOUT)) {
2d21ac55 636 error = 0;
0a7de745
A
637 }
638 if (error) {
639 return error;
640 }
2d21ac55
A
641
642 lck_mtx_lock(&nmp->nm_lock);
643 VFSATTR_RETURN(fsap, f_iosize, nfs_iosize);
644 VFSATTR_RETURN(fsap, f_bsize, nmp->nm_fsattr.nfsa_bsize);
645 bsize = nmp->nm_fsattr.nfsa_bsize;
0a7de745 646 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL)) {
2d21ac55 647 VFSATTR_RETURN(fsap, f_blocks, nmp->nm_fsattr.nfsa_space_total / bsize);
0a7de745
A
648 }
649 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE)) {
2d21ac55 650 VFSATTR_RETURN(fsap, f_bfree, nmp->nm_fsattr.nfsa_space_free / bsize);
0a7de745
A
651 }
652 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_AVAIL)) {
2d21ac55 653 VFSATTR_RETURN(fsap, f_bavail, nmp->nm_fsattr.nfsa_space_avail / bsize);
0a7de745 654 }
2d21ac55 655 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL) &&
0a7de745 656 NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE)) {
2d21ac55 657 VFSATTR_RETURN(fsap, f_bused,
0a7de745
A
658 (nmp->nm_fsattr.nfsa_space_total / bsize) -
659 (nmp->nm_fsattr.nfsa_space_free / bsize));
660 }
661 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_TOTAL)) {
2d21ac55 662 VFSATTR_RETURN(fsap, f_files, nmp->nm_fsattr.nfsa_files_total);
0a7de745
A
663 }
664 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_FREE)) {
2d21ac55 665 VFSATTR_RETURN(fsap, f_ffree, nmp->nm_fsattr.nfsa_files_free);
0a7de745 666 }
2d21ac55 667 lck_mtx_unlock(&nmp->nm_lock);
91447636
A
668 }
669
5ba3f43e
A
670 if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
671 /*%%% IF fail over support is implemented we may need to take nm_lock */
cb323159 672 nfs_get_volname(mp, fsap->f_vol_name, MAXPATHLEN, ctx);
5ba3f43e
A
673 VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
674 }
cb323159
A
675 if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)
676 ) {
91447636 677 u_int32_t caps, valid;
b0d623f7 678 nfsnode_t np = nmp->nm_dnp;
91447636 679
b0d623f7 680 nfsm_assert(error, VFSTONFS(mp) && np, ENXIO);
0a7de745
A
681 if (error) {
682 return error;
683 }
2d21ac55 684 lck_mtx_lock(&nmp->nm_lock);
91447636
A
685
686 /*
687 * The capabilities[] array defines what this volume supports.
688 *
689 * The valid[] array defines which bits this code understands
cb323159
A
690 * the meaning of (whether the volume has that capability or
691 * not). Any zero bits here means "I don't know what you're
692 * asking about" and the caller cannot tell whether that
693 * capability is present or not.
91447636
A
694 */
695 caps = valid = 0;
2d21ac55
A
696 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SYMLINK_SUPPORT)) {
697 valid |= VOL_CAP_FMT_SYMBOLICLINKS;
0a7de745 698 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_SYMLINK) {
2d21ac55 699 caps |= VOL_CAP_FMT_SYMBOLICLINKS;
0a7de745 700 }
2d21ac55
A
701 }
702 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_LINK_SUPPORT)) {
703 valid |= VOL_CAP_FMT_HARDLINKS;
0a7de745 704 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_LINK) {
2d21ac55 705 caps |= VOL_CAP_FMT_HARDLINKS;
0a7de745 706 }
2d21ac55
A
707 }
708 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CASE_INSENSITIVE)) {
709 valid |= VOL_CAP_FMT_CASE_SENSITIVE;
0a7de745 710 if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_CASE_INSENSITIVE)) {
2d21ac55 711 caps |= VOL_CAP_FMT_CASE_SENSITIVE;
0a7de745 712 }
2d21ac55
A
713 }
714 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CASE_PRESERVING)) {
715 valid |= VOL_CAP_FMT_CASE_PRESERVING;
0a7de745 716 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_CASE_PRESERVING) {
2d21ac55 717 caps |= VOL_CAP_FMT_CASE_PRESERVING;
0a7de745 718 }
2d21ac55 719 }
593a1d5f 720 /* Note: VOL_CAP_FMT_2TB_FILESIZE is actually used to test for "large file support" */
2d21ac55 721 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXFILESIZE)) {
593a1d5f 722 /* Is server's max file size at least 4GB? */
0a7de745 723 if (nmp->nm_fsattr.nfsa_maxfilesize >= 0x100000000ULL) {
91447636 724 caps |= VOL_CAP_FMT_2TB_FILESIZE;
0a7de745 725 }
2d21ac55
A
726 } else if (nfsvers >= NFS_VER3) {
727 /*
728 * NFSv3 and up supports 64 bits of file size.
593a1d5f 729 * So, we'll just assume maxfilesize >= 4GB
2d21ac55
A
730 */
731 caps |= VOL_CAP_FMT_2TB_FILESIZE;
732 }
cb323159 733#if CONFIG_NFS4
2d21ac55
A
734 if (nfsvers >= NFS_VER4) {
735 caps |= VOL_CAP_FMT_HIDDEN_FILES;
736 valid |= VOL_CAP_FMT_HIDDEN_FILES;
737 // VOL_CAP_FMT_OPENDENYMODES
6d2010ae
A
738// caps |= VOL_CAP_FMT_OPENDENYMODES;
739// valid |= VOL_CAP_FMT_OPENDENYMODES;
91447636 740 }
cb323159 741#endif
813fb2f6
A
742 // no version of nfs supports immutable files
743 caps |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
744 valid |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
745
91447636 746 fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
0a7de745
A
747 // VOL_CAP_FMT_PERSISTENTOBJECTIDS |
748 // VOL_CAP_FMT_SYMBOLICLINKS |
749 // VOL_CAP_FMT_HARDLINKS |
750 // VOL_CAP_FMT_JOURNAL |
751 // VOL_CAP_FMT_JOURNAL_ACTIVE |
752 // VOL_CAP_FMT_NO_ROOT_TIMES |
753 // VOL_CAP_FMT_SPARSE_FILES |
754 // VOL_CAP_FMT_ZERO_RUNS |
755 // VOL_CAP_FMT_CASE_SENSITIVE |
756 // VOL_CAP_FMT_CASE_PRESERVING |
757 // VOL_CAP_FMT_FAST_STATFS |
758 // VOL_CAP_FMT_2TB_FILESIZE |
759 // VOL_CAP_FMT_OPENDENYMODES |
760 // VOL_CAP_FMT_HIDDEN_FILES |
761 caps;
91447636 762 fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
0a7de745
A
763 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
764 // VOL_CAP_FMT_SYMBOLICLINKS |
765 // VOL_CAP_FMT_HARDLINKS |
766 // VOL_CAP_FMT_JOURNAL |
767 // VOL_CAP_FMT_JOURNAL_ACTIVE |
768 // VOL_CAP_FMT_NO_ROOT_TIMES |
769 // VOL_CAP_FMT_SPARSE_FILES |
770 // VOL_CAP_FMT_ZERO_RUNS |
771 // VOL_CAP_FMT_CASE_SENSITIVE |
772 // VOL_CAP_FMT_CASE_PRESERVING |
773 VOL_CAP_FMT_FAST_STATFS |
774 VOL_CAP_FMT_2TB_FILESIZE |
775 // VOL_CAP_FMT_OPENDENYMODES |
776 // VOL_CAP_FMT_HIDDEN_FILES |
777 valid;
91447636
A
778
779 /*
780 * We don't support most of the interfaces.
781 *
cb323159
A
782 * We MAY support locking, but we don't have any easy way of
783 * probing. We can tell if there's no lockd running or if
784 * locks have been disabled for a mount, so we can definitely
785 * answer NO in that case. Any attempt to send a request to
786 * lockd to test for locking support may cause the lazily-
787 * launched locking daemons to be started unnecessarily. So
788 * we avoid that. However, we do record if we ever successfully
789 * perform a lock operation on a mount point, so if it looks
790 * like lock ops have worked, we do report that we support them.
91447636
A
791 */
792 caps = valid = 0;
cb323159 793#if CONFIG_NFS4
2d21ac55
A
794 if (nfsvers >= NFS_VER4) {
795 caps = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
796 valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
0a7de745 797 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL) {
6d2010ae 798 caps |= VOL_CAP_INT_EXTENDED_SECURITY;
0a7de745 799 }
6d2010ae 800 valid |= VOL_CAP_INT_EXTENDED_SECURITY;
0a7de745 801 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR) {
6d2010ae 802 caps |= VOL_CAP_INT_EXTENDED_ATTR;
0a7de745 803 }
6d2010ae
A
804 valid |= VOL_CAP_INT_EXTENDED_ATTR;
805#if NAMEDSTREAMS
0a7de745 806 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR) {
6d2010ae 807 caps |= VOL_CAP_INT_NAMEDSTREAMS;
0a7de745 808 }
6d2010ae
A
809 valid |= VOL_CAP_INT_NAMEDSTREAMS;
810#endif
cb323159
A
811 } else
812#endif
813 if (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED) {
91447636
A
814 /* locks disabled on this mount, so they definitely won't work */
815 valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
816 } else if (nmp->nm_state & NFSSTA_LOCKSWORK) {
817 caps = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
818 valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
819 }
820 fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] =
0a7de745
A
821 // VOL_CAP_INT_SEARCHFS |
822 // VOL_CAP_INT_ATTRLIST |
823 // VOL_CAP_INT_NFSEXPORT |
824 // VOL_CAP_INT_READDIRATTR |
825 // VOL_CAP_INT_EXCHANGEDATA |
826 // VOL_CAP_INT_COPYFILE |
827 // VOL_CAP_INT_ALLOCATE |
828 // VOL_CAP_INT_VOL_RENAME |
829 // VOL_CAP_INT_ADVLOCK |
830 // VOL_CAP_INT_FLOCK |
831 // VOL_CAP_INT_EXTENDED_SECURITY |
832 // VOL_CAP_INT_USERACCESS |
833 // VOL_CAP_INT_MANLOCK |
834 // VOL_CAP_INT_NAMEDSTREAMS |
835 // VOL_CAP_INT_EXTENDED_ATTR |
836 VOL_CAP_INT_REMOTE_EVENT |
837 caps;
91447636 838 fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
0a7de745
A
839 VOL_CAP_INT_SEARCHFS |
840 VOL_CAP_INT_ATTRLIST |
841 VOL_CAP_INT_NFSEXPORT |
842 VOL_CAP_INT_READDIRATTR |
843 VOL_CAP_INT_EXCHANGEDATA |
844 VOL_CAP_INT_COPYFILE |
845 VOL_CAP_INT_ALLOCATE |
846 VOL_CAP_INT_VOL_RENAME |
847 // VOL_CAP_INT_ADVLOCK |
848 // VOL_CAP_INT_FLOCK |
849 // VOL_CAP_INT_EXTENDED_SECURITY |
850 // VOL_CAP_INT_USERACCESS |
851 // VOL_CAP_INT_MANLOCK |
852 // VOL_CAP_INT_NAMEDSTREAMS |
853 // VOL_CAP_INT_EXTENDED_ATTR |
854 VOL_CAP_INT_REMOTE_EVENT |
855 valid;
91447636
A
856
857 fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
858 fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
859
860 fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
861 fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
862
863 VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
2d21ac55 864 lck_mtx_unlock(&nmp->nm_lock);
91447636
A
865 }
866
867 if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
868 fsap->f_attributes.validattr.commonattr = 0;
869 fsap->f_attributes.validattr.volattr =
0a7de745 870 ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
91447636
A
871 fsap->f_attributes.validattr.dirattr = 0;
872 fsap->f_attributes.validattr.fileattr = 0;
873 fsap->f_attributes.validattr.forkattr = 0;
874
875 fsap->f_attributes.nativeattr.commonattr = 0;
876 fsap->f_attributes.nativeattr.volattr =
0a7de745 877 ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
91447636
A
878 fsap->f_attributes.nativeattr.dirattr = 0;
879 fsap->f_attributes.nativeattr.fileattr = 0;
880 fsap->f_attributes.nativeattr.forkattr = 0;
881
882 VFSATTR_SET_SUPPORTED(fsap, f_attributes);
883 }
884
0a7de745 885 return error;
1c79356b
A
886}
887
888/*
889 * nfs version 3 fsinfo rpc call
890 */
b0d623f7 891int
2d21ac55 892nfs3_fsinfo(struct nfsmount *nmp, nfsnode_t np, vfs_context_t ctx)
1c79356b 893{
b0d623f7 894 int error = 0, lockerror, status, nmlocked = 0;
fa4905b1 895 u_int64_t xid;
b0d623f7 896 uint32_t val, prefsize, maxsize;
2d21ac55
A
897 struct nfsm_chain nmreq, nmrep;
898
899 nfsm_chain_null(&nmreq);
900 nfsm_chain_null(&nmrep);
901
902 nfsm_chain_build_alloc_init(error, &nmreq, NFSX_FH(nmp->nm_vers));
903 nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize);
904 nfsm_chain_build_done(error, &nmreq);
905 nfsmout_if(error);
6d2010ae 906 error = nfs_request(np, NULL, &nmreq, NFSPROC_FSINFO, ctx, NULL, &nmrep, &xid, &status);
0a7de745 907 if ((lockerror = nfs_node_lock(np))) {
2d21ac55 908 error = lockerror;
0a7de745 909 }
2d21ac55 910 nfsm_chain_postop_attr_update(error, &nmrep, np, &xid);
0a7de745 911 if (!lockerror) {
b0d623f7 912 nfs_node_unlock(np);
0a7de745
A
913 }
914 if (!error) {
2d21ac55 915 error = status;
0a7de745 916 }
2d21ac55
A
917 nfsmout_if(error);
918
919 lck_mtx_lock(&nmp->nm_lock);
920 nmlocked = 1;
921
922 nfsm_chain_get_32(error, &nmrep, maxsize);
923 nfsm_chain_get_32(error, &nmrep, prefsize);
924 nfsmout_if(error);
925 nmp->nm_fsattr.nfsa_maxread = maxsize;
0a7de745 926 if (prefsize < nmp->nm_rsize) {
2d21ac55 927 nmp->nm_rsize = (prefsize + NFS_FABLKSIZE - 1) &
0a7de745
A
928 ~(NFS_FABLKSIZE - 1);
929 }
6d2010ae 930 if ((maxsize > 0) && (maxsize < nmp->nm_rsize)) {
2d21ac55 931 nmp->nm_rsize = maxsize & ~(NFS_FABLKSIZE - 1);
0a7de745 932 if (nmp->nm_rsize == 0) {
2d21ac55 933 nmp->nm_rsize = maxsize;
0a7de745 934 }
2d21ac55
A
935 }
936 nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED); // skip rtmult
937
938 nfsm_chain_get_32(error, &nmrep, maxsize);
939 nfsm_chain_get_32(error, &nmrep, prefsize);
940 nfsmout_if(error);
941 nmp->nm_fsattr.nfsa_maxwrite = maxsize;
0a7de745 942 if (prefsize < nmp->nm_wsize) {
2d21ac55 943 nmp->nm_wsize = (prefsize + NFS_FABLKSIZE - 1) &
0a7de745
A
944 ~(NFS_FABLKSIZE - 1);
945 }
6d2010ae 946 if ((maxsize > 0) && (maxsize < nmp->nm_wsize)) {
2d21ac55 947 nmp->nm_wsize = maxsize & ~(NFS_FABLKSIZE - 1);
0a7de745 948 if (nmp->nm_wsize == 0) {
2d21ac55 949 nmp->nm_wsize = maxsize;
0a7de745 950 }
1c79356b 951 }
2d21ac55
A
952 nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED); // skip wtmult
953
954 nfsm_chain_get_32(error, &nmrep, prefsize);
955 nfsmout_if(error);
0a7de745 956 if ((prefsize > 0) && (prefsize < nmp->nm_readdirsize)) {
2d21ac55 957 nmp->nm_readdirsize = prefsize;
0a7de745 958 }
6d2010ae 959 if ((nmp->nm_fsattr.nfsa_maxread > 0) &&
0a7de745 960 (nmp->nm_fsattr.nfsa_maxread < nmp->nm_readdirsize)) {
6d2010ae 961 nmp->nm_readdirsize = nmp->nm_fsattr.nfsa_maxread;
0a7de745 962 }
2d21ac55 963
593a1d5f 964 nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_maxfilesize);
2d21ac55
A
965
966 nfsm_chain_adv(error, &nmrep, 2 * NFSX_UNSIGNED); // skip time_delta
967
968 /* convert FS properties to our own flags */
969 nfsm_chain_get_32(error, &nmrep, val);
970 nfsmout_if(error);
0a7de745 971 if (val & NFSV3FSINFO_LINK) {
2d21ac55 972 nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_LINK;
0a7de745
A
973 }
974 if (val & NFSV3FSINFO_SYMLINK) {
2d21ac55 975 nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_SYMLINK;
0a7de745
A
976 }
977 if (val & NFSV3FSINFO_HOMOGENEOUS) {
2d21ac55 978 nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_HOMOGENEOUS;
0a7de745
A
979 }
980 if (val & NFSV3FSINFO_CANSETTIME) {
2d21ac55 981 nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_SET_TIME;
0a7de745 982 }
2d21ac55
A
983 nmp->nm_state |= NFSSTA_GOTFSINFO;
984 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXREAD);
985 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXWRITE);
986 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXFILESIZE);
987 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_LINK_SUPPORT);
988 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SYMLINK_SUPPORT);
989 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_HOMOGENEOUS);
990 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CANSETTIME);
991nfsmout:
0a7de745 992 if (nmlocked) {
2d21ac55 993 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 994 }
2d21ac55
A
995 nfsm_chain_cleanup(&nmreq);
996 nfsm_chain_cleanup(&nmrep);
0a7de745 997 return error;
1c79356b
A
998}
999
1000/*
1001 * Mount a remote root fs via. nfs. This depends on the info in the
1002 * nfs_diskless structure that has been filled in properly by some primary
1003 * bootstrap.
1004 * It goes something like this:
1005 * - do enough of "ifconfig" by calling ifioctl() so that the system
1006 * can talk to the server
1007 * - If nfs_diskless.mygateway is filled in, use that address as
1008 * a default gateway.
1009 * - hand craft the swap nfs vnode hanging off a fake mount point
1010 * if swdevt[0].sw_dev == NODEV
1011 * - build the rootfs mount point and call mountnfs() to do the rest.
1012 */
cb323159 1013#if CONFIG_NETBOOT
1c79356b 1014int
2d21ac55 1015nfs_mountroot(void)
1c79356b
A
1016{
1017 struct nfs_diskless nd;
2d21ac55
A
1018 mount_t mp = NULL;
1019 vnode_t vp = NULL;
1020 vfs_context_t ctx;
1c79356b
A
1021 int error;
1022#if !defined(NO_MOUNT_PRIVATE)
2d21ac55
A
1023 mount_t mppriv = NULL;
1024 vnode_t vppriv = NULL;
1c79356b 1025#endif /* NO_MOUNT_PRIVATE */
91447636 1026 int v3, sotype;
1c79356b 1027
1c79356b
A
1028 /*
1029 * Call nfs_boot_init() to fill in the nfs_diskless struct.
9bccf70c
A
1030 * Note: networking must already have been configured before
1031 * we're called.
1c79356b
A
1032 */
1033 bzero((caddr_t) &nd, sizeof(nd));
2d21ac55 1034 error = nfs_boot_init(&nd);
0a7de745 1035 if (error) {
6d2010ae 1036 panic("nfs_boot_init: unable to initialize NFS root system information, "
0a7de745
A
1037 "error %d, check configuration: %s\n", error, PE_boot_args());
1038 }
1c79356b 1039
91447636
A
1040 /*
1041 * Try NFSv3 first, then fallback to NFSv2.
1042 * Likewise, try TCP first, then fall back to UDP.
1043 */
90556fb8 1044 v3 = 1;
91447636 1045 sotype = SOCK_STREAM;
90556fb8
A
1046
1047tryagain:
2d21ac55 1048 error = nfs_boot_getfh(&nd, v3, sotype);
90556fb8 1049 if (error) {
4a249263 1050 if (error == EHOSTDOWN || error == EHOSTUNREACH) {
0a7de745 1051 if (nd.nd_root.ndm_mntfrom) {
b0d623f7 1052 FREE_ZONE(nd.nd_root.ndm_mntfrom,
0a7de745
A
1053 MAXPATHLEN, M_NAMEI);
1054 }
1055 if (nd.nd_root.ndm_path) {
2d21ac55 1056 FREE_ZONE(nd.nd_root.ndm_path,
0a7de745
A
1057 MAXPATHLEN, M_NAMEI);
1058 }
1059 if (nd.nd_private.ndm_mntfrom) {
b0d623f7 1060 FREE_ZONE(nd.nd_private.ndm_mntfrom,
0a7de745
A
1061 MAXPATHLEN, M_NAMEI);
1062 }
1063 if (nd.nd_private.ndm_path) {
2d21ac55 1064 FREE_ZONE(nd.nd_private.ndm_path,
0a7de745
A
1065 MAXPATHLEN, M_NAMEI);
1066 }
1067 return error;
4a249263 1068 }
90556fb8 1069 if (v3) {
91447636 1070 if (sotype == SOCK_STREAM) {
6d2010ae 1071 printf("NFS mount (v3,TCP) failed with error %d, trying UDP...\n", error);
91447636
A
1072 sotype = SOCK_DGRAM;
1073 goto tryagain;
1074 }
6d2010ae 1075 printf("NFS mount (v3,UDP) failed with error %d, trying v2...\n", error);
90556fb8 1076 v3 = 0;
91447636
A
1077 sotype = SOCK_STREAM;
1078 goto tryagain;
1079 } else if (sotype == SOCK_STREAM) {
6d2010ae 1080 printf("NFS mount (v2,TCP) failed with error %d, trying UDP...\n", error);
91447636 1081 sotype = SOCK_DGRAM;
90556fb8 1082 goto tryagain;
6d2010ae
A
1083 } else {
1084 printf("NFS mount (v2,UDP) failed with error %d, giving up...\n", error);
90556fb8 1085 }
0a7de745 1086 switch (error) {
2d21ac55 1087 case EPROGUNAVAIL:
6d2010ae 1088 panic("NFS mount failed: NFS server mountd not responding, check server configuration: %s", PE_boot_args());
2d21ac55
A
1089 case EACCES:
1090 case EPERM:
6d2010ae 1091 panic("NFS mount failed: NFS server refused mount, check server configuration: %s", PE_boot_args());
2d21ac55 1092 default:
6d2010ae 1093 panic("NFS mount failed with error %d, check configuration: %s", error, PE_boot_args());
2d21ac55 1094 }
90556fb8
A
1095 }
1096
2d21ac55
A
1097 ctx = vfs_context_kernel();
1098
1c79356b
A
1099 /*
1100 * Create the root mount point.
1101 */
1102#if !defined(NO_MOUNT_PRIVATE)
2d21ac55
A
1103 {
1104 //PWC hack until we have a real "mount" tool to remount root rw
0a7de745
A
1105 int rw_root = 0;
1106 int flags = MNT_ROOTFS | MNT_RDONLY;
1107 PE_parse_boot_argn("-rwroot_hack", &rw_root, sizeof(rw_root));
1108 if (rw_root) {
2d21ac55
A
1109 flags = MNT_ROOTFS;
1110 kprintf("-rwroot_hack in effect: mounting root fs read/write\n");
1111 }
0a7de745
A
1112
1113 if ((error = nfs_mount_diskless(&nd.nd_root, "/", flags, &vp, &mp, ctx)))
1c79356b 1114#else
2d21ac55 1115 if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_ROOTFS, &vp, &mp, ctx)))
1c79356b 1116#endif /* NO_MOUNT_PRIVATE */
0a7de745
A
1117 {
1118 if (v3) {
1119 if (sotype == SOCK_STREAM) {
1120 printf("NFS root mount (v3,TCP) failed with %d, trying UDP...\n", error);
1121 sotype = SOCK_DGRAM;
1122 goto tryagain;
1123 }
1124 printf("NFS root mount (v3,UDP) failed with %d, trying v2...\n", error);
1125 v3 = 0;
1126 sotype = SOCK_STREAM;
1127 goto tryagain;
1128 } else if (sotype == SOCK_STREAM) {
1129 printf("NFS root mount (v2,TCP) failed with %d, trying UDP...\n", error);
91447636
A
1130 sotype = SOCK_DGRAM;
1131 goto tryagain;
0a7de745
A
1132 } else {
1133 printf("NFS root mount (v2,UDP) failed with error %d, giving up...\n", error);
91447636 1134 }
0a7de745 1135 panic("NFS root mount failed with error %d, check configuration: %s\n", error, PE_boot_args());
90556fb8 1136 }
1c79356b 1137 }
b0d623f7 1138 printf("root on %s\n", nd.nd_root.ndm_mntfrom);
1c79356b 1139
91447636
A
1140 vfs_unbusy(mp);
1141 mount_list_add(mp);
1c79356b 1142 rootvp = vp;
0a7de745 1143
1c79356b
A
1144#if !defined(NO_MOUNT_PRIVATE)
1145 if (nd.nd_private.ndm_saddr.sin_addr.s_addr) {
0a7de745
A
1146 error = nfs_mount_diskless_private(&nd.nd_private, "/private",
1147 0, &vppriv, &mppriv, ctx);
1148 if (error) {
1149 panic("NFS /private mount failed with error %d, check configuration: %s\n", error, PE_boot_args());
1150 }
1151 printf("private on %s\n", nd.nd_private.ndm_mntfrom);
2d21ac55 1152
0a7de745
A
1153 vfs_unbusy(mppriv);
1154 mount_list_add(mppriv);
1c79356b
A
1155 }
1156
1157#endif /* NO_MOUNT_PRIVATE */
1158
0a7de745 1159 if (nd.nd_root.ndm_mntfrom) {
b0d623f7 1160 FREE_ZONE(nd.nd_root.ndm_mntfrom, MAXPATHLEN, M_NAMEI);
0a7de745
A
1161 }
1162 if (nd.nd_root.ndm_path) {
90556fb8 1163 FREE_ZONE(nd.nd_root.ndm_path, MAXPATHLEN, M_NAMEI);
0a7de745
A
1164 }
1165 if (nd.nd_private.ndm_mntfrom) {
b0d623f7 1166 FREE_ZONE(nd.nd_private.ndm_mntfrom, MAXPATHLEN, M_NAMEI);
0a7de745
A
1167 }
1168 if (nd.nd_private.ndm_path) {
90556fb8 1169 FREE_ZONE(nd.nd_private.ndm_path, MAXPATHLEN, M_NAMEI);
0a7de745 1170 }
90556fb8 1171
1c79356b 1172 /* Get root attributes (for the time). */
6d2010ae 1173 error = nfs_getattr(VTONFS(vp), NULL, ctx, NGA_UNCACHED);
0a7de745 1174 if (error) {
6d2010ae 1175 panic("NFS mount: failed to get attributes for root directory, error %d, check server", error);
0a7de745
A
1176 }
1177 return 0;
1c79356b
A
1178}
1179
1180/*
1181 * Internal version of mount system call for diskless setup.
1182 */
1183static int
91447636
A
1184nfs_mount_diskless(
1185 struct nfs_dlmount *ndmntp,
1186 const char *mntname,
1187 int mntflag,
1188 vnode_t *vpp,
2d21ac55
A
1189 mount_t *mpp,
1190 vfs_context_t ctx)
1c79356b 1191{
91447636 1192 mount_t mp;
6d2010ae
A
1193 int error, numcomps;
1194 char *xdrbuf, *p, *cp, *frompath, *endserverp;
1195 char uaddr[MAX_IPv4_STR_LEN];
1196 struct xdrbuf xb;
1197 uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
1198 uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN];
1199 uint32_t mflags[NFS_MFLAG_BITMAP_LEN];
1200 uint32_t argslength_offset, attrslength_offset, end_offset;
1c79356b 1201
b0d623f7 1202 if ((error = vfs_rootmountalloc("nfs", ndmntp->ndm_mntfrom, &mp))) {
6d2010ae 1203 printf("nfs_mount_diskless: NFS not configured\n");
0a7de745 1204 return error;
1c79356b 1205 }
91447636
A
1206
1207 mp->mnt_flag |= mntflag;
0a7de745 1208 if (!(mntflag & MNT_RDONLY)) {
91447636 1209 mp->mnt_flag &= ~MNT_RDONLY;
0a7de745 1210 }
1c79356b 1211
6d2010ae
A
1212 /* find the server-side path being mounted */
1213 frompath = ndmntp->ndm_mntfrom;
1214 if (*frompath == '[') { /* skip IPv6 literal address */
0a7de745 1215 while (*frompath && (*frompath != ']')) {
6d2010ae 1216 frompath++;
0a7de745
A
1217 }
1218 if (*frompath == ']') {
6d2010ae 1219 frompath++;
0a7de745 1220 }
6d2010ae 1221 }
0a7de745 1222 while (*frompath && (*frompath != ':')) {
6d2010ae 1223 frompath++;
0a7de745 1224 }
6d2010ae 1225 endserverp = frompath;
0a7de745 1226 while (*frompath && (*frompath == ':')) {
6d2010ae 1227 frompath++;
0a7de745 1228 }
6d2010ae
A
1229 /* count fs location path components */
1230 p = frompath;
0a7de745 1231 while (*p && (*p == '/')) {
6d2010ae 1232 p++;
0a7de745 1233 }
6d2010ae
A
1234 numcomps = 0;
1235 while (*p) {
1236 numcomps++;
0a7de745 1237 while (*p && (*p != '/')) {
6d2010ae 1238 p++;
0a7de745
A
1239 }
1240 while (*p && (*p == '/')) {
6d2010ae 1241 p++;
0a7de745 1242 }
6d2010ae
A
1243 }
1244
1245 /* convert address to universal address string */
1246 if (inet_ntop(AF_INET, &ndmntp->ndm_saddr.sin_addr, uaddr, sizeof(uaddr)) != uaddr) {
1247 printf("nfs_mount_diskless: bad address\n");
0a7de745 1248 return EINVAL;
6d2010ae
A
1249 }
1250
1251 /* prepare mount attributes */
1252 NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
1253 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
1254 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
1255 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
1256 NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
1257 NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
1258 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
1259
1260 /* prepare mount flags */
1261 NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
1262 NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
1263 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
1264 NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
1265
1266 /* build xdr buffer */
1267 xb_init_buffer(&xb, NULL, 0);
1268 xb_add_32(error, &xb, NFS_ARGSVERSION_XDR);
1269 argslength_offset = xb_offset(&xb);
1270 xb_add_32(error, &xb, 0); // args length
1271 xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
1272 xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
1273 attrslength_offset = xb_offset(&xb);
1274 xb_add_32(error, &xb, 0); // attrs length
1275 xb_add_32(error, &xb, ndmntp->ndm_nfsv3 ? 3 : 2); // NFS version
1276 xb_add_string(error, &xb, ((ndmntp->ndm_sotype == SOCK_DGRAM) ? "udp" : "tcp"), 3);
1277 xb_add_32(error, &xb, ntohs(ndmntp->ndm_saddr.sin_port)); // NFS port
1278 xb_add_fh(error, &xb, &ndmntp->ndm_fh[0], ndmntp->ndm_fhlen);
1279 /* fs location */
1280 xb_add_32(error, &xb, 1); /* fs location count */
1281 xb_add_32(error, &xb, 1); /* server count */
1282 xb_add_string(error, &xb, ndmntp->ndm_mntfrom, (endserverp - ndmntp->ndm_mntfrom)); /* server name */
1283 xb_add_32(error, &xb, 1); /* address count */
1284 xb_add_string(error, &xb, uaddr, strlen(uaddr)); /* address */
1285 xb_add_32(error, &xb, 0); /* empty server info */
1286 xb_add_32(error, &xb, numcomps); /* pathname component count */
1287 p = frompath;
0a7de745 1288 while (*p && (*p == '/')) {
6d2010ae 1289 p++;
0a7de745 1290 }
6d2010ae
A
1291 while (*p) {
1292 cp = p;
0a7de745 1293 while (*p && (*p != '/')) {
6d2010ae 1294 p++;
0a7de745 1295 }
6d2010ae 1296 xb_add_string(error, &xb, cp, (p - cp)); /* component */
0a7de745 1297 if (error) {
6d2010ae 1298 break;
0a7de745
A
1299 }
1300 while (*p && (*p == '/')) {
6d2010ae 1301 p++;
0a7de745 1302 }
6d2010ae
A
1303 }
1304 xb_add_32(error, &xb, 0); /* empty fsl info */
1305 xb_add_32(error, &xb, mntflag); /* MNT flags */
1306 xb_build_done(error, &xb);
1307
1308 /* update opaque counts */
1309 end_offset = xb_offset(&xb);
1310 if (!error) {
1311 error = xb_seek(&xb, argslength_offset);
0a7de745 1312 xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD /*version*/);
6d2010ae
A
1313 }
1314 if (!error) {
1315 error = xb_seek(&xb, attrslength_offset);
0a7de745 1316 xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
6d2010ae 1317 }
91447636 1318 if (error) {
6d2010ae
A
1319 printf("nfs_mount_diskless: error %d assembling mount args\n", error);
1320 xb_cleanup(&xb);
0a7de745 1321 return error;
91447636 1322 }
6d2010ae
A
1323 /* grab the assembled buffer */
1324 xdrbuf = xb_buffer_base(&xb);
1325 xb.xb_flags &= ~XB_CLEANUP;
1326
1327 /* do the mount */
1328 if ((error = mountnfs(xdrbuf, mp, ctx, vpp))) {
91447636
A
1329 printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
1330 // XXX vfs_rootmountfailed(mp);
1331 mount_list_lock();
1332 mp->mnt_vtable->vfc_refcount--;
1333 mount_list_unlock();
1334 vfs_unbusy(mp);
1335 mount_lock_destroy(mp);
2d21ac55
A
1336#if CONFIG_MACF
1337 mac_mount_label_destroy(mp);
1338#endif
91447636 1339 FREE_ZONE(mp, sizeof(struct mount), M_MOUNT);
6d2010ae
A
1340 } else {
1341 *mpp = mp;
1c79356b 1342 }
6d2010ae 1343 xb_cleanup(&xb);
0a7de745 1344 return error;
1c79356b
A
1345}
1346
1347#if !defined(NO_MOUNT_PRIVATE)
1348/*
1349 * Internal version of mount system call to mount "/private"
1350 * separately in diskless setup
1351 */
1352static int
91447636
A
1353nfs_mount_diskless_private(
1354 struct nfs_dlmount *ndmntp,
1355 const char *mntname,
1356 int mntflag,
1357 vnode_t *vpp,
2d21ac55
A
1358 mount_t *mpp,
1359 vfs_context_t ctx)
1c79356b 1360{
91447636 1361 mount_t mp;
6d2010ae 1362 int error, numcomps;
91447636
A
1363 proc_t procp;
1364 struct vfstable *vfsp;
1c79356b 1365 struct nameidata nd;
91447636 1366 vnode_t vp;
6d2010ae
A
1367 char *xdrbuf = NULL, *p, *cp, *frompath, *endserverp;
1368 char uaddr[MAX_IPv4_STR_LEN];
1369 struct xdrbuf xb;
1370 uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
1371 uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN], mflags[NFS_MFLAG_BITMAP_LEN];
1372 uint32_t argslength_offset, attrslength_offset, end_offset;
1c79356b
A
1373
1374 procp = current_proc(); /* XXX */
cb323159 1375 xb_init(&xb, XDRBUF_NONE);
1c79356b
A
1376
1377 {
0a7de745
A
1378 /*
1379 * mimic main()!. Temporarily set up rootvnode and other stuff so
1380 * that namei works. Need to undo this because main() does it, too
1381 */
1382 struct filedesc *fdp; /* pointer to file descriptor state */
1c79356b 1383 fdp = procp->p_fd;
91447636 1384 mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
1c79356b
A
1385
1386 /* Get the vnode for '/'. Set fdp->fd_cdir to reference it. */
0a7de745 1387 if (VFS_ROOT(mountlist.tqh_first, &rootvnode, NULL)) {
1c79356b 1388 panic("cannot find root vnode");
0a7de745 1389 }
91447636
A
1390 error = vnode_ref(rootvnode);
1391 if (error) {
1392 printf("nfs_mountroot: vnode_ref() failed on root vnode!\n");
2d21ac55 1393 goto out;
91447636 1394 }
1c79356b 1395 fdp->fd_cdir = rootvnode;
1c79356b
A
1396 fdp->fd_rdir = NULL;
1397 }
1398
1399 /*
1400 * Get vnode to be covered
1401 */
6d2010ae 1402 NDINIT(&nd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
2d21ac55 1403 CAST_USER_ADDR_T(mntname), ctx);
1c79356b 1404 if ((error = namei(&nd))) {
91447636 1405 printf("nfs_mountroot: private namei failed!\n");
2d21ac55 1406 goto out;
1c79356b
A
1407 }
1408 {
91447636
A
1409 /* undo vnode_ref() in mimic main()! */
1410 vnode_rele(rootvnode);
1c79356b 1411 }
91447636 1412 nameidone(&nd);
1c79356b 1413 vp = nd.ni_vp;
91447636 1414
2d21ac55 1415 if ((error = VNOP_FSYNC(vp, MNT_WAIT, ctx)) ||
91447636
A
1416 (error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0))) {
1417 vnode_put(vp);
2d21ac55 1418 goto out;
1c79356b 1419 }
91447636
A
1420 if (vnode_vtype(vp) != VDIR) {
1421 vnode_put(vp);
2d21ac55
A
1422 error = ENOTDIR;
1423 goto out;
1c79356b 1424 }
0a7de745
A
1425 for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) {
1426 if (!strncmp(vfsp->vfc_name, "nfs", sizeof(vfsp->vfc_name))) {
1c79356b 1427 break;
0a7de745
A
1428 }
1429 }
1c79356b 1430 if (vfsp == NULL) {
91447636
A
1431 printf("nfs_mountroot: private NFS not configured\n");
1432 vnode_put(vp);
2d21ac55
A
1433 error = ENODEV;
1434 goto out;
1c79356b 1435 }
91447636
A
1436 if (vnode_mountedhere(vp) != NULL) {
1437 vnode_put(vp);
2d21ac55
A
1438 error = EBUSY;
1439 goto out;
1c79356b
A
1440 }
1441
1442 /*
1443 * Allocate and initialize the filesystem.
1444 */
b0d623f7 1445 mp = _MALLOC_ZONE((u_int32_t)sizeof(struct mount), M_MOUNT, M_WAITOK);
91447636
A
1446 if (!mp) {
1447 printf("nfs_mountroot: unable to allocate mount structure\n");
1448 vnode_put(vp);
2d21ac55
A
1449 error = ENOMEM;
1450 goto out;
91447636 1451 }
b0d623f7 1452 bzero((char *)mp, sizeof(struct mount));
0b4e3aa0 1453
55e303ae
A
1454 /* Initialize the default IO constraints */
1455 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
1456 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
2d21ac55
A
1457 mp->mnt_ioflags = 0;
1458 mp->mnt_realrootvp = NULLVP;
5ba3f43e 1459 mp->mnt_authcache_ttl = 0; /* Allways go to our lookup */
0b4e3aa0 1460
91447636
A
1461 mount_lock_init(mp);
1462 TAILQ_INIT(&mp->mnt_vnodelist);
1463 TAILQ_INIT(&mp->mnt_workerqueue);
1464 TAILQ_INIT(&mp->mnt_newvnodes);
1465 (void)vfs_busy(mp, LK_NOWAIT);
1466 TAILQ_INIT(&mp->mnt_vnodelist);
1467 mount_list_lock();
1c79356b 1468 vfsp->vfc_refcount++;
91447636
A
1469 mount_list_unlock();
1470 mp->mnt_vtable = vfsp;
1471 mp->mnt_op = vfsp->vfc_vfsops;
1472 // mp->mnt_stat.f_type = vfsp->vfc_typenum;
1c79356b
A
1473 mp->mnt_flag = mntflag;
1474 mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
0a7de745 1475 strncpy(mp->mnt_vfsstat.f_fstypename, vfsp->vfc_name, MFSNAMELEN - 1);
1c79356b
A
1476 vp->v_mountedhere = mp;
1477 mp->mnt_vnodecovered = vp;
fe8ab488 1478 vp = NULLVP;
91447636 1479 mp->mnt_vfsstat.f_owner = kauth_cred_getuid(kauth_cred_get());
b0d623f7
A
1480 (void) copystr(mntname, mp->mnt_vfsstat.f_mntonname, MAXPATHLEN - 1, 0);
1481 (void) copystr(ndmntp->ndm_mntfrom, mp->mnt_vfsstat.f_mntfromname, MAXPATHLEN - 1, 0);
2d21ac55
A
1482#if CONFIG_MACF
1483 mac_mount_label_init(mp);
1484 mac_mount_label_associate(ctx, mp);
1485#endif
1c79356b 1486
6d2010ae
A
1487 /* find the server-side path being mounted */
1488 frompath = ndmntp->ndm_mntfrom;
1489 if (*frompath == '[') { /* skip IPv6 literal address */
0a7de745 1490 while (*frompath && (*frompath != ']')) {
6d2010ae 1491 frompath++;
0a7de745
A
1492 }
1493 if (*frompath == ']') {
6d2010ae 1494 frompath++;
0a7de745 1495 }
6d2010ae 1496 }
0a7de745 1497 while (*frompath && (*frompath != ':')) {
6d2010ae 1498 frompath++;
0a7de745 1499 }
6d2010ae 1500 endserverp = frompath;
0a7de745 1501 while (*frompath && (*frompath == ':')) {
6d2010ae 1502 frompath++;
0a7de745 1503 }
6d2010ae
A
1504 /* count fs location path components */
1505 p = frompath;
0a7de745 1506 while (*p && (*p == '/')) {
6d2010ae 1507 p++;
0a7de745 1508 }
6d2010ae
A
1509 numcomps = 0;
1510 while (*p) {
1511 numcomps++;
0a7de745 1512 while (*p && (*p != '/')) {
6d2010ae 1513 p++;
0a7de745
A
1514 }
1515 while (*p && (*p == '/')) {
6d2010ae 1516 p++;
0a7de745 1517 }
6d2010ae
A
1518 }
1519
1520 /* convert address to universal address string */
1521 if (inet_ntop(AF_INET, &ndmntp->ndm_saddr.sin_addr, uaddr, sizeof(uaddr)) != uaddr) {
1522 printf("nfs_mountroot: bad address\n");
1523 error = EINVAL;
1524 goto out;
1525 }
1526
1527 /* prepare mount attributes */
1528 NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
1529 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
1530 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
1531 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
1532 NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
1533 NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
1534 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
1535
1536 /* prepare mount flags */
1537 NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
1538 NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
1539 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
1540 NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
1541
1542 /* build xdr buffer */
1543 xb_init_buffer(&xb, NULL, 0);
1544 xb_add_32(error, &xb, NFS_ARGSVERSION_XDR);
1545 argslength_offset = xb_offset(&xb);
1546 xb_add_32(error, &xb, 0); // args length
1547 xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
1548 xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
1549 attrslength_offset = xb_offset(&xb);
1550 xb_add_32(error, &xb, 0); // attrs length
1551 xb_add_32(error, &xb, ndmntp->ndm_nfsv3 ? 3 : 2); // NFS version
1552 xb_add_string(error, &xb, ((ndmntp->ndm_sotype == SOCK_DGRAM) ? "udp" : "tcp"), 3);
1553 xb_add_32(error, &xb, ntohs(ndmntp->ndm_saddr.sin_port)); // NFS port
1554 xb_add_fh(error, &xb, &ndmntp->ndm_fh[0], ndmntp->ndm_fhlen);
1555 /* fs location */
1556 xb_add_32(error, &xb, 1); /* fs location count */
1557 xb_add_32(error, &xb, 1); /* server count */
1558 xb_add_string(error, &xb, ndmntp->ndm_mntfrom, (endserverp - ndmntp->ndm_mntfrom)); /* server name */
1559 xb_add_32(error, &xb, 1); /* address count */
1560 xb_add_string(error, &xb, uaddr, strlen(uaddr)); /* address */
1561 xb_add_32(error, &xb, 0); /* empty server info */
1562 xb_add_32(error, &xb, numcomps); /* pathname component count */
1563 p = frompath;
0a7de745 1564 while (*p && (*p == '/')) {
6d2010ae 1565 p++;
0a7de745 1566 }
6d2010ae
A
1567 while (*p) {
1568 cp = p;
0a7de745 1569 while (*p && (*p != '/')) {
6d2010ae 1570 p++;
0a7de745 1571 }
6d2010ae 1572 xb_add_string(error, &xb, cp, (p - cp)); /* component */
0a7de745 1573 if (error) {
6d2010ae 1574 break;
0a7de745
A
1575 }
1576 while (*p && (*p == '/')) {
6d2010ae 1577 p++;
0a7de745 1578 }
6d2010ae
A
1579 }
1580 xb_add_32(error, &xb, 0); /* empty fsl info */
1581 xb_add_32(error, &xb, mntflag); /* MNT flags */
1582 xb_build_done(error, &xb);
1583
1584 /* update opaque counts */
1585 end_offset = xb_offset(&xb);
1586 if (!error) {
1587 error = xb_seek(&xb, argslength_offset);
0a7de745 1588 xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD /*version*/);
6d2010ae
A
1589 }
1590 if (!error) {
1591 error = xb_seek(&xb, attrslength_offset);
0a7de745 1592 xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
6d2010ae 1593 }
91447636 1594 if (error) {
6d2010ae 1595 printf("nfs_mountroot: error %d assembling mount args\n", error);
2d21ac55 1596 goto out;
91447636 1597 }
6d2010ae
A
1598 /* grab the assembled buffer */
1599 xdrbuf = xb_buffer_base(&xb);
1600 xb.xb_flags &= ~XB_CLEANUP;
1601
1602 /* do the mount */
1603 if ((error = mountnfs(xdrbuf, mp, ctx, &vp))) {
91447636 1604 printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
fe8ab488 1605 vnode_put(mp->mnt_vnodecovered);
91447636
A
1606 mount_list_lock();
1607 vfsp->vfc_refcount--;
1608 mount_list_unlock();
1609 vfs_unbusy(mp);
1610 mount_lock_destroy(mp);
2d21ac55
A
1611#if CONFIG_MACF
1612 mac_mount_label_destroy(mp);
1613#endif
0a7de745 1614 FREE_ZONE(mp, sizeof(struct mount), M_MOUNT);
2d21ac55 1615 goto out;
1c79356b
A
1616 }
1617
1618 *mpp = mp;
1619 *vpp = vp;
2d21ac55 1620out:
6d2010ae 1621 xb_cleanup(&xb);
0a7de745 1622 return error;
1c79356b
A
1623}
1624#endif /* NO_MOUNT_PRIVATE */
1625
cb323159
A
1626#endif
1627
1c79356b 1628/*
6d2010ae 1629 * Convert old style NFS mount args to XDR.
1c79356b 1630 */
6d2010ae
A
1631static int
1632nfs_convert_old_nfs_args(mount_t mp, user_addr_t data, vfs_context_t ctx, int argsversion, int inkernel, char **xdrbufp)
1c79356b 1633{
6d2010ae 1634 int error = 0, args64bit, argsize, numcomps;
91447636
A
1635 struct user_nfs_args args;
1636 struct nfs_args tempargs;
6d2010ae 1637 caddr_t argsp;
55e303ae 1638 size_t len;
6d2010ae
A
1639 u_char nfh[NFS4_FHSIZE];
1640 char *mntfrom, *endserverp, *frompath, *p, *cp;
1641 struct sockaddr_storage ss;
1642 void *sinaddr;
1643 char uaddr[MAX_IPv6_STR_LEN];
1644 uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
1645 uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN], mflags[NFS_MFLAG_BITMAP_LEN];
1646 uint32_t nfsvers, nfslockmode = 0, argslength_offset, attrslength_offset, end_offset;
1647 struct xdrbuf xb;
1648
1649 *xdrbufp = NULL;
1650
1651 /* allocate a temporary buffer for mntfrom */
1652 MALLOC_ZONE(mntfrom, char*, MAXPATHLEN, M_NAMEI, M_WAITOK);
0a7de745
A
1653 if (!mntfrom) {
1654 return ENOMEM;
1655 }
6d2010ae
A
1656
1657 args64bit = (inkernel || vfs_context_is64bit(ctx));
1658 argsp = args64bit ? (void*)&args : (void*)&tempargs;
1659
1660 argsize = args64bit ? sizeof(args) : sizeof(tempargs);
1661 switch (argsversion) {
91447636 1662 case 3:
6d2010ae 1663 argsize -= NFS_ARGSVERSION4_INCSIZE;
91447636 1664 case 4:
6d2010ae 1665 argsize -= NFS_ARGSVERSION5_INCSIZE;
2d21ac55 1666 case 5:
6d2010ae 1667 argsize -= NFS_ARGSVERSION6_INCSIZE;
b0d623f7 1668 case 6:
91447636
A
1669 break;
1670 default:
6d2010ae
A
1671 error = EPROGMISMATCH;
1672 goto nfsmout;
1c79356b 1673 }
91447636 1674
6d2010ae 1675 /* read in the structure */
0a7de745 1676 if (inkernel) {
6d2010ae 1677 bcopy(CAST_DOWN(void *, data), argsp, argsize);
0a7de745 1678 } else {
6d2010ae 1679 error = copyin(data, argsp, argsize);
0a7de745 1680 }
6d2010ae
A
1681 nfsmout_if(error);
1682
1683 if (!args64bit) {
91447636
A
1684 args.addrlen = tempargs.addrlen;
1685 args.sotype = tempargs.sotype;
1686 args.proto = tempargs.proto;
1687 args.fhsize = tempargs.fhsize;
1688 args.flags = tempargs.flags;
1689 args.wsize = tempargs.wsize;
1690 args.rsize = tempargs.rsize;
1691 args.readdirsize = tempargs.readdirsize;
1692 args.timeo = tempargs.timeo;
1693 args.retrans = tempargs.retrans;
1694 args.maxgrouplist = tempargs.maxgrouplist;
1695 args.readahead = tempargs.readahead;
1696 args.leaseterm = tempargs.leaseterm;
1697 args.deadthresh = tempargs.deadthresh;
1698 args.addr = CAST_USER_ADDR_T(tempargs.addr);
1699 args.fh = CAST_USER_ADDR_T(tempargs.fh);
1700 args.hostname = CAST_USER_ADDR_T(tempargs.hostname);
6d2010ae 1701 if (args.version >= 4) {
91447636
A
1702 args.acregmin = tempargs.acregmin;
1703 args.acregmax = tempargs.acregmax;
1704 args.acdirmin = tempargs.acdirmin;
1705 args.acdirmax = tempargs.acdirmax;
1706 }
0a7de745 1707 if (args.version >= 5) {
2d21ac55 1708 args.auth = tempargs.auth;
0a7de745
A
1709 }
1710 if (args.version >= 6) {
b0d623f7 1711 args.deadtimeout = tempargs.deadtimeout;
0a7de745 1712 }
91447636
A
1713 }
1714
6d2010ae
A
1715 if ((args.fhsize < 0) || (args.fhsize > NFS4_FHSIZE)) {
1716 error = EINVAL;
1717 goto nfsmout;
1718 }
2d21ac55 1719 if (args.fhsize > 0) {
0a7de745 1720 if (inkernel) {
6d2010ae 1721 bcopy(CAST_DOWN(void *, args.fh), (caddr_t)nfh, args.fhsize);
0a7de745 1722 } else {
6d2010ae 1723 error = copyin(args.fh, (caddr_t)nfh, args.fhsize);
0a7de745 1724 }
6d2010ae 1725 nfsmout_if(error);
2d21ac55 1726 }
91447636 1727
0a7de745
A
1728 if (inkernel) {
1729 error = copystr(CAST_DOWN(void *, args.hostname), mntfrom, MAXPATHLEN - 1, &len);
1730 } else {
1731 error = copyinstr(args.hostname, mntfrom, MAXPATHLEN - 1, &len);
1732 }
6d2010ae 1733 nfsmout_if(error);
91447636
A
1734 bzero(&mntfrom[len], MAXPATHLEN - len);
1735
6d2010ae
A
1736 /* find the server-side path being mounted */
1737 frompath = mntfrom;
1738 if (*frompath == '[') { /* skip IPv6 literal address */
0a7de745 1739 while (*frompath && (*frompath != ']')) {
6d2010ae 1740 frompath++;
0a7de745
A
1741 }
1742 if (*frompath == ']') {
6d2010ae 1743 frompath++;
0a7de745 1744 }
6d2010ae 1745 }
0a7de745 1746 while (*frompath && (*frompath != ':')) {
6d2010ae 1747 frompath++;
0a7de745 1748 }
6d2010ae 1749 endserverp = frompath;
0a7de745 1750 while (*frompath && (*frompath == ':')) {
6d2010ae 1751 frompath++;
0a7de745 1752 }
6d2010ae
A
1753 /* count fs location path components */
1754 p = frompath;
0a7de745 1755 while (*p && (*p == '/')) {
6d2010ae 1756 p++;
0a7de745 1757 }
6d2010ae
A
1758 numcomps = 0;
1759 while (*p) {
1760 numcomps++;
0a7de745 1761 while (*p && (*p != '/')) {
6d2010ae 1762 p++;
0a7de745
A
1763 }
1764 while (*p && (*p == '/')) {
6d2010ae 1765 p++;
0a7de745 1766 }
6d2010ae
A
1767 }
1768
1769 /* copy socket address */
0a7de745 1770 if (inkernel) {
6d2010ae 1771 bcopy(CAST_DOWN(void *, args.addr), &ss, args.addrlen);
0a7de745
A
1772 } else {
1773 if ((size_t)args.addrlen > sizeof(struct sockaddr_storage)) {
316670eb 1774 error = EINVAL;
0a7de745 1775 } else {
316670eb 1776 error = copyin(args.addr, &ss, args.addrlen);
0a7de745 1777 }
316670eb 1778 }
6d2010ae
A
1779 nfsmout_if(error);
1780 ss.ss_len = args.addrlen;
1781
1782 /* convert address to universal address string */
0a7de745 1783 if (ss.ss_family == AF_INET) {
6d2010ae 1784 sinaddr = &((struct sockaddr_in*)&ss)->sin_addr;
0a7de745 1785 } else if (ss.ss_family == AF_INET6) {
6d2010ae 1786 sinaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
0a7de745 1787 } else {
6d2010ae 1788 sinaddr = NULL;
0a7de745 1789 }
6d2010ae
A
1790 if (!sinaddr || (inet_ntop(ss.ss_family, sinaddr, uaddr, sizeof(uaddr)) != uaddr)) {
1791 error = EINVAL;
1792 goto nfsmout;
1793 }
1794
1795 /* prepare mount flags */
1796 NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
1797 NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
1798 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_SOFT);
1799 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_INTR);
1800 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
1801 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOCONNECT);
1802 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_DUMBTIMER);
1803 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_CALLUMNT);
1804 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RDIRPLUS);
1805 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NONEGNAMECACHE);
1806 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_MUTEJUKEBOX);
1807 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOQUOTA);
0a7de745 1808 if (args.flags & NFSMNT_SOFT) {
6d2010ae 1809 NFS_BITMAP_SET(mflags, NFS_MFLAG_SOFT);
0a7de745
A
1810 }
1811 if (args.flags & NFSMNT_INT) {
6d2010ae 1812 NFS_BITMAP_SET(mflags, NFS_MFLAG_INTR);
0a7de745
A
1813 }
1814 if (args.flags & NFSMNT_RESVPORT) {
6d2010ae 1815 NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
0a7de745
A
1816 }
1817 if (args.flags & NFSMNT_NOCONN) {
6d2010ae 1818 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOCONNECT);
0a7de745
A
1819 }
1820 if (args.flags & NFSMNT_DUMBTIMR) {
6d2010ae 1821 NFS_BITMAP_SET(mflags, NFS_MFLAG_DUMBTIMER);
0a7de745
A
1822 }
1823 if (args.flags & NFSMNT_CALLUMNT) {
6d2010ae 1824 NFS_BITMAP_SET(mflags, NFS_MFLAG_CALLUMNT);
0a7de745
A
1825 }
1826 if (args.flags & NFSMNT_RDIRPLUS) {
6d2010ae 1827 NFS_BITMAP_SET(mflags, NFS_MFLAG_RDIRPLUS);
0a7de745
A
1828 }
1829 if (args.flags & NFSMNT_NONEGNAMECACHE) {
6d2010ae 1830 NFS_BITMAP_SET(mflags, NFS_MFLAG_NONEGNAMECACHE);
0a7de745
A
1831 }
1832 if (args.flags & NFSMNT_MUTEJUKEBOX) {
6d2010ae 1833 NFS_BITMAP_SET(mflags, NFS_MFLAG_MUTEJUKEBOX);
0a7de745
A
1834 }
1835 if (args.flags & NFSMNT_NOQUOTA) {
6d2010ae 1836 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOQUOTA);
0a7de745 1837 }
6d2010ae
A
1838
1839 /* prepare mount attributes */
1840 NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
1841 NFS_BITMAP_SET(mattrs, NFS_MATTR_FLAGS);
1842 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
1843 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
1844 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
1845 NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
1846 NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
1847 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
1848 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFROM);
0a7de745 1849 if (args.flags & NFSMNT_NFSV4) {
6d2010ae 1850 nfsvers = 4;
0a7de745 1851 } else if (args.flags & NFSMNT_NFSV3) {
6d2010ae 1852 nfsvers = 3;
0a7de745 1853 } else {
6d2010ae 1854 nfsvers = 2;
0a7de745
A
1855 }
1856 if ((args.flags & NFSMNT_RSIZE) && (args.rsize > 0)) {
6d2010ae 1857 NFS_BITMAP_SET(mattrs, NFS_MATTR_READ_SIZE);
0a7de745
A
1858 }
1859 if ((args.flags & NFSMNT_WSIZE) && (args.wsize > 0)) {
6d2010ae 1860 NFS_BITMAP_SET(mattrs, NFS_MATTR_WRITE_SIZE);
0a7de745
A
1861 }
1862 if ((args.flags & NFSMNT_TIMEO) && (args.timeo > 0)) {
6d2010ae 1863 NFS_BITMAP_SET(mattrs, NFS_MATTR_REQUEST_TIMEOUT);
0a7de745
A
1864 }
1865 if ((args.flags & NFSMNT_RETRANS) && (args.retrans > 0)) {
6d2010ae 1866 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT);
0a7de745
A
1867 }
1868 if ((args.flags & NFSMNT_MAXGRPS) && (args.maxgrouplist > 0)) {
6d2010ae 1869 NFS_BITMAP_SET(mattrs, NFS_MATTR_MAX_GROUP_LIST);
0a7de745
A
1870 }
1871 if ((args.flags & NFSMNT_READAHEAD) && (args.readahead > 0)) {
6d2010ae 1872 NFS_BITMAP_SET(mattrs, NFS_MATTR_READAHEAD);
0a7de745
A
1873 }
1874 if ((args.flags & NFSMNT_READDIRSIZE) && (args.readdirsize > 0)) {
6d2010ae 1875 NFS_BITMAP_SET(mattrs, NFS_MATTR_READDIR_SIZE);
0a7de745 1876 }
6d2010ae
A
1877 if ((args.flags & NFSMNT_NOLOCKS) ||
1878 (args.flags & NFSMNT_LOCALLOCKS)) {
1879 NFS_BITMAP_SET(mattrs, NFS_MATTR_LOCK_MODE);
0a7de745 1880 if (args.flags & NFSMNT_NOLOCKS) {
6d2010ae 1881 nfslockmode = NFS_LOCK_MODE_DISABLED;
0a7de745 1882 } else if (args.flags & NFSMNT_LOCALLOCKS) {
6d2010ae 1883 nfslockmode = NFS_LOCK_MODE_LOCAL;
0a7de745 1884 } else {
6d2010ae 1885 nfslockmode = NFS_LOCK_MODE_ENABLED;
0a7de745 1886 }
6d2010ae
A
1887 }
1888 if (args.version >= 4) {
0a7de745 1889 if ((args.flags & NFSMNT_ACREGMIN) && (args.acregmin > 0)) {
6d2010ae 1890 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
0a7de745
A
1891 }
1892 if ((args.flags & NFSMNT_ACREGMAX) && (args.acregmax > 0)) {
6d2010ae 1893 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
0a7de745
A
1894 }
1895 if ((args.flags & NFSMNT_ACDIRMIN) && (args.acdirmin > 0)) {
6d2010ae 1896 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
0a7de745
A
1897 }
1898 if ((args.flags & NFSMNT_ACDIRMAX) && (args.acdirmax > 0)) {
6d2010ae 1899 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
0a7de745 1900 }
6d2010ae
A
1901 }
1902 if (args.version >= 5) {
0a7de745 1903 if ((args.flags & NFSMNT_SECFLAVOR) || (args.flags & NFSMNT_SECSYSOK)) {
6d2010ae 1904 NFS_BITMAP_SET(mattrs, NFS_MATTR_SECURITY);
0a7de745 1905 }
6d2010ae
A
1906 }
1907 if (args.version >= 6) {
0a7de745 1908 if ((args.flags & NFSMNT_DEADTIMEOUT) && (args.deadtimeout > 0)) {
6d2010ae 1909 NFS_BITMAP_SET(mattrs, NFS_MATTR_DEAD_TIMEOUT);
0a7de745 1910 }
6d2010ae
A
1911 }
1912
1913 /* build xdr buffer */
1914 xb_init_buffer(&xb, NULL, 0);
1915 xb_add_32(error, &xb, args.version);
1916 argslength_offset = xb_offset(&xb);
1917 xb_add_32(error, &xb, 0); // args length
1918 xb_add_32(error, &xb, NFS_XDRARGS_VERSION_0);
1919 xb_add_bitmap(error, &xb, mattrs, NFS_MATTR_BITMAP_LEN);
1920 attrslength_offset = xb_offset(&xb);
1921 xb_add_32(error, &xb, 0); // attrs length
1922 xb_add_bitmap(error, &xb, mflags_mask, NFS_MFLAG_BITMAP_LEN); /* mask */
1923 xb_add_bitmap(error, &xb, mflags, NFS_MFLAG_BITMAP_LEN); /* value */
1924 xb_add_32(error, &xb, nfsvers);
0a7de745 1925 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READ_SIZE)) {
6d2010ae 1926 xb_add_32(error, &xb, args.rsize);
0a7de745
A
1927 }
1928 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_WRITE_SIZE)) {
6d2010ae 1929 xb_add_32(error, &xb, args.wsize);
0a7de745
A
1930 }
1931 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READDIR_SIZE)) {
6d2010ae 1932 xb_add_32(error, &xb, args.readdirsize);
0a7de745
A
1933 }
1934 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READAHEAD)) {
6d2010ae 1935 xb_add_32(error, &xb, args.readahead);
0a7de745 1936 }
6d2010ae
A
1937 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN)) {
1938 xb_add_32(error, &xb, args.acregmin);
1939 xb_add_32(error, &xb, 0);
1940 }
1941 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX)) {
1942 xb_add_32(error, &xb, args.acregmax);
1943 xb_add_32(error, &xb, 0);
1944 }
1945 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN)) {
1946 xb_add_32(error, &xb, args.acdirmin);
1947 xb_add_32(error, &xb, 0);
1948 }
1949 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX)) {
1950 xb_add_32(error, &xb, args.acdirmax);
1951 xb_add_32(error, &xb, 0);
1952 }
0a7de745 1953 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCK_MODE)) {
6d2010ae 1954 xb_add_32(error, &xb, nfslockmode);
0a7de745 1955 }
6d2010ae 1956 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SECURITY)) {
0a7de745
A
1957 uint32_t flavors[2], i = 0;
1958 if (args.flags & NFSMNT_SECFLAVOR) {
6d2010ae 1959 flavors[i++] = args.auth;
0a7de745
A
1960 }
1961 if ((args.flags & NFSMNT_SECSYSOK) && ((i == 0) || (flavors[0] != RPCAUTH_SYS))) {
6d2010ae 1962 flavors[i++] = RPCAUTH_SYS;
0a7de745 1963 }
6d2010ae
A
1964 xb_add_word_array(error, &xb, flavors, i);
1965 }
0a7de745 1966 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MAX_GROUP_LIST)) {
6d2010ae 1967 xb_add_32(error, &xb, args.maxgrouplist);
0a7de745
A
1968 }
1969 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOCKET_TYPE)) {
6d2010ae 1970 xb_add_string(error, &xb, ((args.sotype == SOCK_DGRAM) ? "udp" : "tcp"), 3);
0a7de745
A
1971 }
1972 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_PORT)) {
1973 xb_add_32(error, &xb, ((ss.ss_family == AF_INET) ?
1974 ntohs(((struct sockaddr_in*)&ss)->sin_port) :
1975 ntohs(((struct sockaddr_in6*)&ss)->sin6_port)));
1976 }
6d2010ae
A
1977 /* NFS_MATTR_MOUNT_PORT (not available in old args) */
1978 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REQUEST_TIMEOUT)) {
1979 /* convert from .1s increments to time */
0a7de745
A
1980 xb_add_32(error, &xb, args.timeo / 10);
1981 xb_add_32(error, &xb, (args.timeo % 10) * 100000000);
6d2010ae 1982 }
0a7de745 1983 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT)) {
6d2010ae 1984 xb_add_32(error, &xb, args.retrans);
0a7de745 1985 }
6d2010ae
A
1986 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
1987 xb_add_32(error, &xb, args.deadtimeout);
1988 xb_add_32(error, &xb, 0);
1989 }
0a7de745 1990 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FH)) {
6d2010ae 1991 xb_add_fh(error, &xb, &nfh[0], args.fhsize);
0a7de745 1992 }
6d2010ae
A
1993 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FS_LOCATIONS)) {
1994 xb_add_32(error, &xb, 1); /* fs location count */
1995 xb_add_32(error, &xb, 1); /* server count */
1996 xb_add_string(error, &xb, mntfrom, (endserverp - mntfrom)); /* server name */
1997 xb_add_32(error, &xb, 1); /* address count */
1998 xb_add_string(error, &xb, uaddr, strlen(uaddr)); /* address */
1999 xb_add_32(error, &xb, 0); /* empty server info */
2000 xb_add_32(error, &xb, numcomps); /* pathname component count */
2001 nfsmout_if(error);
2002 p = frompath;
0a7de745 2003 while (*p && (*p == '/')) {
6d2010ae 2004 p++;
0a7de745 2005 }
6d2010ae
A
2006 while (*p) {
2007 cp = p;
0a7de745 2008 while (*p && (*p != '/')) {
6d2010ae 2009 p++;
0a7de745 2010 }
6d2010ae
A
2011 xb_add_string(error, &xb, cp, (p - cp)); /* component */
2012 nfsmout_if(error);
0a7de745 2013 while (*p && (*p == '/')) {
6d2010ae 2014 p++;
0a7de745 2015 }
6d2010ae
A
2016 }
2017 xb_add_32(error, &xb, 0); /* empty fsl info */
2018 }
0a7de745 2019 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFLAGS)) {
6d2010ae 2020 xb_add_32(error, &xb, (vfs_flags(mp) & MNT_VISFLAGMASK)); /* VFS MNT_* flags */
0a7de745
A
2021 }
2022 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFROM)) {
6d2010ae 2023 xb_add_string(error, &xb, mntfrom, strlen(mntfrom)); /* fixed f_mntfromname */
0a7de745 2024 }
6d2010ae
A
2025 xb_build_done(error, &xb);
2026
2027 /* update opaque counts */
2028 end_offset = xb_offset(&xb);
2029 error = xb_seek(&xb, argslength_offset);
0a7de745 2030 xb_add_32(error, &xb, end_offset - argslength_offset + XDRWORD /*version*/);
6d2010ae
A
2031 nfsmout_if(error);
2032 error = xb_seek(&xb, attrslength_offset);
0a7de745 2033 xb_add_32(error, &xb, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
6d2010ae
A
2034
2035 if (!error) {
2036 /* grab the assembled buffer */
2037 *xdrbufp = xb_buffer_base(&xb);
2038 xb.xb_flags &= ~XB_CLEANUP;
2039 }
2040nfsmout:
2041 xb_cleanup(&xb);
2042 FREE_ZONE(mntfrom, MAXPATHLEN, M_NAMEI);
0a7de745 2043 return error;
6d2010ae
A
2044}
2045
2046/*
2047 * VFS Operations.
2048 *
2049 * mount system call
2050 */
2051int
2052nfs_vfs_mount(mount_t mp, vnode_t vp, user_addr_t data, vfs_context_t ctx)
2053{
2054 int error = 0, inkernel = vfs_iskernelmount(mp);
2055 uint32_t argsversion, argslength;
2056 char *xdrbuf = NULL;
2057
2058 /* read in version */
0a7de745 2059 if (inkernel) {
6d2010ae 2060 bcopy(CAST_DOWN(void *, data), &argsversion, sizeof(argsversion));
0a7de745
A
2061 } else if ((error = copyin(data, &argsversion, sizeof(argsversion)))) {
2062 return error;
2063 }
91447636 2064
6d2010ae 2065 /* If we have XDR args, then all values in the buffer are in network order */
0a7de745 2066 if (argsversion == htonl(NFS_ARGSVERSION_XDR)) {
6d2010ae 2067 argsversion = NFS_ARGSVERSION_XDR;
0a7de745 2068 }
6d2010ae
A
2069
2070 switch (argsversion) {
2071 case 3:
2072 case 4:
2073 case 5:
2074 case 6:
2075 /* convert old-style args to xdr */
2076 error = nfs_convert_old_nfs_args(mp, data, ctx, argsversion, inkernel, &xdrbuf);
2077 break;
2078 case NFS_ARGSVERSION_XDR:
2079 /* copy in xdr buffer */
0a7de745 2080 if (inkernel) {
6d2010ae 2081 bcopy(CAST_DOWN(void *, (data + XDRWORD)), &argslength, XDRWORD);
0a7de745 2082 } else {
6d2010ae 2083 error = copyin((data + XDRWORD), &argslength, XDRWORD);
0a7de745
A
2084 }
2085 if (error) {
6d2010ae 2086 break;
0a7de745 2087 }
6d2010ae
A
2088 argslength = ntohl(argslength);
2089 /* put a reasonable limit on the size of the XDR args */
0a7de745 2090 if (argslength > 16 * 1024) {
6d2010ae
A
2091 error = E2BIG;
2092 break;
2093 }
2094 /* allocate xdr buffer */
2095 xdrbuf = xb_malloc(xdr_rndup(argslength));
2096 if (!xdrbuf) {
2097 error = ENOMEM;
2098 break;
2099 }
0a7de745 2100 if (inkernel) {
6d2010ae 2101 bcopy(CAST_DOWN(void *, data), xdrbuf, argslength);
0a7de745 2102 } else {
6d2010ae 2103 error = copyin(data, xdrbuf, argslength);
0a7de745 2104 }
6d2010ae
A
2105 break;
2106 default:
2107 error = EPROGMISMATCH;
2108 }
2109
2110 if (error) {
0a7de745 2111 if (xdrbuf) {
6d2010ae 2112 xb_free(xdrbuf);
0a7de745
A
2113 }
2114 return error;
6d2010ae
A
2115 }
2116 error = mountnfs(xdrbuf, mp, ctx, &vp);
0a7de745 2117 return error;
1c79356b
A
2118}
2119
2120/*
2121 * Common code for mount and mountroot
2122 */
2d21ac55 2123
6d2010ae 2124/* Set up an NFSv2/v3 mount */
b0d623f7 2125int
2d21ac55
A
2126nfs3_mount(
2127 struct nfsmount *nmp,
2128 vfs_context_t ctx,
2d21ac55
A
2129 nfsnode_t *npp)
2130{
2131 int error = 0;
2132 struct nfs_vattr nvattr;
2133 u_int64_t xid;
2d21ac55
A
2134
2135 *npp = NULL;
2136
0a7de745
A
2137 if (!nmp->nm_fh) {
2138 return EINVAL;
2139 }
6d2010ae 2140
2d21ac55
A
2141 /*
2142 * Get file attributes for the mountpoint. These are needed
2143 * in order to properly create the root vnode.
2144 */
6d2010ae 2145 error = nfs3_getattr_rpc(NULL, nmp->nm_mountp, nmp->nm_fh->fh_data, nmp->nm_fh->fh_len, 0,
0a7de745
A
2146 ctx, &nvattr, &xid);
2147 if (error) {
2d21ac55 2148 goto out;
0a7de745 2149 }
2d21ac55 2150
6d2010ae 2151 error = nfs_nget(nmp->nm_mountp, NULL, NULL, nmp->nm_fh->fh_data, nmp->nm_fh->fh_len,
0a7de745
A
2152 &nvattr, &xid, RPCAUTH_UNKNOWN, NG_MARKROOT, npp);
2153 if (*npp) {
b0d623f7 2154 nfs_node_unlock(*npp);
0a7de745
A
2155 }
2156 if (error) {
2d21ac55 2157 goto out;
0a7de745 2158 }
2d21ac55
A
2159
2160 /*
2161 * Try to make sure we have all the general info from the server.
2162 */
2163 if (nmp->nm_vers == NFS_VER2) {
2164 NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXNAME);
2165 nmp->nm_fsattr.nfsa_maxname = NFS_MAXNAMLEN;
2166 } else if (nmp->nm_vers == NFS_VER3) {
2167 /* get the NFSv3 FSINFO */
2168 error = nfs3_fsinfo(nmp, *npp, ctx);
0a7de745 2169 if (error) {
2d21ac55 2170 goto out;
0a7de745 2171 }
2d21ac55
A
2172 /* If the server indicates all pathconf info is */
2173 /* the same, grab a copy of that info now */
2174 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_HOMOGENEOUS) &&
2175 (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS)) {
2176 struct nfs_fsattr nfsa;
2177 if (!nfs3_pathconf_rpc(*npp, &nfsa, ctx)) {
2178 /* cache a copy of the results */
2179 lck_mtx_lock(&nmp->nm_lock);
2180 nfs3_pathconf_cache(nmp, &nfsa);
2181 lck_mtx_unlock(&nmp->nm_lock);
2182 }
2183 }
2184 }
2185out:
2186 if (*npp && error) {
2187 vnode_put(NFSTOV(*npp));
b0d623f7 2188 vnode_recycle(NFSTOV(*npp));
2d21ac55
A
2189 *npp = NULL;
2190 }
0a7de745 2191 return error;
2d21ac55
A
2192}
2193
cb323159 2194#if CONFIG_NFS4
6d2010ae
A
2195/*
2196 * Update an NFSv4 mount path with the contents of the symlink.
2197 *
2198 * Read the link for the given file handle.
2199 * Insert the link's components into the path.
2200 */
b0d623f7 2201int
6d2010ae 2202nfs4_mount_update_path_with_symlink(struct nfsmount *nmp, struct nfs_fs_path *nfsp, uint32_t curcomp, fhandle_t *dirfhp, int *depthp, fhandle_t *fhp, vfs_context_t ctx)
2d21ac55 2203{
6d2010ae
A
2204 int error = 0, status, numops;
2205 uint32_t len = 0, comp, newcomp, linkcompcount;
2d21ac55 2206 u_int64_t xid;
6d2010ae
A
2207 struct nfsm_chain nmreq, nmrep;
2208 struct nfsreq rq, *req = &rq;
2209 struct nfsreq_secinfo_args si;
2210 char *link = NULL, *p, *q, ch;
2211 struct nfs_fs_path nfsp2;
2212
2213 bzero(&nfsp2, sizeof(nfsp2));
0a7de745 2214 if (dirfhp->fh_len) {
6d2010ae 2215 NFSREQ_SECINFO_SET(&si, NULL, dirfhp->fh_data, dirfhp->fh_len, nfsp->np_components[curcomp], 0);
0a7de745 2216 } else {
6d2010ae 2217 NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, nfsp->np_components[curcomp], 0);
0a7de745 2218 }
6d2010ae
A
2219 nfsm_chain_null(&nmreq);
2220 nfsm_chain_null(&nmrep);
2d21ac55 2221
0a7de745
A
2222 MALLOC_ZONE(link, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
2223 if (!link) {
6d2010ae 2224 error = ENOMEM;
0a7de745 2225 }
2d21ac55 2226
6d2010ae
A
2227 // PUTFH, READLINK
2228 numops = 2;
2229 nfsm_chain_build_alloc_init(error, &nmreq, 12 * NFSX_UNSIGNED);
3e170ce0 2230 nfsm_chain_add_compound_header(error, &nmreq, "readlink", nmp->nm_minor_vers, numops);
2d21ac55 2231 numops--;
6d2010ae
A
2232 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
2233 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, fhp->fh_data, fhp->fh_len);
2d21ac55 2234 numops--;
6d2010ae 2235 nfsm_chain_add_32(error, &nmreq, NFS_OP_READLINK);
2d21ac55
A
2236 nfsm_chain_build_done(error, &nmreq);
2237 nfsm_assert(error, (numops == 0), EPROTO);
2238 nfsmout_if(error);
6d2010ae
A
2239
2240 error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
0a7de745
A
2241 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
2242 if (!error) {
6d2010ae 2243 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
0a7de745 2244 }
6d2010ae 2245
2d21ac55
A
2246 nfsm_chain_skip_tag(error, &nmrep);
2247 nfsm_chain_get_32(error, &nmrep, numops);
6d2010ae
A
2248 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
2249 nfsm_chain_op_check(error, &nmrep, NFS_OP_READLINK);
2250 nfsm_chain_get_32(error, &nmrep, len);
2d21ac55 2251 nfsmout_if(error);
0a7de745 2252 if (len == 0) {
6d2010ae 2253 error = ENOENT;
0a7de745 2254 } else if (len >= MAXPATHLEN) {
6d2010ae 2255 len = MAXPATHLEN - 1;
0a7de745 2256 }
6d2010ae 2257 nfsm_chain_get_opaque(error, &nmrep, len, link);
2d21ac55 2258 nfsmout_if(error);
6d2010ae
A
2259 /* make sure link string is terminated properly */
2260 link[len] = '\0';
2261
2262 /* count the number of components in link */
2263 p = link;
0a7de745 2264 while (*p && (*p == '/')) {
6d2010ae 2265 p++;
0a7de745 2266 }
6d2010ae
A
2267 linkcompcount = 0;
2268 while (*p) {
2269 linkcompcount++;
0a7de745 2270 while (*p && (*p != '/')) {
6d2010ae 2271 p++;
0a7de745
A
2272 }
2273 while (*p && (*p == '/')) {
6d2010ae 2274 p++;
0a7de745 2275 }
6d2010ae 2276 }
2d21ac55 2277
6d2010ae 2278 /* free up used components */
0a7de745 2279 for (comp = 0; comp <= curcomp; comp++) {
6d2010ae
A
2280 if (nfsp->np_components[comp]) {
2281 FREE(nfsp->np_components[comp], M_TEMP);
2282 nfsp->np_components[comp] = NULL;
2d21ac55
A
2283 }
2284 }
6d2010ae
A
2285
2286 /* set up new path */
2287 nfsp2.np_compcount = nfsp->np_compcount - curcomp - 1 + linkcompcount;
0a7de745 2288 MALLOC(nfsp2.np_components, char **, nfsp2.np_compcount * sizeof(char*), M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2289 if (!nfsp2.np_components) {
2290 error = ENOMEM;
2291 goto nfsmout;
2292 }
2293
2294 /* add link components */
2295 p = link;
0a7de745 2296 while (*p && (*p == '/')) {
6d2010ae 2297 p++;
0a7de745
A
2298 }
2299 for (newcomp = 0; newcomp < linkcompcount; newcomp++) {
6d2010ae
A
2300 /* find end of component */
2301 q = p;
0a7de745 2302 while (*q && (*q != '/')) {
6d2010ae 2303 q++;
0a7de745
A
2304 }
2305 MALLOC(nfsp2.np_components[newcomp], char *, q - p + 1, M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2306 if (!nfsp2.np_components[newcomp]) {
2307 error = ENOMEM;
2308 break;
2d21ac55 2309 }
6d2010ae
A
2310 ch = *q;
2311 *q = '\0';
0a7de745 2312 strlcpy(nfsp2.np_components[newcomp], p, q - p + 1);
6d2010ae
A
2313 *q = ch;
2314 p = q;
0a7de745 2315 while (*p && (*p == '/')) {
6d2010ae 2316 p++;
0a7de745 2317 }
2d21ac55 2318 }
6d2010ae 2319 nfsmout_if(error);
2d21ac55 2320
6d2010ae 2321 /* add remaining components */
0a7de745 2322 for (comp = curcomp + 1; comp < nfsp->np_compcount; comp++, newcomp++) {
6d2010ae
A
2323 nfsp2.np_components[newcomp] = nfsp->np_components[comp];
2324 nfsp->np_components[comp] = NULL;
2325 }
2326
2327 /* move new path into place */
2328 FREE(nfsp->np_components, M_TEMP);
2329 nfsp->np_components = nfsp2.np_components;
2330 nfsp->np_compcount = nfsp2.np_compcount;
2331 nfsp2.np_components = NULL;
2d21ac55 2332
6d2010ae
A
2333 /* for absolute link, let the caller now that the next dirfh is root */
2334 if (link[0] == '/') {
2335 dirfhp->fh_len = 0;
2336 *depthp = 0;
2337 }
2d21ac55 2338nfsmout:
0a7de745 2339 if (link) {
6d2010ae 2340 FREE_ZONE(link, MAXPATHLEN, M_NAMEI);
0a7de745 2341 }
6d2010ae 2342 if (nfsp2.np_components) {
0a7de745
A
2343 for (comp = 0; comp < nfsp2.np_compcount; comp++) {
2344 if (nfsp2.np_components[comp]) {
6d2010ae 2345 FREE(nfsp2.np_components[comp], M_TEMP);
0a7de745
A
2346 }
2347 }
6d2010ae
A
2348 FREE(nfsp2.np_components, M_TEMP);
2349 }
2350 nfsm_chain_cleanup(&nmreq);
2351 nfsm_chain_cleanup(&nmrep);
0a7de745 2352 return error;
2d21ac55
A
2353}
2354
6d2010ae 2355/* Set up an NFSv4 mount */
b0d623f7 2356int
6d2010ae
A
2357nfs4_mount(
2358 struct nfsmount *nmp,
2d21ac55 2359 vfs_context_t ctx,
6d2010ae 2360 nfsnode_t *npp)
1c79356b 2361{
6d2010ae
A
2362 struct nfsm_chain nmreq, nmrep;
2363 int error = 0, numops, status, interval, isdotdot, loopcnt = 0, depth = 0;
2364 struct nfs_fs_path fspath, *nfsp, fspath2;
2365 uint32_t bitmap[NFS_ATTR_BITMAP_LEN], comp, comp2;
2366 fhandle_t fh, dirfh;
2367 struct nfs_vattr nvattr;
2368 u_int64_t xid;
2369 struct nfsreq rq, *req = &rq;
2370 struct nfsreq_secinfo_args si;
2371 struct nfs_sec sec;
2372 struct nfs_fs_locations nfsls;
2373
2374 *npp = NULL;
2375 fh.fh_len = dirfh.fh_len = 0;
2376 TAILQ_INIT(&nmp->nm_open_owners);
2377 TAILQ_INIT(&nmp->nm_delegations);
2378 TAILQ_INIT(&nmp->nm_dreturnq);
2379 nmp->nm_stategenid = 1;
2380 NVATTR_INIT(&nvattr);
2381 bzero(&nfsls, sizeof(nfsls));
2382 nfsm_chain_null(&nmreq);
2383 nfsm_chain_null(&nmrep);
0c530ab8 2384
55e303ae 2385 /*
6d2010ae
A
2386 * If no security flavors were specified we'll want to default to the server's
2387 * preferred flavor. For NFSv4.0 we need a file handle and name to get that via
2388 * SECINFO, so we'll do that on the last component of the server path we are
2389 * mounting. If we are mounting the server's root, we'll need to defer the
2390 * SECINFO call to the first successful LOOKUP request.
55e303ae 2391 */
0a7de745 2392 if (!nmp->nm_sec.count) {
6d2010ae 2393 nmp->nm_state |= NFSSTA_NEEDSECINFO;
0a7de745 2394 }
6d2010ae
A
2395
2396 /* make a copy of the current location's path */
2397 nfsp = &nmp->nm_locations.nl_locations[nmp->nm_locations.nl_current.nli_loc]->nl_path;
2398 bzero(&fspath, sizeof(fspath));
2399 fspath.np_compcount = nfsp->np_compcount;
2400 if (fspath.np_compcount > 0) {
0a7de745 2401 MALLOC(fspath.np_components, char **, fspath.np_compcount * sizeof(char*), M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2402 if (!fspath.np_components) {
2403 error = ENOMEM;
2404 goto nfsmout;
91447636 2405 }
0a7de745 2406 for (comp = 0; comp < nfsp->np_compcount; comp++) {
6d2010ae 2407 int slen = strlen(nfsp->np_components[comp]);
0a7de745 2408 MALLOC(fspath.np_components[comp], char *, slen + 1, M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2409 if (!fspath.np_components[comp]) {
2410 error = ENOMEM;
2411 break;
2412 }
0a7de745 2413 strlcpy(fspath.np_components[comp], nfsp->np_components[comp], slen + 1);
6d2010ae 2414 }
0a7de745 2415 if (error) {
6d2010ae 2416 goto nfsmout;
0a7de745 2417 }
1c79356b 2418 }
91447636 2419
6d2010ae
A
2420 /* for mirror mounts, we can just use the file handle passed in */
2421 if (nmp->nm_fh) {
2422 dirfh.fh_len = nmp->nm_fh->fh_len;
2423 bcopy(nmp->nm_fh->fh_data, dirfh.fh_data, dirfh.fh_len);
2424 NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, NULL, 0);
2425 goto gotfh;
55e303ae 2426 }
6d2010ae
A
2427
2428 /* otherwise, we need to get the fh for the directory we are mounting */
2429
2430 /* if no components, just get root */
2431 if (fspath.np_compcount == 0) {
2432nocomponents:
2433 // PUTROOTFH + GETATTR(FH)
2434 NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, NULL, 0);
2435 numops = 2;
2436 nfsm_chain_build_alloc_init(error, &nmreq, 9 * NFSX_UNSIGNED);
3e170ce0 2437 nfsm_chain_add_compound_header(error, &nmreq, "mount", nmp->nm_minor_vers, numops);
6d2010ae
A
2438 numops--;
2439 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTROOTFH);
2440 numops--;
2441 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
2442 NFS_CLEAR_ATTRIBUTES(bitmap);
2443 NFS4_DEFAULT_ATTRIBUTES(bitmap);
2444 NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
2445 nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
2446 nfsm_chain_build_done(error, &nmreq);
2447 nfsm_assert(error, (numops == 0), EPROTO);
2448 nfsmout_if(error);
2449 error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
0a7de745
A
2450 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
2451 if (!error) {
6d2010ae 2452 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
0a7de745 2453 }
6d2010ae
A
2454 nfsm_chain_skip_tag(error, &nmrep);
2455 nfsm_chain_get_32(error, &nmrep, numops);
2456 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTROOTFH);
2457 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
2458 nfsmout_if(error);
2459 NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
2460 error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, &dirfh, NULL, NULL);
2461 if (!error && !NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
2462 printf("nfs: mount didn't return filehandle?\n");
2463 error = EBADRPC;
2d21ac55 2464 }
6d2010ae
A
2465 nfsmout_if(error);
2466 nfsm_chain_cleanup(&nmrep);
2467 nfsm_chain_null(&nmreq);
2468 NVATTR_CLEANUP(&nvattr);
2469 goto gotfh;
2470 }
2d21ac55 2471
6d2010ae 2472 /* look up each path component */
0a7de745 2473 for (comp = 0; comp < fspath.np_compcount;) {
6d2010ae
A
2474 isdotdot = 0;
2475 if (fspath.np_components[comp][0] == '.') {
2476 if (fspath.np_components[comp][1] == '\0') {
2477 /* skip "." */
2478 comp++;
2479 continue;
2480 }
2481 /* treat ".." specially */
2482 if ((fspath.np_components[comp][1] == '.') &&
0a7de745
A
2483 (fspath.np_components[comp][2] == '\0')) {
2484 isdotdot = 1;
2485 }
6d2010ae
A
2486 if (isdotdot && (dirfh.fh_len == 0)) {
2487 /* ".." in root directory is same as "." */
2488 comp++;
2489 continue;
2490 }
2491 }
2492 // PUT(ROOT)FH + LOOKUP(P) + GETFH + GETATTR
0a7de745 2493 if (dirfh.fh_len == 0) {
6d2010ae 2494 NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, isdotdot ? NULL : fspath.np_components[comp], 0);
0a7de745 2495 } else {
6d2010ae 2496 NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, isdotdot ? NULL : fspath.np_components[comp], 0);
0a7de745 2497 }
6d2010ae
A
2498 numops = 4;
2499 nfsm_chain_build_alloc_init(error, &nmreq, 18 * NFSX_UNSIGNED);
3e170ce0 2500 nfsm_chain_add_compound_header(error, &nmreq, "mount", nmp->nm_minor_vers, numops);
6d2010ae
A
2501 numops--;
2502 if (dirfh.fh_len) {
2503 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
2504 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, dirfh.fh_data, dirfh.fh_len);
2505 } else {
2506 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTROOTFH);
2507 }
2508 numops--;
2509 if (isdotdot) {
2510 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUPP);
2511 } else {
2512 nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
2513 nfsm_chain_add_name(error, &nmreq,
0a7de745 2514 fspath.np_components[comp], strlen(fspath.np_components[comp]), nmp);
6d2010ae
A
2515 }
2516 numops--;
2517 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETFH);
2518 numops--;
2519 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
2520 NFS_CLEAR_ATTRIBUTES(bitmap);
2521 NFS4_DEFAULT_ATTRIBUTES(bitmap);
2522 /* if no namedattr support or component is ".zfs", clear NFS_FATTR_NAMED_ATTR */
0a7de745 2523 if (!NMFLAG(nmp, NAMEDATTR) || !strcmp(fspath.np_components[comp], ".zfs")) {
6d2010ae 2524 NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
0a7de745 2525 }
6d2010ae
A
2526 nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
2527 nfsm_chain_build_done(error, &nmreq);
2528 nfsm_assert(error, (numops == 0), EPROTO);
2529 nfsmout_if(error);
2530 error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
0a7de745
A
2531 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
2532 if (!error) {
6d2010ae 2533 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
0a7de745 2534 }
6d2010ae
A
2535 nfsm_chain_skip_tag(error, &nmrep);
2536 nfsm_chain_get_32(error, &nmrep, numops);
2537 nfsm_chain_op_check(error, &nmrep, dirfh.fh_len ? NFS_OP_PUTFH : NFS_OP_PUTROOTFH);
2538 nfsm_chain_op_check(error, &nmrep, isdotdot ? NFS_OP_LOOKUPP : NFS_OP_LOOKUP);
2539 nfsmout_if(error);
2540 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETFH);
2541 nfsm_chain_get_32(error, &nmrep, fh.fh_len);
0a7de745 2542 if (fh.fh_len > sizeof(fh.fh_data)) {
d26ffc64 2543 error = EBADRPC;
0a7de745 2544 }
d26ffc64 2545 nfsmout_if(error);
6d2010ae
A
2546 nfsm_chain_get_opaque(error, &nmrep, fh.fh_len, fh.fh_data);
2547 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
2548 if (!error) {
2549 NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
2550 error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL, &nfsls);
2551 }
2552 nfsm_chain_cleanup(&nmrep);
2553 nfsm_chain_null(&nmreq);
2554 if (error) {
2555 /* LOOKUP succeeded but GETATTR failed? This could be a referral. */
2556 /* Try the lookup again with a getattr for fs_locations. */
2557 nfs_fs_locations_cleanup(&nfsls);
2558 error = nfs4_get_fs_locations(nmp, NULL, dirfh.fh_data, dirfh.fh_len, fspath.np_components[comp], ctx, &nfsls);
0a7de745 2559 if (!error && (nfsls.nl_numlocs < 1)) {
6d2010ae 2560 error = ENOENT;
0a7de745 2561 }
6d2010ae
A
2562 nfsmout_if(error);
2563 if (++loopcnt > MAXSYMLINKS) {
2564 /* too many symlink/referral redirections */
2565 error = ELOOP;
2566 goto nfsmout;
2567 }
2568 /* tear down the current connection */
2569 nfs_disconnect(nmp);
2570 /* replace fs locations */
2571 nfs_fs_locations_cleanup(&nmp->nm_locations);
2572 nmp->nm_locations = nfsls;
2573 bzero(&nfsls, sizeof(nfsls));
2574 /* initiate a connection using the new fs locations */
2575 error = nfs_mount_connect(nmp);
0a7de745 2576 if (!error && !(nmp->nm_locations.nl_current.nli_flags & NLI_VALID)) {
6d2010ae 2577 error = EIO;
0a7de745 2578 }
6d2010ae
A
2579 nfsmout_if(error);
2580 /* add new server's remote path to beginning of our path and continue */
2581 nfsp = &nmp->nm_locations.nl_locations[nmp->nm_locations.nl_current.nli_loc]->nl_path;
2582 bzero(&fspath2, sizeof(fspath2));
2583 fspath2.np_compcount = (fspath.np_compcount - comp - 1) + nfsp->np_compcount;
2584 if (fspath2.np_compcount > 0) {
0a7de745 2585 MALLOC(fspath2.np_components, char **, fspath2.np_compcount * sizeof(char*), M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2586 if (!fspath2.np_components) {
2587 error = ENOMEM;
2588 goto nfsmout;
2589 }
0a7de745 2590 for (comp2 = 0; comp2 < nfsp->np_compcount; comp2++) {
6d2010ae 2591 int slen = strlen(nfsp->np_components[comp2]);
0a7de745 2592 MALLOC(fspath2.np_components[comp2], char *, slen + 1, M_TEMP, M_WAITOK | M_ZERO);
6d2010ae
A
2593 if (!fspath2.np_components[comp2]) {
2594 /* clean up fspath2, then error out */
2595 while (comp2 > 0) {
2596 comp2--;
2597 FREE(fspath2.np_components[comp2], M_TEMP);
2598 }
2599 FREE(fspath2.np_components, M_TEMP);
2600 error = ENOMEM;
2601 goto nfsmout;
2602 }
0a7de745
A
2603 strlcpy(fspath2.np_components[comp2], nfsp->np_components[comp2], slen + 1);
2604 }
2605 if ((fspath.np_compcount - comp - 1) > 0) {
2606 bcopy(&fspath.np_components[comp + 1], &fspath2.np_components[nfsp->np_compcount], (fspath.np_compcount - comp - 1) * sizeof(char*));
6d2010ae 2607 }
6d2010ae
A
2608 /* free up unused parts of old path (prior components and component array) */
2609 do {
2610 FREE(fspath.np_components[comp], M_TEMP);
2611 } while (comp-- > 0);
2612 FREE(fspath.np_components, M_TEMP);
2613 /* put new path in place */
2614 fspath = fspath2;
2615 }
2616 /* reset dirfh and component index */
2617 dirfh.fh_len = 0;
2618 comp = 0;
2619 NVATTR_CLEANUP(&nvattr);
0a7de745 2620 if (fspath.np_compcount == 0) {
6d2010ae 2621 goto nocomponents;
0a7de745 2622 }
6d2010ae
A
2623 continue;
2624 }
2625 nfsmout_if(error);
2626 /* if file handle is for a symlink, then update the path with the symlink contents */
2627 if (NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_TYPE) && (nvattr.nva_type == VLNK)) {
0a7de745 2628 if (++loopcnt > MAXSYMLINKS) {
6d2010ae 2629 error = ELOOP;
0a7de745 2630 } else {
6d2010ae 2631 error = nfs4_mount_update_path_with_symlink(nmp, &fspath, comp, &dirfh, &depth, &fh, ctx);
0a7de745 2632 }
6d2010ae
A
2633 nfsmout_if(error);
2634 /* directory file handle is either left the same or reset to root (if link was absolute) */
2635 /* path traversal starts at beginning of the path again */
2636 comp = 0;
2637 NVATTR_CLEANUP(&nvattr);
2638 nfs_fs_locations_cleanup(&nfsls);
2639 continue;
2640 }
2641 NVATTR_CLEANUP(&nvattr);
2642 nfs_fs_locations_cleanup(&nfsls);
2643 /* not a symlink... */
0a7de745 2644 if ((nmp->nm_state & NFSSTA_NEEDSECINFO) && (comp == (fspath.np_compcount - 1)) && !isdotdot) {
6d2010ae 2645 /* need to get SECINFO for the directory being mounted */
0a7de745 2646 if (dirfh.fh_len == 0) {
6d2010ae 2647 NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, isdotdot ? NULL : fspath.np_components[comp], 0);
0a7de745 2648 } else {
6d2010ae 2649 NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, isdotdot ? NULL : fspath.np_components[comp], 0);
0a7de745 2650 }
6d2010ae
A
2651 sec.count = NX_MAX_SEC_FLAVORS;
2652 error = nfs4_secinfo_rpc(nmp, &si, vfs_context_ucred(ctx), sec.flavors, &sec.count);
2653 /* [sigh] some implementations return "illegal" error for unsupported ops */
0a7de745 2654 if (error == NFSERR_OP_ILLEGAL) {
6d2010ae 2655 error = 0;
0a7de745 2656 }
6d2010ae
A
2657 nfsmout_if(error);
2658 /* set our default security flavor to the first in the list */
0a7de745 2659 if (sec.count) {
6d2010ae 2660 nmp->nm_auth = sec.flavors[0];
0a7de745 2661 }
6d2010ae
A
2662 nmp->nm_state &= ~NFSSTA_NEEDSECINFO;
2663 }
2664 /* advance directory file handle, component index, & update depth */
2665 dirfh = fh;
2666 comp++;
0a7de745 2667 if (!isdotdot) { /* going down the hierarchy */
6d2010ae 2668 depth++;
0a7de745 2669 } else if (--depth <= 0) { /* going up the hierarchy */
6d2010ae 2670 dirfh.fh_len = 0; /* clear dirfh when we hit root */
0a7de745 2671 }
6d2010ae 2672 }
2d21ac55 2673
6d2010ae
A
2674gotfh:
2675 /* get attrs for mount point root */
5ba3f43e 2676 numops = NMFLAG(nmp, NAMEDATTR) ? 3 : 2; // PUTFH + GETATTR + OPENATTR
6d2010ae 2677 nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED);
3e170ce0 2678 nfsm_chain_add_compound_header(error, &nmreq, "mount", nmp->nm_minor_vers, numops);
6d2010ae
A
2679 numops--;
2680 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
2681 nfsm_chain_add_fh(error, &nmreq, NFS_VER4, dirfh.fh_data, dirfh.fh_len);
2682 numops--;
2683 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
2684 NFS_CLEAR_ATTRIBUTES(bitmap);
2685 NFS4_DEFAULT_ATTRIBUTES(bitmap);
2686 /* if no namedattr support or last component is ".zfs", clear NFS_FATTR_NAMED_ATTR */
0a7de745 2687 if (!NMFLAG(nmp, NAMEDATTR) || ((fspath.np_compcount > 0) && !strcmp(fspath.np_components[fspath.np_compcount - 1], ".zfs"))) {
6d2010ae 2688 NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
0a7de745 2689 }
6d2010ae 2690 nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
5ba3f43e 2691 if (NMFLAG(nmp, NAMEDATTR)) {
6d2010ae
A
2692 numops--;
2693 nfsm_chain_add_32(error, &nmreq, NFS_OP_OPENATTR);
2694 nfsm_chain_add_32(error, &nmreq, 0);
2695 }
2696 nfsm_chain_build_done(error, &nmreq);
2697 nfsm_assert(error, (numops == 0), EPROTO);
2698 nfsmout_if(error);
2699 error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
0a7de745
A
2700 vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
2701 if (!error) {
6d2010ae 2702 error = nfs_request_async_finish(req, &nmrep, &xid, &status);
0a7de745 2703 }
6d2010ae
A
2704 nfsm_chain_skip_tag(error, &nmrep);
2705 nfsm_chain_get_32(error, &nmrep, numops);
2706 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
2707 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
2708 nfsmout_if(error);
2709 NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
2710 error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL, NULL);
2711 nfsmout_if(error);
5ba3f43e 2712 if (NMFLAG(nmp, NAMEDATTR)) {
6d2010ae 2713 nfsm_chain_op_check(error, &nmrep, NFS_OP_OPENATTR);
0a7de745 2714 if (error == ENOENT) {
6d2010ae 2715 error = 0;
0a7de745 2716 }
6d2010ae
A
2717 /* [sigh] some implementations return "illegal" error for unsupported ops */
2718 if (error || !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_NAMED_ATTR)) {
2719 nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_NAMED_ATTR;
2720 } else {
2721 nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_NAMED_ATTR;
2722 }
2723 } else {
2724 nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_NAMED_ATTR;
2725 }
0a7de745 2726 if (NMFLAG(nmp, NOACL)) { /* make sure ACL support is turned off */
6d2010ae 2727 nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_ACL;
0a7de745
A
2728 }
2729 if (NMFLAG(nmp, ACLONLY) && !(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL)) {
6d2010ae 2730 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_ACLONLY);
0a7de745 2731 }
6d2010ae
A
2732 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_FH_EXPIRE_TYPE)) {
2733 uint32_t fhtype = ((nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_FHTYPE_MASK) >> NFS_FSFLAG_FHTYPE_SHIFT);
0a7de745 2734 if (fhtype != NFS_FH_PERSISTENT) {
6d2010ae 2735 printf("nfs: warning: non-persistent file handles! for %s\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
0a7de745 2736 }
1c79356b
A
2737 }
2738
6d2010ae
A
2739 /* make sure it's a directory */
2740 if (!NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_TYPE) || (nvattr.nva_type != VDIR)) {
2741 error = ENOTDIR;
2742 goto nfsmout;
1c79356b
A
2743 }
2744
6d2010ae
A
2745 /* save the NFS fsid */
2746 nmp->nm_fsid = nvattr.nva_fsid;
2747
2748 /* create the root node */
2749 error = nfs_nget(nmp->nm_mountp, NULL, NULL, dirfh.fh_data, dirfh.fh_len, &nvattr, &xid, rq.r_auth, NG_MARKROOT, npp);
2750 nfsmout_if(error);
2751
0a7de745 2752 if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL) {
6d2010ae 2753 vfs_setextendedsecurity(nmp->nm_mountp);
0a7de745 2754 }
1c79356b 2755
6d2010ae
A
2756 /* adjust I/O sizes to server limits */
2757 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXREAD) && (nmp->nm_fsattr.nfsa_maxread > 0)) {
2758 if (nmp->nm_fsattr.nfsa_maxread < (uint64_t)nmp->nm_rsize) {
2759 nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread & ~(NFS_FABLKSIZE - 1);
0a7de745 2760 if (nmp->nm_rsize == 0) {
6d2010ae 2761 nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread;
0a7de745 2762 }
6d2010ae
A
2763 }
2764 }
2765 if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXWRITE) && (nmp->nm_fsattr.nfsa_maxwrite > 0)) {
2766 if (nmp->nm_fsattr.nfsa_maxwrite < (uint64_t)nmp->nm_wsize) {
2767 nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite & ~(NFS_FABLKSIZE - 1);
0a7de745 2768 if (nmp->nm_wsize == 0) {
6d2010ae 2769 nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite;
0a7de745 2770 }
6d2010ae 2771 }
1c79356b 2772 }
1c79356b 2773
6d2010ae
A
2774 /* set up lease renew timer */
2775 nmp->nm_renew_timer = thread_call_allocate(nfs4_renew_timer, nmp);
2776 interval = nmp->nm_fsattr.nfsa_lease / 2;
0a7de745 2777 if (interval < 1) {
6d2010ae 2778 interval = 1;
0a7de745 2779 }
6d2010ae
A
2780 nfs_interval_timer_start(nmp->nm_renew_timer, interval * 1000);
2781
2782nfsmout:
2783 if (fspath.np_components) {
0a7de745
A
2784 for (comp = 0; comp < fspath.np_compcount; comp++) {
2785 if (fspath.np_components[comp]) {
6d2010ae 2786 FREE(fspath.np_components[comp], M_TEMP);
0a7de745
A
2787 }
2788 }
6d2010ae 2789 FREE(fspath.np_components, M_TEMP);
1c79356b 2790 }
6d2010ae
A
2791 NVATTR_CLEANUP(&nvattr);
2792 nfs_fs_locations_cleanup(&nfsls);
0a7de745 2793 if (*npp) {
6d2010ae 2794 nfs_node_unlock(*npp);
0a7de745 2795 }
6d2010ae
A
2796 nfsm_chain_cleanup(&nmreq);
2797 nfsm_chain_cleanup(&nmrep);
0a7de745 2798 return error;
6d2010ae 2799}
cb323159 2800#endif /* CONFIG_NFS4 */
6d2010ae
A
2801
2802/*
2803 * Thread to handle initial NFS mount connection.
2804 */
2805void
2806nfs_mount_connect_thread(void *arg, __unused wait_result_t wr)
2807{
2808 struct nfsmount *nmp = arg;
2809 int error = 0, savederror = 0, slpflag = (NMFLAG(nmp, INTR) ? PCATCH : 0);
2810 int done = 0, timeo, tries, maxtries;
1c79356b 2811
6d2010ae
A
2812 if (NM_OMFLAG(nmp, MNTQUICK)) {
2813 timeo = 8;
2814 maxtries = 1;
2815 } else {
2816 timeo = 30;
2817 maxtries = 2;
1c79356b 2818 }
1c79356b 2819
6d2010ae
A
2820 for (tries = 0; tries < maxtries; tries++) {
2821 error = nfs_connect(nmp, 1, timeo);
2822 switch (error) {
2823 case ETIMEDOUT:
2824 case EAGAIN:
2825 case EPIPE:
2826 case EADDRNOTAVAIL:
2827 case ENETDOWN:
2828 case ENETUNREACH:
2829 case ENETRESET:
2830 case ECONNABORTED:
2831 case ECONNRESET:
2832 case EISCONN:
2833 case ENOTCONN:
2834 case ESHUTDOWN:
2835 case ECONNREFUSED:
2836 case EHOSTDOWN:
2837 case EHOSTUNREACH:
2838 /* just keep retrying on any of these errors */
2839 break;
2840 case 0:
2841 default:
2842 /* looks like we got an answer... */
2843 done = 1;
2844 break;
2845 }
91447636 2846
6d2010ae 2847 /* save the best error */
0a7de745 2848 if (nfs_connect_error_class(error) >= nfs_connect_error_class(savederror)) {
6d2010ae 2849 savederror = error;
0a7de745 2850 }
6d2010ae
A
2851 if (done) {
2852 error = savederror;
2853 break;
2854 }
2855
2856 /* pause before next attempt */
0a7de745 2857 if ((error = nfs_sigintr(nmp, NULL, current_thread(), 0))) {
6d2010ae 2858 break;
0a7de745
A
2859 }
2860 error = tsleep(nmp, PSOCK | slpflag, "nfs_mount_connect_retry", 2 * hz);
2861 if (error && (error != EWOULDBLOCK)) {
6d2010ae 2862 break;
0a7de745 2863 }
6d2010ae
A
2864 error = savederror;
2865 }
2866
2867 /* update status of mount connect */
2868 lck_mtx_lock(&nmp->nm_lock);
0a7de745 2869 if (!nmp->nm_mounterror) {
6d2010ae 2870 nmp->nm_mounterror = error;
0a7de745 2871 }
6d2010ae
A
2872 nmp->nm_state &= ~NFSSTA_MOUNT_THREAD;
2873 lck_mtx_unlock(&nmp->nm_lock);
2874 wakeup(&nmp->nm_nss);
2875}
2876
2877int
2878nfs_mount_connect(struct nfsmount *nmp)
2879{
2880 int error = 0, slpflag;
2881 thread_t thd;
cb323159 2882 struct timespec ts = { .tv_sec = 2, .tv_nsec = 0 };
6d2010ae
A
2883
2884 /*
2885 * Set up the socket. Perform initial search for a location/server/address to
2886 * connect to and negotiate any unspecified mount parameters. This work is
2887 * done on a kernel thread to satisfy reserved port usage needs.
2888 */
2889 slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
2890 lck_mtx_lock(&nmp->nm_lock);
2891 /* set flag that the thread is running */
2892 nmp->nm_state |= NFSSTA_MOUNT_THREAD;
2893 if (kernel_thread_start(nfs_mount_connect_thread, nmp, &thd) != KERN_SUCCESS) {
2894 nmp->nm_state &= ~NFSSTA_MOUNT_THREAD;
2895 nmp->nm_mounterror = EIO;
2896 printf("nfs mount %s start socket connect thread failed\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
2897 } else {
2898 thread_deallocate(thd);
2899 }
2900
2901 /* wait until mount connect thread is finished/gone */
2902 while (nmp->nm_state & NFSSTA_MOUNT_THREAD) {
0a7de745 2903 error = msleep(&nmp->nm_nss, &nmp->nm_lock, slpflag | PSOCK, "nfsconnectthread", &ts);
6d2010ae
A
2904 if ((error && (error != EWOULDBLOCK)) || ((error = nfs_sigintr(nmp, NULL, current_thread(), 1)))) {
2905 /* record error */
0a7de745 2906 if (!nmp->nm_mounterror) {
6d2010ae 2907 nmp->nm_mounterror = error;
0a7de745 2908 }
6d2010ae
A
2909 /* signal the thread that we are aborting */
2910 nmp->nm_sockflags |= NMSOCK_UNMOUNT;
0a7de745 2911 if (nmp->nm_nss) {
6d2010ae 2912 wakeup(nmp->nm_nss);
0a7de745 2913 }
6d2010ae
A
2914 /* and continue waiting on it to finish */
2915 slpflag = 0;
2916 }
2917 }
2918 lck_mtx_unlock(&nmp->nm_lock);
2919
2920 /* grab mount connect status */
2921 error = nmp->nm_mounterror;
2922
0a7de745 2923 return error;
6d2010ae
A
2924}
2925
3e170ce0
A
2926/* Table of maximum minor version for a given version */
2927uint32_t maxminorverstab[] = {
2928 0, /* Version 0 (does not exist) */
2929 0, /* Version 1 (does not exist) */
2930 0, /* Version 2 */
2931 0, /* Version 3 */
2932 0, /* Version 4 */
2933};
2934
2935#define NFS_MAX_SUPPORTED_VERSION ((long)(sizeof (maxminorverstab) / sizeof (uint32_t) - 1))
2936#define NFS_MAX_SUPPORTED_MINOR_VERSION(v) ((long)(maxminorverstab[(v)]))
2937
2938#define DEFAULT_NFS_MIN_VERS VER2PVER(2, 0)
2939#define DEFAULT_NFS_MAX_VERS VER2PVER(3, 0)
2940
6d2010ae
A
2941/*
2942 * Common code to mount an NFS file system.
2943 */
2944int
2945mountnfs(
2946 char *xdrbuf,
2947 mount_t mp,
2948 vfs_context_t ctx,
2949 vnode_t *vpp)
2950{
2951 struct nfsmount *nmp;
2952 nfsnode_t np;
2953 int error = 0;
2954 struct vfsstatfs *sbp;
2955 struct xdrbuf xb;
3e170ce0 2956 uint32_t i, val, maxio, iosize, len;
6d2010ae
A
2957 uint32_t *mattrs;
2958 uint32_t *mflags_mask;
2959 uint32_t *mflags;
2960 uint32_t argslength, attrslength;
cb323159
A
2961 uid_t set_owner;
2962 struct nfs_location_index firstloc = {
2963 .nli_flags = NLI_VALID,
2964 .nli_loc = 0,
2965 .nli_serv = 0,
2966 .nli_addr = 0
2967 };
39037602
A
2968 static const struct nfs_etype nfs_default_etypes = {
2969 .count = NFS_MAX_ETYPES,
2970 .selected = NFS_MAX_ETYPES,
2971 .etypes = { NFS_AES256_CTS_HMAC_SHA1_96,
2972 NFS_AES128_CTS_HMAC_SHA1_96,
0a7de745 2973 NFS_DES3_CBC_SHA1_KD}
39037602 2974 };
cb323159 2975
6d2010ae 2976 /* make sure mbuf constants are set up */
0a7de745 2977 if (!nfs_mbuf_mhlen) {
6d2010ae 2978 nfs_mbuf_init();
0a7de745 2979 }
6d2010ae
A
2980
2981 if (vfs_flags(mp) & MNT_UPDATE) {
2982 nmp = VFSTONFS(mp);
2983 /* update paths, file handles, etc, here XXX */
2984 xb_free(xdrbuf);
0a7de745 2985 return 0;
6d2010ae
A
2986 } else {
2987 /* allocate an NFS mount structure for this mount */
2988 MALLOC_ZONE(nmp, struct nfsmount *,
0a7de745 2989 sizeof(struct nfsmount), M_NFSMNT, M_WAITOK);
6d2010ae
A
2990 if (!nmp) {
2991 xb_free(xdrbuf);
0a7de745 2992 return ENOMEM;
6d2010ae 2993 }
0a7de745 2994 bzero((caddr_t)nmp, sizeof(struct nfsmount));
6d2010ae
A
2995 lck_mtx_init(&nmp->nm_lock, nfs_mount_grp, LCK_ATTR_NULL);
2996 TAILQ_INIT(&nmp->nm_resendq);
2997 TAILQ_INIT(&nmp->nm_iodq);
2998 TAILQ_INIT(&nmp->nm_gsscl);
2999 LIST_INIT(&nmp->nm_monlist);
3000 vfs_setfsprivate(mp, nmp);
3001 vfs_getnewfsid(mp);
3002 nmp->nm_mountp = mp;
3003 vfs_setauthopaque(mp);
5ba3f43e
A
3004 /*
3005 * Disable cache_lookup_path for NFS. NFS lookup always needs
3006 * to be called to check if the directory attribute cache is
3007 * valid and possibly purge the directory before calling
3008 * cache_lookup.
3009 */
3010 vfs_setauthcache_ttl(mp, 0);
6d2010ae
A
3011
3012 nfs_nhinit_finish();
3013
3014 nmp->nm_args = xdrbuf;
3015
3016 /* set up defaults */
fe8ab488 3017 nmp->nm_ref = 0;
6d2010ae 3018 nmp->nm_vers = 0;
3e170ce0
A
3019 nmp->nm_min_vers = DEFAULT_NFS_MIN_VERS;
3020 nmp->nm_max_vers = DEFAULT_NFS_MAX_VERS;
6d2010ae
A
3021 nmp->nm_timeo = NFS_TIMEO;
3022 nmp->nm_retry = NFS_RETRANS;
3023 nmp->nm_sotype = 0;
3024 nmp->nm_sofamily = 0;
3025 nmp->nm_nfsport = 0;
3026 nmp->nm_wsize = NFS_WSIZE;
3027 nmp->nm_rsize = NFS_RSIZE;
3028 nmp->nm_readdirsize = NFS_READDIRSIZE;
3029 nmp->nm_numgrps = NFS_MAXGRPS;
3030 nmp->nm_readahead = NFS_DEFRAHEAD;
3031 nmp->nm_tprintf_delay = nfs_tprintf_delay;
0a7de745 3032 if (nmp->nm_tprintf_delay < 0) {
6d2010ae 3033 nmp->nm_tprintf_delay = 0;
0a7de745 3034 }
6d2010ae 3035 nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
0a7de745 3036 if (nmp->nm_tprintf_initial_delay < 0) {
6d2010ae 3037 nmp->nm_tprintf_initial_delay = 0;
0a7de745 3038 }
6d2010ae
A
3039 nmp->nm_acregmin = NFS_MINATTRTIMO;
3040 nmp->nm_acregmax = NFS_MAXATTRTIMO;
3041 nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
3042 nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
39037602 3043 nmp->nm_etype = nfs_default_etypes;
6d2010ae 3044 nmp->nm_auth = RPCAUTH_SYS;
fe8ab488 3045 nmp->nm_iodlink.tqe_next = NFSNOLIST;
6d2010ae 3046 nmp->nm_deadtimeout = 0;
316670eb 3047 nmp->nm_curdeadtimeout = 0;
ea3f0419 3048 NFS_BITMAP_SET(nmp->nm_flags, NFS_MFLAG_RDIRPLUS); /* enable RDIRPLUS by default. It will be reverted later in case NFSv2 is used */
6d2010ae 3049 NFS_BITMAP_SET(nmp->nm_flags, NFS_MFLAG_NOACL);
39236c6e
A
3050 nmp->nm_realm = NULL;
3051 nmp->nm_principal = NULL;
3052 nmp->nm_sprinc = NULL;
6d2010ae
A
3053 }
3054
3055 mattrs = nmp->nm_mattrs;
3056 mflags = nmp->nm_mflags;
3057 mflags_mask = nmp->nm_mflags_mask;
3058
3059 /* set up NFS mount with args */
0a7de745 3060 xb_init_buffer(&xb, xdrbuf, 2 * XDRWORD);
6d2010ae
A
3061 xb_get_32(error, &xb, val); /* version */
3062 xb_get_32(error, &xb, argslength); /* args length */
3063 nfsmerr_if(error);
0a7de745 3064 xb_init_buffer(&xb, xdrbuf, argslength); /* restart parsing with actual buffer length */
6d2010ae
A
3065 xb_get_32(error, &xb, val); /* version */
3066 xb_get_32(error, &xb, argslength); /* args length */
3067 xb_get_32(error, &xb, val); /* XDR args version */
d9a64523 3068 if (val != NFS_XDRARGS_VERSION_0 || argslength < ((4 + NFS_MATTR_BITMAP_LEN + 1) * XDRWORD)) {
6d2010ae 3069 error = EINVAL;
d9a64523 3070 }
6d2010ae
A
3071 len = NFS_MATTR_BITMAP_LEN;
3072 xb_get_bitmap(error, &xb, mattrs, len); /* mount attribute bitmap */
3073 attrslength = 0;
3074 xb_get_32(error, &xb, attrslength); /* attrs length */
0a7de745 3075 if (!error && (attrslength > (argslength - ((4 + NFS_MATTR_BITMAP_LEN + 1) * XDRWORD)))) {
6d2010ae 3076 error = EINVAL;
0a7de745 3077 }
6d2010ae
A
3078 nfsmerr_if(error);
3079 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FLAGS)) {
3080 len = NFS_MFLAG_BITMAP_LEN;
3081 xb_get_bitmap(error, &xb, mflags_mask, len); /* mount flag mask */
3082 len = NFS_MFLAG_BITMAP_LEN;
3083 xb_get_bitmap(error, &xb, mflags, len); /* mount flag values */
3084 if (!error) {
3085 /* clear all mask bits and OR in all the ones that are set */
3086 nmp->nm_flags[0] &= ~mflags_mask[0];
3087 nmp->nm_flags[0] |= (mflags_mask[0] & mflags[0]);
3088 }
3089 }
3090 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION)) {
3e170ce0 3091 /* Can't specify a single version and a range */
0a7de745 3092 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION_RANGE)) {
3e170ce0 3093 error = EINVAL;
0a7de745 3094 }
3e170ce0
A
3095 xb_get_32(error, &xb, nmp->nm_vers);
3096 if (nmp->nm_vers > NFS_MAX_SUPPORTED_VERSION ||
0a7de745 3097 nmp->nm_vers < NFS_VER2) {
3e170ce0 3098 error = EINVAL;
0a7de745
A
3099 }
3100 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_MINOR_VERSION)) {
3e170ce0 3101 xb_get_32(error, &xb, nmp->nm_minor_vers);
0a7de745 3102 } else {
3e170ce0 3103 nmp->nm_minor_vers = maxminorverstab[nmp->nm_vers];
0a7de745
A
3104 }
3105 if (nmp->nm_minor_vers > maxminorverstab[nmp->nm_vers]) {
6d2010ae 3106 error = EINVAL;
0a7de745
A
3107 }
3108 nmp->nm_max_vers = nmp->nm_min_vers =
3109 VER2PVER(nmp->nm_vers, nmp->nm_minor_vers);
3110 }
6d2010ae 3111 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_MINOR_VERSION)) {
3e170ce0 3112 /* should have also gotten NFS version (and already gotten minor version) */
0a7de745 3113 if (!NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION)) {
6d2010ae 3114 error = EINVAL;
0a7de745 3115 }
6d2010ae 3116 }
3e170ce0
A
3117 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION_RANGE)) {
3118 xb_get_32(error, &xb, nmp->nm_min_vers);
3119 xb_get_32(error, &xb, nmp->nm_max_vers);
3120 if ((nmp->nm_min_vers > nmp->nm_max_vers) ||
3121 (PVER2MAJOR(nmp->nm_max_vers) > NFS_MAX_SUPPORTED_VERSION) ||
3122 (PVER2MINOR(nmp->nm_min_vers) > maxminorverstab[PVER2MAJOR(nmp->nm_min_vers)]) ||
0a7de745 3123 (PVER2MINOR(nmp->nm_max_vers) > maxminorverstab[PVER2MAJOR(nmp->nm_max_vers)])) {
3e170ce0 3124 error = EINVAL;
0a7de745 3125 }
3e170ce0 3126 }
0a7de745 3127 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READ_SIZE)) {
6d2010ae 3128 xb_get_32(error, &xb, nmp->nm_rsize);
0a7de745
A
3129 }
3130 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_WRITE_SIZE)) {
6d2010ae 3131 xb_get_32(error, &xb, nmp->nm_wsize);
0a7de745
A
3132 }
3133 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READDIR_SIZE)) {
6d2010ae 3134 xb_get_32(error, &xb, nmp->nm_readdirsize);
0a7de745
A
3135 }
3136 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READAHEAD)) {
6d2010ae 3137 xb_get_32(error, &xb, nmp->nm_readahead);
0a7de745 3138 }
6d2010ae
A
3139 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN)) {
3140 xb_get_32(error, &xb, nmp->nm_acregmin);
3141 xb_skip(error, &xb, XDRWORD);
3142 }
3143 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX)) {
3144 xb_get_32(error, &xb, nmp->nm_acregmax);
3145 xb_skip(error, &xb, XDRWORD);
3146 }
3147 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN)) {
3148 xb_get_32(error, &xb, nmp->nm_acdirmin);
3149 xb_skip(error, &xb, XDRWORD);
3150 }
3151 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX)) {
3152 xb_get_32(error, &xb, nmp->nm_acdirmax);
3153 xb_skip(error, &xb, XDRWORD);
3154 }
3155 nfsmerr_if(error);
3156 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCK_MODE)) {
3157 xb_get_32(error, &xb, val);
3158 switch (val) {
3159 case NFS_LOCK_MODE_DISABLED:
3160 case NFS_LOCK_MODE_LOCAL:
cb323159 3161#if CONFIG_NFS4
6d2010ae
A
3162 if (nmp->nm_vers >= NFS_VER4) {
3163 /* disabled/local lock mode only allowed on v2/v3 */
3164 error = EINVAL;
3165 break;
3166 }
cb323159 3167#endif
0a7de745 3168 /* FALLTHROUGH */
6d2010ae
A
3169 case NFS_LOCK_MODE_ENABLED:
3170 nmp->nm_lockmode = val;
3171 break;
3172 default:
3173 error = EINVAL;
3174 }
3175 }
3176 nfsmerr_if(error);
3177 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SECURITY)) {
3178 uint32_t seccnt;
3179 xb_get_32(error, &xb, seccnt);
0a7de745 3180 if (!error && ((seccnt < 1) || (seccnt > NX_MAX_SEC_FLAVORS))) {
6d2010ae 3181 error = EINVAL;
0a7de745 3182 }
6d2010ae
A
3183 nfsmerr_if(error);
3184 nmp->nm_sec.count = seccnt;
0a7de745 3185 for (i = 0; i < seccnt; i++) {
6d2010ae
A
3186 xb_get_32(error, &xb, nmp->nm_sec.flavors[i]);
3187 /* Check for valid security flavor */
3188 switch (nmp->nm_sec.flavors[i]) {
3189 case RPCAUTH_NONE:
2d21ac55
A
3190 case RPCAUTH_SYS:
3191 case RPCAUTH_KRB5:
3192 case RPCAUTH_KRB5I:
3193 case RPCAUTH_KRB5P:
2d21ac55
A
3194 break;
3195 default:
3196 error = EINVAL;
2d21ac55
A
3197 }
3198 }
6d2010ae
A
3199 /* start with the first flavor */
3200 nmp->nm_auth = nmp->nm_sec.flavors[0];
2d21ac55 3201 }
39037602
A
3202 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_KERB_ETYPE)) {
3203 uint32_t etypecnt;
3204 xb_get_32(error, &xb, etypecnt);
0a7de745 3205 if (!error && ((etypecnt < 1) || (etypecnt > NFS_MAX_ETYPES))) {
39037602 3206 error = EINVAL;
0a7de745 3207 }
39037602
A
3208 nfsmerr_if(error);
3209 nmp->nm_etype.count = etypecnt;
3210 xb_get_32(error, &xb, nmp->nm_etype.selected);
3211 nfsmerr_if(error);
3212 if (etypecnt) {
3213 nmp->nm_etype.selected = etypecnt; /* Nothing is selected yet, so set selected to count */
0a7de745 3214 for (i = 0; i < etypecnt; i++) {
39037602
A
3215 xb_get_32(error, &xb, nmp->nm_etype.etypes[i]);
3216 /* Check for valid encryption type */
3217 switch (nmp->nm_etype.etypes[i]) {
3218 case NFS_DES3_CBC_SHA1_KD:
3219 case NFS_AES128_CTS_HMAC_SHA1_96:
3220 case NFS_AES256_CTS_HMAC_SHA1_96:
3221 break;
3222 default:
3223 error = EINVAL;
3224 }
3225 }
3226 }
3227 }
0a7de745 3228 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MAX_GROUP_LIST)) {
6d2010ae 3229 xb_get_32(error, &xb, nmp->nm_numgrps);
0a7de745 3230 }
6d2010ae 3231 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOCKET_TYPE)) {
cb323159 3232 char sotype[16];
6d2010ae 3233
cb323159 3234 *sotype = '\0';
6d2010ae 3235 xb_get_32(error, &xb, val);
cb323159 3236 if (!error && ((val < 3) || (val > sizeof(sotype)))) {
6d2010ae 3237 error = EINVAL;
0a7de745 3238 }
6d2010ae
A
3239 nfsmerr_if(error);
3240 error = xb_get_bytes(&xb, sotype, val, 0);
3241 nfsmerr_if(error);
3242 sotype[val] = '\0';
3243 if (!strcmp(sotype, "tcp")) {
3244 nmp->nm_sotype = SOCK_STREAM;
3245 } else if (!strcmp(sotype, "udp")) {
3246 nmp->nm_sotype = SOCK_DGRAM;
3247 } else if (!strcmp(sotype, "tcp4")) {
3248 nmp->nm_sotype = SOCK_STREAM;
3249 nmp->nm_sofamily = AF_INET;
3250 } else if (!strcmp(sotype, "udp4")) {
3251 nmp->nm_sotype = SOCK_DGRAM;
3252 nmp->nm_sofamily = AF_INET;
3253 } else if (!strcmp(sotype, "tcp6")) {
3254 nmp->nm_sotype = SOCK_STREAM;
3255 nmp->nm_sofamily = AF_INET6;
3256 } else if (!strcmp(sotype, "udp6")) {
3257 nmp->nm_sotype = SOCK_DGRAM;
3258 nmp->nm_sofamily = AF_INET6;
3259 } else if (!strcmp(sotype, "inet4")) {
3260 nmp->nm_sofamily = AF_INET;
3261 } else if (!strcmp(sotype, "inet6")) {
3262 nmp->nm_sofamily = AF_INET6;
3263 } else if (!strcmp(sotype, "inet")) {
3264 nmp->nm_sofamily = 0; /* ok */
cb323159
A
3265 } else if (!strcmp(sotype, "ticotsord")) {
3266 nmp->nm_sofamily = AF_LOCAL;
3267 nmp->nm_sotype = SOCK_STREAM;
3268 } else if (!strcmp(sotype, "ticlts")) {
3269 nmp->nm_sofamily = AF_LOCAL;
3270 nmp->nm_sotype = SOCK_DGRAM;
6d2010ae
A
3271 } else {
3272 error = EINVAL;
3273 }
cb323159 3274#if CONFIG_NFS4
6d2010ae 3275 if (!error && (nmp->nm_vers >= NFS_VER4) && nmp->nm_sotype &&
0a7de745
A
3276 (nmp->nm_sotype != SOCK_STREAM)) {
3277 error = EINVAL; /* NFSv4 is only allowed over TCP. */
3278 }
cb323159
A
3279#endif
3280 if (error) {
3281 NFS_VFS_DBG("EINVAL sotype = \"%s\"\n", sotype);
3282 }
6d2010ae 3283 nfsmerr_if(error);
b0d623f7 3284 }
0a7de745 3285 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_PORT)) {
6d2010ae 3286 xb_get_32(error, &xb, nmp->nm_nfsport);
0a7de745
A
3287 }
3288 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MOUNT_PORT)) {
6d2010ae 3289 xb_get_32(error, &xb, nmp->nm_mountport);
0a7de745 3290 }
6d2010ae
A
3291 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REQUEST_TIMEOUT)) {
3292 /* convert from time to 0.1s units */
3293 xb_get_32(error, &xb, nmp->nm_timeo);
3294 xb_get_32(error, &xb, val);
3295 nfsmerr_if(error);
0a7de745 3296 if (val >= 1000000000) {
6d2010ae 3297 error = EINVAL;
0a7de745 3298 }
6d2010ae
A
3299 nfsmerr_if(error);
3300 nmp->nm_timeo *= 10;
0a7de745 3301 nmp->nm_timeo += (val + 100000000 - 1) / 100000000;
6d2010ae
A
3302 /* now convert to ticks */
3303 nmp->nm_timeo = (nmp->nm_timeo * NFS_HZ + 5) / 10;
3304 }
3305 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT)) {
3306 xb_get_32(error, &xb, val);
0a7de745 3307 if (!error && (val > 1)) {
6d2010ae 3308 nmp->nm_retry = val;
0a7de745 3309 }
6d2010ae
A
3310 }
3311 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
3312 xb_get_32(error, &xb, nmp->nm_deadtimeout);
3313 xb_skip(error, &xb, XDRWORD);
3314 }
3315 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FH)) {
3316 nfsmerr_if(error);
0a7de745
A
3317 MALLOC(nmp->nm_fh, fhandle_t *, sizeof(fhandle_t), M_TEMP, M_WAITOK | M_ZERO);
3318 if (!nmp->nm_fh) {
6d2010ae 3319 error = ENOMEM;
0a7de745 3320 }
6d2010ae
A
3321 xb_get_32(error, &xb, nmp->nm_fh->fh_len);
3322 nfsmerr_if(error);
0a7de745 3323 if ((size_t)nmp->nm_fh->fh_len > sizeof(nmp->nm_fh->fh_data)) {
ecc0ceb4 3324 error = EINVAL;
0a7de745 3325 } else {
ecc0ceb4 3326 error = xb_get_bytes(&xb, (char*)&nmp->nm_fh->fh_data[0], nmp->nm_fh->fh_len, 0);
0a7de745 3327 }
6d2010ae
A
3328 }
3329 nfsmerr_if(error);
3330 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FS_LOCATIONS)) {
3331 uint32_t loc, serv, addr, comp;
3332 struct nfs_fs_location *fsl;
3333 struct nfs_fs_server *fss;
3334 struct nfs_fs_path *fsp;
3335
3336 xb_get_32(error, &xb, nmp->nm_locations.nl_numlocs); /* fs location count */
3337 /* sanity check location count */
0a7de745 3338 if (!error && ((nmp->nm_locations.nl_numlocs < 1) || (nmp->nm_locations.nl_numlocs > 256))) {
cb323159 3339 NFS_VFS_DBG("Invalid number of fs_locations: %d", nmp->nm_locations.nl_numlocs);
6d2010ae 3340 error = EINVAL;
0a7de745 3341 }
6d2010ae 3342 nfsmerr_if(error);
0a7de745
A
3343 MALLOC(nmp->nm_locations.nl_locations, struct nfs_fs_location **, nmp->nm_locations.nl_numlocs * sizeof(struct nfs_fs_location*), M_TEMP, M_WAITOK | M_ZERO);
3344 if (!nmp->nm_locations.nl_locations) {
6d2010ae 3345 error = ENOMEM;
0a7de745 3346 }
6d2010ae
A
3347 for (loc = 0; loc < nmp->nm_locations.nl_numlocs; loc++) {
3348 nfsmerr_if(error);
0a7de745
A
3349 MALLOC(fsl, struct nfs_fs_location *, sizeof(struct nfs_fs_location), M_TEMP, M_WAITOK | M_ZERO);
3350 if (!fsl) {
6d2010ae 3351 error = ENOMEM;
0a7de745 3352 }
6d2010ae
A
3353 nmp->nm_locations.nl_locations[loc] = fsl;
3354 xb_get_32(error, &xb, fsl->nl_servcount); /* server count */
3355 /* sanity check server count */
0a7de745 3356 if (!error && ((fsl->nl_servcount < 1) || (fsl->nl_servcount > 256))) {
cb323159 3357 NFS_VFS_DBG("Invalid server count %d", fsl->nl_servcount);
6d2010ae 3358 error = EINVAL;
0a7de745 3359 }
6d2010ae 3360 nfsmerr_if(error);
0a7de745
A
3361 MALLOC(fsl->nl_servers, struct nfs_fs_server **, fsl->nl_servcount * sizeof(struct nfs_fs_server*), M_TEMP, M_WAITOK | M_ZERO);
3362 if (!fsl->nl_servers) {
6d2010ae 3363 error = ENOMEM;
cb323159 3364 NFS_VFS_DBG("Server count = %d, error = %d\n", fsl->nl_servcount, error);
0a7de745 3365 }
6d2010ae
A
3366 for (serv = 0; serv < fsl->nl_servcount; serv++) {
3367 nfsmerr_if(error);
0a7de745
A
3368 MALLOC(fss, struct nfs_fs_server *, sizeof(struct nfs_fs_server), M_TEMP, M_WAITOK | M_ZERO);
3369 if (!fss) {
6d2010ae 3370 error = ENOMEM;
0a7de745 3371 }
6d2010ae
A
3372 fsl->nl_servers[serv] = fss;
3373 xb_get_32(error, &xb, val); /* server name length */
3374 /* sanity check server name length */
cb323159
A
3375 if (!error && (val > MAXPATHLEN)) {
3376 NFS_VFS_DBG("Invalid server name length %d", val);
6d2010ae 3377 error = EINVAL;
0a7de745 3378 }
6d2010ae 3379 nfsmerr_if(error);
0a7de745
A
3380 MALLOC(fss->ns_name, char *, val + 1, M_TEMP, M_WAITOK | M_ZERO);
3381 if (!fss->ns_name) {
6d2010ae 3382 error = ENOMEM;
0a7de745 3383 }
6d2010ae
A
3384 nfsmerr_if(error);
3385 error = xb_get_bytes(&xb, fss->ns_name, val, 0); /* server name */
3386 xb_get_32(error, &xb, fss->ns_addrcount); /* address count */
3387 /* sanity check address count (OK to be zero) */
0a7de745 3388 if (!error && (fss->ns_addrcount > 256)) {
cb323159 3389 NFS_VFS_DBG("Invalid address count %d", fss->ns_addrcount);
6d2010ae 3390 error = EINVAL;
0a7de745 3391 }
6d2010ae
A
3392 nfsmerr_if(error);
3393 if (fss->ns_addrcount > 0) {
0a7de745
A
3394 MALLOC(fss->ns_addresses, char **, fss->ns_addrcount * sizeof(char *), M_TEMP, M_WAITOK | M_ZERO);
3395 if (!fss->ns_addresses) {
6d2010ae 3396 error = ENOMEM;
0a7de745 3397 }
6d2010ae
A
3398 for (addr = 0; addr < fss->ns_addrcount; addr++) {
3399 xb_get_32(error, &xb, val); /* address length */
3400 /* sanity check address length */
cb323159
A
3401 if (!error && val > 128) {
3402 NFS_VFS_DBG("Invalid address length %d", val);
6d2010ae 3403 error = EINVAL;
0a7de745 3404 }
6d2010ae 3405 nfsmerr_if(error);
0a7de745
A
3406 MALLOC(fss->ns_addresses[addr], char *, val + 1, M_TEMP, M_WAITOK | M_ZERO);
3407 if (!fss->ns_addresses[addr]) {
6d2010ae 3408 error = ENOMEM;
0a7de745 3409 }
6d2010ae
A
3410 nfsmerr_if(error);
3411 error = xb_get_bytes(&xb, fss->ns_addresses[addr], val, 0); /* address */
3412 }
3413 }
3414 xb_get_32(error, &xb, val); /* server info length */
3415 xb_skip(error, &xb, val); /* skip server info */
3416 }
3417 /* get pathname */
3418 fsp = &fsl->nl_path;
3419 xb_get_32(error, &xb, fsp->np_compcount); /* component count */
3420 /* sanity check component count */
0a7de745 3421 if (!error && (fsp->np_compcount > MAXPATHLEN)) {
cb323159 3422 NFS_VFS_DBG("Invalid component count %d", fsp->np_compcount);
6d2010ae 3423 error = EINVAL;
0a7de745 3424 }
6d2010ae
A
3425 nfsmerr_if(error);
3426 if (fsp->np_compcount) {
0a7de745
A
3427 MALLOC(fsp->np_components, char **, fsp->np_compcount * sizeof(char*), M_TEMP, M_WAITOK | M_ZERO);
3428 if (!fsp->np_components) {
6d2010ae 3429 error = ENOMEM;
0a7de745 3430 }
6d2010ae
A
3431 }
3432 for (comp = 0; comp < fsp->np_compcount; comp++) {
3433 xb_get_32(error, &xb, val); /* component length */
3434 /* sanity check component length */
3435 if (!error && (val == 0)) {
3436 /*
3437 * Apparently some people think a path with zero components should
3438 * be encoded with one zero-length component. So, just ignore any
3439 * zero length components.
3440 */
3441 comp--;
3442 fsp->np_compcount--;
3443 if (fsp->np_compcount == 0) {
3444 FREE(fsp->np_components, M_TEMP);
3445 fsp->np_components = NULL;
3446 }
3447 continue;
3448 }
0a7de745 3449 if (!error && ((val < 1) || (val > MAXPATHLEN))) {
cb323159 3450 NFS_VFS_DBG("Invalid component path length %d", val);
6d2010ae 3451 error = EINVAL;
0a7de745 3452 }
6d2010ae 3453 nfsmerr_if(error);
0a7de745
A
3454 MALLOC(fsp->np_components[comp], char *, val + 1, M_TEMP, M_WAITOK | M_ZERO);
3455 if (!fsp->np_components[comp]) {
6d2010ae 3456 error = ENOMEM;
0a7de745 3457 }
6d2010ae
A
3458 nfsmerr_if(error);
3459 error = xb_get_bytes(&xb, fsp->np_components[comp], val, 0); /* component */
3460 }
3461 xb_get_32(error, &xb, val); /* fs location info length */
cb323159
A
3462 NFS_VFS_DBG("Skipping fs location info bytes %d", val);
3463 xb_skip(error, &xb, xdr_rndup(val)); /* skip fs location info */
6d2010ae
A
3464 }
3465 }
0a7de745 3466 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFLAGS)) {
6d2010ae 3467 xb_skip(error, &xb, XDRWORD);
0a7de745 3468 }
6d2010ae
A
3469 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFROM)) {
3470 xb_get_32(error, &xb, len);
3471 nfsmerr_if(error);
3472 val = len;
0a7de745 3473 if (val >= sizeof(vfs_statfs(mp)->f_mntfromname)) {
6d2010ae 3474 val = sizeof(vfs_statfs(mp)->f_mntfromname) - 1;
0a7de745 3475 }
6d2010ae 3476 error = xb_get_bytes(&xb, vfs_statfs(mp)->f_mntfromname, val, 0);
0a7de745 3477 if ((len - val) > 0) {
6d2010ae 3478 xb_skip(error, &xb, len - val);
0a7de745 3479 }
6d2010ae
A
3480 nfsmerr_if(error);
3481 vfs_statfs(mp)->f_mntfromname[val] = '\0';
3482 }
3483 nfsmerr_if(error);
3484
39236c6e
A
3485 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REALM)) {
3486 xb_get_32(error, &xb, len);
0a7de745
A
3487 if (!error && ((len < 1) || (len > MAXPATHLEN))) {
3488 error = EINVAL;
3489 }
39236c6e
A
3490 nfsmerr_if(error);
3491 /* allocate an extra byte for a leading '@' if its not already prepended to the realm */
0a7de745
A
3492 MALLOC(nmp->nm_realm, char *, len + 2, M_TEMP, M_WAITOK | M_ZERO);
3493 if (!nmp->nm_realm) {
39236c6e 3494 error = ENOMEM;
0a7de745 3495 }
39236c6e
A
3496 nfsmerr_if(error);
3497 error = xb_get_bytes(&xb, nmp->nm_realm, len, 0);
3498 if (error == 0 && *nmp->nm_realm != '@') {
3499 bcopy(nmp->nm_realm, &nmp->nm_realm[1], len);
3500 nmp->nm_realm[0] = '@';
3501 }
3502 }
3503 nfsmerr_if(error);
3504
3505 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_PRINCIPAL)) {
3506 xb_get_32(error, &xb, len);
0a7de745
A
3507 if (!error && ((len < 1) || (len > MAXPATHLEN))) {
3508 error = EINVAL;
3509 }
39236c6e 3510 nfsmerr_if(error);
0a7de745
A
3511 MALLOC(nmp->nm_principal, char *, len + 1, M_TEMP, M_WAITOK | M_ZERO);
3512 if (!nmp->nm_principal) {
39236c6e 3513 error = ENOMEM;
0a7de745 3514 }
39236c6e
A
3515 nfsmerr_if(error);
3516 error = xb_get_bytes(&xb, nmp->nm_principal, len, 0);
3517 }
3518 nfsmerr_if(error);
3519
3520 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SVCPRINCIPAL)) {
3521 xb_get_32(error, &xb, len);
0a7de745
A
3522 if (!error && ((len < 1) || (len > MAXPATHLEN))) {
3523 error = EINVAL;
3524 }
39236c6e 3525 nfsmerr_if(error);
0a7de745
A
3526 MALLOC(nmp->nm_sprinc, char *, len + 1, M_TEMP, M_WAITOK | M_ZERO);
3527 if (!nmp->nm_sprinc) {
39236c6e 3528 error = ENOMEM;
0a7de745 3529 }
39236c6e
A
3530 nfsmerr_if(error);
3531 error = xb_get_bytes(&xb, nmp->nm_sprinc, len, 0);
3532 }
3533 nfsmerr_if(error);
3534
cb323159
A
3535 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_NFS_PORT)) {
3536 if (nmp->nm_nfsport) {
3537 error = EINVAL;
3538 NFS_VFS_DBG("Can't have ports specified over incompatible socket families");
3539 }
3540 nfsmerr_if(error);
3541 xb_get_32(error, &xb, len);
3542 if (!error && ((len < 1) || (len > sizeof(((struct sockaddr_un *)0)->sun_path)))) {
3543 error = EINVAL;
3544 }
3545 nfsmerr_if(error);
3546 MALLOC(nmp->nm_nfs_localport, char *, len + 1, M_TEMP, M_WAITOK | M_ZERO);
3547 if (!nmp->nm_nfs_localport) {
3548 error = ENOMEM;
3549 }
3550 nfsmerr_if(error);
3551 error = xb_get_bytes(&xb, nmp->nm_nfs_localport, len, 0);
3552 nmp->nm_sofamily = AF_LOCAL;
3553 nmp->nm_nfsport = 1; /* We use the now deprecated tpcmux port to indcate that we have an AF_LOCAL port */
3554 NFS_VFS_DBG("Setting nfs local port %s (%d)\n", nmp->nm_nfs_localport, nmp->nm_nfsport);
3555 }
3556 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_MOUNT_PORT)) {
3557 if (nmp->nm_mountport) {
3558 error = EINVAL;
3559 NFS_VFS_DBG("Can't have ports specified over mulitple socket families");
3560 }
3561 nfsmerr_if(error);
3562 xb_get_32(error, &xb, len);
3563 if (!error && ((len < 1) || (len > sizeof(((struct sockaddr_un *)0)->sun_path)))) {
3564 error = EINVAL;
3565 }
3566 nfsmerr_if(error);
3567 MALLOC(nmp->nm_mount_localport, char *, len + 1, M_TEMP, M_WAITOK | M_ZERO);
3568 if (!nmp->nm_mount_localport) {
3569 error = ENOMEM;
3570 }
3571 nfsmerr_if(error);
3572 error = xb_get_bytes(&xb, nmp->nm_mount_localport, len, 0);
3573 nmp->nm_sofamily = AF_LOCAL;
3574 nmp->nm_mountport = 1; /* We use the now deprecated tpcmux port to indcate that we have an AF_LOCAL port */
3575 NFS_VFS_DBG("Setting mount local port %s (%d)\n", nmp->nm_mount_localport, nmp->nm_mountport);
3576 }
3577 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SET_MOUNT_OWNER)) {
3578 xb_get_32(error, &xb, set_owner);
3579 nfsmerr_if(error);
3580 error = vfs_context_suser(ctx);
3581 /*
3582 * root can set owner to whatever, user can set owner to self
3583 */
3584 if ((error) && (set_owner == kauth_cred_getuid(vfs_context_ucred(ctx)))) {
3585 /* ok for non-root can set owner to self */
3586 error = 0;
3587 }
3588 nfsmerr_if(error);
3589 }
3590
6d2010ae
A
3591 /*
3592 * Sanity check/finalize settings.
3593 */
3594
0a7de745 3595 if (nmp->nm_timeo < NFS_MINTIMEO) {
6d2010ae 3596 nmp->nm_timeo = NFS_MINTIMEO;
0a7de745 3597 } else if (nmp->nm_timeo > NFS_MAXTIMEO) {
6d2010ae 3598 nmp->nm_timeo = NFS_MAXTIMEO;
0a7de745
A
3599 }
3600 if (nmp->nm_retry > NFS_MAXREXMIT) {
6d2010ae 3601 nmp->nm_retry = NFS_MAXREXMIT;
0a7de745 3602 }
6d2010ae 3603
0a7de745 3604 if (nmp->nm_numgrps > NFS_MAXGRPS) {
6d2010ae 3605 nmp->nm_numgrps = NFS_MAXGRPS;
0a7de745
A
3606 }
3607 if (nmp->nm_readahead > NFS_MAXRAHEAD) {
6d2010ae 3608 nmp->nm_readahead = NFS_MAXRAHEAD;
0a7de745
A
3609 }
3610 if (nmp->nm_acregmin > nmp->nm_acregmax) {
6d2010ae 3611 nmp->nm_acregmin = nmp->nm_acregmax;
0a7de745
A
3612 }
3613 if (nmp->nm_acdirmin > nmp->nm_acdirmax) {
6d2010ae 3614 nmp->nm_acdirmin = nmp->nm_acdirmax;
0a7de745 3615 }
6d2010ae
A
3616
3617 /* need at least one fs location */
0a7de745 3618 if (nmp->nm_locations.nl_numlocs < 1) {
6d2010ae 3619 error = EINVAL;
0a7de745 3620 }
6d2010ae
A
3621 nfsmerr_if(error);
3622
0a7de745 3623 if (!NM_OMATTR_GIVEN(nmp, MNTFROM)) {
cb323159 3624 /* init mount's mntfromname to first location */
6d2010ae 3625 nfs_location_mntfromname(&nmp->nm_locations, firstloc,
cb323159
A
3626 vfs_statfs(mp)->f_mntfromname,
3627 sizeof(vfs_statfs(mp)->f_mntfromname), 0);
0a7de745 3628 }
6d2010ae
A
3629
3630 /* Need to save the mounting credential for v4. */
3631 nmp->nm_mcred = vfs_context_ucred(ctx);
0a7de745 3632 if (IS_VALID_CRED(nmp->nm_mcred)) {
6d2010ae 3633 kauth_cred_ref(nmp->nm_mcred);
0a7de745 3634 }
6d2010ae
A
3635
3636 /*
3637 * If a reserved port is required, check for that privilege.
3638 * (Note that mirror mounts are exempt because the privilege was
3639 * already checked for the original mount.)
3640 */
0a7de745 3641 if (NMFLAG(nmp, RESVPORT) && !vfs_iskernelmount(mp)) {
6d2010ae 3642 error = priv_check_cred(nmp->nm_mcred, PRIV_NETINET_RESERVEDPORT, 0);
0a7de745 3643 }
6d2010ae
A
3644 nfsmerr_if(error);
3645
2d21ac55 3646 /* set up the version-specific function tables */
0a7de745 3647 if (nmp->nm_vers < NFS_VER4) {
2d21ac55 3648 nmp->nm_funcs = &nfs3_funcs;
0a7de745 3649 } else {
cb323159 3650#if CONFIG_NFS4
2d21ac55 3651 nmp->nm_funcs = &nfs4_funcs;
cb323159
A
3652#else
3653 /* don't go any further if we don't support NFS4 */
3654 nmp->nm_funcs = NULL;
3655 error = ENOTSUP;
3656 nfsmerr_if(error);
3657#endif
0a7de745 3658 }
2d21ac55 3659
cb323159
A
3660 /* do mount's initial socket connection */
3661 error = nfs_mount_connect(nmp);
3662 nfsmerr_if(error);
3663
6d2010ae 3664 /* sanity check settings now that version/connection is set */
0a7de745 3665 if (nmp->nm_vers == NFS_VER2) { /* ignore RDIRPLUS on NFSv2 */
6d2010ae 3666 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_RDIRPLUS);
0a7de745 3667 }
cb323159 3668#if CONFIG_NFS4
b0d623f7 3669 if (nmp->nm_vers >= NFS_VER4) {
0a7de745 3670 if (NFS_BITMAP_ISSET(nmp->nm_flags, NFS_MFLAG_ACLONLY)) { /* aclonly trumps noacl */
6d2010ae 3671 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_NOACL);
0a7de745 3672 }
6d2010ae 3673 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_CALLUMNT);
0a7de745 3674 if (nmp->nm_lockmode != NFS_LOCK_MODE_ENABLED) {
6d2010ae 3675 error = EINVAL; /* disabled/local lock mode only allowed on v2/v3 */
0a7de745 3676 }
6d2010ae 3677 } else {
cb323159
A
3678#endif
3679 /* ignore these if not v4 */
3680 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_NOCALLBACK);
3681 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_NAMEDATTR);
3682 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_NOACL);
3683 NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_ACLONLY);
3684#if CONFIG_NFS4
3685}
3686#endif
6d2010ae
A
3687 nfsmerr_if(error);
3688
3689 if (nmp->nm_sotype == SOCK_DGRAM) {
3690 /* I/O size defaults for UDP are different */
0a7de745 3691 if (!NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READ_SIZE)) {
6d2010ae 3692 nmp->nm_rsize = NFS_DGRAM_RSIZE;
0a7de745
A
3693 }
3694 if (!NFS_BITMAP_ISSET(mattrs, NFS_MATTR_WRITE_SIZE)) {
6d2010ae 3695 nmp->nm_wsize = NFS_DGRAM_WSIZE;
0a7de745 3696 }
b0d623f7 3697 }
2d21ac55 3698
6d2010ae
A
3699 /* round down I/O sizes to multiple of NFS_FABLKSIZE */
3700 nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
0a7de745 3701 if (nmp->nm_rsize <= 0) {
6d2010ae 3702 nmp->nm_rsize = NFS_FABLKSIZE;
0a7de745 3703 }
6d2010ae 3704 nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
0a7de745 3705 if (nmp->nm_wsize <= 0) {
6d2010ae 3706 nmp->nm_wsize = NFS_FABLKSIZE;
0a7de745 3707 }
6d2010ae
A
3708
3709 /* and limit I/O sizes to maximum allowed */
3710 maxio = (nmp->nm_vers == NFS_VER2) ? NFS_V2MAXDATA :
0a7de745
A
3711 (nmp->nm_sotype == SOCK_DGRAM) ? NFS_MAXDGRAMDATA : NFS_MAXDATA;
3712 if (maxio > NFS_MAXBSIZE) {
6d2010ae 3713 maxio = NFS_MAXBSIZE;
0a7de745
A
3714 }
3715 if (nmp->nm_rsize > maxio) {
6d2010ae 3716 nmp->nm_rsize = maxio;
0a7de745
A
3717 }
3718 if (nmp->nm_wsize > maxio) {
6d2010ae 3719 nmp->nm_wsize = maxio;
0a7de745 3720 }
6d2010ae 3721
0a7de745 3722 if (nmp->nm_readdirsize > maxio) {
6d2010ae 3723 nmp->nm_readdirsize = maxio;
0a7de745
A
3724 }
3725 if (nmp->nm_readdirsize > nmp->nm_rsize) {
6d2010ae 3726 nmp->nm_readdirsize = nmp->nm_rsize;
0a7de745 3727 }
6d2010ae
A
3728
3729 /* Set up the sockets and related info */
0a7de745 3730 if (nmp->nm_sotype == SOCK_DGRAM) {
6d2010ae 3731 TAILQ_INIT(&nmp->nm_cwndq);
0a7de745 3732 }
1c79356b 3733
cb323159
A
3734 if (nmp->nm_saddr->sa_family == AF_LOCAL) {
3735 struct sockaddr_un *un = (struct sockaddr_un *)nmp->nm_saddr;
3736 size_t size;
3737 int n = snprintf(vfs_statfs(mp)->f_mntfromname, sizeof(vfs_statfs(mp)->f_mntfromname), "<%s>:", un->sun_path);
3738
3739 if (n > 0 && (size_t)n < sizeof(vfs_statfs(mp)->f_mntfromname)) {
3740 size = sizeof(vfs_statfs(mp)->f_mntfromname) - n;
3741 nfs_location_mntfromname(&nmp->nm_locations, firstloc,
3742 &vfs_statfs(mp)->f_mntfromname[n], size, 1);
3743 }
3744 }
3745
3746
91447636 3747 /*
2d21ac55
A
3748 * Get the root node/attributes from the NFS server and
3749 * do any basic, version-specific setup.
91447636 3750 */
6d2010ae
A
3751 error = nmp->nm_funcs->nf_mount(nmp, ctx, &np);
3752 nfsmerr_if(error);
91447636 3753
1c79356b 3754 /*
2d21ac55 3755 * A reference count is needed on the node representing the
1c79356b
A
3756 * remote root. If this object is not persistent, then backward
3757 * traversals of the mount point (i.e. "..") will not work if
2d21ac55 3758 * the node gets flushed out of the cache.
1c79356b 3759 */
2d21ac55
A
3760 nmp->nm_dnp = np;
3761 *vpp = NFSTOV(np);
cb323159
A
3762
3763
2d21ac55
A
3764 /* get usecount and drop iocount */
3765 error = vnode_ref(*vpp);
3766 vnode_put(*vpp);
b0d623f7
A
3767 if (error) {
3768 vnode_recycle(*vpp);
6d2010ae 3769 goto nfsmerr;
b0d623f7 3770 }
1c79356b
A
3771
3772 /*
2d21ac55 3773 * Do statfs to ensure static info gets set to reasonable values.
1c79356b 3774 */
b0d623f7
A
3775 if ((error = nmp->nm_funcs->nf_update_statfs(nmp, ctx))) {
3776 int error2 = vnode_getwithref(*vpp);
3777 vnode_rele(*vpp);
0a7de745 3778 if (!error2) {
b0d623f7 3779 vnode_put(*vpp);
0a7de745 3780 }
b0d623f7 3781 vnode_recycle(*vpp);
6d2010ae
A
3782 goto nfsmerr;
3783 }
3784 sbp = vfs_statfs(mp);
3785 sbp->f_bsize = nmp->nm_fsattr.nfsa_bsize;
3786 sbp->f_blocks = nmp->nm_fsattr.nfsa_space_total / sbp->f_bsize;
3787 sbp->f_bfree = nmp->nm_fsattr.nfsa_space_free / sbp->f_bsize;
3788 sbp->f_bavail = nmp->nm_fsattr.nfsa_space_avail / sbp->f_bsize;
3789 sbp->f_bused = (nmp->nm_fsattr.nfsa_space_total / sbp->f_bsize) -
0a7de745 3790 (nmp->nm_fsattr.nfsa_space_free / sbp->f_bsize);
6d2010ae
A
3791 sbp->f_files = nmp->nm_fsattr.nfsa_files_total;
3792 sbp->f_ffree = nmp->nm_fsattr.nfsa_files_free;
3793 sbp->f_iosize = nfs_iosize;
3794
cb323159
A
3795 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SET_MOUNT_OWNER)) {
3796 sbp->f_owner = set_owner;
3797 }
3798
6d2010ae
A
3799 /*
3800 * Calculate the size used for I/O buffers. Use the larger
3801 * of the two sizes to minimise NFS requests but make sure
3802 * that it is at least one VM page to avoid wasting buffer
3803 * space and to allow easy mmapping of I/O buffers.
3804 * The read/write RPC calls handle the splitting up of
3805 * buffers into multiple requests if the buffer size is
3806 * larger than the I/O size.
3807 */
3808 iosize = max(nmp->nm_rsize, nmp->nm_wsize);
0a7de745 3809 if (iosize < PAGE_SIZE) {
6d2010ae 3810 iosize = PAGE_SIZE;
0a7de745 3811 }
6d2010ae
A
3812 nmp->nm_biosize = trunc_page_32(iosize);
3813
3814 /* For NFSv3 and greater, there is a (relatively) reliable ACCESS call. */
cb323159
A
3815 if (nmp->nm_vers > NFS_VER2 && !NMFLAG(nmp, NOOPAQUE_AUTH)
3816 ) {
6d2010ae 3817 vfs_setauthopaqueaccess(mp);
0a7de745 3818 }
6d2010ae
A
3819
3820 switch (nmp->nm_lockmode) {
3821 case NFS_LOCK_MODE_DISABLED:
3822 break;
3823 case NFS_LOCK_MODE_LOCAL:
3824 vfs_setlocklocal(nmp->nm_mountp);
3825 break;
3826 case NFS_LOCK_MODE_ENABLED:
3827 default:
0a7de745 3828 if (nmp->nm_vers <= NFS_VER3) {
6d2010ae 3829 nfs_lockd_mount_register(nmp);
0a7de745 3830 }
6d2010ae
A
3831 break;
3832 }
3833
cb323159 3834
6d2010ae
A
3835 /* success! */
3836 lck_mtx_lock(&nmp->nm_lock);
3837 nmp->nm_state |= NFSSTA_MOUNTED;
3838 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 3839 return 0;
6d2010ae 3840nfsmerr:
39037602 3841 nfs_mount_drain_and_cleanup(nmp);
0a7de745 3842 return error;
6d2010ae
A
3843}
3844
3845#if CONFIG_TRIGGERS
3846
3847/*
3848 * We've detected a file system boundary on the server and
3849 * need to mount a new file system so that our file systems
3850 * MIRROR the file systems on the server.
3851 *
3852 * Build the mount arguments for the new mount and call kernel_mount().
3853 */
3854int
3855nfs_mirror_mount_domount(vnode_t dvp, vnode_t vp, vfs_context_t ctx)
3856{
3857 nfsnode_t np = VTONFS(vp);
cb323159 3858#if CONFIG_NFS4
6d2010ae 3859 nfsnode_t dnp = VTONFS(dvp);
cb323159 3860#endif
6d2010ae
A
3861 struct nfsmount *nmp = NFSTONMP(np);
3862 char fstype[MFSTYPENAMELEN], *mntfromname = NULL, *path = NULL, *relpath, *p, *cp;
3863 int error = 0, pathbuflen = MAXPATHLEN, i, mntflags = 0, referral, skipcopy = 0;
3864 size_t nlen;
3865 struct xdrbuf xb, xbnew;
3866 uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
3867 uint32_t newmattrs[NFS_MATTR_BITMAP_LEN];
3868 uint32_t newmflags[NFS_MFLAG_BITMAP_LEN];
3869 uint32_t newmflags_mask[NFS_MFLAG_BITMAP_LEN];
3870 uint32_t argslength = 0, val, count, mlen, mlen2, rlen, relpathcomps;
3871 uint32_t argslength_offset, attrslength_offset, end_offset;
3872 uint32_t numlocs, loc, numserv, serv, numaddr, addr, numcomp, comp;
3873 char buf[XDRWORD];
3874 struct nfs_fs_locations nfsls;
3875
3876 referral = (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL);
0a7de745 3877 if (referral) {
6d2010ae 3878 bzero(&nfsls, sizeof(nfsls));
0a7de745 3879 }
6d2010ae 3880
cb323159 3881 xb_init(&xbnew, XDRBUF_NONE);
6d2010ae 3882
0a7de745
A
3883 if (!nmp || (nmp->nm_state & (NFSSTA_FORCE | NFSSTA_DEAD))) {
3884 return ENXIO;
3885 }
6d2010ae
A
3886
3887 /* allocate a couple path buffers we need */
0a7de745 3888 MALLOC_ZONE(mntfromname, char *, pathbuflen, M_NAMEI, M_WAITOK);
6d2010ae
A
3889 if (!mntfromname) {
3890 error = ENOMEM;
3891 goto nfsmerr;
3892 }
0a7de745 3893 MALLOC_ZONE(path, char *, pathbuflen, M_NAMEI, M_WAITOK);
6d2010ae
A
3894 if (!path) {
3895 error = ENOMEM;
3896 goto nfsmerr;
3897 }
3898
3899 /* get the path for the directory being mounted on */
3900 error = vn_getpath(vp, path, &pathbuflen);
3901 if (error) {
3902 error = ENOMEM;
3903 goto nfsmerr;
3904 }
3905
3906 /*
3907 * Set up the mntfromname for the new mount based on the
3908 * current mount's mntfromname and the directory's path
3909 * relative to the current mount's mntonname.
3910 * Set up relpath to point at the relative path on the current mount.
3911 * Also, count the number of components in relpath.
3912 * We'll be adding those to each fs location path in the new args.
3913 */
3914 nlen = strlcpy(mntfromname, vfs_statfs(nmp->nm_mountp)->f_mntfromname, MAXPATHLEN);
0a7de745
A
3915 if ((nlen > 0) && (mntfromname[nlen - 1] == '/')) { /* avoid double '/' in new name */
3916 mntfromname[nlen - 1] = '\0';
6d2010ae
A
3917 nlen--;
3918 }
3919 relpath = mntfromname + nlen;
3920 nlen = strlcat(mntfromname, path + strlen(vfs_statfs(nmp->nm_mountp)->f_mntonname), MAXPATHLEN);
3921 if (nlen >= MAXPATHLEN) {
3922 error = ENAMETOOLONG;
3923 goto nfsmerr;
3924 }
3925 /* count the number of components in relpath */
3926 p = relpath;
0a7de745 3927 while (*p && (*p == '/')) {
6d2010ae 3928 p++;
0a7de745 3929 }
6d2010ae
A
3930 relpathcomps = 0;
3931 while (*p) {
3932 relpathcomps++;
0a7de745 3933 while (*p && (*p != '/')) {
6d2010ae 3934 p++;
0a7de745
A
3935 }
3936 while (*p && (*p == '/')) {
6d2010ae 3937 p++;
0a7de745 3938 }
6d2010ae
A
3939 }
3940
3941 /* grab a copy of the file system type */
3942 vfs_name(vnode_mount(vp), fstype);
3943
3944 /* for referrals, fetch the fs locations */
3945 if (referral) {
3946 const char *vname = vnode_getname(NFSTOV(np));
3947 if (!vname) {
3948 error = ENOENT;
cb323159
A
3949 }
3950#if CONFIG_NFS4
3951 else {
6d2010ae
A
3952 error = nfs4_get_fs_locations(nmp, dnp, NULL, 0, vname, ctx, &nfsls);
3953 vnode_putname(vname);
0a7de745 3954 if (!error && (nfsls.nl_numlocs < 1)) {
6d2010ae 3955 error = ENOENT;
0a7de745 3956 }
6d2010ae 3957 }
cb323159 3958#endif
6d2010ae
A
3959 nfsmerr_if(error);
3960 }
3961
3962 /* set up NFS mount args based on current mount args */
3963
3964#define xb_copy_32(E, XBSRC, XBDST, V) \
3965 do { \
0a7de745
A
3966 if (E) break; \
3967 xb_get_32((E), (XBSRC), (V)); \
3968 if (skipcopy) break; \
3969 xb_add_32((E), (XBDST), (V)); \
6d2010ae
A
3970 } while (0)
3971#define xb_copy_opaque(E, XBSRC, XBDST) \
3972 do { \
0a7de745
A
3973 uint32_t __count, __val; \
3974 xb_copy_32((E), (XBSRC), (XBDST), __count); \
3975 if (E) break; \
3976 __count = nfsm_rndup(__count); \
3977 __count /= XDRWORD; \
3978 while (__count-- > 0) \
3979 xb_copy_32((E), (XBSRC), (XBDST), __val); \
6d2010ae
A
3980 } while (0)
3981
0a7de745 3982 xb_init_buffer(&xb, nmp->nm_args, 2 * XDRWORD);
6d2010ae
A
3983 xb_get_32(error, &xb, val); /* version */
3984 xb_get_32(error, &xb, argslength); /* args length */
3985 xb_init_buffer(&xb, nmp->nm_args, argslength);
3986
3987 xb_init_buffer(&xbnew, NULL, 0);
3988 xb_copy_32(error, &xb, &xbnew, val); /* version */
3989 argslength_offset = xb_offset(&xbnew);
3990 xb_copy_32(error, &xb, &xbnew, val); /* args length */
3991 xb_copy_32(error, &xb, &xbnew, val); /* XDR args version */
3992 count = NFS_MATTR_BITMAP_LEN;
3993 xb_get_bitmap(error, &xb, mattrs, count); /* mount attribute bitmap */
3994 nfsmerr_if(error);
0a7de745 3995 for (i = 0; i < NFS_MATTR_BITMAP_LEN; i++) {
6d2010ae 3996 newmattrs[i] = mattrs[i];
0a7de745
A
3997 }
3998 if (referral) {
6d2010ae 3999 NFS_BITMAP_SET(newmattrs, NFS_MATTR_FS_LOCATIONS);
cb323159 4000 NFS_BITMAP_CLR(newmattrs, NFS_MATTR_MNTFROM);
0a7de745 4001 } else {
6d2010ae 4002 NFS_BITMAP_SET(newmattrs, NFS_MATTR_FH);
0a7de745 4003 }
6d2010ae
A
4004 NFS_BITMAP_SET(newmattrs, NFS_MATTR_FLAGS);
4005 NFS_BITMAP_SET(newmattrs, NFS_MATTR_MNTFLAGS);
cb323159 4006 NFS_BITMAP_SET(newmattrs, NFS_MATTR_SET_MOUNT_OWNER);
6d2010ae
A
4007 xb_add_bitmap(error, &xbnew, newmattrs, NFS_MATTR_BITMAP_LEN);
4008 attrslength_offset = xb_offset(&xbnew);
4009 xb_copy_32(error, &xb, &xbnew, val); /* attrs length */
4010 NFS_BITMAP_ZERO(newmflags_mask, NFS_MFLAG_BITMAP_LEN);
4011 NFS_BITMAP_ZERO(newmflags, NFS_MFLAG_BITMAP_LEN);
4012 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FLAGS)) {
4013 count = NFS_MFLAG_BITMAP_LEN;
4014 xb_get_bitmap(error, &xb, newmflags_mask, count); /* mount flag mask bitmap */
4015 count = NFS_MFLAG_BITMAP_LEN;
4016 xb_get_bitmap(error, &xb, newmflags, count); /* mount flag bitmap */
4017 }
4018 NFS_BITMAP_SET(newmflags_mask, NFS_MFLAG_EPHEMERAL);
4019 NFS_BITMAP_SET(newmflags, NFS_MFLAG_EPHEMERAL);
4020 xb_add_bitmap(error, &xbnew, newmflags_mask, NFS_MFLAG_BITMAP_LEN);
4021 xb_add_bitmap(error, &xbnew, newmflags, NFS_MFLAG_BITMAP_LEN);
0a7de745 4022 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION)) {
6d2010ae 4023 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4024 }
4025 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_MINOR_VERSION)) {
6d2010ae 4026 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4027 }
3e170ce0
A
4028 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_VERSION_RANGE)) {
4029 xb_copy_32(error, &xb, &xbnew, val);
4030 xb_copy_32(error, &xb, &xbnew, val);
4031 }
0a7de745 4032 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READ_SIZE)) {
6d2010ae 4033 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4034 }
4035 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_WRITE_SIZE)) {
6d2010ae 4036 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4037 }
4038 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READDIR_SIZE)) {
6d2010ae 4039 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4040 }
4041 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_READAHEAD)) {
6d2010ae 4042 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4043 }
6d2010ae
A
4044 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN)) {
4045 xb_copy_32(error, &xb, &xbnew, val);
4046 xb_copy_32(error, &xb, &xbnew, val);
4047 }
4048 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX)) {
4049 xb_copy_32(error, &xb, &xbnew, val);
4050 xb_copy_32(error, &xb, &xbnew, val);
4051 }
4052 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN)) {
4053 xb_copy_32(error, &xb, &xbnew, val);
4054 xb_copy_32(error, &xb, &xbnew, val);
4055 }
4056 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX)) {
4057 xb_copy_32(error, &xb, &xbnew, val);
4058 xb_copy_32(error, &xb, &xbnew, val);
4059 }
0a7de745 4060 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCK_MODE)) {
6d2010ae 4061 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4062 }
6d2010ae
A
4063 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SECURITY)) {
4064 xb_copy_32(error, &xb, &xbnew, count);
0a7de745 4065 while (!error && (count-- > 0)) {
6d2010ae 4066 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4067 }
6d2010ae 4068 }
39037602
A
4069 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_KERB_ETYPE)) {
4070 xb_copy_32(error, &xb, &xbnew, count);
4071 xb_add_32(error, &xbnew, -1);
0a7de745 4072 while (!error && (count-- > 0)) {
39037602 4073 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4074 }
39037602 4075 }
0a7de745 4076 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MAX_GROUP_LIST)) {
6d2010ae 4077 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4078 }
4079 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOCKET_TYPE)) {
6d2010ae 4080 xb_copy_opaque(error, &xb, &xbnew);
0a7de745
A
4081 }
4082 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_NFS_PORT)) {
6d2010ae 4083 xb_copy_32(error, &xb, &xbnew, val);
0a7de745
A
4084 }
4085 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MOUNT_PORT)) {
6d2010ae 4086 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4087 }
6d2010ae
A
4088 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REQUEST_TIMEOUT)) {
4089 xb_copy_32(error, &xb, &xbnew, val);
4090 xb_copy_32(error, &xb, &xbnew, val);
4091 }
0a7de745 4092 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT)) {
6d2010ae 4093 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4094 }
6d2010ae
A
4095 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_DEAD_TIMEOUT)) {
4096 xb_copy_32(error, &xb, &xbnew, val);
4097 xb_copy_32(error, &xb, &xbnew, val);
4098 }
4099 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FH)) {
4100 xb_get_32(error, &xb, count);
4101 xb_skip(error, &xb, count);
4102 }
4103 if (!referral) {
4104 /* set the initial file handle to the directory's file handle */
4105 xb_add_fh(error, &xbnew, np->n_fhp, np->n_fhsize);
4106 }
4107 /* copy/extend/skip fs locations */
4108 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_FS_LOCATIONS)) {
4109 numlocs = numserv = numaddr = numcomp = 0;
0a7de745 4110 if (referral) { /* don't copy the fs locations for a referral */
6d2010ae 4111 skipcopy = 1;
0a7de745 4112 }
6d2010ae
A
4113 xb_copy_32(error, &xb, &xbnew, numlocs); /* location count */
4114 for (loc = 0; !error && (loc < numlocs); loc++) {
4115 xb_copy_32(error, &xb, &xbnew, numserv); /* server count */
4116 for (serv = 0; !error && (serv < numserv); serv++) {
4117 xb_copy_opaque(error, &xb, &xbnew); /* server name */
4118 xb_copy_32(error, &xb, &xbnew, numaddr); /* address count */
0a7de745 4119 for (addr = 0; !error && (addr < numaddr); addr++) {
6d2010ae 4120 xb_copy_opaque(error, &xb, &xbnew); /* address */
0a7de745 4121 }
6d2010ae
A
4122 xb_copy_opaque(error, &xb, &xbnew); /* server info */
4123 }
4124 /* pathname */
4125 xb_get_32(error, &xb, numcomp); /* component count */
0a7de745
A
4126 if (!skipcopy) {
4127 uint64_t totalcomps = numcomp + relpathcomps;
4128
4129 /* set error to ERANGE in the event of overflow */
4130 if (totalcomps > UINT32_MAX) {
4131 nfsmerr_if((error = ERANGE));
4132 }
4133
4134 xb_add_32(error, &xbnew, (uint32_t) totalcomps); /* new component count */
4135 }
4136 for (comp = 0; !error && (comp < numcomp); comp++) {
6d2010ae 4137 xb_copy_opaque(error, &xb, &xbnew); /* component */
0a7de745 4138 }
6d2010ae 4139 /* add additional components */
cb323159
A
4140 p = relpath;
4141 while (*p && (*p == '/')) {
4142 p++;
4143 }
4144 while (*p && !error) {
4145 cp = p;
4146 while (*p && (*p != '/')) {
6d2010ae 4147 p++;
0a7de745 4148 }
cb323159
A
4149 xb_add_string(error, &xbnew, cp, (p - cp)); /* component */
4150 while (*p && (*p == '/')) {
4151 p++;
6d2010ae
A
4152 }
4153 }
4154 xb_copy_opaque(error, &xb, &xbnew); /* fs location info */
4155 }
0a7de745 4156 if (referral) {
6d2010ae 4157 skipcopy = 0;
0a7de745 4158 }
6d2010ae
A
4159 }
4160 if (referral) {
4161 /* add referral's fs locations */
0a7de745 4162 xb_add_32(error, &xbnew, nfsls.nl_numlocs); /* FS_LOCATIONS */
6d2010ae
A
4163 for (loc = 0; !error && (loc < nfsls.nl_numlocs); loc++) {
4164 xb_add_32(error, &xbnew, nfsls.nl_locations[loc]->nl_servcount);
4165 for (serv = 0; !error && (serv < nfsls.nl_locations[loc]->nl_servcount); serv++) {
4166 xb_add_string(error, &xbnew, nfsls.nl_locations[loc]->nl_servers[serv]->ns_name,
0a7de745 4167 strlen(nfsls.nl_locations[loc]->nl_servers[serv]->ns_name));
6d2010ae 4168 xb_add_32(error, &xbnew, nfsls.nl_locations[loc]->nl_servers[serv]->ns_addrcount);
0a7de745 4169 for (addr = 0; !error && (addr < nfsls.nl_locations[loc]->nl_servers[serv]->ns_addrcount); addr++) {
6d2010ae 4170 xb_add_string(error, &xbnew, nfsls.nl_locations[loc]->nl_servers[serv]->ns_addresses[addr],
0a7de745
A
4171 strlen(nfsls.nl_locations[loc]->nl_servers[serv]->ns_addresses[addr]));
4172 }
6d2010ae
A
4173 xb_add_32(error, &xbnew, 0); /* empty server info */
4174 }
4175 xb_add_32(error, &xbnew, nfsls.nl_locations[loc]->nl_path.np_compcount);
0a7de745 4176 for (comp = 0; !error && (comp < nfsls.nl_locations[loc]->nl_path.np_compcount); comp++) {
6d2010ae 4177 xb_add_string(error, &xbnew, nfsls.nl_locations[loc]->nl_path.np_components[comp],
0a7de745
A
4178 strlen(nfsls.nl_locations[loc]->nl_path.np_components[comp]));
4179 }
6d2010ae
A
4180 xb_add_32(error, &xbnew, 0); /* empty fs location info */
4181 }
4182 }
0a7de745 4183 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFLAGS)) {
6d2010ae 4184 xb_get_32(error, &xb, mntflags);
0a7de745 4185 }
6d2010ae
A
4186 /*
4187 * We add the following mount flags to the ones for the mounted-on mount:
4188 * MNT_DONTBROWSE - to keep the mount from showing up as a separate volume
4189 * MNT_AUTOMOUNTED - to keep DiskArb from retriggering the mount after
4190 * an unmount (looking for /.autodiskmounted)
4191 */
4192 mntflags |= (MNT_AUTOMOUNTED | MNT_DONTBROWSE);
4193 xb_add_32(error, &xbnew, mntflags);
4194 if (!referral && NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MNTFROM)) {
4195 /* copy mntfrom string and add relpath */
4196 rlen = strlen(relpath);
4197 xb_get_32(error, &xb, mlen);
4198 nfsmerr_if(error);
4199 mlen2 = mlen + ((relpath[0] != '/') ? 1 : 0) + rlen;
4200 xb_add_32(error, &xbnew, mlen2);
0a7de745 4201 count = mlen / XDRWORD;
6d2010ae 4202 /* copy the original string */
0a7de745 4203 while (count-- > 0) {
6d2010ae 4204 xb_copy_32(error, &xb, &xbnew, val);
0a7de745 4205 }
6d2010ae 4206 if (!error && (mlen % XDRWORD)) {
0a7de745
A
4207 error = xb_get_bytes(&xb, buf, mlen % XDRWORD, 0);
4208 if (!error) {
4209 error = xb_add_bytes(&xbnew, buf, mlen % XDRWORD, 1);
4210 }
6d2010ae
A
4211 }
4212 /* insert a '/' if the relative path doesn't start with one */
4213 if (!error && (relpath[0] != '/')) {
4214 buf[0] = '/';
4215 error = xb_add_bytes(&xbnew, buf, 1, 1);
4216 }
4217 /* add the additional relative path */
0a7de745 4218 if (!error) {
6d2010ae 4219 error = xb_add_bytes(&xbnew, relpath, rlen, 1);
0a7de745 4220 }
6d2010ae
A
4221 /* make sure the resulting string has the right number of pad bytes */
4222 if (!error && (mlen2 != nfsm_rndup(mlen2))) {
4223 bzero(buf, sizeof(buf));
4224 count = nfsm_rndup(mlen2) - mlen2;
4225 error = xb_add_bytes(&xbnew, buf, count, 1);
4226 }
4227 }
cb323159
A
4228 /*
4229 * The following string copies rely on the fact that we already validated
4230 * these data when creating the initial mount point.
4231 */
4232 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REALM)) {
4233 xb_add_string(error, &xbnew, nmp->nm_realm, strlen(nmp->nm_realm));
4234 }
4235 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_PRINCIPAL)) {
4236 xb_add_string(error, &xbnew, nmp->nm_principal, strlen(nmp->nm_principal));
4237 }
4238 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SVCPRINCIPAL)) {
4239 xb_add_string(error, &xbnew, nmp->nm_sprinc, strlen(nmp->nm_sprinc));
4240 }
4241 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_NFS_PORT)) {
4242 xb_add_string(error, &xbnew, nmp->nm_nfs_localport, strlen(nmp->nm_nfs_localport));
4243 }
4244 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_MOUNT_PORT)) {
4245 xb_add_string(error, &xbnew, nmp->nm_mount_localport, strlen(nmp->nm_mount_localport));
4246 }
4247 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SET_MOUNT_OWNER)) {
4248 /* drop embedded owner value */
4249 xb_get_32(error, &xb, count);
4250 }
4251 /* New mount always gets same owner as this mount */
4252 xb_add_32(error, &xbnew, vnode_mount(vp)->mnt_vfsstat.f_owner);
6d2010ae
A
4253 xb_build_done(error, &xbnew);
4254
4255 /* update opaque counts */
4256 end_offset = xb_offset(&xbnew);
4257 if (!error) {
4258 error = xb_seek(&xbnew, argslength_offset);
0a7de745 4259 argslength = end_offset - argslength_offset + XDRWORD /*version*/;
6d2010ae
A
4260 xb_add_32(error, &xbnew, argslength);
4261 }
4262 if (!error) {
4263 error = xb_seek(&xbnew, attrslength_offset);
0a7de745 4264 xb_add_32(error, &xbnew, end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
6d2010ae
A
4265 }
4266 nfsmerr_if(error);
4267
4268 /*
4269 * For kernel_mount() call, use the existing mount flags (instead of the
4270 * original flags) because flags like MNT_NOSUID and MNT_NODEV may have
cb323159
A
4271 * been silently enforced. Also, in terms of MACF, the _kernel_ is
4272 * performing the mount (and enforcing all of the mount options), so we
4273 * use the kernel context for the mount call.
6d2010ae
A
4274 */
4275 mntflags = vnode_vfsvisflags(vp);
4276 mntflags |= (MNT_AUTOMOUNTED | MNT_DONTBROWSE);
cb323159 4277 ctx = vfs_context_kernel();
6d2010ae
A
4278
4279 /* do the mount */
4280 error = kernel_mount(fstype, dvp, vp, path, xb_buffer_base(&xbnew), argslength,
0a7de745 4281 mntflags, KERNEL_MOUNT_PERMIT_UNMOUNT | KERNEL_MOUNT_NOAUTH, ctx);
6d2010ae
A
4282
4283nfsmerr:
0a7de745 4284 if (error) {
6d2010ae 4285 printf("nfs: mirror mount of %s on %s failed (%d)\n",
0a7de745
A
4286 mntfromname, path, error);
4287 }
6d2010ae
A
4288 /* clean up */
4289 xb_cleanup(&xbnew);
0a7de745 4290 if (referral) {
6d2010ae 4291 nfs_fs_locations_cleanup(&nfsls);
0a7de745
A
4292 }
4293 if (path) {
6d2010ae 4294 FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
0a7de745
A
4295 }
4296 if (mntfromname) {
6d2010ae 4297 FREE_ZONE(mntfromname, MAXPATHLEN, M_NAMEI);
0a7de745
A
4298 }
4299 if (!error) {
6d2010ae 4300 nfs_ephemeral_mount_harvester_start();
0a7de745
A
4301 }
4302 return error;
6d2010ae
A
4303}
4304
4305/*
4306 * trigger vnode functions
4307 */
cb323159 4308#define NFS_TRIGGER_DEBUG 1
6d2010ae
A
4309
4310resolver_result_t
4311nfs_mirror_mount_trigger_resolve(
4312 vnode_t vp,
4313 const struct componentname *cnp,
4314 enum path_operation pop,
4315 __unused int flags,
4316 __unused void *data,
4317 vfs_context_t ctx)
4318{
cb323159
A
4319 nfsnode_t np = VTONFS(vp);
4320 vnode_t pvp = NULLVP;
4321 int error = 0;
4322 int didBusy = 0;
6d2010ae
A
4323 resolver_result_t result;
4324
4325 /*
4326 * We have a trigger node that doesn't have anything mounted on it yet.
4327 * We'll do the mount if either:
4328 * (a) this isn't the last component of the path OR
4329 * (b) this is an op that looks like it should trigger the mount.
4330 */
4331 if (cnp->cn_flags & ISLASTCN) {
4332 switch (pop) {
4333 case OP_MOUNT:
4334 case OP_UNMOUNT:
4335 case OP_STATFS:
4336 case OP_LINK:
4337 case OP_UNLINK:
4338 case OP_RENAME:
4339 case OP_MKNOD:
4340 case OP_MKFIFO:
4341 case OP_SYMLINK:
4342 case OP_ACCESS:
4343 case OP_GETATTR:
4344 case OP_MKDIR:
4345 case OP_RMDIR:
4346 case OP_REVOKE:
4347 case OP_GETXATTR:
4348 case OP_LISTXATTR:
4349 /* don't perform the mount for these operations */
4350 result = vfs_resolver_result(np->n_trigseq, RESOLVER_NOCHANGE, 0);
4351#ifdef NFS_TRIGGER_DEBUG
4352 NP(np, "nfs trigger RESOLVE: no change, last %d nameiop %d, seq %d",
0a7de745 4353 (cnp->cn_flags & ISLASTCN) ? 1 : 0, cnp->cn_nameiop, np->n_trigseq);
6d2010ae 4354#endif
0a7de745 4355 return result;
6d2010ae
A
4356 case OP_OPEN:
4357 case OP_CHDIR:
4358 case OP_CHROOT:
4359 case OP_TRUNCATE:
4360 case OP_COPYFILE:
4361 case OP_PATHCONF:
4362 case OP_READLINK:
4363 case OP_SETATTR:
4364 case OP_EXCHANGEDATA:
4365 case OP_SEARCHFS:
4366 case OP_FSCTL:
4367 case OP_SETXATTR:
4368 case OP_REMOVEXATTR:
4369 default:
4370 /* go ahead and do the mount */
4371 break;
4372 }
4373 }
4374
4375 if (vnode_mountedhere(vp) != NULL) {
4376 /*
4377 * Um... there's already something mounted.
4378 * Been there. Done that. Let's just say it succeeded.
4379 */
4380 error = 0;
4381 goto skipmount;
4382 }
4383
4384 if ((error = nfs_node_set_busy(np, vfs_context_thread(ctx)))) {
4385 result = vfs_resolver_result(np->n_trigseq, RESOLVER_ERROR, error);
4386#ifdef NFS_TRIGGER_DEBUG
4387 NP(np, "nfs trigger RESOLVE: busy error %d, last %d nameiop %d, seq %d",
0a7de745 4388 error, (cnp->cn_flags & ISLASTCN) ? 1 : 0, cnp->cn_nameiop, np->n_trigseq);
6d2010ae 4389#endif
0a7de745 4390 return result;
6d2010ae 4391 }
cb323159
A
4392 didBusy = 1;
4393
4394 /* Check again, in case the mount happened while we were setting busy */
4395 if (vnode_mountedhere(vp) != NULL) {
4396 /* Been there. Done that. Let's just say it succeeded. */
4397 error = 0;
4398 goto skipmount;
4399 }
4400 nfs_node_lock_force(np);
4401 if (np->n_flag & NDISARMTRIGGER) {
4402 error = ECANCELED;
4403 nfs_node_unlock(np);
4404 goto skipmount;
4405 }
4406 nfs_node_unlock(np);
6d2010ae
A
4407
4408 pvp = vnode_getparent(vp);
0a7de745 4409 if (pvp == NULLVP) {
6d2010ae 4410 error = EINVAL;
0a7de745
A
4411 }
4412 if (!error) {
6d2010ae 4413 error = nfs_mirror_mount_domount(pvp, vp, ctx);
0a7de745 4414 }
6d2010ae 4415skipmount:
0a7de745 4416 if (!error) {
6d2010ae 4417 np->n_trigseq++;
0a7de745 4418 }
6d2010ae
A
4419 result = vfs_resolver_result(np->n_trigseq, error ? RESOLVER_ERROR : RESOLVER_RESOLVED, error);
4420#ifdef NFS_TRIGGER_DEBUG
4421 NP(np, "nfs trigger RESOLVE: %s %d, last %d nameiop %d, seq %d",
0a7de745
A
4422 error ? "error" : "resolved", error,
4423 (cnp->cn_flags & ISLASTCN) ? 1 : 0, cnp->cn_nameiop, np->n_trigseq);
6d2010ae
A
4424#endif
4425
0a7de745 4426 if (pvp != NULLVP) {
6d2010ae 4427 vnode_put(pvp);
0a7de745 4428 }
cb323159
A
4429 if (didBusy) {
4430 nfs_node_clear_busy(np);
4431 }
0a7de745 4432 return result;
6d2010ae
A
4433}
4434
4435resolver_result_t
4436nfs_mirror_mount_trigger_unresolve(
4437 vnode_t vp,
4438 int flags,
4439 __unused void *data,
4440 vfs_context_t ctx)
4441{
4442 nfsnode_t np = VTONFS(vp);
4443 mount_t mp;
4444 int error;
4445 resolver_result_t result;
4446
4447 if ((error = nfs_node_set_busy(np, vfs_context_thread(ctx)))) {
4448 result = vfs_resolver_result(np->n_trigseq, RESOLVER_ERROR, error);
4449#ifdef NFS_TRIGGER_DEBUG
4450 NP(np, "nfs trigger UNRESOLVE: busy error %d, seq %d", error, np->n_trigseq);
4451#endif
0a7de745 4452 return result;
6d2010ae
A
4453 }
4454
4455 mp = vnode_mountedhere(vp);
0a7de745 4456 if (!mp) {
6d2010ae 4457 error = EINVAL;
0a7de745
A
4458 }
4459 if (!error) {
6d2010ae 4460 error = vfs_unmountbyfsid(&(vfs_statfs(mp)->f_fsid), flags, ctx);
0a7de745
A
4461 }
4462 if (!error) {
6d2010ae 4463 np->n_trigseq++;
0a7de745 4464 }
6d2010ae
A
4465 result = vfs_resolver_result(np->n_trigseq, error ? RESOLVER_ERROR : RESOLVER_UNRESOLVED, error);
4466#ifdef NFS_TRIGGER_DEBUG
4467 NP(np, "nfs trigger UNRESOLVE: %s %d, seq %d",
0a7de745 4468 error ? "error" : "unresolved", error, np->n_trigseq);
6d2010ae
A
4469#endif
4470 nfs_node_clear_busy(np);
0a7de745 4471 return result;
6d2010ae
A
4472}
4473
4474resolver_result_t
4475nfs_mirror_mount_trigger_rearm(
4476 vnode_t vp,
4477 __unused int flags,
4478 __unused void *data,
4479 vfs_context_t ctx)
4480{
4481 nfsnode_t np = VTONFS(vp);
4482 int error;
4483 resolver_result_t result;
4484
4485 if ((error = nfs_node_set_busy(np, vfs_context_thread(ctx)))) {
4486 result = vfs_resolver_result(np->n_trigseq, RESOLVER_ERROR, error);
4487#ifdef NFS_TRIGGER_DEBUG
4488 NP(np, "nfs trigger REARM: busy error %d, seq %d", error, np->n_trigseq);
4489#endif
0a7de745 4490 return result;
6d2010ae
A
4491 }
4492
4493 np->n_trigseq++;
4494 result = vfs_resolver_result(np->n_trigseq,
0a7de745 4495 vnode_mountedhere(vp) ? RESOLVER_RESOLVED : RESOLVER_UNRESOLVED, 0);
6d2010ae
A
4496#ifdef NFS_TRIGGER_DEBUG
4497 NP(np, "nfs trigger REARM: %s, seq %d",
0a7de745 4498 vnode_mountedhere(vp) ? "resolved" : "unresolved", np->n_trigseq);
6d2010ae
A
4499#endif
4500 nfs_node_clear_busy(np);
0a7de745 4501 return result;
6d2010ae
A
4502}
4503
4504/*
4505 * Periodically attempt to unmount ephemeral (mirror) mounts in an attempt to limit
4506 * the number of unused mounts.
4507 */
4508
0a7de745 4509#define NFS_EPHEMERAL_MOUNT_HARVEST_INTERVAL 120 /* how often the harvester runs */
6d2010ae 4510struct nfs_ephemeral_mount_harvester_info {
0a7de745
A
4511 fsid_t fsid; /* FSID that we need to try to unmount */
4512 uint32_t mountcount; /* count of ephemeral mounts seen in scan */
4513};
6d2010ae
A
4514/* various globals for the harvester */
4515static thread_call_t nfs_ephemeral_mount_harvester_timer = NULL;
4516static int nfs_ephemeral_mount_harvester_on = 0;
4517
4518kern_return_t thread_terminate(thread_t);
4519
4520static int
4521nfs_ephemeral_mount_harvester_callback(mount_t mp, void *arg)
4522{
4523 struct nfs_ephemeral_mount_harvester_info *hinfo = arg;
4524 struct nfsmount *nmp;
4525 struct timeval now;
4526
0a7de745
A
4527 if (strcmp(mp->mnt_vfsstat.f_fstypename, "nfs")) {
4528 return VFS_RETURNED;
4529 }
6d2010ae 4530 nmp = VFSTONFS(mp);
cb323159
A
4531 if (!nmp || !NMFLAG(nmp, EPHEMERAL)
4532 ) {
0a7de745
A
4533 return VFS_RETURNED;
4534 }
6d2010ae
A
4535 hinfo->mountcount++;
4536
4537 /* avoid unmounting mounts that have been triggered within the last harvest interval */
4538 microtime(&now);
0a7de745
A
4539 if ((nmp->nm_mounttime >> 32) > ((uint32_t)now.tv_sec - NFS_EPHEMERAL_MOUNT_HARVEST_INTERVAL)) {
4540 return VFS_RETURNED;
4541 }
6d2010ae
A
4542
4543 if (hinfo->fsid.val[0] || hinfo->fsid.val[1]) {
4544 /* attempt to unmount previously-found ephemeral mount */
4545 vfs_unmountbyfsid(&hinfo->fsid, 0, vfs_context_kernel());
4546 hinfo->fsid.val[0] = hinfo->fsid.val[1] = 0;
b0d623f7 4547 }
55e303ae
A
4548
4549 /*
6d2010ae
A
4550 * We can't call unmount here since we hold a mount iter ref
4551 * on mp so save its fsid for the next call iteration to unmount.
55e303ae 4552 */
6d2010ae
A
4553 hinfo->fsid.val[0] = mp->mnt_vfsstat.f_fsid.val[0];
4554 hinfo->fsid.val[1] = mp->mnt_vfsstat.f_fsid.val[1];
1c79356b 4555
0a7de745 4556 return VFS_RETURNED;
6d2010ae 4557}
91447636 4558
6d2010ae
A
4559/*
4560 * Spawn a thread to do the ephemeral mount harvesting.
4561 */
4562static void
4563nfs_ephemeral_mount_harvester_timer_func(void)
4564{
4565 thread_t thd;
1c79356b 4566
0a7de745 4567 if (kernel_thread_start(nfs_ephemeral_mount_harvester, NULL, &thd) == KERN_SUCCESS) {
6d2010ae 4568 thread_deallocate(thd);
0a7de745 4569 }
6d2010ae
A
4570}
4571
4572/*
4573 * Iterate all mounts looking for NFS ephemeral mounts to try to unmount.
4574 */
4575void
4576nfs_ephemeral_mount_harvester(__unused void *arg, __unused wait_result_t wr)
4577{
4578 struct nfs_ephemeral_mount_harvester_info hinfo;
4579 uint64_t deadline;
4580
4581 hinfo.mountcount = 0;
4582 hinfo.fsid.val[0] = hinfo.fsid.val[1] = 0;
4583 vfs_iterate(VFS_ITERATE_TAIL_FIRST, nfs_ephemeral_mount_harvester_callback, &hinfo);
4584 if (hinfo.fsid.val[0] || hinfo.fsid.val[1]) {
4585 /* attempt to unmount last found ephemeral mount */
4586 vfs_unmountbyfsid(&hinfo.fsid, 0, vfs_context_kernel());
2d21ac55 4587 }
6d2010ae
A
4588
4589 lck_mtx_lock(nfs_global_mutex);
4590 if (!hinfo.mountcount) {
4591 /* no more ephemeral mounts - don't need timer */
4592 nfs_ephemeral_mount_harvester_on = 0;
4593 } else {
4594 /* re-arm the timer */
4595 clock_interval_to_deadline(NFS_EPHEMERAL_MOUNT_HARVEST_INTERVAL, NSEC_PER_SEC, &deadline);
4596 thread_call_enter_delayed(nfs_ephemeral_mount_harvester_timer, deadline);
4597 nfs_ephemeral_mount_harvester_on = 1;
2d21ac55 4598 }
6d2010ae
A
4599 lck_mtx_unlock(nfs_global_mutex);
4600
4601 /* thread done */
4602 thread_terminate(current_thread());
1c79356b
A
4603}
4604
6d2010ae
A
4605/*
4606 * Make sure the NFS ephemeral mount harvester timer is running.
4607 */
b0d623f7 4608void
6d2010ae
A
4609nfs_ephemeral_mount_harvester_start(void)
4610{
4611 uint64_t deadline;
4612
4613 lck_mtx_lock(nfs_global_mutex);
4614 if (nfs_ephemeral_mount_harvester_on) {
4615 lck_mtx_unlock(nfs_global_mutex);
4616 return;
4617 }
0a7de745 4618 if (nfs_ephemeral_mount_harvester_timer == NULL) {
6d2010ae 4619 nfs_ephemeral_mount_harvester_timer = thread_call_allocate((thread_call_func_t)nfs_ephemeral_mount_harvester_timer_func, NULL);
0a7de745 4620 }
6d2010ae
A
4621 clock_interval_to_deadline(NFS_EPHEMERAL_MOUNT_HARVEST_INTERVAL, NSEC_PER_SEC, &deadline);
4622 thread_call_enter_delayed(nfs_ephemeral_mount_harvester_timer, deadline);
4623 nfs_ephemeral_mount_harvester_on = 1;
4624 lck_mtx_unlock(nfs_global_mutex);
4625}
4626
4627#endif
4628
4629/*
4630 * Send a MOUNT protocol MOUNT request to the server to get the initial file handle (and security).
4631 */
4632int
4633nfs3_mount_rpc(struct nfsmount *nmp, struct sockaddr *sa, int sotype, int nfsvers, char *path, vfs_context_t ctx, int timeo, fhandle_t *fh, struct nfs_sec *sec)
b0d623f7 4634{
6d2010ae 4635 int error = 0, slen, mntproto;
b0d623f7
A
4636 thread_t thd = vfs_context_thread(ctx);
4637 kauth_cred_t cred = vfs_context_ucred(ctx);
b0d623f7
A
4638 uint64_t xid = 0;
4639 struct nfsm_chain nmreq, nmrep;
4640 mbuf_t mreq;
6d2010ae
A
4641 uint32_t mntvers, mntport, val;
4642 struct sockaddr_storage ss;
4643 struct sockaddr *saddr = (struct sockaddr*)&ss;
cb323159 4644 struct sockaddr_un *sun = (struct sockaddr_un*)saddr;
6d2010ae 4645
b0d623f7
A
4646 nfsm_chain_null(&nmreq);
4647 nfsm_chain_null(&nmrep);
4648
6d2010ae
A
4649 mntvers = (nfsvers == NFS_VER2) ? RPCMNT_VER1 : RPCMNT_VER3;
4650 mntproto = (NM_OMFLAG(nmp, MNTUDP) || (sotype == SOCK_DGRAM)) ? IPPROTO_UDP : IPPROTO_TCP;
4651 sec->count = 0;
4652
4653 bcopy(sa, saddr, min(sizeof(ss), sa->sa_len));
4654 if (saddr->sa_family == AF_INET) {
0a7de745 4655 if (nmp->nm_mountport) {
6d2010ae 4656 ((struct sockaddr_in*)saddr)->sin_port = htons(nmp->nm_mountport);
0a7de745 4657 }
6d2010ae 4658 mntport = ntohs(((struct sockaddr_in*)saddr)->sin_port);
cb323159 4659 } else if (saddr->sa_family == AF_INET6) {
0a7de745 4660 if (nmp->nm_mountport) {
6d2010ae 4661 ((struct sockaddr_in6*)saddr)->sin6_port = htons(nmp->nm_mountport);
0a7de745 4662 }
6d2010ae 4663 mntport = ntohs(((struct sockaddr_in6*)saddr)->sin6_port);
cb323159
A
4664 } else { /* Local domain socket */
4665 mntport = ((struct sockaddr_un *)saddr)->sun_path[0]; /* Do we have and address? */
4666 mntproto = IPPROTO_TCP; /* XXX rpcbind only listens on streams sockets for now */
6d2010ae
A
4667 }
4668
4669 while (!mntport) {
cb323159
A
4670 error = nfs_portmap_lookup(nmp, ctx, saddr, NULL, RPCPROG_MNT, mntvers,
4671 mntproto == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM, timeo);
6d2010ae 4672 nfsmout_if(error);
0a7de745 4673 if (saddr->sa_family == AF_INET) {
6d2010ae 4674 mntport = ntohs(((struct sockaddr_in*)saddr)->sin_port);
cb323159 4675 } else if (saddr->sa_family == AF_INET6) {
6d2010ae 4676 mntport = ntohs(((struct sockaddr_in6*)saddr)->sin6_port);
cb323159
A
4677 } else if (saddr->sa_family == AF_LOCAL) {
4678 mntport = ((struct sockaddr_un*)saddr)->sun_path[0];
0a7de745 4679 }
6d2010ae
A
4680 if (!mntport) {
4681 /* if not found and TCP, then retry with UDP */
4682 if (mntproto == IPPROTO_UDP) {
4683 error = EPROGUNAVAIL;
4684 break;
4685 }
4686 mntproto = IPPROTO_UDP;
4687 bcopy(sa, saddr, min(sizeof(ss), sa->sa_len));
cb323159
A
4688 if (saddr->sa_family == AF_LOCAL) {
4689 strlcpy(sun->sun_path, RPCB_TICLTS_PATH, sizeof(sun->sun_path));
4690 }
6d2010ae
A
4691 }
4692 }
4693 nfsmout_if(error || !mntport);
4694
4695 /* MOUNT protocol MOUNT request */
4696 slen = strlen(path);
4697 nfsm_chain_build_alloc_init(error, &nmreq, NFSX_UNSIGNED + nfsm_rndup(slen));
4698 nfsm_chain_add_name(error, &nmreq, path, slen, nmp);
b0d623f7
A
4699 nfsm_chain_build_done(error, &nmreq);
4700 nfsmout_if(error);
6d2010ae 4701 error = nfsm_rpchead2(nmp, (mntproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM,
0a7de745
A
4702 RPCPROG_MNT, mntvers, RPCMNT_MOUNT,
4703 RPCAUTH_SYS, cred, NULL, nmreq.nmc_mhead, &xid, &mreq);
b0d623f7
A
4704 nfsmout_if(error);
4705 nmreq.nmc_mhead = NULL;
6d2010ae 4706 error = nfs_aux_request(nmp, thd, saddr, NULL,
0a7de745
A
4707 ((mntproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM),
4708 mreq, R_XID32(xid), 1, timeo, &nmrep);
b0d623f7 4709 nfsmout_if(error);
6d2010ae 4710 nfsm_chain_get_32(error, &nmrep, val);
0a7de745 4711 if (!error && val) {
6d2010ae 4712 error = val;
0a7de745 4713 }
6d2010ae
A
4714 nfsm_chain_get_fh(error, &nmrep, nfsvers, fh);
4715 if (!error && (nfsvers > NFS_VER2)) {
4716 sec->count = NX_MAX_SEC_FLAVORS;
4717 error = nfsm_chain_get_secinfo(&nmrep, &sec->flavors[0], &sec->count);
4718 }
4719nfsmout:
b0d623f7
A
4720 nfsm_chain_cleanup(&nmreq);
4721 nfsm_chain_cleanup(&nmrep);
0a7de745 4722 return error;
6d2010ae
A
4723}
4724
4725
4726/*
4727 * Send a MOUNT protocol UNMOUNT request to tell the server we've unmounted it.
4728 */
4729void
4730nfs3_umount_rpc(struct nfsmount *nmp, vfs_context_t ctx, int timeo)
4731{
4732 int error = 0, slen, mntproto;
4733 thread_t thd = vfs_context_thread(ctx);
4734 kauth_cred_t cred = vfs_context_ucred(ctx);
4735 char *path;
4736 uint64_t xid = 0;
4737 struct nfsm_chain nmreq, nmrep;
4738 mbuf_t mreq;
4739 uint32_t mntvers, mntport;
4740 struct sockaddr_storage ss;
4741 struct sockaddr *saddr = (struct sockaddr*)&ss;
4742
0a7de745 4743 if (!nmp->nm_saddr) {
6d2010ae 4744 return;
0a7de745 4745 }
6d2010ae
A
4746
4747 nfsm_chain_null(&nmreq);
4748 nfsm_chain_null(&nmrep);
4749
4750 mntvers = (nmp->nm_vers == NFS_VER2) ? RPCMNT_VER1 : RPCMNT_VER3;
4751 mntproto = (NM_OMFLAG(nmp, MNTUDP) || (nmp->nm_sotype == SOCK_DGRAM)) ? IPPROTO_UDP : IPPROTO_TCP;
4752 mntport = nmp->nm_mountport;
4753
4754 bcopy(nmp->nm_saddr, saddr, min(sizeof(ss), nmp->nm_saddr->sa_len));
0a7de745 4755 if (saddr->sa_family == AF_INET) {
6d2010ae 4756 ((struct sockaddr_in*)saddr)->sin_port = htons(mntport);
cb323159 4757 } else if (saddr->sa_family == AF_INET6) {
6d2010ae 4758 ((struct sockaddr_in6*)saddr)->sin6_port = htons(mntport);
cb323159
A
4759 } else { /* Local domain socket */
4760 mntport = ((struct sockaddr_un *)saddr)->sun_path[0]; /* Do we have and address? */
0a7de745 4761 }
6d2010ae
A
4762
4763 while (!mntport) {
4764 error = nfs_portmap_lookup(nmp, ctx, saddr, NULL, RPCPROG_MNT, mntvers, mntproto, timeo);
0a7de745
A
4765 nfsmout_if(error);
4766 if (saddr->sa_family == AF_INET) {
6d2010ae 4767 mntport = ntohs(((struct sockaddr_in*)saddr)->sin_port);
cb323159 4768 } else if (saddr->sa_family == AF_INET6) {
6d2010ae 4769 mntport = ntohs(((struct sockaddr_in6*)saddr)->sin6_port);
cb323159
A
4770 } else { /* Local domain socket */
4771 mntport = ((struct sockaddr_un *)saddr)->sun_path[0]; /* Do we have and address? */
0a7de745 4772 }
6d2010ae
A
4773 /* if not found and mntvers > VER1, then retry with VER1 */
4774 if (!mntport) {
4775 if (mntvers > RPCMNT_VER1) {
4776 mntvers = RPCMNT_VER1;
4777 } else if (mntproto == IPPROTO_TCP) {
4778 mntproto = IPPROTO_UDP;
4779 mntvers = (nmp->nm_vers == NFS_VER2) ? RPCMNT_VER1 : RPCMNT_VER3;
4780 } else {
4781 break;
4782 }
4783 bcopy(nmp->nm_saddr, saddr, min(sizeof(ss), nmp->nm_saddr->sa_len));
4784 }
4785 }
4786 nfsmout_if(!mntport);
b0d623f7
A
4787
4788 /* MOUNT protocol UNMOUNT request */
b0d623f7 4789 path = &vfs_statfs(nmp->nm_mountp)->f_mntfromname[0];
0a7de745 4790 while (*path && (*path != '/')) {
b0d623f7 4791 path++;
0a7de745 4792 }
b0d623f7
A
4793 slen = strlen(path);
4794 nfsm_chain_build_alloc_init(error, &nmreq, NFSX_UNSIGNED + nfsm_rndup(slen));
6d2010ae 4795 nfsm_chain_add_name(error, &nmreq, path, slen, nmp);
b0d623f7
A
4796 nfsm_chain_build_done(error, &nmreq);
4797 nfsmout_if(error);
6d2010ae 4798 error = nfsm_rpchead2(nmp, (mntproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM,
0a7de745
A
4799 RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMOUNT,
4800 RPCAUTH_SYS, cred, NULL, nmreq.nmc_mhead, &xid, &mreq);
b0d623f7
A
4801 nfsmout_if(error);
4802 nmreq.nmc_mhead = NULL;
6d2010ae 4803 error = nfs_aux_request(nmp, thd, saddr, NULL,
0a7de745
A
4804 ((mntproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM),
4805 mreq, R_XID32(xid), 1, timeo, &nmrep);
b0d623f7
A
4806nfsmout:
4807 nfsm_chain_cleanup(&nmreq);
4808 nfsm_chain_cleanup(&nmrep);
4809}
1c79356b
A
4810
4811/*
4812 * unmount system call
4813 */
b0d623f7 4814int
2d21ac55 4815nfs_vfs_unmount(
91447636
A
4816 mount_t mp,
4817 int mntflags,
2d21ac55 4818 __unused vfs_context_t ctx)
1c79356b 4819{
2d21ac55 4820 struct nfsmount *nmp;
91447636 4821 vnode_t vp;
6d2010ae 4822 int error, flags = 0;
cb323159 4823 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
1c79356b 4824
1c79356b 4825 nmp = VFSTONFS(mp);
2d21ac55 4826 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
4827 /*
4828 * Set the flag indicating that an unmount attempt is in progress.
4829 */
4830 nmp->nm_state |= NFSSTA_UNMOUNTING;
55e303ae
A
4831 /*
4832 * During a force unmount we want to...
4833 * Mark that we are doing a force unmount.
4834 * Make the mountpoint soft.
4835 */
4836 if (mntflags & MNT_FORCE) {
4837 flags |= FORCECLOSE;
4838 nmp->nm_state |= NFSSTA_FORCE;
6d2010ae 4839 NFS_BITMAP_SET(nmp->nm_flags, NFS_MFLAG_SOFT);
55e303ae 4840 }
6d2010ae
A
4841 /*
4842 * Wait for any in-progress monitored node scan to complete.
4843 */
0a7de745
A
4844 while (nmp->nm_state & NFSSTA_MONITOR_SCAN) {
4845 msleep(&nmp->nm_state, &nmp->nm_lock, PZERO - 1, "nfswaitmonscan", &ts);
4846 }
1c79356b
A
4847 /*
4848 * Goes something like this..
1c79356b 4849 * - Call vflush() to clear out vnodes for this file system,
0b4e3aa0 4850 * except for the swap files. Deal with them in 2nd pass.
1c79356b 4851 * - Decrement reference on the vnode representing remote root.
6d2010ae 4852 * - Clean up the NFS mount structure.
1c79356b 4853 */
2d21ac55
A
4854 vp = NFSTOV(nmp->nm_dnp);
4855 lck_mtx_unlock(&nmp->nm_lock);
b0d623f7 4856
0b4e3aa0 4857 /*
2d21ac55 4858 * vflush will check for busy vnodes on mountpoint.
0b4e3aa0
A
4859 * Will do the right thing for MNT_FORCE. That is, we should
4860 * not get EBUSY back.
4861 */
4862 error = vflush(mp, vp, SKIPSWAP | flags);
55e303ae 4863 if (mntflags & MNT_FORCE) {
fa4905b1 4864 error = vflush(mp, NULLVP, flags); /* locks vp in the process */
55e303ae 4865 } else {
0a7de745 4866 if (vnode_isinuse(vp, 1)) {
6d2010ae 4867 error = EBUSY;
0a7de745 4868 } else {
6d2010ae 4869 error = vflush(mp, vp, flags);
0a7de745 4870 }
0b4e3aa0 4871 }
6d2010ae
A
4872 if (error) {
4873 lck_mtx_lock(&nmp->nm_lock);
4874 nmp->nm_state &= ~NFSSTA_UNMOUNTING;
4875 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 4876 return error;
6d2010ae 4877 }
1c79356b 4878
2d21ac55 4879 lck_mtx_lock(&nmp->nm_lock);
b0d623f7 4880 nmp->nm_dnp = NULL;
2d21ac55 4881 lck_mtx_unlock(&nmp->nm_lock);
1c79356b
A
4882
4883 /*
4884 * Release the root vnode reference held by mountnfs()
4885 */
b0d623f7 4886 error = vnode_get(vp);
91447636 4887 vnode_rele(vp);
0a7de745 4888 if (!error) {
b0d623f7 4889 vnode_put(vp);
0a7de745 4890 }
91447636 4891
2d21ac55
A
4892 vflush(mp, NULLVP, FORCECLOSE);
4893
fe8ab488
A
4894 /* Wait for all other references to be released and free the mount */
4895 nfs_mount_drain_and_cleanup(nmp);
0a7de745
A
4896
4897 return 0;
6d2010ae 4898}
2d21ac55 4899
6d2010ae
A
4900/*
4901 * cleanup/destroy NFS fs locations structure
4902 */
4903void
4904nfs_fs_locations_cleanup(struct nfs_fs_locations *nfslsp)
4905{
4906 struct nfs_fs_location *fsl;
4907 struct nfs_fs_server *fss;
4908 struct nfs_fs_path *fsp;
4909 uint32_t loc, serv, addr, comp;
4910
4911 /* free up fs locations */
0a7de745 4912 if (!nfslsp->nl_numlocs || !nfslsp->nl_locations) {
6d2010ae 4913 return;
0a7de745 4914 }
6d2010ae
A
4915
4916 for (loc = 0; loc < nfslsp->nl_numlocs; loc++) {
4917 fsl = nfslsp->nl_locations[loc];
0a7de745 4918 if (!fsl) {
6d2010ae 4919 continue;
0a7de745 4920 }
6d2010ae
A
4921 if ((fsl->nl_servcount > 0) && fsl->nl_servers) {
4922 for (serv = 0; serv < fsl->nl_servcount; serv++) {
4923 fss = fsl->nl_servers[serv];
0a7de745 4924 if (!fss) {
6d2010ae 4925 continue;
0a7de745 4926 }
6d2010ae 4927 if ((fss->ns_addrcount > 0) && fss->ns_addresses) {
0a7de745 4928 for (addr = 0; addr < fss->ns_addrcount; addr++) {
6d2010ae 4929 FREE(fss->ns_addresses[addr], M_TEMP);
0a7de745 4930 }
6d2010ae
A
4931 FREE(fss->ns_addresses, M_TEMP);
4932 }
4933 FREE(fss->ns_name, M_TEMP);
4934 FREE(fss, M_TEMP);
4935 }
4936 FREE(fsl->nl_servers, M_TEMP);
4937 }
4938 fsp = &fsl->nl_path;
4939 if (fsp->np_compcount && fsp->np_components) {
0a7de745
A
4940 for (comp = 0; comp < fsp->np_compcount; comp++) {
4941 if (fsp->np_components[comp]) {
6d2010ae 4942 FREE(fsp->np_components[comp], M_TEMP);
0a7de745
A
4943 }
4944 }
6d2010ae
A
4945 FREE(fsp->np_components, M_TEMP);
4946 }
4947 FREE(fsl, M_TEMP);
4948 }
4949 FREE(nfslsp->nl_locations, M_TEMP);
4950 nfslsp->nl_numlocs = 0;
4951 nfslsp->nl_locations = NULL;
4952}
4953
fe8ab488
A
4954void
4955nfs_mount_rele(struct nfsmount *nmp)
4956{
4957 int wup = 0;
4958
4959 lck_mtx_lock(&nmp->nm_lock);
0a7de745 4960 if (nmp->nm_ref < 1) {
fe8ab488 4961 panic("nfs zombie mount underflow\n");
0a7de745 4962 }
fe8ab488 4963 nmp->nm_ref--;
0a7de745 4964 if (nmp->nm_ref == 0) {
fe8ab488 4965 wup = nmp->nm_state & NFSSTA_MOUNT_DRAIN;
0a7de745 4966 }
fe8ab488 4967 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 4968 if (wup) {
fe8ab488 4969 wakeup(&nmp->nm_ref);
0a7de745 4970 }
fe8ab488
A
4971}
4972
4973void
4974nfs_mount_drain_and_cleanup(struct nfsmount *nmp)
4975{
4976 lck_mtx_lock(&nmp->nm_lock);
4977 nmp->nm_state |= NFSSTA_MOUNT_DRAIN;
4978 while (nmp->nm_ref > 0) {
0a7de745 4979 msleep(&nmp->nm_ref, &nmp->nm_lock, PZERO - 1, "nfs_mount_drain", NULL);
fe8ab488
A
4980 }
4981 assert(nmp->nm_ref == 0);
4982 lck_mtx_unlock(&nmp->nm_lock);
4983 nfs_mount_cleanup(nmp);
4984}
4985
6d2010ae 4986/*
fe8ab488 4987 * nfs_mount_zombie
6d2010ae
A
4988 */
4989void
fe8ab488 4990nfs_mount_zombie(struct nfsmount *nmp, int nm_state_flags)
6d2010ae
A
4991{
4992 struct nfsreq *req, *treq;
3e170ce0 4993 struct nfs_reqqhead iodq, resendq;
cb323159 4994 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
6d2010ae
A
4995 struct nfs_open_owner *noop, *nextnoop;
4996 nfsnode_t np;
4997 int docallback;
2d21ac55 4998
fe8ab488
A
4999 lck_mtx_lock(&nmp->nm_lock);
5000 nmp->nm_state |= nm_state_flags;
5001 nmp->nm_ref++;
5002 lck_mtx_unlock(&nmp->nm_lock);
cb323159 5003#if CONFIG_NFS4
b0d623f7 5004 /* stop callbacks */
0a7de745 5005 if ((nmp->nm_vers >= NFS_VER4) && !NMFLAG(nmp, NOCALLBACK) && nmp->nm_cbid) {
b0d623f7 5006 nfs4_mount_callback_shutdown(nmp);
0a7de745 5007 }
cb323159
A
5008#endif
5009#if CONFIG_NFS_GSS
6d2010ae 5010 /* Destroy any RPCSEC_GSS contexts */
04b8595b 5011 nfs_gss_clnt_ctx_unmount(nmp);
cb323159 5012#endif
6d2010ae
A
5013
5014 /* mark the socket for termination */
5015 lck_mtx_lock(&nmp->nm_lock);
5016 nmp->nm_sockflags |= NMSOCK_UNMOUNT;
2d21ac55 5017
b0d623f7 5018 /* Have the socket thread send the unmount RPC, if requested/appropriate. */
6d2010ae 5019 if ((nmp->nm_vers < NFS_VER4) && (nmp->nm_state & NFSSTA_MOUNTED) &&
0a7de745 5020 !(nmp->nm_state & (NFSSTA_FORCE | NFSSTA_DEAD)) && NMFLAG(nmp, CALLUMNT)) {
b0d623f7 5021 nfs_mount_sock_thread_wake(nmp);
0a7de745 5022 }
b0d623f7 5023
2d21ac55 5024 /* wait for the socket thread to terminate */
fe8ab488 5025 while (nmp->nm_sockthd && current_thread() != nmp->nm_sockthd) {
2d21ac55 5026 wakeup(&nmp->nm_sockthd);
0a7de745 5027 msleep(&nmp->nm_sockthd, &nmp->nm_lock, PZERO - 1, "nfswaitsockthd", &ts);
2d21ac55 5028 }
2d21ac55 5029 lck_mtx_unlock(&nmp->nm_lock);
b0d623f7
A
5030
5031 /* tear down the socket */
1c79356b 5032 nfs_disconnect(nmp);
b0d623f7 5033
2d21ac55
A
5034 lck_mtx_lock(&nmp->nm_lock);
5035
cb323159 5036#if CONFIG_NFS4
6d2010ae
A
5037 if ((nmp->nm_vers >= NFS_VER4) && !NMFLAG(nmp, NOCALLBACK) && nmp->nm_cbid) {
5038 /* clear out any pending delegation return requests */
5039 while ((np = TAILQ_FIRST(&nmp->nm_dreturnq))) {
5040 TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
5041 np->n_dreturn.tqe_next = NFSNOLIST;
b0d623f7
A
5042 }
5043 }
5044
2d21ac55 5045 /* cancel any renew timer */
b0d623f7 5046 if ((nmp->nm_vers >= NFS_VER4) && nmp->nm_renew_timer) {
2d21ac55
A
5047 thread_call_cancel(nmp->nm_renew_timer);
5048 thread_call_free(nmp->nm_renew_timer);
a39ff7e2 5049 nmp->nm_renew_timer = NULL;
2d21ac55 5050 }
cb323159 5051#endif
2d21ac55 5052 lck_mtx_unlock(&nmp->nm_lock);
1c79356b 5053
0a7de745 5054 if (nmp->nm_state & NFSSTA_MOUNTED) {
6d2010ae
A
5055 switch (nmp->nm_lockmode) {
5056 case NFS_LOCK_MODE_DISABLED:
5057 case NFS_LOCK_MODE_LOCAL:
5058 break;
5059 case NFS_LOCK_MODE_ENABLED:
5060 default:
fe8ab488 5061 if (nmp->nm_vers <= NFS_VER3) {
6d2010ae 5062 nfs_lockd_mount_unregister(nmp);
fe8ab488
A
5063 nmp->nm_lockmode = NFS_LOCK_MODE_DISABLED;
5064 }
6d2010ae
A
5065 break;
5066 }
0a7de745 5067 }
2d21ac55 5068
cb323159 5069#if CONFIG_NFS4
b0d623f7
A
5070 if ((nmp->nm_vers >= NFS_VER4) && nmp->nm_longid) {
5071 /* remove/deallocate the client ID data */
5072 lck_mtx_lock(nfs_global_mutex);
5073 TAILQ_REMOVE(&nfsclientids, nmp->nm_longid, nci_link);
0a7de745 5074 if (nmp->nm_longid->nci_id) {
b0d623f7 5075 FREE(nmp->nm_longid->nci_id, M_TEMP);
0a7de745 5076 }
b0d623f7 5077 FREE(nmp->nm_longid, M_TEMP);
a39ff7e2 5078 nmp->nm_longid = NULL;
b0d623f7
A
5079 lck_mtx_unlock(nfs_global_mutex);
5080 }
cb323159 5081#endif
2d21ac55 5082 /*
3e170ce0
A
5083 * Be sure all requests for this mount are completed
5084 * and removed from the resend queue.
5085 */
5086 TAILQ_INIT(&resendq);
5087 lck_mtx_lock(nfs_request_mutex);
5088 TAILQ_FOREACH(req, &nfs_reqq, r_chain) {
5089 if (req->r_nmp == nmp) {
5090 lck_mtx_lock(&req->r_mtx);
0a7de745 5091 if (!req->r_error && req->r_nmrep.nmc_mhead == NULL) {
3e170ce0 5092 req->r_error = EIO;
0a7de745 5093 }
3e170ce0
A
5094 if (req->r_flags & R_RESENDQ) {
5095 lck_mtx_lock(&nmp->nm_lock);
5096 req->r_flags &= ~R_RESENDQ;
5097 if (req->r_rchain.tqe_next != NFSREQNOLIST) {
5098 TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
5099 /*
0a7de745 5100 * Queue up the request so that we can unreference them
3e170ce0
A
5101 * with out holding nfs_request_mutex
5102 */
5103 TAILQ_INSERT_TAIL(&resendq, req, r_rchain);
5104 }
5105 lck_mtx_unlock(&nmp->nm_lock);
5106 }
5107 wakeup(req);
5108 lck_mtx_unlock(&req->r_mtx);
5109 }
5110 }
5111 lck_mtx_unlock(nfs_request_mutex);
5112
5113 /* Since we've drop the request mutex we can now safely unreference the request */
5114 TAILQ_FOREACH_SAFE(req, &resendq, r_rchain, treq) {
5115 TAILQ_REMOVE(&resendq, req, r_rchain);
d9a64523
A
5116 /* Make sure we don't try and remove again in nfs_request_destroy */
5117 req->r_rchain.tqe_next = NFSREQNOLIST;
3e170ce0
A
5118 nfs_request_rele(req);
5119 }
5120
5121 /*
5122 * Now handle and outstanding async requests. We need to walk the
5123 * request queue again this time with the nfsiod_mutex held. No
5124 * other iods can grab our requests until we've put them on our own
5125 * local iod queue for processing.
2d21ac55
A
5126 */
5127 TAILQ_INIT(&iodq);
5128 lck_mtx_lock(nfs_request_mutex);
3e170ce0 5129 lck_mtx_lock(nfsiod_mutex);
2d21ac55
A
5130 TAILQ_FOREACH(req, &nfs_reqq, r_chain) {
5131 if (req->r_nmp == nmp) {
3e170ce0
A
5132 lck_mtx_lock(&req->r_mtx);
5133 if (req->r_callback.rcb_func
5134 && !(req->r_flags & R_WAITSENT) && !(req->r_flags & R_IOD)) {
0a7de745 5135 /*
3e170ce0
A
5136 * Since R_IOD is not set then we need to handle it. If
5137 * we're not on a list add it to our iod queue. Otherwise
5138 * we must already be on nm_iodq which is added to our
5139 * local queue below.
5140 * %%% We should really keep a back pointer to our iod queue
5141 * that we're on.
5142 */
5143 req->r_flags |= R_IOD;
fe8ab488 5144 if (req->r_achain.tqe_next == NFSREQNOLIST) {
2d21ac55 5145 TAILQ_INSERT_TAIL(&iodq, req, r_achain);
b0d623f7 5146 }
2d21ac55 5147 }
3e170ce0 5148 lck_mtx_unlock(&req->r_mtx);
ccc36f2f 5149 }
1c79356b 5150 }
2d21ac55
A
5151
5152 /* finish any async I/O RPCs queued up */
0a7de745 5153 if (nmp->nm_iodlink.tqe_next != NFSNOLIST) {
fe8ab488 5154 TAILQ_REMOVE(&nfsiodmounts, nmp, nm_iodlink);
0a7de745 5155 }
2d21ac55
A
5156 TAILQ_CONCAT(&iodq, &nmp->nm_iodq, r_achain);
5157 lck_mtx_unlock(nfsiod_mutex);
3e170ce0
A
5158 lck_mtx_unlock(nfs_request_mutex);
5159
2d21ac55
A
5160 TAILQ_FOREACH_SAFE(req, &iodq, r_achain, treq) {
5161 TAILQ_REMOVE(&iodq, req, r_achain);
3e170ce0 5162 req->r_achain.tqe_next = NFSREQNOLIST;
2d21ac55 5163 lck_mtx_lock(&req->r_mtx);
2d21ac55
A
5164 docallback = !(req->r_flags & R_WAITSENT);
5165 lck_mtx_unlock(&req->r_mtx);
0a7de745 5166 if (docallback) {
2d21ac55 5167 req->r_callback.rcb_func(req);
0a7de745 5168 }
2d21ac55
A
5169 }
5170
6d2010ae
A
5171 /* clean up common state */
5172 lck_mtx_lock(&nmp->nm_lock);
0a7de745
A
5173 while ((np = LIST_FIRST(&nmp->nm_monlist))) {
5174 LIST_REMOVE(np, n_monlink);
5175 np->n_monlink.le_next = NFSNOLIST;
5176 }
6d2010ae 5177 TAILQ_FOREACH_SAFE(noop, &nmp->nm_open_owners, noo_link, nextnoop) {
0a7de745
A
5178 os_ref_count_t newcount;
5179
6d2010ae
A
5180 TAILQ_REMOVE(&nmp->nm_open_owners, noop, noo_link);
5181 noop->noo_flags &= ~NFS_OPEN_OWNER_LINK;
0a7de745
A
5182 newcount = os_ref_release_locked(&noop->noo_refcnt);
5183
5184 if (newcount) {
6d2010ae 5185 continue;
0a7de745 5186 }
6d2010ae
A
5187 nfs_open_owner_destroy(noop);
5188 }
5189 lck_mtx_unlock(&nmp->nm_lock);
5190
cb323159 5191#if CONFIG_NFS4
6d2010ae 5192 /* clean up NFSv4 state */
b0d623f7
A
5193 if (nmp->nm_vers >= NFS_VER4) {
5194 lck_mtx_lock(&nmp->nm_lock);
6d2010ae
A
5195 while ((np = TAILQ_FIRST(&nmp->nm_delegations))) {
5196 TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
5197 np->n_dlink.tqe_next = NFSNOLIST;
b0d623f7
A
5198 }
5199 lck_mtx_unlock(&nmp->nm_lock);
b0d623f7 5200 }
cb323159 5201#endif
fe8ab488
A
5202 nfs_mount_rele(nmp);
5203}
5204
5205/*
5206 * cleanup/destroy an nfsmount
5207 */
5208void
5209nfs_mount_cleanup(struct nfsmount *nmp)
5210{
0a7de745 5211 if (!nmp) {
fe8ab488 5212 return;
0a7de745 5213 }
fe8ab488
A
5214
5215 nfs_mount_zombie(nmp, 0);
5216
5217 NFS_VFS_DBG("Unmounting %s from %s\n",
0a7de745
A
5218 vfs_statfs(nmp->nm_mountp)->f_mntfromname,
5219 vfs_statfs(nmp->nm_mountp)->f_mntonname);
39037602
A
5220 NFS_VFS_DBG("nfs state = 0x%8.8x\n", nmp->nm_state);
5221 NFS_VFS_DBG("nfs socket flags = 0x%8.8x\n", nmp->nm_sockflags);
fe8ab488
A
5222 NFS_VFS_DBG("nfs mount ref count is %d\n", nmp->nm_ref);
5223 NFS_VFS_DBG("mount ref count is %d\n", nmp->nm_mountp->mnt_count);
0a7de745
A
5224
5225 if (nmp->nm_mountp) {
fe8ab488 5226 vfs_setfsprivate(nmp->nm_mountp, NULL);
0a7de745 5227 }
fe8ab488
A
5228
5229 lck_mtx_lock(&nmp->nm_lock);
0a7de745 5230 if (nmp->nm_ref) {
39037602 5231 panic("Some one has grabbed a ref %d state flags = 0x%8.8x\n", nmp->nm_ref, nmp->nm_state);
0a7de745 5232 }
fe8ab488 5233
0a7de745 5234 if (nmp->nm_saddr) {
fe8ab488 5235 FREE(nmp->nm_saddr, M_SONAME);
0a7de745
A
5236 }
5237 if ((nmp->nm_vers < NFS_VER4) && nmp->nm_rqsaddr) {
fe8ab488 5238 FREE(nmp->nm_rqsaddr, M_SONAME);
0a7de745 5239 }
fe8ab488 5240
0a7de745 5241 if (IS_VALID_CRED(nmp->nm_mcred)) {
6d2010ae 5242 kauth_cred_unref(&nmp->nm_mcred);
0a7de745 5243 }
b0d623f7 5244
6d2010ae
A
5245 nfs_fs_locations_cleanup(&nmp->nm_locations);
5246
0a7de745 5247 if (nmp->nm_realm) {
39236c6e 5248 FREE(nmp->nm_realm, M_TEMP);
0a7de745
A
5249 }
5250 if (nmp->nm_principal) {
39236c6e 5251 FREE(nmp->nm_principal, M_TEMP);
0a7de745
A
5252 }
5253 if (nmp->nm_sprinc) {
39236c6e 5254 FREE(nmp->nm_sprinc, M_TEMP);
0a7de745
A
5255 }
5256
5257 if (nmp->nm_args) {
6d2010ae 5258 xb_free(nmp->nm_args);
0a7de745 5259 }
fe8ab488
A
5260
5261 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 5262
2d21ac55 5263 lck_mtx_destroy(&nmp->nm_lock, nfs_mount_grp);
0a7de745 5264 if (nmp->nm_fh) {
6d2010ae 5265 FREE(nmp->nm_fh, M_TEMP);
0a7de745 5266 }
cb323159
A
5267
5268
0a7de745 5269 FREE_ZONE(nmp, sizeof(struct nfsmount), M_NFSMNT);
1c79356b
A
5270}
5271
5272/*
5273 * Return root of a filesystem
5274 */
b0d623f7 5275int
2d21ac55 5276nfs_vfs_root(mount_t mp, vnode_t *vpp, __unused vfs_context_t ctx)
1c79356b 5277{
91447636 5278 vnode_t vp;
1c79356b 5279 struct nfsmount *nmp;
91447636 5280 int error;
b0d623f7 5281 u_int32_t vpid;
1c79356b
A
5282
5283 nmp = VFSTONFS(mp);
0a7de745
A
5284 if (!nmp || !nmp->nm_dnp) {
5285 return ENXIO;
5286 }
2d21ac55 5287 vp = NFSTOV(nmp->nm_dnp);
91447636
A
5288 vpid = vnode_vid(vp);
5289 while ((error = vnode_getwithvid(vp, vpid))) {
5290 /* vnode_get() may return ENOENT if the dir changes. */
5291 /* If that happens, just try it again, else return the error. */
0a7de745
A
5292 if ((error != ENOENT) || (vnode_vid(vp) == vpid)) {
5293 return error;
5294 }
91447636 5295 vpid = vnode_vid(vp);
55e303ae 5296 }
1c79356b 5297 *vpp = vp;
0a7de745 5298 return 0;
1c79356b
A
5299}
5300
2d21ac55
A
5301/*
5302 * Do operations associated with quotas
5303 */
5304#if !QUOTA
b0d623f7 5305int
2d21ac55
A
5306nfs_vfs_quotactl(
5307 __unused mount_t mp,
5308 __unused int cmds,
5309 __unused uid_t uid,
5310 __unused caddr_t datap,
5311 __unused vfs_context_t context)
5312{
0a7de745 5313 return ENOTSUP;
2d21ac55
A
5314}
5315#else
2d21ac55 5316
5ba3f43e
A
5317static int
5318nfs_sa_getport(struct sockaddr *sa, int *error)
5319{
5320 int port = 0;
5321
0a7de745 5322 if (sa->sa_family == AF_INET6) {
5ba3f43e 5323 port = ntohs(((struct sockaddr_in6*)sa)->sin6_port);
0a7de745 5324 } else if (sa->sa_family == AF_INET) {
5ba3f43e 5325 port = ntohs(((struct sockaddr_in*)sa)->sin_port);
0a7de745 5326 } else if (error) {
5ba3f43e 5327 *error = EIO;
0a7de745 5328 }
5ba3f43e
A
5329
5330 return port;
5331}
5332
5333static void
5334nfs_sa_setport(struct sockaddr *sa, int port)
5335{
0a7de745 5336 if (sa->sa_family == AF_INET6) {
5ba3f43e 5337 ((struct sockaddr_in6*)sa)->sin6_port = htons(port);
0a7de745 5338 } else if (sa->sa_family == AF_INET) {
5ba3f43e 5339 ((struct sockaddr_in*)sa)->sin_port = htons(port);
0a7de745 5340 }
5ba3f43e
A
5341}
5342
b0d623f7
A
5343int
5344nfs3_getquota(struct nfsmount *nmp, vfs_context_t ctx, uid_t id, int type, struct dqblk *dqb)
2d21ac55 5345{
6d2010ae
A
5346 int error = 0, slen, timeo;
5347 int rqport = 0, rqproto, rqvers = (type == GRPQUOTA) ? RPCRQUOTA_EXT_VER : RPCRQUOTA_VER;
2d21ac55
A
5348 thread_t thd = vfs_context_thread(ctx);
5349 kauth_cred_t cred = vfs_context_ucred(ctx);
5350 char *path;
5351 uint64_t xid = 0;
5352 struct nfsm_chain nmreq, nmrep;
5353 mbuf_t mreq;
b0d623f7 5354 uint32_t val = 0, bsize = 0;
6d2010ae 5355 struct sockaddr *rqsaddr;
2d21ac55 5356 struct timeval now;
cb323159 5357 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
2d21ac55 5358
0a7de745
A
5359 if (!nmp->nm_saddr) {
5360 return ENXIO;
5361 }
2d21ac55 5362
cb323159 5363 if (NMFLAG(nmp, NOQUOTA) || nmp->nm_saddr->sa_family == AF_LOCAL /* XXX for now */) {
0a7de745
A
5364 return ENOTSUP;
5365 }
2d21ac55 5366
5ba3f43e
A
5367 /*
5368 * Allocate an address for rquotad if needed
5369 */
5370 if (!nmp->nm_rqsaddr) {
5371 int need_free = 0;
5372
0a7de745 5373 MALLOC(rqsaddr, struct sockaddr *, sizeof(struct sockaddr_storage), M_SONAME, M_WAITOK | M_ZERO);
5ba3f43e
A
5374 bcopy(nmp->nm_saddr, rqsaddr, min(sizeof(struct sockaddr_storage), nmp->nm_saddr->sa_len));
5375 /* Set the port to zero, will call rpcbind to get the port below */
5376 nfs_sa_setport(rqsaddr, 0);
5377 microuptime(&now);
5378
5379 lck_mtx_lock(&nmp->nm_lock);
5380 if (!nmp->nm_rqsaddr) {
5381 nmp->nm_rqsaddr = rqsaddr;
5382 nmp->nm_rqsaddrstamp = now.tv_sec;
5383 } else {
5384 need_free = 1;
5385 }
5386 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 5387 if (need_free) {
5ba3f43e 5388 FREE(rqsaddr, M_SONAME);
0a7de745 5389 }
5ba3f43e 5390 }
2d21ac55 5391
6d2010ae
A
5392 timeo = NMFLAG(nmp, SOFT) ? 10 : 60;
5393 rqproto = IPPROTO_UDP; /* XXX should prefer TCP if mount is TCP */
5394
5395 /* check if we have a recently cached rquota port */
2d21ac55 5396 microuptime(&now);
5ba3f43e
A
5397 lck_mtx_lock(&nmp->nm_lock);
5398 rqsaddr = nmp->nm_rqsaddr;
5399 rqport = nfs_sa_getport(rqsaddr, &error);
5400 while (!error && (!rqport || ((nmp->nm_rqsaddrstamp + 60) <= (uint32_t)now.tv_sec))) {
5401 error = nfs_sigintr(nmp, NULL, thd, 1);
5402 if (error) {
5403 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 5404 return error;
5ba3f43e
A
5405 }
5406 if (nmp->nm_state & NFSSTA_RQUOTAINPROG) {
5407 nmp->nm_state |= NFSSTA_WANTRQUOTA;
0a7de745 5408 msleep(&nmp->nm_rqsaddr, &nmp->nm_lock, PZERO - 1, "nfswaitrquotaaddr", &ts);
5ba3f43e
A
5409 rqport = nfs_sa_getport(rqsaddr, &error);
5410 continue;
5411 }
5412 nmp->nm_state |= NFSSTA_RQUOTAINPROG;
5413 lck_mtx_unlock(&nmp->nm_lock);
5414
6d2010ae 5415 /* send portmap request to get rquota port */
6d2010ae 5416 error = nfs_portmap_lookup(nmp, ctx, rqsaddr, NULL, RPCPROG_RQUOTA, rqvers, rqproto, timeo);
0a7de745 5417 if (error) {
5ba3f43e 5418 goto out;
0a7de745 5419 }
5ba3f43e 5420 rqport = nfs_sa_getport(rqsaddr, &error);
0a7de745 5421 if (error) {
5ba3f43e 5422 goto out;
0a7de745 5423 }
5ba3f43e
A
5424
5425 if (!rqport) {
5426 /*
5427 * We overload PMAPPORT for the port if rquotad is not
5428 * currently registered or up at the server. In the
5429 * while loop above, port will be set and we will defer
5430 * for a bit. Perhaps the service isn't online yet.
5431 *
5432 * Note that precludes using indirect, but we're not doing
5433 * that here.
5434 */
5435 rqport = PMAPPORT;
5436 nfs_sa_setport(rqsaddr, rqport);
5437 }
6d2010ae
A
5438 microuptime(&now);
5439 nmp->nm_rqsaddrstamp = now.tv_sec;
0a7de745 5440out:
5ba3f43e
A
5441 lck_mtx_lock(&nmp->nm_lock);
5442 nmp->nm_state &= ~NFSSTA_RQUOTAINPROG;
5443 if (nmp->nm_state & NFSSTA_WANTRQUOTA) {
5444 nmp->nm_state &= ~NFSSTA_WANTRQUOTA;
5445 wakeup(&nmp->nm_rqsaddr);
5446 }
6d2010ae 5447 }
5ba3f43e 5448 lck_mtx_unlock(&nmp->nm_lock);
0a7de745
A
5449 if (error) {
5450 return error;
5451 }
5ba3f43e
A
5452
5453 /* Using PMAPPORT for unavailabe rquota service */
0a7de745
A
5454 if (rqport == PMAPPORT) {
5455 return ENOTSUP;
5456 }
2d21ac55 5457
2d21ac55 5458 /* rquota request */
6d2010ae
A
5459 nfsm_chain_null(&nmreq);
5460 nfsm_chain_null(&nmrep);
2d21ac55 5461 path = &vfs_statfs(nmp->nm_mountp)->f_mntfromname[0];
0a7de745 5462 while (*path && (*path != '/')) {
2d21ac55 5463 path++;
0a7de745 5464 }
2d21ac55
A
5465 slen = strlen(path);
5466 nfsm_chain_build_alloc_init(error, &nmreq, 3 * NFSX_UNSIGNED + nfsm_rndup(slen));
6d2010ae 5467 nfsm_chain_add_name(error, &nmreq, path, slen, nmp);
0a7de745 5468 if (type == GRPQUOTA) {
2d21ac55 5469 nfsm_chain_add_32(error, &nmreq, type);
0a7de745 5470 }
2d21ac55
A
5471 nfsm_chain_add_32(error, &nmreq, id);
5472 nfsm_chain_build_done(error, &nmreq);
5473 nfsmout_if(error);
6d2010ae 5474 error = nfsm_rpchead2(nmp, (rqproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM,
0a7de745
A
5475 RPCPROG_RQUOTA, rqvers, RPCRQUOTA_GET,
5476 RPCAUTH_SYS, cred, NULL, nmreq.nmc_mhead, &xid, &mreq);
2d21ac55
A
5477 nfsmout_if(error);
5478 nmreq.nmc_mhead = NULL;
6d2010ae 5479 error = nfs_aux_request(nmp, thd, rqsaddr, NULL,
0a7de745
A
5480 (rqproto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM,
5481 mreq, R_XID32(xid), 0, timeo, &nmrep);
2d21ac55
A
5482 nfsmout_if(error);
5483
5484 /* parse rquota response */
5485 nfsm_chain_get_32(error, &nmrep, val);
5486 if (!error && (val != RQUOTA_STAT_OK)) {
0a7de745 5487 if (val == RQUOTA_STAT_NOQUOTA) {
2d21ac55 5488 error = ENOENT;
0a7de745 5489 } else if (val == RQUOTA_STAT_EPERM) {
2d21ac55 5490 error = EPERM;
0a7de745 5491 } else {
2d21ac55 5492 error = EIO;
0a7de745 5493 }
2d21ac55
A
5494 }
5495 nfsm_chain_get_32(error, &nmrep, bsize);
5496 nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED);
5497 nfsm_chain_get_32(error, &nmrep, val);
5498 nfsmout_if(error);
5499 dqb->dqb_bhardlimit = (uint64_t)val * bsize;
5500 nfsm_chain_get_32(error, &nmrep, val);
5501 nfsmout_if(error);
5502 dqb->dqb_bsoftlimit = (uint64_t)val * bsize;
5503 nfsm_chain_get_32(error, &nmrep, val);
5504 nfsmout_if(error);
5505 dqb->dqb_curbytes = (uint64_t)val * bsize;
5506 nfsm_chain_get_32(error, &nmrep, dqb->dqb_ihardlimit);
5507 nfsm_chain_get_32(error, &nmrep, dqb->dqb_isoftlimit);
5508 nfsm_chain_get_32(error, &nmrep, dqb->dqb_curinodes);
5509 nfsm_chain_get_32(error, &nmrep, dqb->dqb_btime);
5510 nfsm_chain_get_32(error, &nmrep, dqb->dqb_itime);
5511 nfsmout_if(error);
5512 dqb->dqb_id = id;
5513nfsmout:
5514 nfsm_chain_cleanup(&nmreq);
5515 nfsm_chain_cleanup(&nmrep);
0a7de745 5516 return error;
2d21ac55 5517}
cb323159 5518#if CONFIG_NFS4
b0d623f7
A
5519int
5520nfs4_getquota(struct nfsmount *nmp, vfs_context_t ctx, uid_t id, int type, struct dqblk *dqb)
2d21ac55
A
5521{
5522 nfsnode_t np;
5523 int error = 0, status, nfsvers, numops;
5524 u_int64_t xid;
5525 struct nfsm_chain nmreq, nmrep;
5526 uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
5527 thread_t thd = vfs_context_thread(ctx);
5528 kauth_cred_t cred = vfs_context_ucred(ctx);
6d2010ae 5529 struct nfsreq_secinfo_args si;
2d21ac55 5530
0a7de745
A
5531 if (type != USRQUOTA) { /* NFSv4 only supports user quotas */
5532 return ENOTSUP;
5533 }
2d21ac55
A
5534
5535 /* first check that the server supports any of the quota attributes */
5536 if (!NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_HARD) &&
5537 !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_SOFT) &&
0a7de745
A
5538 !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_USED)) {
5539 return ENOTSUP;
5540 }
2d21ac55
A
5541
5542 /*
5543 * The credential passed to the server needs to have
5544 * an effective uid that matches the given uid.
5545 */
5546 if (id != kauth_cred_getuid(cred)) {
6d2010ae
A
5547 struct posix_cred temp_pcred;
5548 posix_cred_t pcred = posix_cred_get(cred);
5549 bzero(&temp_pcred, sizeof(temp_pcred));
5550 temp_pcred.cr_uid = id;
5551 temp_pcred.cr_ngroups = pcred->cr_ngroups;
5552 bcopy(pcred->cr_groups, temp_pcred.cr_groups, sizeof(temp_pcred.cr_groups));
5553 cred = posix_cred_create(&temp_pcred);
0a7de745
A
5554 if (!IS_VALID_CRED(cred)) {
5555 return ENOMEM;
5556 }
2d21ac55
A
5557 } else {
5558 kauth_cred_ref(cred);
5559 }
5560
5561 nfsvers = nmp->nm_vers;
5562 np = nmp->nm_dnp;
0a7de745 5563 if (!np) {
b0d623f7 5564 error = ENXIO;
0a7de745 5565 }
b0d623f7 5566 if (error || ((error = vnode_get(NFSTOV(np))))) {
2d21ac55 5567 kauth_cred_unref(&cred);
0a7de745 5568 return error;
2d21ac55
A
5569 }
5570
6d2010ae 5571 NFSREQ_SECINFO_SET(&si, np, NULL, 0, NULL, 0);
2d21ac55
A
5572 nfsm_chain_null(&nmreq);
5573 nfsm_chain_null(&nmrep);
5574
5575 // PUTFH + GETATTR
5576 numops = 2;
5577 nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
3e170ce0 5578 nfsm_chain_add_compound_header(error, &nmreq, "quota", nmp->nm_minor_vers, numops);
2d21ac55
A
5579 numops--;
5580 nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
5581 nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
5582 numops--;
5583 nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
5584 NFS_CLEAR_ATTRIBUTES(bitmap);
5585 NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_AVAIL_HARD);
5586 NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_AVAIL_SOFT);
5587 NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_USED);
6d2010ae 5588 nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, NULL);
2d21ac55
A
5589 nfsm_chain_build_done(error, &nmreq);
5590 nfsm_assert(error, (numops == 0), EPROTO);
5591 nfsmout_if(error);
6d2010ae 5592 error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, 0, &nmrep, &xid, &status);
2d21ac55
A
5593 nfsm_chain_skip_tag(error, &nmrep);
5594 nfsm_chain_get_32(error, &nmrep, numops);
5595 nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
5596 nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
5597 nfsm_assert(error, NFSTONMP(np), ENXIO);
5598 nfsmout_if(error);
6d2010ae 5599 error = nfs4_parsefattr(&nmrep, NULL, NULL, NULL, dqb, NULL);
2d21ac55
A
5600 nfsmout_if(error);
5601 nfsm_assert(error, NFSTONMP(np), ENXIO);
5602nfsmout:
5603 nfsm_chain_cleanup(&nmreq);
5604 nfsm_chain_cleanup(&nmrep);
5605 vnode_put(NFSTOV(np));
5606 kauth_cred_unref(&cred);
0a7de745 5607 return error;
2d21ac55 5608}
cb323159 5609#endif /* CONFIG_NFS4 */
b0d623f7 5610int
2d21ac55
A
5611nfs_vfs_quotactl(mount_t mp, int cmds, uid_t uid, caddr_t datap, vfs_context_t ctx)
5612{
5613 struct nfsmount *nmp;
5614 int cmd, type, error, nfsvers;
6d2010ae 5615 uid_t euid = kauth_cred_getuid(vfs_context_ucred(ctx));
2d21ac55
A
5616 struct dqblk *dqb = (struct dqblk*)datap;
5617
fe8ab488 5618 nmp = VFSTONFS(mp);
0a7de745
A
5619 if (nfs_mount_gone(nmp)) {
5620 return ENXIO;
5621 }
2d21ac55
A
5622 nfsvers = nmp->nm_vers;
5623
0a7de745 5624 if (uid == ~0U) {
6d2010ae 5625 uid = euid;
0a7de745 5626 }
2d21ac55
A
5627
5628 /* we can only support Q_GETQUOTA */
5629 cmd = cmds >> SUBCMDSHIFT;
5630 switch (cmd) {
5631 case Q_GETQUOTA:
5632 break;
5633 case Q_QUOTAON:
5634 case Q_QUOTAOFF:
5635 case Q_SETQUOTA:
5636 case Q_SETUSE:
5637 case Q_SYNC:
5638 case Q_QUOTASTAT:
0a7de745 5639 return ENOTSUP;
2d21ac55 5640 default:
0a7de745 5641 return EINVAL;
2d21ac55
A
5642 }
5643
5644 type = cmds & SUBCMDMASK;
0a7de745
A
5645 if ((u_int)type >= MAXQUOTAS) {
5646 return EINVAL;
5647 }
5648 if ((uid != euid) && ((error = vfs_context_suser(ctx)))) {
5649 return error;
5650 }
2d21ac55 5651
0a7de745
A
5652 if (vfs_busy(mp, LK_NOWAIT)) {
5653 return 0;
5654 }
2d21ac55
A
5655 bzero(dqb, sizeof(*dqb));
5656 error = nmp->nm_funcs->nf_getquota(nmp, ctx, uid, type, dqb);
5657 vfs_unbusy(mp);
0a7de745 5658 return error;
2d21ac55
A
5659}
5660#endif
5661
1c79356b
A
5662/*
5663 * Flush out the buffer cache
5664 */
b0d623f7 5665int nfs_sync_callout(vnode_t, void *);
91447636
A
5666
5667struct nfs_sync_cargs {
0a7de745
A
5668 vfs_context_t ctx;
5669 int waitfor;
5670 int error;
91447636
A
5671};
5672
b0d623f7 5673int
91447636 5674nfs_sync_callout(vnode_t vp, void *arg)
1c79356b 5675{
91447636 5676 struct nfs_sync_cargs *cargs = (struct nfs_sync_cargs*)arg;
6d2010ae 5677 nfsnode_t np = VTONFS(vp);
91447636 5678 int error;
1c79356b 5679
6d2010ae
A
5680 if (np->n_flag & NREVOKE) {
5681 vn_revoke(vp, REVOKEALL, cargs->ctx);
0a7de745 5682 return VNODE_RETURNED;
6d2010ae
A
5683 }
5684
0a7de745
A
5685 if (LIST_EMPTY(&np->n_dirtyblkhd)) {
5686 return VNODE_RETURNED;
5687 }
5688 if (np->n_wrbusy > 0) {
5689 return VNODE_RETURNED;
5690 }
5691 if (np->n_bflag & (NBFLUSHINPROG | NBINVALINPROG)) {
5692 return VNODE_RETURNED;
5693 }
91447636 5694
6d2010ae 5695 error = nfs_flush(np, cargs->waitfor, vfs_context_thread(cargs->ctx), 0);
0a7de745 5696 if (error) {
91447636 5697 cargs->error = error;
0a7de745 5698 }
91447636 5699
0a7de745 5700 return VNODE_RETURNED;
91447636
A
5701}
5702
b0d623f7 5703int
2d21ac55 5704nfs_vfs_sync(mount_t mp, int waitfor, vfs_context_t ctx)
91447636
A
5705{
5706 struct nfs_sync_cargs cargs;
5707
5708 cargs.waitfor = waitfor;
6d2010ae 5709 cargs.ctx = ctx;
91447636
A
5710 cargs.error = 0;
5711
5712 vnode_iterate(mp, 0, nfs_sync_callout, &cargs);
5713
0a7de745 5714 return cargs.error;
1c79356b
A
5715}
5716
5717/*
5718 * NFS flat namespace lookup.
5719 * Currently unsupported.
5720 */
91447636 5721/*ARGSUSED*/
b0d623f7 5722int
2d21ac55 5723nfs_vfs_vget(
91447636
A
5724 __unused mount_t mp,
5725 __unused ino64_t ino,
5726 __unused vnode_t *vpp,
2d21ac55 5727 __unused vfs_context_t ctx)
1c79356b 5728{
0a7de745 5729 return ENOTSUP;
1c79356b
A
5730}
5731
5732/*
5733 * At this point, this should never happen
5734 */
91447636 5735/*ARGSUSED*/
b0d623f7 5736int
2d21ac55 5737nfs_vfs_fhtovp(
91447636
A
5738 __unused mount_t mp,
5739 __unused int fhlen,
5740 __unused unsigned char *fhp,
5741 __unused vnode_t *vpp,
2d21ac55 5742 __unused vfs_context_t ctx)
1c79356b 5743{
0a7de745 5744 return ENOTSUP;
1c79356b
A
5745}
5746
5747/*
5748 * Vnode pointer to File handle, should never happen either
5749 */
91447636 5750/*ARGSUSED*/
b0d623f7 5751int
2d21ac55 5752nfs_vfs_vptofh(
91447636
A
5753 __unused vnode_t vp,
5754 __unused int *fhlenp,
5755 __unused unsigned char *fhp,
2d21ac55 5756 __unused vfs_context_t ctx)
1c79356b 5757{
0a7de745 5758 return ENOTSUP;
1c79356b
A
5759}
5760
5761/*
5762 * Vfs start routine, a no-op.
5763 */
91447636 5764/*ARGSUSED*/
b0d623f7 5765int
2d21ac55 5766nfs_vfs_start(
91447636
A
5767 __unused mount_t mp,
5768 __unused int flags,
2d21ac55 5769 __unused vfs_context_t ctx)
1c79356b 5770{
0a7de745 5771 return 0;
1c79356b
A
5772}
5773
6d2010ae
A
5774/*
5775 * Build the mount info buffer for NFS_MOUNTINFO.
5776 */
5777int
5778nfs_mountinfo_assemble(struct nfsmount *nmp, struct xdrbuf *xb)
5779{
5780 struct xdrbuf xbinfo, xborig;
cb323159 5781 char sotype[16];
6d2010ae
A
5782 uint32_t origargsvers, origargslength;
5783 uint32_t infolength_offset, curargsopaquelength_offset, curargslength_offset, attrslength_offset, curargs_end_offset, end_offset;
5784 uint32_t miattrs[NFS_MIATTR_BITMAP_LEN];
5785 uint32_t miflags_mask[NFS_MIFLAG_BITMAP_LEN];
5786 uint32_t miflags[NFS_MIFLAG_BITMAP_LEN];
5787 uint32_t mattrs[NFS_MATTR_BITMAP_LEN];
5788 uint32_t mflags_mask[NFS_MFLAG_BITMAP_LEN];
5789 uint32_t mflags[NFS_MFLAG_BITMAP_LEN];
5790 uint32_t loc, serv, addr, comp;
5791 int i, timeo, error = 0;
5792
5793 /* set up mount info attr and flag bitmaps */
5794 NFS_BITMAP_ZERO(miattrs, NFS_MIATTR_BITMAP_LEN);
5795 NFS_BITMAP_SET(miattrs, NFS_MIATTR_FLAGS);
5796 NFS_BITMAP_SET(miattrs, NFS_MIATTR_ORIG_ARGS);
5797 NFS_BITMAP_SET(miattrs, NFS_MIATTR_CUR_ARGS);
5798 NFS_BITMAP_SET(miattrs, NFS_MIATTR_CUR_LOC_INDEX);
5799 NFS_BITMAP_ZERO(miflags_mask, NFS_MIFLAG_BITMAP_LEN);
5800 NFS_BITMAP_ZERO(miflags, NFS_MIFLAG_BITMAP_LEN);
5801 NFS_BITMAP_SET(miflags_mask, NFS_MIFLAG_DEAD);
5802 NFS_BITMAP_SET(miflags_mask, NFS_MIFLAG_NOTRESP);
5803 NFS_BITMAP_SET(miflags_mask, NFS_MIFLAG_RECOVERY);
0a7de745 5804 if (nmp->nm_state & NFSSTA_DEAD) {
6d2010ae 5805 NFS_BITMAP_SET(miflags, NFS_MIFLAG_DEAD);
0a7de745
A
5806 }
5807 if ((nmp->nm_state & (NFSSTA_TIMEO | NFSSTA_JUKEBOXTIMEO)) ||
5808 ((nmp->nm_state & NFSSTA_LOCKTIMEO) && (nmp->nm_lockmode == NFS_LOCK_MODE_ENABLED))) {
6d2010ae 5809 NFS_BITMAP_SET(miflags, NFS_MIFLAG_NOTRESP);
0a7de745
A
5810 }
5811 if (nmp->nm_state & NFSSTA_RECOVER) {
6d2010ae 5812 NFS_BITMAP_SET(miflags, NFS_MIFLAG_RECOVERY);
0a7de745 5813 }
6d2010ae
A
5814
5815 /* get original mount args length */
0a7de745 5816 xb_init_buffer(&xborig, nmp->nm_args, 2 * XDRWORD);
6d2010ae
A
5817 xb_get_32(error, &xborig, origargsvers); /* version */
5818 xb_get_32(error, &xborig, origargslength); /* args length */
5819 nfsmerr_if(error);
5820
5821 /* set up current mount attributes bitmap */
5822 NFS_BITMAP_ZERO(mattrs, NFS_MATTR_BITMAP_LEN);
5823 NFS_BITMAP_SET(mattrs, NFS_MATTR_FLAGS);
5824 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_VERSION);
cb323159 5825#if CONFIG_NFS4
0a7de745 5826 if (nmp->nm_vers >= NFS_VER4) {
6d2010ae 5827 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_MINOR_VERSION);
0a7de745 5828 }
cb323159 5829#endif
6d2010ae
A
5830 NFS_BITMAP_SET(mattrs, NFS_MATTR_READ_SIZE);
5831 NFS_BITMAP_SET(mattrs, NFS_MATTR_WRITE_SIZE);
5832 NFS_BITMAP_SET(mattrs, NFS_MATTR_READDIR_SIZE);
5833 NFS_BITMAP_SET(mattrs, NFS_MATTR_READAHEAD);
5834 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_REG_MIN);
5835 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_REG_MAX);
5836 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MIN);
5837 NFS_BITMAP_SET(mattrs, NFS_MATTR_ATTRCACHE_DIR_MAX);
5838 NFS_BITMAP_SET(mattrs, NFS_MATTR_LOCK_MODE);
5839 NFS_BITMAP_SET(mattrs, NFS_MATTR_SECURITY);
0a7de745 5840 if (nmp->nm_etype.selected < nmp->nm_etype.count) {
39037602 5841 NFS_BITMAP_SET(mattrs, NFS_MATTR_KERB_ETYPE);
0a7de745 5842 }
6d2010ae
A
5843 NFS_BITMAP_SET(mattrs, NFS_MATTR_MAX_GROUP_LIST);
5844 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOCKET_TYPE);
cb323159
A
5845 if (nmp->nm_saddr->sa_family != AF_LOCAL) {
5846 NFS_BITMAP_SET(mattrs, NFS_MATTR_NFS_PORT);
5847 }
5848 if ((nmp->nm_vers < NFS_VER4) && nmp->nm_mountport && !nmp->nm_mount_localport) {
6d2010ae 5849 NFS_BITMAP_SET(mattrs, NFS_MATTR_MOUNT_PORT);
0a7de745 5850 }
6d2010ae 5851 NFS_BITMAP_SET(mattrs, NFS_MATTR_REQUEST_TIMEOUT);
0a7de745 5852 if (NMFLAG(nmp, SOFT)) {
6d2010ae 5853 NFS_BITMAP_SET(mattrs, NFS_MATTR_SOFT_RETRY_COUNT);
0a7de745
A
5854 }
5855 if (nmp->nm_deadtimeout) {
6d2010ae 5856 NFS_BITMAP_SET(mattrs, NFS_MATTR_DEAD_TIMEOUT);
0a7de745
A
5857 }
5858 if (nmp->nm_fh) {
6d2010ae 5859 NFS_BITMAP_SET(mattrs, NFS_MATTR_FH);
0a7de745 5860 }
6d2010ae
A
5861 NFS_BITMAP_SET(mattrs, NFS_MATTR_FS_LOCATIONS);
5862 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFLAGS);
0a7de745 5863 if (origargsvers < NFS_ARGSVERSION_XDR) {
6d2010ae 5864 NFS_BITMAP_SET(mattrs, NFS_MATTR_MNTFROM);
0a7de745
A
5865 }
5866 if (nmp->nm_realm) {
39236c6e 5867 NFS_BITMAP_SET(mattrs, NFS_MATTR_REALM);
0a7de745
A
5868 }
5869 if (nmp->nm_principal) {
39236c6e 5870 NFS_BITMAP_SET(mattrs, NFS_MATTR_PRINCIPAL);
0a7de745
A
5871 }
5872 if (nmp->nm_sprinc) {
39236c6e 5873 NFS_BITMAP_SET(mattrs, NFS_MATTR_SVCPRINCIPAL);
0a7de745 5874 }
cb323159
A
5875 if (nmp->nm_nfs_localport) {
5876 NFS_BITMAP_SET(mattrs, NFS_MATTR_LOCAL_NFS_PORT);
5877 }
5878 if ((nmp->nm_vers < NFS_VER4) && nmp->nm_mount_localport) {
5879 NFS_BITMAP_SET(mattrs, NFS_MATTR_LOCAL_MOUNT_PORT);
5880 }
0a7de745 5881
6d2010ae
A
5882 /* set up current mount flags bitmap */
5883 /* first set the flags that we will be setting - either on OR off */
5884 NFS_BITMAP_ZERO(mflags_mask, NFS_MFLAG_BITMAP_LEN);
5885 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_SOFT);
5886 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_INTR);
5887 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RESVPORT);
0a7de745 5888 if (nmp->nm_sotype == SOCK_DGRAM) {
6d2010ae 5889 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOCONNECT);
0a7de745 5890 }
6d2010ae 5891 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_DUMBTIMER);
0a7de745 5892 if (nmp->nm_vers < NFS_VER4) {
6d2010ae 5893 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_CALLUMNT);
0a7de745
A
5894 }
5895 if (nmp->nm_vers >= NFS_VER3) {
6d2010ae 5896 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_RDIRPLUS);
0a7de745 5897 }
6d2010ae
A
5898 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NONEGNAMECACHE);
5899 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_MUTEJUKEBOX);
cb323159 5900#if CONFIG_NFS4
6d2010ae
A
5901 if (nmp->nm_vers >= NFS_VER4) {
5902 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_EPHEMERAL);
5903 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOCALLBACK);
5ba3f43e 5904 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NAMEDATTR);
6d2010ae
A
5905 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOACL);
5906 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_ACLONLY);
5907 }
cb323159 5908#endif
6d2010ae
A
5909 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NFC);
5910 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_NOQUOTA);
0a7de745 5911 if (nmp->nm_vers < NFS_VER4) {
6d2010ae 5912 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_MNTUDP);
0a7de745 5913 }
6d2010ae
A
5914 NFS_BITMAP_SET(mflags_mask, NFS_MFLAG_MNTQUICK);
5915 /* now set the flags that should be set */
5916 NFS_BITMAP_ZERO(mflags, NFS_MFLAG_BITMAP_LEN);
0a7de745 5917 if (NMFLAG(nmp, SOFT)) {
6d2010ae 5918 NFS_BITMAP_SET(mflags, NFS_MFLAG_SOFT);
0a7de745
A
5919 }
5920 if (NMFLAG(nmp, INTR)) {
6d2010ae 5921 NFS_BITMAP_SET(mflags, NFS_MFLAG_INTR);
0a7de745
A
5922 }
5923 if (NMFLAG(nmp, RESVPORT)) {
6d2010ae 5924 NFS_BITMAP_SET(mflags, NFS_MFLAG_RESVPORT);
0a7de745
A
5925 }
5926 if ((nmp->nm_sotype == SOCK_DGRAM) && NMFLAG(nmp, NOCONNECT)) {
6d2010ae 5927 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOCONNECT);
0a7de745
A
5928 }
5929 if (NMFLAG(nmp, DUMBTIMER)) {
6d2010ae 5930 NFS_BITMAP_SET(mflags, NFS_MFLAG_DUMBTIMER);
0a7de745
A
5931 }
5932 if ((nmp->nm_vers < NFS_VER4) && NMFLAG(nmp, CALLUMNT)) {
6d2010ae 5933 NFS_BITMAP_SET(mflags, NFS_MFLAG_CALLUMNT);
0a7de745
A
5934 }
5935 if ((nmp->nm_vers >= NFS_VER3) && NMFLAG(nmp, RDIRPLUS)) {
6d2010ae 5936 NFS_BITMAP_SET(mflags, NFS_MFLAG_RDIRPLUS);
0a7de745
A
5937 }
5938 if (NMFLAG(nmp, NONEGNAMECACHE)) {
6d2010ae 5939 NFS_BITMAP_SET(mflags, NFS_MFLAG_NONEGNAMECACHE);
0a7de745
A
5940 }
5941 if (NMFLAG(nmp, MUTEJUKEBOX)) {
6d2010ae 5942 NFS_BITMAP_SET(mflags, NFS_MFLAG_MUTEJUKEBOX);
0a7de745 5943 }
cb323159 5944#if CONFIG_NFS4
6d2010ae 5945 if (nmp->nm_vers >= NFS_VER4) {
0a7de745 5946 if (NMFLAG(nmp, EPHEMERAL)) {
6d2010ae 5947 NFS_BITMAP_SET(mflags, NFS_MFLAG_EPHEMERAL);
0a7de745
A
5948 }
5949 if (NMFLAG(nmp, NOCALLBACK)) {
6d2010ae 5950 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOCALLBACK);
0a7de745
A
5951 }
5952 if (NMFLAG(nmp, NAMEDATTR)) {
5ba3f43e 5953 NFS_BITMAP_SET(mflags, NFS_MFLAG_NAMEDATTR);
0a7de745
A
5954 }
5955 if (NMFLAG(nmp, NOACL)) {
6d2010ae 5956 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOACL);
0a7de745
A
5957 }
5958 if (NMFLAG(nmp, ACLONLY)) {
6d2010ae 5959 NFS_BITMAP_SET(mflags, NFS_MFLAG_ACLONLY);
0a7de745 5960 }
6d2010ae 5961 }
cb323159 5962#endif
0a7de745 5963 if (NMFLAG(nmp, NFC)) {
6d2010ae 5964 NFS_BITMAP_SET(mflags, NFS_MFLAG_NFC);
0a7de745 5965 }
6d2010ae
A
5966 if (NMFLAG(nmp, NOQUOTA) || ((nmp->nm_vers >= NFS_VER4) &&
5967 !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_HARD) &&
5968 !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_SOFT) &&
0a7de745 5969 !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_USED))) {
6d2010ae 5970 NFS_BITMAP_SET(mflags, NFS_MFLAG_NOQUOTA);
0a7de745
A
5971 }
5972 if ((nmp->nm_vers < NFS_VER4) && NMFLAG(nmp, MNTUDP)) {
6d2010ae 5973 NFS_BITMAP_SET(mflags, NFS_MFLAG_MNTUDP);
0a7de745
A
5974 }
5975 if (NMFLAG(nmp, MNTQUICK)) {
6d2010ae 5976 NFS_BITMAP_SET(mflags, NFS_MFLAG_MNTQUICK);
0a7de745 5977 }
6d2010ae
A
5978
5979 /* assemble info buffer: */
5980 xb_init_buffer(&xbinfo, NULL, 0);
5981 xb_add_32(error, &xbinfo, NFS_MOUNT_INFO_VERSION);
5982 infolength_offset = xb_offset(&xbinfo);
5983 xb_add_32(error, &xbinfo, 0);
5984 xb_add_bitmap(error, &xbinfo, miattrs, NFS_MIATTR_BITMAP_LEN);
5985 xb_add_bitmap(error, &xbinfo, miflags, NFS_MIFLAG_BITMAP_LEN);
5986 xb_add_32(error, &xbinfo, origargslength);
0a7de745 5987 if (!error) {
6d2010ae 5988 error = xb_add_bytes(&xbinfo, nmp->nm_args, origargslength, 0);
0a7de745 5989 }
6d2010ae
A
5990
5991 /* the opaque byte count for the current mount args values: */
5992 curargsopaquelength_offset = xb_offset(&xbinfo);
5993 xb_add_32(error, &xbinfo, 0);
5994
5995 /* Encode current mount args values */
5996 xb_add_32(error, &xbinfo, NFS_ARGSVERSION_XDR);
5997 curargslength_offset = xb_offset(&xbinfo);
5998 xb_add_32(error, &xbinfo, 0);
5999 xb_add_32(error, &xbinfo, NFS_XDRARGS_VERSION_0);
6000 xb_add_bitmap(error, &xbinfo, mattrs, NFS_MATTR_BITMAP_LEN);
6001 attrslength_offset = xb_offset(&xbinfo);
6002 xb_add_32(error, &xbinfo, 0);
6003 xb_add_bitmap(error, &xbinfo, mflags_mask, NFS_MFLAG_BITMAP_LEN);
6004 xb_add_bitmap(error, &xbinfo, mflags, NFS_MFLAG_BITMAP_LEN);
0a7de745 6005 xb_add_32(error, &xbinfo, nmp->nm_vers); /* NFS_VERSION */
cb323159 6006#if CONFIG_NFS4
0a7de745
A
6007 if (nmp->nm_vers >= NFS_VER4) {
6008 xb_add_32(error, &xbinfo, nmp->nm_minor_vers); /* NFS_MINOR_VERSION */
6009 }
cb323159 6010#endif
0a7de745
A
6011 xb_add_32(error, &xbinfo, nmp->nm_rsize); /* READ_SIZE */
6012 xb_add_32(error, &xbinfo, nmp->nm_wsize); /* WRITE_SIZE */
6013 xb_add_32(error, &xbinfo, nmp->nm_readdirsize); /* READDIR_SIZE */
6014 xb_add_32(error, &xbinfo, nmp->nm_readahead); /* READAHEAD */
6015 xb_add_32(error, &xbinfo, nmp->nm_acregmin); /* ATTRCACHE_REG_MIN */
6016 xb_add_32(error, &xbinfo, 0); /* ATTRCACHE_REG_MIN */
6017 xb_add_32(error, &xbinfo, nmp->nm_acregmax); /* ATTRCACHE_REG_MAX */
6018 xb_add_32(error, &xbinfo, 0); /* ATTRCACHE_REG_MAX */
6019 xb_add_32(error, &xbinfo, nmp->nm_acdirmin); /* ATTRCACHE_DIR_MIN */
6020 xb_add_32(error, &xbinfo, 0); /* ATTRCACHE_DIR_MIN */
6021 xb_add_32(error, &xbinfo, nmp->nm_acdirmax); /* ATTRCACHE_DIR_MAX */
6022 xb_add_32(error, &xbinfo, 0); /* ATTRCACHE_DIR_MAX */
6023 xb_add_32(error, &xbinfo, nmp->nm_lockmode); /* LOCK_MODE */
6d2010ae 6024 if (nmp->nm_sec.count) {
0a7de745 6025 xb_add_32(error, &xbinfo, nmp->nm_sec.count); /* SECURITY */
6d2010ae 6026 nfsmerr_if(error);
0a7de745 6027 for (i = 0; i < nmp->nm_sec.count; i++) {
6d2010ae 6028 xb_add_32(error, &xbinfo, nmp->nm_sec.flavors[i]);
0a7de745 6029 }
6d2010ae 6030 } else if (nmp->nm_servsec.count) {
0a7de745 6031 xb_add_32(error, &xbinfo, nmp->nm_servsec.count); /* SECURITY */
6d2010ae 6032 nfsmerr_if(error);
0a7de745 6033 for (i = 0; i < nmp->nm_servsec.count; i++) {
6d2010ae 6034 xb_add_32(error, &xbinfo, nmp->nm_servsec.flavors[i]);
0a7de745 6035 }
6d2010ae 6036 } else {
0a7de745 6037 xb_add_32(error, &xbinfo, 1); /* SECURITY */
6d2010ae
A
6038 xb_add_32(error, &xbinfo, nmp->nm_auth);
6039 }
39037602
A
6040 if (nmp->nm_etype.selected < nmp->nm_etype.count) {
6041 xb_add_32(error, &xbinfo, nmp->nm_etype.count);
6042 xb_add_32(error, &xbinfo, nmp->nm_etype.selected);
0a7de745 6043 for (uint32_t j = 0; j < nmp->nm_etype.count; j++) {
39037602 6044 xb_add_32(error, &xbinfo, nmp->nm_etype.etypes[j]);
0a7de745 6045 }
39037602
A
6046 nfsmerr_if(error);
6047 }
0a7de745 6048 xb_add_32(error, &xbinfo, nmp->nm_numgrps); /* MAX_GROUP_LIST */
6d2010ae 6049 nfsmerr_if(error);
cb323159
A
6050
6051 switch (nmp->nm_saddr->sa_family) {
6052 case AF_INET:
6053 case AF_INET6:
6054 snprintf(sotype, sizeof(sotype), "%s%s", (nmp->nm_sotype == SOCK_DGRAM) ? "udp" : "tcp",
6055 nmp->nm_sofamily ? (nmp->nm_sofamily == AF_INET) ? "4" : "6" : "");
6056 xb_add_string(error, &xbinfo, sotype, strlen(sotype)); /* SOCKET_TYPE */
6057 xb_add_32(error, &xbinfo, ntohs(((struct sockaddr_in*)nmp->nm_saddr)->sin_port)); /* NFS_PORT */
6058 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_MOUNT_PORT)) {
6059 xb_add_32(error, &xbinfo, nmp->nm_mountport); /* MOUNT_PORT */
6060 }
6061 break;
6062 case AF_LOCAL:
6063 strlcpy(sotype, (nmp->nm_sotype == SOCK_DGRAM) ? "ticlts" : "ticotsord", sizeof(sotype));
6064 xb_add_string(error, &xbinfo, sotype, strlen(sotype));
6065 break;
6066 default:
6067 NFS_VFS_DBG("Unsupported address family %d\n", nmp->nm_saddr->sa_family);
6068 printf("Unsupported address family %d\n", nmp->nm_saddr->sa_family);
6069 error = EINVAL;
6070 break;
0a7de745 6071 }
cb323159 6072
6d2010ae 6073 timeo = (nmp->nm_timeo * 10) / NFS_HZ;
0a7de745
A
6074 xb_add_32(error, &xbinfo, timeo / 10); /* REQUEST_TIMEOUT */
6075 xb_add_32(error, &xbinfo, (timeo % 10) * 100000000); /* REQUEST_TIMEOUT */
6076 if (NMFLAG(nmp, SOFT)) {
6077 xb_add_32(error, &xbinfo, nmp->nm_retry); /* SOFT_RETRY_COUNT */
6078 }
6d2010ae 6079 if (nmp->nm_deadtimeout) {
0a7de745
A
6080 xb_add_32(error, &xbinfo, nmp->nm_deadtimeout); /* DEAD_TIMEOUT */
6081 xb_add_32(error, &xbinfo, 0); /* DEAD_TIMEOUT */
6d2010ae 6082 }
0a7de745 6083 if (nmp->nm_fh) {
6d2010ae 6084 xb_add_fh(error, &xbinfo, &nmp->nm_fh->fh_data[0], nmp->nm_fh->fh_len); /* FH */
0a7de745
A
6085 }
6086 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_numlocs); /* FS_LOCATIONS */
6d2010ae
A
6087 for (loc = 0; !error && (loc < nmp->nm_locations.nl_numlocs); loc++) {
6088 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_servcount);
6089 for (serv = 0; !error && (serv < nmp->nm_locations.nl_locations[loc]->nl_servcount); serv++) {
6090 xb_add_string(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_name,
0a7de745 6091 strlen(nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_name));
6d2010ae 6092 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addrcount);
0a7de745 6093 for (addr = 0; !error && (addr < nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addrcount); addr++) {
6d2010ae 6094 xb_add_string(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addresses[addr],
0a7de745
A
6095 strlen(nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addresses[addr]));
6096 }
6d2010ae
A
6097 xb_add_32(error, &xbinfo, 0); /* empty server info */
6098 }
6099 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_path.np_compcount);
0a7de745 6100 for (comp = 0; !error && (comp < nmp->nm_locations.nl_locations[loc]->nl_path.np_compcount); comp++) {
6d2010ae 6101 xb_add_string(error, &xbinfo, nmp->nm_locations.nl_locations[loc]->nl_path.np_components[comp],
0a7de745
A
6102 strlen(nmp->nm_locations.nl_locations[loc]->nl_path.np_components[comp]));
6103 }
6d2010ae
A
6104 xb_add_32(error, &xbinfo, 0); /* empty fs location info */
6105 }
0a7de745
A
6106 xb_add_32(error, &xbinfo, vfs_flags(nmp->nm_mountp)); /* MNTFLAGS */
6107 if (origargsvers < NFS_ARGSVERSION_XDR) {
6d2010ae 6108 xb_add_string(error, &xbinfo, vfs_statfs(nmp->nm_mountp)->f_mntfromname,
0a7de745
A
6109 strlen(vfs_statfs(nmp->nm_mountp)->f_mntfromname)); /* MNTFROM */
6110 }
6111 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_REALM)) {
39236c6e 6112 xb_add_string(error, &xbinfo, nmp->nm_realm, strlen(nmp->nm_realm));
0a7de745
A
6113 }
6114 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_PRINCIPAL)) {
39236c6e 6115 xb_add_string(error, &xbinfo, nmp->nm_principal, strlen(nmp->nm_principal));
0a7de745
A
6116 }
6117 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_SVCPRINCIPAL)) {
39236c6e 6118 xb_add_string(error, &xbinfo, nmp->nm_sprinc, strlen(nmp->nm_sprinc));
0a7de745 6119 }
cb323159
A
6120 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_NFS_PORT)) {
6121 struct sockaddr_un *un = (struct sockaddr_un *)nmp->nm_saddr;
6122 xb_add_string(error, &xbinfo, un->sun_path, strlen(un->sun_path));
6123 }
6124 if (NFS_BITMAP_ISSET(mattrs, NFS_MATTR_LOCAL_MOUNT_PORT)) {
6125 xb_add_string(error, &xbinfo, nmp->nm_mount_localport, strlen(nmp->nm_mount_localport));
6126 }
6d2010ae
A
6127 curargs_end_offset = xb_offset(&xbinfo);
6128
6129 /* NFS_MIATTR_CUR_LOC_INDEX */
6130 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_current.nli_flags);
6131 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_current.nli_loc);
6132 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_current.nli_serv);
6133 xb_add_32(error, &xbinfo, nmp->nm_locations.nl_current.nli_addr);
6134
6135 xb_build_done(error, &xbinfo);
6136
6137 /* update opaque counts */
6138 end_offset = xb_offset(&xbinfo);
6139 if (!error) {
6140 error = xb_seek(&xbinfo, attrslength_offset);
0a7de745 6141 xb_add_32(error, &xbinfo, curargs_end_offset - attrslength_offset - XDRWORD /*don't include length field*/);
6d2010ae
A
6142 }
6143 if (!error) {
6144 error = xb_seek(&xbinfo, curargslength_offset);
0a7de745 6145 xb_add_32(error, &xbinfo, curargs_end_offset - curargslength_offset + XDRWORD /*version*/);
6d2010ae
A
6146 }
6147 if (!error) {
6148 error = xb_seek(&xbinfo, curargsopaquelength_offset);
0a7de745 6149 xb_add_32(error, &xbinfo, curargs_end_offset - curargslength_offset + XDRWORD /*version*/);
6d2010ae
A
6150 }
6151 if (!error) {
6152 error = xb_seek(&xbinfo, infolength_offset);
0a7de745 6153 xb_add_32(error, &xbinfo, end_offset - infolength_offset + XDRWORD /*version*/);
6d2010ae
A
6154 }
6155 nfsmerr_if(error);
6156
6157 /* copy result xdrbuf to caller */
6158 *xb = xbinfo;
6159
6160 /* and mark the local copy as not needing cleanup */
6161 xbinfo.xb_flags &= ~XB_CLEANUP;
6162nfsmerr:
6163 xb_cleanup(&xbinfo);
0a7de745 6164 return error;
6d2010ae
A
6165}
6166
1c79356b
A
6167/*
6168 * Do that sysctl thang...
6169 */
b0d623f7 6170int
2d21ac55 6171nfs_vfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
0a7de745 6172 user_addr_t newp, size_t newlen, vfs_context_t ctx)
1c79356b 6173{
fe8ab488 6174 int error = 0, val;
5ba3f43e 6175#ifndef CONFIG_EMBEDDED
fe8ab488 6176 int softnobrowse;
0a7de745 6177#endif
91447636 6178 struct sysctl_req *req = NULL;
b0d623f7 6179 union union_vfsidctl vc;
91447636
A
6180 mount_t mp;
6181 struct nfsmount *nmp = NULL;
55e303ae 6182 struct vfsquery vq;
fe8ab488 6183 struct nfsreq *rq;
91447636 6184 boolean_t is_64_bit;
6d2010ae
A
6185 fsid_t fsid;
6186 struct xdrbuf xb;
fe8ab488
A
6187 struct netfs_status *nsp = NULL;
6188 int timeoutmask;
cb323159 6189 uint totlen, count, numThreads;
ea3f0419 6190#if CONFIG_NFS_SERVER
cb323159 6191 uint pos;
2d21ac55
A
6192 struct nfs_exportfs *nxfs;
6193 struct nfs_export *nx;
6194 struct nfs_active_user_list *ulist;
527f9951 6195 struct nfs_export_stat_desc stat_desc = {};
2d21ac55
A
6196 struct nfs_export_stat_rec statrec;
6197 struct nfs_user_stat_node *unode, *unode_next;
527f9951 6198 struct nfs_user_stat_desc ustat_desc = {};
2d21ac55
A
6199 struct nfs_user_stat_user_rec ustat_rec;
6200 struct nfs_user_stat_path_rec upath_rec;
6201 uint bytes_avail, bytes_total, recs_copied;
fe8ab488 6202 uint numExports, numRecs;
ea3f0419 6203#endif /* CONFIG_NFS_SERVER */
1c79356b
A
6204
6205 /*
6206 * All names at this level are terminal.
6207 */
0a7de745
A
6208 if (namelen > 1) {
6209 return ENOTDIR; /* overloaded */
6210 }
2d21ac55 6211 is_64_bit = vfs_context_is64bit(ctx);
91447636 6212
55e303ae
A
6213 /* common code for "new style" VFS_CTL sysctl, get the mount. */
6214 switch (name[0]) {
6215 case VFS_CTL_TIMEO:
e5568f75 6216 case VFS_CTL_NOLOCKS:
fe8ab488 6217 case VFS_CTL_NSTATUS:
5ba3f43e 6218#ifndef CONFIG_EMBEDDED
fe8ab488 6219 case VFS_CTL_QUERY:
0a7de745 6220#endif
91447636 6221 req = CAST_DOWN(struct sysctl_req *, oldp);
fe8ab488
A
6222 if (req == NULL) {
6223 return EFAULT;
6224 }
b0d623f7 6225 error = SYSCTL_IN(req, &vc, is_64_bit? sizeof(vc.vc64):sizeof(vc.vc32));
0a7de745
A
6226 if (error) {
6227 return error;
6228 }
b0d623f7 6229 mp = vfs_getvfs(&vc.vc32.vc_fsid); /* works for 32 and 64 */
0a7de745
A
6230 if (mp == NULL) {
6231 return ENOENT;
6232 }
55e303ae 6233 nmp = VFSTONFS(mp);
0a7de745
A
6234 if (!nmp) {
6235 return ENOENT;
6236 }
55e303ae 6237 bzero(&vq, sizeof(vq));
91447636
A
6238 req->newidx = 0;
6239 if (is_64_bit) {
b0d623f7
A
6240 req->newptr = vc.vc64.vc_ptr;
6241 req->newlen = (size_t)vc.vc64.vc_len;
2d21ac55 6242 } else {
b0d623f7
A
6243 req->newptr = CAST_USER_ADDR_T(vc.vc32.vc_ptr);
6244 req->newlen = vc.vc32.vc_len;
91447636 6245 }
fe8ab488 6246 break;
5ba3f43e
A
6247#if CONFIG_EMBEDDED
6248 case VFS_CTL_QUERY:
6249 return EPERM;
6250#endif
55e303ae
A
6251 }
6252
0a7de745 6253 switch (name[0]) {
1c79356b 6254 case NFS_NFSSTATS:
2d21ac55 6255 if (!oldp) {
1c79356b 6256 *oldlenp = sizeof nfsstats;
0a7de745 6257 return 0;
1c79356b
A
6258 }
6259
2d21ac55 6260 if (*oldlenp < sizeof nfsstats) {
1c79356b 6261 *oldlenp = sizeof nfsstats;
0a7de745 6262 return ENOMEM;
1c79356b
A
6263 }
6264
55e303ae 6265 error = copyout(&nfsstats, oldp, sizeof nfsstats);
0a7de745
A
6266 if (error) {
6267 return error;
6268 }
1c79356b 6269
0a7de745
A
6270 if (newp && newlen != sizeof nfsstats) {
6271 return EINVAL;
6272 }
1c79356b 6273
0a7de745 6274 if (newp) {
1c79356b 6275 return copyin(newp, &nfsstats, sizeof nfsstats);
0a7de745
A
6276 }
6277 return 0;
6d2010ae
A
6278 case NFS_MOUNTINFO:
6279 /* read in the fsid */
0a7de745
A
6280 if (*oldlenp < sizeof(fsid)) {
6281 return EINVAL;
6282 }
6283 if ((error = copyin(oldp, &fsid, sizeof(fsid)))) {
6284 return error;
6285 }
6d2010ae
A
6286 /* swizzle it back to host order */
6287 fsid.val[0] = ntohl(fsid.val[0]);
6288 fsid.val[1] = ntohl(fsid.val[1]);
6289 /* find mount and make sure it's NFS */
0a7de745
A
6290 if (((mp = vfs_getvfs(&fsid))) == NULL) {
6291 return ENOENT;
6292 }
6293 if (strcmp(mp->mnt_vfsstat.f_fstypename, "nfs")) {
6294 return EINVAL;
6295 }
6296 if (((nmp = VFSTONFS(mp))) == NULL) {
6297 return ENOENT;
6298 }
cb323159 6299 xb_init(&xb, XDRBUF_NONE);
0a7de745
A
6300 if ((error = nfs_mountinfo_assemble(nmp, &xb))) {
6301 return error;
6302 }
6303 if (*oldlenp < xb.xb_u.xb_buffer.xbb_len) {
6d2010ae 6304 error = ENOMEM;
0a7de745 6305 } else {
6d2010ae 6306 error = copyout(xb_buffer_base(&xb), oldp, xb.xb_u.xb_buffer.xbb_len);
0a7de745 6307 }
6d2010ae
A
6308 *oldlenp = xb.xb_u.xb_buffer.xbb_len;
6309 xb_cleanup(&xb);
6310 break;
ea3f0419 6311#if CONFIG_NFS_SERVER
2d21ac55
A
6312 case NFS_EXPORTSTATS:
6313 /* setup export stat descriptor */
6314 stat_desc.rec_vers = NFS_EXPORT_STAT_REC_VERSION;
6315
6316 if (!nfsrv_is_initialized()) {
6317 stat_desc.rec_count = 0;
0a7de745 6318 if (oldp && (*oldlenp >= sizeof(struct nfs_export_stat_desc))) {
2d21ac55 6319 error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
0a7de745 6320 }
2d21ac55 6321 *oldlenp = sizeof(struct nfs_export_stat_desc);
0a7de745 6322 return error;
2d21ac55
A
6323 }
6324
6325 /* Count the number of exported directories */
6326 lck_rw_lock_shared(&nfsrv_export_rwlock);
6327 numExports = 0;
6328 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next)
0a7de745
A
6329 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next)
6330 numExports += 1;
2d21ac55
A
6331
6332 /* update stat descriptor's export record count */
6333 stat_desc.rec_count = numExports;
6334
6335 /* calculate total size of required buffer */
6336 totlen = sizeof(struct nfs_export_stat_desc) + (numExports * sizeof(struct nfs_export_stat_rec));
6337
6338 /* Check caller's buffer */
6339 if (oldp == 0) {
6340 lck_rw_done(&nfsrv_export_rwlock);
6341 /* indicate required buffer len */
6342 *oldlenp = totlen;
0a7de745 6343 return 0;
2d21ac55
A
6344 }
6345
6346 /* We require the caller's buffer to be at least large enough to hold the descriptor */
6347 if (*oldlenp < sizeof(struct nfs_export_stat_desc)) {
6348 lck_rw_done(&nfsrv_export_rwlock);
6349 /* indicate required buffer len */
6350 *oldlenp = totlen;
0a7de745 6351 return ENOMEM;
2d21ac55
A
6352 }
6353
6354 /* indicate required buffer len */
6355 *oldlenp = totlen;
6356
6357 /* check if export table is empty */
6358 if (!numExports) {
6359 lck_rw_done(&nfsrv_export_rwlock);
6360 error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
0a7de745 6361 return error;
2d21ac55
A
6362 }
6363
6364 /* calculate how many actual export stat records fit into caller's buffer */
6365 numRecs = (*oldlenp - sizeof(struct nfs_export_stat_desc)) / sizeof(struct nfs_export_stat_rec);
6366
6367 if (!numRecs) {
6368 /* caller's buffer can only accomodate descriptor */
6369 lck_rw_done(&nfsrv_export_rwlock);
6370 stat_desc.rec_count = 0;
6371 error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
0a7de745 6372 return error;
2d21ac55
A
6373 }
6374
6375 /* adjust to actual number of records to copyout to caller's buffer */
0a7de745 6376 if (numRecs > numExports) {
2d21ac55 6377 numRecs = numExports;
0a7de745 6378 }
2d21ac55
A
6379
6380 /* set actual number of records we are returning */
6381 stat_desc.rec_count = numRecs;
6382
6383 /* first copy out the stat descriptor */
6384 pos = 0;
6385 error = copyout(&stat_desc, oldp + pos, sizeof(struct nfs_export_stat_desc));
6386 if (error) {
6387 lck_rw_done(&nfsrv_export_rwlock);
0a7de745 6388 return error;
2d21ac55
A
6389 }
6390 pos += sizeof(struct nfs_export_stat_desc);
6391
6392 /* Loop through exported directories */
6393 count = 0;
6394 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
6395 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
0a7de745 6396 if (count >= numRecs) {
2d21ac55 6397 break;
0a7de745 6398 }
2d21ac55
A
6399
6400 /* build exported filesystem path */
5ba3f43e 6401 memset(statrec.path, 0, sizeof(statrec.path));
2d21ac55 6402 snprintf(statrec.path, sizeof(statrec.path), "%s%s%s",
0a7de745
A
6403 nxfs->nxfs_path, ((nxfs->nxfs_path[1] && nx->nx_path[0]) ? "/" : ""),
6404 nx->nx_path);
2d21ac55
A
6405
6406 /* build the 64-bit export stat counters */
6407 statrec.ops = ((uint64_t)nx->nx_stats.ops.hi << 32) |
0a7de745 6408 nx->nx_stats.ops.lo;
2d21ac55 6409 statrec.bytes_read = ((uint64_t)nx->nx_stats.bytes_read.hi << 32) |
0a7de745 6410 nx->nx_stats.bytes_read.lo;
2d21ac55 6411 statrec.bytes_written = ((uint64_t)nx->nx_stats.bytes_written.hi << 32) |
0a7de745 6412 nx->nx_stats.bytes_written.lo;
2d21ac55
A
6413 error = copyout(&statrec, oldp + pos, sizeof(statrec));
6414 if (error) {
6415 lck_rw_done(&nfsrv_export_rwlock);
0a7de745 6416 return error;
2d21ac55
A
6417 }
6418 /* advance buffer position */
6419 pos += sizeof(statrec);
6420 }
6421 }
6422 lck_rw_done(&nfsrv_export_rwlock);
6423 break;
6424 case NFS_USERSTATS:
6425 /* init structures used for copying out of kernel */
6426 ustat_desc.rec_vers = NFS_USER_STAT_REC_VERSION;
6427 ustat_rec.rec_type = NFS_USER_STAT_USER_REC;
6428 upath_rec.rec_type = NFS_USER_STAT_PATH_REC;
6429
6430 /* initialize counters */
6431 bytes_total = sizeof(struct nfs_user_stat_desc);
6432 bytes_avail = *oldlenp;
6433 recs_copied = 0;
6434
0a7de745 6435 if (!nfsrv_is_initialized()) { /* NFS server not initialized, so no stats */
2d21ac55 6436 goto ustat_skip;
0a7de745 6437 }
2d21ac55
A
6438
6439 /* reclaim old expired user nodes */
6440 nfsrv_active_user_list_reclaim();
6441
6442 /* reserve space for the buffer descriptor */
0a7de745 6443 if (bytes_avail >= sizeof(struct nfs_user_stat_desc)) {
2d21ac55 6444 bytes_avail -= sizeof(struct nfs_user_stat_desc);
0a7de745 6445 } else {
2d21ac55 6446 bytes_avail = 0;
0a7de745 6447 }
2d21ac55
A
6448
6449 /* put buffer position past the buffer descriptor */
6450 pos = sizeof(struct nfs_user_stat_desc);
6451
6452 /* Loop through exported directories */
6453 lck_rw_lock_shared(&nfsrv_export_rwlock);
6454 LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
6455 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
6456 /* copy out path */
6457 if (bytes_avail >= sizeof(struct nfs_user_stat_path_rec)) {
5ba3f43e 6458 memset(upath_rec.path, 0, sizeof(upath_rec.path));
2d21ac55
A
6459 snprintf(upath_rec.path, sizeof(upath_rec.path), "%s%s%s",
6460 nxfs->nxfs_path, ((nxfs->nxfs_path[1] && nx->nx_path[0]) ? "/" : ""),
6461 nx->nx_path);
6462
6463 error = copyout(&upath_rec, oldp + pos, sizeof(struct nfs_user_stat_path_rec));
6464 if (error) {
6465 /* punt */
6466 goto ustat_done;
6467 }
6468
6469 pos += sizeof(struct nfs_user_stat_path_rec);
6470 bytes_avail -= sizeof(struct nfs_user_stat_path_rec);
6471 recs_copied++;
0a7de745 6472 } else {
2d21ac55
A
6473 /* Caller's buffer is exhausted */
6474 bytes_avail = 0;
6475 }
6476
6477 bytes_total += sizeof(struct nfs_user_stat_path_rec);
6478
6479 /* Scan through all user nodes of this export */
6480 ulist = &nx->nx_user_list;
6481 lck_mtx_lock(&ulist->user_mutex);
6482 for (unode = TAILQ_FIRST(&ulist->user_lru); unode; unode = unode_next) {
6483 unode_next = TAILQ_NEXT(unode, lru_link);
6484
6485 /* copy out node if there is space */
6486 if (bytes_avail >= sizeof(struct nfs_user_stat_user_rec)) {
6487 /* prepare a user stat rec for copying out */
6488 ustat_rec.uid = unode->uid;
5ba3f43e 6489 memset(&ustat_rec.sock, 0, sizeof(ustat_rec.sock));
2d21ac55
A
6490 bcopy(&unode->sock, &ustat_rec.sock, unode->sock.ss_len);
6491 ustat_rec.ops = unode->ops;
6492 ustat_rec.bytes_read = unode->bytes_read;
6493 ustat_rec.bytes_written = unode->bytes_written;
6494 ustat_rec.tm_start = unode->tm_start;
6495 ustat_rec.tm_last = unode->tm_last;
6496
6497 error = copyout(&ustat_rec, oldp + pos, sizeof(struct nfs_user_stat_user_rec));
6498
6499 if (error) {
6500 /* punt */
6501 lck_mtx_unlock(&ulist->user_mutex);
6502 goto ustat_done;
6503 }
6504
6505 pos += sizeof(struct nfs_user_stat_user_rec);
6506 bytes_avail -= sizeof(struct nfs_user_stat_user_rec);
6507 recs_copied++;
0a7de745 6508 } else {
2d21ac55
A
6509 /* Caller's buffer is exhausted */
6510 bytes_avail = 0;
6511 }
6512 bytes_total += sizeof(struct nfs_user_stat_user_rec);
6513 }
6514 /* can unlock this export's list now */
6515 lck_mtx_unlock(&ulist->user_mutex);
6516 }
6517 }
6518
6519ustat_done:
6520 /* unlock the export table */
6521 lck_rw_done(&nfsrv_export_rwlock);
6522
6523ustat_skip:
6524 /* indicate number of actual records copied */
6525 ustat_desc.rec_count = recs_copied;
6526
6527 if (!error) {
6528 /* check if there was enough room for the buffer descriptor */
0a7de745 6529 if (*oldlenp >= sizeof(struct nfs_user_stat_desc)) {
2d21ac55 6530 error = copyout(&ustat_desc, oldp, sizeof(struct nfs_user_stat_desc));
0a7de745 6531 } else {
2d21ac55 6532 error = ENOMEM;
0a7de745 6533 }
2d21ac55
A
6534
6535 /* always indicate required buffer size */
6536 *oldlenp = bytes_total;
6537 }
6538 break;
6539 case NFS_USERCOUNT:
6540 if (!oldp) {
6541 *oldlenp = sizeof(nfsrv_user_stat_node_count);
0a7de745 6542 return 0;
2d21ac55
A
6543 }
6544
6545 if (*oldlenp < sizeof(nfsrv_user_stat_node_count)) {
6546 *oldlenp = sizeof(nfsrv_user_stat_node_count);
0a7de745 6547 return ENOMEM;
1c79356b 6548 }
2d21ac55
A
6549
6550 if (nfsrv_is_initialized()) {
6551 /* reclaim old expired user nodes */
6552 nfsrv_active_user_list_reclaim();
6553 }
6554
6555 error = copyout(&nfsrv_user_stat_node_count, oldp, sizeof(nfsrv_user_stat_node_count));
6556 break;
ea3f0419 6557#endif /* CONFIG_NFS_SERVER */
e5568f75 6558 case VFS_CTL_NOLOCKS:
0a7de745 6559 if (req->oldptr != USER_ADDR_NULL) {
2d21ac55 6560 lck_mtx_lock(&nmp->nm_lock);
6d2010ae 6561 val = (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED) ? 1 : 0;
2d21ac55 6562 lck_mtx_unlock(&nmp->nm_lock);
0a7de745
A
6563 error = SYSCTL_OUT(req, &val, sizeof(val));
6564 if (error) {
6565 return error;
6566 }
6567 }
6568 if (req->newptr != USER_ADDR_NULL) {
6569 error = SYSCTL_IN(req, &val, sizeof(val));
6570 if (error) {
6571 return error;
6572 }
2d21ac55 6573 lck_mtx_lock(&nmp->nm_lock);
6d2010ae 6574 if (nmp->nm_lockmode == NFS_LOCK_MODE_LOCAL) {
2d21ac55
A
6575 /* can't toggle locks when using local locks */
6576 error = EINVAL;
cb323159 6577#if CONFIG_NFS4
6d2010ae
A
6578 } else if ((nmp->nm_vers >= NFS_VER4) && val) {
6579 /* can't disable locks for NFSv4 */
6580 error = EINVAL;
cb323159 6581#endif
2d21ac55 6582 } else if (val) {
0a7de745 6583 if ((nmp->nm_vers <= NFS_VER3) && (nmp->nm_lockmode == NFS_LOCK_MODE_ENABLED)) {
6d2010ae 6584 nfs_lockd_mount_unregister(nmp);
0a7de745 6585 }
6d2010ae 6586 nmp->nm_lockmode = NFS_LOCK_MODE_DISABLED;
2d21ac55
A
6587 nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
6588 } else {
0a7de745 6589 if ((nmp->nm_vers <= NFS_VER3) && (nmp->nm_lockmode == NFS_LOCK_MODE_DISABLED)) {
6d2010ae 6590 nfs_lockd_mount_register(nmp);
0a7de745 6591 }
6d2010ae 6592 nmp->nm_lockmode = NFS_LOCK_MODE_ENABLED;
2d21ac55
A
6593 }
6594 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 6595 }
e5568f75 6596 break;
5ba3f43e 6597#ifndef CONFIG_EMBEDDED
55e303ae 6598 case VFS_CTL_QUERY:
2d21ac55 6599 lck_mtx_lock(&nmp->nm_lock);
cf7d32b8 6600 /* XXX don't allow users to know about/disconnect unresponsive, soft, nobrowse mounts */
6d2010ae 6601 softnobrowse = (NMFLAG(nmp, SOFT) && (vfs_flags(nmp->nm_mountp) & MNT_DONTBROWSE));
0a7de745 6602 if (!softnobrowse && (nmp->nm_state & NFSSTA_TIMEO)) {
b0d623f7 6603 vq.vq_flags |= VQ_NOTRESP;
0a7de745
A
6604 }
6605 if (!softnobrowse && (nmp->nm_state & NFSSTA_JUKEBOXTIMEO) && !NMFLAG(nmp, MUTEJUKEBOX)) {
55e303ae 6606 vq.vq_flags |= VQ_NOTRESP;
0a7de745 6607 }
b0d623f7 6608 if (!softnobrowse && (nmp->nm_state & NFSSTA_LOCKTIMEO) &&
0a7de745 6609 (nmp->nm_lockmode == NFS_LOCK_MODE_ENABLED)) {
2d21ac55 6610 vq.vq_flags |= VQ_NOTRESP;
0a7de745
A
6611 }
6612 if (nmp->nm_state & NFSSTA_DEAD) {
b0d623f7 6613 vq.vq_flags |= VQ_DEAD;
0a7de745 6614 }
2d21ac55 6615 lck_mtx_unlock(&nmp->nm_lock);
55e303ae
A
6616 error = SYSCTL_OUT(req, &vq, sizeof(vq));
6617 break;
5ba3f43e 6618#endif
0a7de745
A
6619 case VFS_CTL_TIMEO:
6620 if (req->oldptr != USER_ADDR_NULL) {
2d21ac55
A
6621 lck_mtx_lock(&nmp->nm_lock);
6622 val = nmp->nm_tprintf_initial_delay;
6623 lck_mtx_unlock(&nmp->nm_lock);
0a7de745
A
6624 error = SYSCTL_OUT(req, &val, sizeof(val));
6625 if (error) {
6626 return error;
6627 }
6628 }
6629 if (req->newptr != USER_ADDR_NULL) {
6630 error = SYSCTL_IN(req, &val, sizeof(val));
6631 if (error) {
6632 return error;
6633 }
2d21ac55 6634 lck_mtx_lock(&nmp->nm_lock);
0a7de745
A
6635 if (val < 0) {
6636 nmp->nm_tprintf_initial_delay = 0;
6637 } else {
2d21ac55 6638 nmp->nm_tprintf_initial_delay = val;
0a7de745 6639 }
2d21ac55 6640 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 6641 }
55e303ae 6642 break;
fe8ab488
A
6643 case VFS_CTL_NSTATUS:
6644 /*
6645 * Return the status of this mount. This is much more
6646 * information than VFS_CTL_QUERY. In addition to the
6647 * vq_flags return the significant mount options along
6648 * with the list of threads blocked on the mount and
6649 * how long the threads have been waiting.
6650 */
6651
6652 lck_mtx_lock(nfs_request_mutex);
6653 lck_mtx_lock(&nmp->nm_lock);
6654
6655 /*
6656 * Count the number of requests waiting for a reply.
6657 * Note: there could be multiple requests from the same thread.
6658 */
6659 numThreads = 0;
6660 TAILQ_FOREACH(rq, &nfs_reqq, r_chain) {
0a7de745 6661 if (rq->r_nmp == nmp) {
fe8ab488 6662 numThreads++;
0a7de745 6663 }
fe8ab488
A
6664 }
6665
6666 /* Calculate total size of result buffer */
6667 totlen = sizeof(struct netfs_status) + (numThreads * sizeof(uint64_t));
6668
0a7de745 6669 if (req->oldptr == USER_ADDR_NULL) { // Caller is querying buffer size
fe8ab488
A
6670 lck_mtx_unlock(&nmp->nm_lock);
6671 lck_mtx_unlock(nfs_request_mutex);
6672 return SYSCTL_OUT(req, NULL, totlen);
6673 }
0a7de745 6674 if (req->oldlen < totlen) { // Check if caller's buffer is big enough
fe8ab488
A
6675 lck_mtx_unlock(&nmp->nm_lock);
6676 lck_mtx_unlock(nfs_request_mutex);
0a7de745 6677 return ERANGE;
fe8ab488
A
6678 }
6679
0a7de745 6680 MALLOC(nsp, struct netfs_status *, totlen, M_TEMP, M_WAITOK | M_ZERO);
fe8ab488
A
6681 if (nsp == NULL) {
6682 lck_mtx_unlock(&nmp->nm_lock);
6683 lck_mtx_unlock(nfs_request_mutex);
0a7de745 6684 return ENOMEM;
fe8ab488
A
6685 }
6686 timeoutmask = NFSSTA_TIMEO | NFSSTA_LOCKTIMEO | NFSSTA_JUKEBOXTIMEO;
0a7de745 6687 if (nmp->nm_state & timeoutmask) {
fe8ab488 6688 nsp->ns_status |= VQ_NOTRESP;
0a7de745
A
6689 }
6690 if (nmp->nm_state & NFSSTA_DEAD) {
fe8ab488 6691 nsp->ns_status |= VQ_DEAD;
0a7de745 6692 }
fe8ab488
A
6693
6694 (void) nfs_mountopts(nmp, nsp->ns_mountopts, sizeof(nsp->ns_mountopts));
6695 nsp->ns_threadcount = numThreads;
0a7de745 6696
fe8ab488
A
6697 /*
6698 * Get the thread ids of threads waiting for a reply
6699 * and find the longest wait time.
6700 */
6701 if (numThreads > 0) {
6702 struct timeval now;
6703 time_t sendtime;
6704
6705 microuptime(&now);
6706 count = 0;
6707 sendtime = now.tv_sec;
6708 TAILQ_FOREACH(rq, &nfs_reqq, r_chain) {
6709 if (rq->r_nmp == nmp) {
0a7de745 6710 if (rq->r_start < sendtime) {
fe8ab488 6711 sendtime = rq->r_start;
0a7de745
A
6712 }
6713 // A thread_id of zero is used to represent an async I/O request.
fe8ab488 6714 nsp->ns_threadids[count] =
0a7de745
A
6715 rq->r_thread ? thread_tid(rq->r_thread) : 0;
6716 if (++count >= numThreads) {
fe8ab488 6717 break;
0a7de745 6718 }
fe8ab488
A
6719 }
6720 }
6721 nsp->ns_waittime = now.tv_sec - sendtime;
6722 }
6723
6724 lck_mtx_unlock(&nmp->nm_lock);
6725 lck_mtx_unlock(nfs_request_mutex);
6726
0a7de745 6727 error = SYSCTL_OUT(req, nsp, totlen);
fe8ab488
A
6728 FREE(nsp, M_TEMP);
6729 break;
1c79356b 6730 default:
0a7de745 6731 return ENOTSUP;
1c79356b 6732 }
0a7de745 6733 return error;
1c79356b 6734}
ea3f0419
A
6735
6736#endif /* CONFIG_NFS_CLIENT */