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