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