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