xref: /xnu-10063.121.3/bsd/nfs/nfs_serv.c (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
1 /*
2  * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
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
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30  * Copyright (c) 1989, 1993
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_serv.c	8.7 (Berkeley) 5/14/95
65  * FreeBSD-Id: nfs_serv.c,v 1.52 1997/10/28 15:59:05 bde Exp $
66  */
67 
68 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70 
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/proc.h>
74 #include <sys/kauth.h>
75 #include <sys/unistd.h>
76 #include <sys/malloc.h>
77 #include <sys/vnode.h>
78 #include <sys/mount_internal.h>
79 #include <sys/socket.h>
80 #include <sys/socketvar.h>
81 #include <sys/kpi_mbuf.h>
82 #include <sys/dirent.h>
83 #include <sys/stat.h>
84 #include <sys/kernel.h>
85 #include <sys/ubc.h>
86 #include <sys/vnode_internal.h>
87 #include <sys/uio_internal.h>
88 #include <libkern/OSAtomic.h>
89 #include <IOKit/IOLib.h>
90 #include <sys/fsevents.h>
91 #include <kern/thread_call.h>
92 #include <sys/time.h>
93 
94 #include <sys/vm.h>
95 #include <sys/vmparam.h>
96 
97 #include <sys/fcntl.h>
98 
99 #include <netinet/in.h>
100 
101 #include <nfs/nfsproto.h>
102 #include <nfs/rpcv2.h>
103 #include <nfs/nfs.h>
104 #include <nfs/xdr_subs.h>
105 #include <nfs/nfsm_subs.h>
106 #include <nfs/nfsrvcache.h>
107 #include <nfs/nfs_gss.h>
108 
109 #if CONFIG_MACF
110 #include <security/mac.h>
111 #include <security/mac_framework.h>
112 #endif
113 
114 /*
115  * NFS server globals
116  */
117 
118 int nfsd_thread_count = 0;
119 int nfsd_thread_max = 0;
120 static LCK_GRP_DECLARE(nfsd_lck_grp, "nfsd");
121 LCK_MTX_DECLARE(nfsd_mutex, &nfsd_lck_grp);
122 struct nfsd_head nfsd_head, nfsd_queue;
123 
124 LCK_GRP_DECLARE(nfsrv_slp_rwlock_group, "nfsrv-slp-rwlock");
125 LCK_GRP_DECLARE(nfsrv_slp_mutex_group, "nfsrv-slp-mutex");
126 struct nfsrv_sockhead nfsrv_socklist, nfsrv_sockwg,
127     nfsrv_sockwait, nfsrv_sockwork;
128 struct nfsrv_sock *nfsrv_udpsock = NULL;
129 struct nfsrv_sock *nfsrv_udp6sock = NULL;
130 
131 /* NFS exports */
132 struct nfsrv_expfs_list nfsrv_exports;
133 struct nfsrv_export_hashhead *nfsrv_export_hashtbl = NULL;
134 int nfsrv_export_hash_size = NFSRVEXPHASHSZ;
135 u_long nfsrv_export_hash;
136 static LCK_GRP_DECLARE(nfsrv_export_rwlock_group, "nfsrv-export-rwlock");
137 LCK_RW_DECLARE(nfsrv_export_rwlock, &nfsrv_export_rwlock_group);
138 
139 #if CONFIG_FSE
140 /* NFS server file modification event generator */
141 struct nfsrv_fmod_hashhead *nfsrv_fmod_hashtbl;
142 u_long nfsrv_fmod_hash;
143 static LCK_GRP_DECLARE(nfsrv_fmod_grp, "nfsrv_fmod");
144 LCK_MTX_DECLARE(nfsrv_fmod_mutex, &nfsrv_fmod_grp);
145 static int nfsrv_fmod_timer_on = 0;
146 int nfsrv_fsevents_enabled = 1;
147 #endif
148 
149 /* NFS server timers */
150 #if CONFIG_FSE
151 thread_call_t   nfsrv_fmod_timer_call;
152 #endif
153 thread_call_t   nfsrv_idlesock_timer_call;
154 thread_call_t   nfsrv_wg_timer_call;
155 int nfsrv_wg_timer_on;
156 
157 /* globals for the active user list */
158 uint32_t nfsrv_user_stat_enabled = 1;
159 uint32_t nfsrv_user_stat_node_count = 0;
160 uint32_t nfsrv_user_stat_max_idle_sec = NFSRV_USER_STAT_DEF_IDLE_SEC;
161 uint32_t nfsrv_user_stat_max_nodes = NFSRV_USER_STAT_DEF_MAX_NODES;
162 LCK_GRP_DECLARE(nfsrv_active_user_mutex_group, "nfs-active-user-mutex");
163 
164 int nfsrv_wg_delay = NFSRV_WGATHERDELAY * 1000;
165 int nfsrv_wg_delay_v3 = 0;
166 
167 int nfsrv_async = 0;
168 
169 int nfsrv_authorize(vnode_t, vnode_t, kauth_action_t, vfs_context_t, struct nfs_export_options*, int);
170 int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
171 void nfsrv_modified(vnode_t, vfs_context_t);
172 
173 extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path);
174 
175 /*
176  * Initialize the data structures for the server.
177  */
178 
179 #define NFSRV_NOT_INITIALIZED   0
180 #define NFSRV_INITIALIZING      1
181 #define NFSRV_INITIALIZED       2
182 static volatile UInt32 nfsrv_initted = NFSRV_NOT_INITIALIZED;
183 
184 int
nfsrv_is_initialized(void)185 nfsrv_is_initialized(void)
186 {
187 	return nfsrv_initted == NFSRV_INITIALIZED;
188 }
189 
190 void
nfsrv_init(void)191 nfsrv_init(void)
192 {
193 	/* make sure we init only once */
194 	if (!OSCompareAndSwap(NFSRV_NOT_INITIALIZED, NFSRV_INITIALIZING, &nfsrv_initted)) {
195 		/* wait until initialization is complete */
196 		while (!nfsrv_is_initialized()) {
197 			IOSleep(500);
198 		}
199 		return;
200 	}
201 
202 	if (sizeof(struct nfsrv_sock) > NFS_SVCALLOC) {
203 		printf("struct nfsrv_sock bloated (> %dbytes)\n", NFS_SVCALLOC);
204 	}
205 
206 	/* init export data structures */
207 	LIST_INIT(&nfsrv_exports);
208 
209 #if CONFIG_FSE
210 	/* init NFS server file modified event generation */
211 	nfsrv_fmod_hashtbl = hashinit(NFSRVFMODHASHSZ, M_TEMP, &nfsrv_fmod_hash);
212 #endif
213 
214 	nfs_gss_svc_init();                 /* Init RPCSEC_GSS security */
215 
216 	/* initialize NFS server timer callouts */
217 #if CONFIG_FSE
218 	nfsrv_fmod_timer_call = thread_call_allocate(nfsrv_fmod_timer, NULL);
219 #endif
220 	nfsrv_idlesock_timer_call = thread_call_allocate(nfsrv_idlesock_timer, NULL);
221 	nfsrv_wg_timer_call = thread_call_allocate(nfsrv_wg_timer, NULL);
222 
223 	/* Init server data structures */
224 	TAILQ_INIT(&nfsrv_socklist);
225 	TAILQ_INIT(&nfsrv_sockwait);
226 	TAILQ_INIT(&nfsrv_sockwork);
227 	TAILQ_INIT(&nfsrv_sockwg);
228 	TAILQ_INIT(&nfsd_head);
229 	TAILQ_INIT(&nfsd_queue);
230 	nfsrv_udpsock = NULL;
231 	nfsrv_udp6sock = NULL;
232 
233 	/* Setup the up-call handling */
234 	nfsrv_uc_init();
235 
236 	/* initialization complete */
237 	nfsrv_initted = NFSRV_INITIALIZED;
238 }
239 
240 
241 /*
242  *
243  * NFS version 2 and 3 server request processing functions
244  *
245  * These functions take the following parameters:
246  *
247  *      struct nfsrv_descript *nd - the NFS request descriptor
248  *      struct nfsrv_sock *slp    - the NFS socket the request came in on
249  *      vfs_context_t ctx         - VFS context
250  *      mbuf_t *mrepp             - pointer to hold the reply mbuf list
251  *
252  * These routines generally have 3 phases:
253  *
254  *   1 - break down and validate the RPC request in the mbuf chain
255  *       provided in nd->nd_nmreq.
256  *   2 - perform the vnode operations for the request
257  *       (many are very similar to syscalls in vfs_syscalls.c and
258  *       should therefore be kept in sync with those implementations)
259  *   3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
260  *
261  */
262 
263 /*
264  * nfs v3 access service
265  */
266 int
nfsrv_access(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)267 nfsrv_access(
268 	struct nfsrv_descript *nd,
269 	struct nfsrv_sock *slp,
270 	vfs_context_t ctx,
271 	mbuf_t *mrepp)
272 {
273 	struct nfsm_chain *nmreq, nmrep;
274 	vnode_t vp;
275 	int error, attrerr;
276 	struct vnode_attr vattr;
277 	struct nfs_filehandle nfh;
278 	u_int32_t nfsmode;
279 	kauth_action_t testaction;
280 	struct nfs_export *nx;
281 	struct nfs_export_options *nxo;
282 
283 	error = 0;
284 	attrerr = ENOENT;
285 	nfsmode = 0;
286 	nmreq = &nd->nd_nmreq;
287 	nfsm_chain_null(&nmrep);
288 	*mrepp = NULL;
289 	vp = NULL;
290 
291 	nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
292 	nfsm_chain_get_32(error, nmreq, nfsmode);
293 	nfsmerr_if(error);
294 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
295 	nfsmerr_if(error);
296 
297 	/* update export stats */
298 	NFSStatAdd64(&nx->nx_stats.ops, 1);
299 
300 	/* update active user stats */
301 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
302 
303 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
304 	nfsmerr_if(error);
305 
306 	/*
307 	 * Each NFS mode bit is tested separately.
308 	 *
309 	 * XXX this code is nominally correct, but returns a pessimistic
310 	 *     rather than optimistic result.  It will be necessary to add
311 	 *     an NFS-specific interface to the vnode_authorize code to
312 	 *     obtain good performance in the optimistic mode.
313 	 */
314 	if (nfsmode & NFS_ACCESS_READ) {
315 		testaction = vnode_isdir(vp) ? KAUTH_VNODE_LIST_DIRECTORY : KAUTH_VNODE_READ_DATA;
316 		if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
317 			nfsmode &= ~NFS_ACCESS_READ;
318 		}
319 	}
320 	if ((nfsmode & NFS_ACCESS_LOOKUP) &&
321 	    (!vnode_isdir(vp) ||
322 	    nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx, nxo, 0))) {
323 		nfsmode &= ~NFS_ACCESS_LOOKUP;
324 	}
325 	if (nfsmode & NFS_ACCESS_MODIFY) {
326 		if (vnode_isdir(vp)) {
327 			testaction =
328 			    KAUTH_VNODE_ADD_FILE |
329 			    KAUTH_VNODE_ADD_SUBDIRECTORY |
330 			    KAUTH_VNODE_DELETE_CHILD;
331 		} else {
332 			testaction =
333 			    KAUTH_VNODE_WRITE_DATA;
334 		}
335 		if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
336 			nfsmode &= ~NFS_ACCESS_MODIFY;
337 		}
338 	}
339 	if (nfsmode & NFS_ACCESS_EXTEND) {
340 		if (vnode_isdir(vp)) {
341 			testaction =
342 			    KAUTH_VNODE_ADD_FILE |
343 			    KAUTH_VNODE_ADD_SUBDIRECTORY;
344 		} else {
345 			testaction =
346 			    KAUTH_VNODE_WRITE_DATA |
347 			    KAUTH_VNODE_APPEND_DATA;
348 		}
349 		if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
350 			nfsmode &= ~NFS_ACCESS_EXTEND;
351 		}
352 	}
353 
354 	/*
355 	 * Note concerning NFS_ACCESS_DELETE:
356 	 * For hard links, the answer may be wrong if the vnode
357 	 * has multiple parents with different permissions.
358 	 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
359 	 * interpret the missing/cleared DELETE bit.
360 	 * So we'll just leave the DELETE bit alone.  At worst,
361 	 * we're telling the client it might be able to do
362 	 * something it really can't.
363 	 */
364 
365 	if ((nfsmode & NFS_ACCESS_EXECUTE) &&
366 	    (vnode_isdir(vp) ||
367 	    nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 0))) {
368 		nfsmode &= ~NFS_ACCESS_EXECUTE;
369 	}
370 
371 	/* get postop attributes */
372 	nfsm_srv_vattr_init(&vattr, NFS_VER3);
373 	attrerr = vnode_getattr(vp, &vattr, ctx);
374 
375 nfsmerr:
376 	/* assemble reply */
377 	nd->nd_repstat = error;
378 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(NFS_VER3) + NFSX_UNSIGNED);
379 	nfsmout_if(error);
380 	*mrepp = nmrep.nmc_mhead;
381 	nfsmout_on_status(nd, error);
382 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr);
383 	if (!nd->nd_repstat) {
384 		nfsm_chain_add_32(error, &nmrep, nfsmode);
385 	}
386 nfsmout:
387 	nfsm_chain_build_done(error, &nmrep);
388 	if (vp) {
389 		vnode_put(vp);
390 	}
391 	if (error) {
392 		nfsm_chain_cleanup(&nmrep);
393 		*mrepp = NULL;
394 	}
395 	return error;
396 }
397 
398 /*
399  * nfs getattr service
400  */
401 int
nfsrv_getattr(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)402 nfsrv_getattr(
403 	struct nfsrv_descript *nd,
404 	struct nfsrv_sock *slp,
405 	vfs_context_t ctx,
406 	mbuf_t *mrepp)
407 {
408 	struct nfsm_chain *nmreq, nmrep;
409 	struct vnode_attr vattr;
410 	vnode_t vp;
411 	int error;
412 	struct nfs_filehandle nfh;
413 	struct nfs_export *nx;
414 	struct nfs_export_options *nxo;
415 
416 	error = 0;
417 	nmreq = &nd->nd_nmreq;
418 	nfsm_chain_null(&nmrep);
419 	*mrepp = NULL;
420 	vp = NULL;
421 
422 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
423 	nfsmerr_if(error);
424 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
425 	nfsmerr_if(error);
426 
427 	/* update export stats */
428 	NFSStatAdd64(&nx->nx_stats.ops, 1);
429 
430 	/* update active user stats */
431 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
432 
433 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
434 	nfsmerr_if(error);
435 
436 #if CONFIG_MACF
437 	if (mac_vnode_check_open(ctx, vp, FREAD)) {
438 		error = ESTALE;
439 	}
440 	nfsmerr_if(error);
441 #endif
442 
443 	nfsm_srv_vattr_init(&vattr, nd->nd_vers);
444 	error = vnode_getattr(vp, &vattr, ctx);
445 
446 #if CONFIG_MACF
447 	/* XXXab: Comment in the VFS code makes it sound like
448 	 *        some arguments can be filtered out, but not
449 	 *        what it actually means. Hopefully not like
450 	 *        they gonna set mtime to 0 or something. For
451 	 *        now trust there are no shenanigans here.
452 	 */
453 	error = mac_vnode_check_getattr(ctx, NOCRED, vp, &vattr);
454 	nfsmerr_if(error);
455 #endif
456 
457 	vnode_put(vp);
458 	vp = NULL;
459 
460 nfsmerr:
461 	/* assemble reply */
462 	nd->nd_repstat = error;
463 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_FATTR(nd->nd_vers));
464 	nfsmout_if(error);
465 	*mrepp = nmrep.nmc_mhead;
466 	nfsmout_if(nd->nd_repstat);
467 	error = nfsm_chain_add_fattr(nd, &nmrep, &vattr);
468 nfsmout:
469 	nfsm_chain_build_done(error, &nmrep);
470 	if (vp) {
471 		vnode_put(vp);
472 	}
473 	if (error) {
474 		nfsm_chain_cleanup(&nmrep);
475 		*mrepp = NULL;
476 	}
477 	return error;
478 }
479 
480 /*
481  * nfs setattr service
482  */
483 int
nfsrv_setattr(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)484 nfsrv_setattr(
485 	struct nfsrv_descript *nd,
486 	struct nfsrv_sock *slp,
487 	vfs_context_t ctx,
488 	mbuf_t *mrepp)
489 {
490 	struct nfsm_chain *nmreq, nmrep;
491 	struct vnode_attr preattr, postattr;
492 	struct vnode_attr vattr, *vap = &vattr;
493 	vnode_t vp;
494 	struct nfs_export *nx;
495 	struct nfs_export_options *nxo;
496 	int error, preattrerr, postattrerr, gcheck;
497 	struct nfs_filehandle nfh;
498 	struct timespec guard = { .tv_sec = 0, .tv_nsec = 0 };
499 	kauth_action_t action;
500 	uid_t saved_uid;
501 
502 	error = 0;
503 	preattrerr = postattrerr = ENOENT;
504 	gcheck = 0;
505 	nmreq = &nd->nd_nmreq;
506 	nfsm_chain_null(&nmrep);
507 	*mrepp = NULL;
508 	vp = NULL;
509 
510 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
511 	nfsmerr_if(error);
512 
513 	VATTR_INIT(vap);
514 	error = nfsm_chain_get_sattr(nd, nmreq, vap);
515 	if (nd->nd_vers == NFS_VER3) {
516 		nfsm_chain_get_32(error, nmreq, gcheck);
517 		if (gcheck) {
518 			nfsm_chain_get_time(error, nmreq, nd->nd_vers, guard.tv_sec, guard.tv_nsec);
519 		}
520 	}
521 	nfsmerr_if(error);
522 
523 	/*
524 	 * Save the original credential UID in case they are
525 	 * mapped and we need to map the IDs in the attributes.
526 	 */
527 	saved_uid = kauth_cred_getuid(nd->nd_cr);
528 
529 	/*
530 	 * Now that we have all the fields, lets do it.
531 	 */
532 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
533 	nfsmerr_if(error);
534 
535 	/* update export stats */
536 	NFSStatAdd64(&nx->nx_stats.ops, 1);
537 
538 	/* update active user stats */
539 	nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
540 
541 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
542 	nfsmerr_if(error);
543 
544 	if (nd->nd_vers == NFS_VER3) {
545 		nfsm_srv_pre_vattr_init(&preattr);
546 		error = preattrerr = vnode_getattr(vp, &preattr, ctx);
547 		if (!error && gcheck && VATTR_IS_SUPPORTED(&preattr, va_change_time) &&
548 		    (preattr.va_change_time.tv_sec != guard.tv_sec ||
549 		    preattr.va_change_time.tv_nsec != guard.tv_nsec)) {
550 			error = NFSERR_NOT_SYNC;
551 		}
552 		if (!preattrerr && !VATTR_ALL_SUPPORTED(&preattr)) {
553 			preattrerr = ENOENT;
554 		}
555 		nfsmerr_if(error);
556 	}
557 
558 	/*
559 	 * If the credentials were mapped, we should
560 	 * map the same values in the attributes.
561 	 */
562 	if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
563 		int ismember;
564 		VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
565 		if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember) {
566 			VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
567 		}
568 	}
569 
570 	/* Authorize the attribute changes.  */
571 	error = vnode_authattr(vp, vap, &action, ctx);
572 	if ((error == EACCES) && (nxo->nxo_flags & NX_READONLY)) {
573 		switch (vnode_vtype(vp)) {
574 		case VREG: case VDIR: case VLNK: case VCPLX:
575 			error = EROFS;
576 			break;
577 		default:
578 			break;
579 		}
580 	}
581 
582 	if (!error) {
583 		error = nfsrv_authorize(vp, NULL, action, ctx, nxo, 0);
584 	}
585 
586 #if CONFIG_MACF
587 	if (!error && mac_vnode_check_open(ctx, vp, FREAD | FWRITE)) {
588 		error = ESTALE;
589 	}
590 
591 	if (!error) {
592 		/* chown case */
593 		if (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid)) {
594 			error = mac_vnode_check_setowner(ctx, vp,
595 			    VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : -1,
596 			    VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : -1);
597 		}
598 		/* chmod case */
599 		if (!error && VATTR_IS_ACTIVE(vap, va_mode)) {
600 			error = mac_vnode_check_setmode(ctx, vp, (mode_t)vap->va_mode);
601 		}
602 		/* truncate case */
603 		if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
604 			/* NOTE: File has not been open for NFS case, so NOCRED for filecred */
605 			error = mac_vnode_check_truncate(ctx, NOCRED, vp);
606 		}
607 		/* set utimes case */
608 		if (!error && (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time))) {
609 			struct timespec current_time;
610 			nanotime(&current_time);
611 
612 			error = mac_vnode_check_setutimes(ctx, vp,
613 			    VATTR_IS_ACTIVE(vap, va_access_time) ? vap->va_access_time : current_time,
614 			    VATTR_IS_ACTIVE(vap, va_modify_time) ? vap->va_modify_time : current_time);
615 		}
616 	}
617 #endif
618 	/* set the new attributes */
619 	if (!error) {
620 		error = vnode_setattr(vp, vap, ctx);
621 	}
622 
623 	if (!error || (nd->nd_vers == NFS_VER3)) {
624 		nfsm_srv_vattr_init(&postattr, nd->nd_vers);
625 		postattrerr = vnode_getattr(vp, &postattr, ctx);
626 		if (!error) {
627 			error = postattrerr;
628 		}
629 	}
630 
631 nfsmerr:
632 	if (vp) {
633 		vnode_put(vp);
634 	}
635 
636 	/* assemble reply */
637 	nd->nd_repstat = error;
638 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCORFATTR(nd->nd_vers));
639 	nfsmout_if(error);
640 	*mrepp = nmrep.nmc_mhead;
641 	nfsmout_on_status(nd, error);
642 	if (nd->nd_vers == NFS_VER3) {
643 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
644 		    preattrerr, &preattr, postattrerr, &postattr);
645 	} else {
646 		error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
647 	}
648 nfsmout:
649 	nfsm_chain_build_done(error, &nmrep);
650 	if (error) {
651 		nfsm_chain_cleanup(&nmrep);
652 		*mrepp = NULL;
653 	}
654 	return error;
655 }
656 
657 /*
658  * nfs lookup rpc
659  */
660 int
nfsrv_lookup(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)661 nfsrv_lookup(
662 	struct nfsrv_descript *nd,
663 	struct nfsrv_sock *slp,
664 	vfs_context_t ctx,
665 	mbuf_t *mrepp)
666 {
667 	struct nameidata ni;
668 	vnode_t vp, dirp = NULL;
669 	struct nfs_filehandle dnfh, nfh = {};
670 	struct nfs_export *nx = NULL;
671 	struct nfs_export_options *nxo;
672 	int error, attrerr, dirattrerr, isdotdot;
673 	uint32_t len = 0;
674 	uid_t saved_uid;
675 	struct vnode_attr va, dirattr, *vap = &va;
676 	struct nfsm_chain *nmreq, nmrep;
677 
678 	error = 0;
679 	attrerr = dirattrerr = ENOENT;
680 	nmreq = &nd->nd_nmreq;
681 	nfsm_chain_null(&nmrep);
682 	saved_uid = kauth_cred_getuid(nd->nd_cr);
683 
684 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
685 	nfsm_chain_get_32(error, nmreq, len);
686 	nfsm_name_len_check(error, nd, len);
687 	nfsmerr_if(error);
688 
689 	NDINIT(&ni, LOOKUP, OP_LOOKUP, LOCKLEAF | CN_FIRMLINK_NOFOLLOW, UIO_SYSSPACE, 0, ctx);
690 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
691 	isdotdot = ((len == 2) && (ni.ni_cnd.cn_pnbuf[0] == '.') && (ni.ni_cnd.cn_pnbuf[1] == '.'));
692 	if (!error) {
693 		error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
694 		if (nx != NULL) {
695 			/* update export stats */
696 			NFSStatAdd64(&nx->nx_stats.ops, 1);
697 
698 			/* update active user stats */
699 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
700 		}
701 		if (!error && mac_vnode_check_open(ctx, ni.ni_vp, FREAD)) {
702 			error = EACCES;
703 			if (dirp) {
704 				vnode_put(dirp);
705 				dirp = NULL;
706 			}
707 
708 			if (ni.ni_vp) {
709 				vnode_put(ni.ni_vp);
710 				ni.ni_vp = NULL;
711 			}
712 		}
713 	}
714 
715 	if (dirp) {
716 		if (nd->nd_vers == NFS_VER3) {
717 			nfsm_srv_vattr_init(&dirattr, NFS_VER3);
718 			dirattrerr = vnode_getattr(dirp, &dirattr, ctx);
719 		}
720 		vnode_put(dirp);
721 	}
722 	nfsmerr_if(error);
723 
724 	nameidone(&ni);
725 
726 	vp = ni.ni_vp;
727 	error = nfsrv_vptofh(nx, nd->nd_vers, (isdotdot ? &dnfh : NULL), vp, ctx, &nfh);
728 	if (!error) {
729 		nfsm_srv_vattr_init(vap, nd->nd_vers);
730 		attrerr = vnode_getattr(vp, vap, ctx);
731 	}
732 	vnode_put(vp);
733 
734 nfsmerr:
735 	/* assemble reply */
736 	nd->nd_repstat = error;
737 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
738 	    NFSX_POSTOPORFATTR(nd->nd_vers) + NFSX_POSTOPATTR(nd->nd_vers));
739 	nfsmout_if(error);
740 	*mrepp = nmrep.nmc_mhead;
741 	if (nd->nd_repstat) {
742 		if (nd->nd_vers == NFS_VER3) {
743 			nfsm_chain_add_postop_attr(error, nd, &nmrep, dirattrerr, &dirattr);
744 		}
745 		goto nfsmout;
746 	}
747 	nfsm_chain_add_fh(error, &nmrep, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
748 	if (nd->nd_vers == NFS_VER3) {
749 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap);
750 		nfsm_chain_add_postop_attr(error, nd, &nmrep, dirattrerr, &dirattr);
751 	} else if (!error) {
752 		error = nfsm_chain_add_fattr(nd, &nmrep, vap);
753 	}
754 nfsmout:
755 	nfsm_chain_build_done(error, &nmrep);
756 	if (error) {
757 		nfsm_chain_cleanup(&nmrep);
758 		*mrepp = NULL;
759 	}
760 	return error;
761 }
762 
763 /*
764  * nfs readlink service
765  */
766 int
nfsrv_readlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)767 nfsrv_readlink(
768 	struct nfsrv_descript *nd,
769 	struct nfsrv_sock *slp,
770 	vfs_context_t ctx,
771 	mbuf_t *mrepp)
772 {
773 	int error, mpcnt, tlen, len, attrerr;
774 	vnode_t vp;
775 	struct vnode_attr vattr;
776 	struct nfs_filehandle nfh;
777 	struct nfs_export *nx;
778 	struct nfs_export_options *nxo;
779 	struct nfsm_chain *nmreq, nmrep;
780 	mbuf_t mpath, mp;
781 	uio_t auio = NULL;
782 	UIO_STACKBUF(uio_buf, 4);
783 	void *uio_bufp = &uio_buf[0];
784 	int uio_buflen = sizeof(uio_buf);
785 
786 	error = 0;
787 	attrerr = ENOENT;
788 	nmreq = &nd->nd_nmreq;
789 	nfsm_chain_null(&nmrep);
790 	mpath = NULL;
791 	vp = NULL;
792 	len = NFS_MAXPATHLEN;
793 
794 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
795 	nfsmerr_if(error);
796 
797 	/* get mbuf list to hold symlink path */
798 	error = nfsm_mbuf_get_list(len, &mpath, &mpcnt);
799 	nfsmerr_if(error);
800 	if (mpcnt > 4) {
801 		uio_buflen = UIO_SIZEOF(mpcnt);
802 		uio_bufp = kalloc_data(uio_buflen, Z_WAITOK);
803 		if (!uio_bufp) {
804 			error = ENOMEM;
805 		}
806 		nfsmerr_if(error);
807 	}
808 	auio = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
809 	if (!auio) {
810 		error = ENOMEM;
811 	}
812 	nfsmerr_if(error);
813 
814 	for (mp = mpath; mp; mp = mbuf_next(mp)) {
815 		uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
816 	}
817 
818 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
819 	nfsmerr_if(error);
820 
821 	/* update export stats */
822 	NFSStatAdd64(&nx->nx_stats.ops, 1);
823 
824 	/* update active user stats */
825 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
826 
827 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
828 	nfsmerr_if(error);
829 
830 	if (vnode_vtype(vp) != VLNK) {
831 		if (nd->nd_vers == NFS_VER3) {
832 			error = EINVAL;
833 		} else {
834 			error = ENXIO;
835 		}
836 	}
837 
838 	if (!error) {
839 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 0);
840 	}
841 #if CONFIG_MACF
842 	if (mac_vnode_check_open(ctx, vp, FREAD)) {
843 		error = ESTALE;
844 	}
845 	nfsmerr_if(error);
846 	if (!error) {
847 		error = mac_vnode_check_readlink(ctx, vp);
848 	}
849 #endif
850 	if (!error) {
851 		error = VNOP_READLINK(vp, auio, ctx);
852 	}
853 	if (vp) {
854 		if (nd->nd_vers == NFS_VER3) {
855 			nfsm_srv_vattr_init(&vattr, NFS_VER3);
856 			attrerr = vnode_getattr(vp, &vattr, ctx);
857 		}
858 		vnode_put(vp);
859 		vp = NULL;
860 	}
861 	if (error) {
862 		mbuf_freem(mpath);
863 		mpath = NULL;
864 	}
865 
866 nfsmerr:
867 	/* assemble reply */
868 	nd->nd_repstat = error;
869 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_UNSIGNED);
870 	nfsmout_if(error);
871 	*mrepp = nmrep.nmc_mhead;
872 	nfsmout_on_status(nd, error);
873 	if (nd->nd_vers == NFS_VER3) {
874 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr);
875 	}
876 	if (error || nd->nd_repstat) {
877 		nfsm_chain_build_done(error, &nmrep);
878 		goto nfsmout;
879 	}
880 	if (auio && (uio_resid(auio) > 0)) {
881 		len -= uio_resid(auio);
882 		tlen = nfsm_rndup(len);
883 		nfsm_adj(mpath, NFS_MAXPATHLEN - tlen, tlen - len);
884 	}
885 	nfsm_chain_add_32(error, &nmrep, len);
886 	nfsm_chain_build_done(error, &nmrep);
887 	nfsmout_if(error);
888 	error = mbuf_setnext(nmrep.nmc_mcur, mpath);
889 	if (!error) {
890 		mpath = NULL;
891 	}
892 nfsmout:
893 	if (vp) {
894 		vnode_put(vp);
895 	}
896 	if (mpath) {
897 		mbuf_freem(mpath);
898 	}
899 	if (uio_bufp != &uio_buf[0]) {
900 		kfree_data(uio_bufp, uio_buflen);
901 	}
902 	if (error) {
903 		nfsm_chain_cleanup(&nmrep);
904 		*mrepp = NULL;
905 	}
906 	return error;
907 }
908 
909 /*
910  * nfs read service
911  */
912 int
nfsrv_read(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)913 nfsrv_read(
914 	struct nfsrv_descript *nd,
915 	struct nfsrv_sock *slp,
916 	vfs_context_t ctx,
917 	mbuf_t *mrepp)
918 {
919 	int error, attrerr, mreadcnt;
920 	uint32_t reqlen, maxlen, count, len, tlen;
921 	mbuf_t mread, m;
922 	vnode_t vp;
923 	struct nfs_filehandle nfh;
924 	struct nfs_export *nx = NULL;
925 	struct nfs_export_options *nxo;
926 	uio_t auio = NULL;
927 	struct vnode_attr vattr, *vap = &vattr;
928 	off_t off;
929 	uid_t saved_uid;
930 	UIO_STACKBUF(uio_buf, 0);
931 	struct nfsm_chain *nmreq, nmrep;
932 
933 	error = 0;
934 	count = 0;
935 	attrerr = ENOENT;
936 	nmreq = &nd->nd_nmreq;
937 	nfsm_chain_null(&nmrep);
938 	mread = NULL;
939 	vp = NULL;
940 	len = reqlen = 0;
941 	saved_uid = kauth_cred_getuid(nd->nd_cr);
942 
943 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
944 	nfsmerr_if(error);
945 	if (nd->nd_vers == NFS_VER3) {
946 		nfsm_chain_get_64(error, nmreq, off);
947 	} else {
948 		nfsm_chain_get_32(error, nmreq, off);
949 	}
950 	nfsm_chain_get_32(error, nmreq, reqlen);
951 	maxlen = NFSRV_NDMAXDATA(nd);
952 	if (reqlen > maxlen) {
953 		reqlen = maxlen;
954 	}
955 	nfsmerr_if(error);
956 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
957 	nfsmerr_if(error);
958 
959 	/* update export stats */
960 	NFSStatAdd64(&nx->nx_stats.ops, 1);
961 
962 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
963 	nfsmerr_if(error);
964 
965 	if (vnode_vtype(vp) != VREG) {
966 		if (nd->nd_vers == NFS_VER3) {
967 			error = EINVAL;
968 		} else {
969 			error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
970 		}
971 	}
972 
973 	if (!error) {
974 		if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 1))) {
975 			error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 1);
976 		}
977 	}
978 #if CONFIG_MACF
979 	if (!error) {
980 		error = mac_vnode_check_open(ctx, vp, FREAD);
981 		if (error) {
982 			error = EACCES;
983 		} else {
984 			/* XXXab: Do we need to do this?! */
985 			error = mac_vnode_check_read(ctx, vfs_context_ucred(ctx), vp);
986 			if (error) {
987 				error = EACCES;
988 			}
989 			/* mac_vnode_check_exec() can't be done here. */
990 		}
991 	}
992 	nfsmerr_if(error);
993 #endif
994 	nfsm_srv_vattr_init(vap, nd->nd_vers);
995 	attrerr = vnode_getattr(vp, vap, ctx);
996 	if (!error) {
997 		error = attrerr;
998 	}
999 	nfsmerr_if(error);
1000 
1001 	if ((u_quad_t)off >= vap->va_data_size) {
1002 		count = 0;
1003 	} else if (((u_quad_t)off + reqlen) > vap->va_data_size) {
1004 		count = (int)nfsm_rndup(vap->va_data_size - off);
1005 	} else {
1006 		count = reqlen;
1007 	}
1008 
1009 	len = count;
1010 	if (count > 0) {
1011 		/* get mbuf list to hold read data */
1012 		error = nfsm_mbuf_get_list(count, &mread, &mreadcnt);
1013 		nfsmerr_if(error);
1014 
1015 		auio = uio_create(mreadcnt, off, UIO_SYSSPACE, UIO_READ);
1016 		if (!auio) {
1017 			error = ENOMEM;
1018 			goto errorexit;
1019 		}
1020 		for (m = mread; m; m = mbuf_next(m)) {
1021 			uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
1022 		}
1023 		error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx);
1024 	} else {
1025 		auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
1026 		if (!auio) {
1027 			error = ENOMEM;
1028 			goto errorexit;
1029 		}
1030 	}
1031 
1032 errorexit:
1033 	if (!error || (nd->nd_vers == NFS_VER3)) {
1034 		nfsm_srv_vattr_init(vap, nd->nd_vers);
1035 		attrerr = vnode_getattr(vp, vap, ctx);
1036 		if (!error && (nd->nd_vers == NFS_VER2)) {
1037 			error = attrerr; /* NFSv2 must have attributes to return */
1038 		}
1039 	}
1040 	nfsmerr_if(error);
1041 
1042 	vnode_put(vp);
1043 	vp = NULL;
1044 
1045 	/* trim off any data not actually read */
1046 	len -= uio_resid(auio);
1047 	tlen = nfsm_rndup(len);
1048 	if (count != tlen || tlen != len) {
1049 		nfsm_adj(mread, count - tlen, tlen - len);
1050 	}
1051 
1052 nfsmerr:
1053 	/* assemble reply */
1054 	nd->nd_repstat = error;
1055 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPORFATTR(nd->nd_vers) + 3 * NFSX_UNSIGNED);
1056 	nfsmout_if(error);
1057 	*mrepp = nmrep.nmc_mhead;
1058 	nfsmout_on_status(nd, error);
1059 	if (nd->nd_vers == NFS_VER3) {
1060 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap);
1061 	}
1062 	if (error || nd->nd_repstat) {
1063 		nfsm_chain_build_done(error, &nmrep);
1064 		goto nfsmout;
1065 	}
1066 	if (nd->nd_vers == NFS_VER3) {
1067 		nfsm_chain_add_32(error, &nmrep, len);
1068 		nfsm_chain_add_32(error, &nmrep, (off + len >= vap->va_data_size) ? TRUE : FALSE);
1069 	} else {
1070 		error = nfsm_chain_add_fattr(nd, &nmrep, vap);
1071 	}
1072 	nfsm_chain_add_32(error, &nmrep, len);
1073 	nfsm_chain_build_done(error, &nmrep);
1074 	nfsmout_if(error);
1075 	error = mbuf_setnext(nmrep.nmc_mcur, mread);
1076 	if (!error) {
1077 		mread = NULL;
1078 	}
1079 
1080 	/* update export stats */
1081 	NFSStatAdd64(&nx->nx_stats.bytes_read, len);
1082 
1083 	/* update active user stats */
1084 	nfsrv_update_user_stat(nx, nd, saved_uid, 1, len, 0);
1085 nfsmout:
1086 	if (vp) {
1087 		vnode_put(vp);
1088 	}
1089 	if (mread) {
1090 		mbuf_freem(mread);
1091 	}
1092 	if (count > 0 && auio != NULL) {
1093 		uio_free(auio);
1094 	}
1095 	if (error) {
1096 		nfsm_chain_cleanup(&nmrep);
1097 		*mrepp = NULL;
1098 	}
1099 	return error;
1100 }
1101 
1102 #if CONFIG_FSE
1103 /*
1104  * NFS File modification reporting
1105  *
1106  * When the contents of a file are changed, a "content modified"
1107  * fsevent needs to be issued.  Normally this would be done at
1108  * file close time.  This is difficult for NFS because the protocol
1109  * has no "close" operation.  The client sends a stream of write
1110  * requests that just stop.  So we keep a hash table full of
1111  * vnodes that have been written to recently, and issue a
1112  * "content modified" fsevent only if there are no writes to
1113  * a vnode for nfsrv_fmod_pendtime milliseconds.
1114  */
1115 int nfsrv_fmod_pending;         /* count of vnodes being written to */
1116 int nfsrv_fmod_pendtime = 1000; /* msec to wait */
1117 int nfsrv_fmod_min_interval = 100;      /* msec min interval between callbacks */
1118 
1119 /*
1120  * This function is called via the kernel's callout
1121  * mechanism.  Calls are made only when there are
1122  * vnodes pending a fsevent creation, and no more
1123  * frequently than every nfsrv_fmod_min_interval ms.
1124  */
1125 void
nfsrv_fmod_timer(__unused void * param0,__unused void * param1)1126 nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
1127 {
1128 	struct nfsrv_fmod_hashhead *headp, firehead;
1129 	struct nfsrv_fmod *fp, *nfp, *pfp;
1130 	uint64_t timenow, next_deadline;
1131 	time_t interval = 0;
1132 	int i, fmod_fire;
1133 
1134 	LIST_INIT(&firehead);
1135 	lck_mtx_lock(&nfsrv_fmod_mutex);
1136 again:
1137 	clock_get_uptime(&timenow);
1138 	clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000,
1139 	    &next_deadline);
1140 
1141 	/*
1142 	 * Scan all the hash chains
1143 	 */
1144 	fmod_fire = 0;
1145 	for (i = 0; i < NFSRVFMODHASHSZ; i++) {
1146 		/*
1147 		 * For each hash chain, look for an entry
1148 		 * that has exceeded the deadline.
1149 		 */
1150 		headp = &nfsrv_fmod_hashtbl[i];
1151 		LIST_FOREACH(fp, headp, fm_link) {
1152 			if (timenow >= fp->fm_deadline) {
1153 				break;
1154 			}
1155 			if (fp->fm_deadline < next_deadline) {
1156 				next_deadline = fp->fm_deadline;
1157 			}
1158 		}
1159 
1160 		/*
1161 		 * If we have an entry that's exceeded the
1162 		 * deadline, then the same is true for all
1163 		 * following entries in the chain, since they're
1164 		 * sorted in time order.
1165 		 */
1166 		pfp = NULL;
1167 		while (fp) {
1168 			/* move each entry to the fire list */
1169 			nfp = LIST_NEXT(fp, fm_link);
1170 			LIST_REMOVE(fp, fm_link);
1171 			fmod_fire++;
1172 			if (pfp) {
1173 				LIST_INSERT_AFTER(pfp, fp, fm_link);
1174 			} else {
1175 				LIST_INSERT_HEAD(&firehead, fp, fm_link);
1176 			}
1177 			pfp = fp;
1178 			fp = nfp;
1179 		}
1180 	}
1181 
1182 	if (fmod_fire) {
1183 		lck_mtx_unlock(&nfsrv_fmod_mutex);
1184 		/*
1185 		 * Fire off the content modified fsevent for each
1186 		 * entry and free it.
1187 		 */
1188 		LIST_FOREACH_SAFE(fp, &firehead, fm_link, nfp) {
1189 			if (nfsrv_fsevents_enabled) {
1190 				fp->fm_context.vc_thread = current_thread();
1191 				add_fsevent(FSE_CONTENT_MODIFIED, &fp->fm_context,
1192 				    FSE_ARG_VNODE, fp->fm_vp,
1193 				    FSE_ARG_DONE);
1194 			}
1195 			vnode_put(fp->fm_vp);
1196 			kauth_cred_unref(&fp->fm_context.vc_ucred);
1197 			LIST_REMOVE(fp, fm_link);
1198 			kfree_type(struct nfsrv_fmod, fp);
1199 		}
1200 		lck_mtx_lock(&nfsrv_fmod_mutex);
1201 		nfsrv_fmod_pending -= fmod_fire;
1202 		goto again;
1203 	}
1204 
1205 	/*
1206 	 * If there are still pending entries, set up another
1207 	 * callout to handle them later. Set the timeout deadline
1208 	 * so that the callout happens when the oldest pending
1209 	 * entry is ready to send its fsevent.
1210 	 */
1211 	if (nfsrv_fmod_pending > 0) {
1212 		interval = ((time_t)(next_deadline - timenow)) / (1000 * 1000);
1213 		if (interval < nfsrv_fmod_min_interval) {
1214 			interval = nfsrv_fmod_min_interval;
1215 		}
1216 	}
1217 
1218 	nfsrv_fmod_timer_on = interval > 0;
1219 	if (nfsrv_fmod_timer_on) {
1220 		nfs_interval_timer_start(nfsrv_fmod_timer_call, interval);
1221 	}
1222 
1223 	lck_mtx_unlock(&nfsrv_fmod_mutex);
1224 }
1225 
1226 /*
1227  * When a vnode has been written to, enter it in the hash
1228  * table of vnodes pending creation of an fsevent. If the
1229  * callout timer isn't already running, schedule a callback
1230  * for nfsrv_fmod_pendtime msec from now.
1231  */
1232 void
nfsrv_modified(vnode_t vp,vfs_context_t ctx)1233 nfsrv_modified(vnode_t vp, vfs_context_t ctx)
1234 {
1235 	uint64_t deadline;
1236 	struct nfsrv_fmod *fp;
1237 	struct nfsrv_fmod_hashhead *head;
1238 
1239 	lck_mtx_lock(&nfsrv_fmod_mutex);
1240 
1241 	/*
1242 	 * Compute the time in the future when the
1243 	 * content modified fsevent is to be issued.
1244 	 */
1245 	clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000, &deadline);
1246 
1247 	/*
1248 	 * Check if there's already a file content change fsevent
1249 	 * pending for this vnode.  If there is, update its
1250 	 * timestamp and make sure it's at the front of the hash chain.
1251 	 */
1252 	head = &nfsrv_fmod_hashtbl[NFSRVFMODHASH(vp)];
1253 	LIST_FOREACH(fp, head, fm_link) {
1254 		if (vp == fp->fm_vp) {
1255 			fp->fm_deadline = deadline;
1256 			if (fp != LIST_FIRST(head)) {
1257 				LIST_REMOVE(fp, fm_link);
1258 				LIST_INSERT_HEAD(head, fp, fm_link);
1259 			}
1260 			lck_mtx_unlock(&nfsrv_fmod_mutex);
1261 			return;
1262 		}
1263 	}
1264 
1265 	/*
1266 	 * First content change fsevent for this vnode.
1267 	 * Allocate a new file mod entry and add it
1268 	 * on the front of the hash chain.
1269 	 */
1270 	if (vnode_get(vp) != 0) {
1271 		goto done;
1272 	}
1273 	fp = kalloc_type(struct nfsrv_fmod, Z_WAITOK | Z_NOFAIL);
1274 	fp->fm_vp = vp;
1275 	kauth_cred_ref(vfs_context_ucred(ctx));
1276 	fp->fm_context = *ctx;
1277 	fp->fm_deadline = deadline;
1278 	LIST_INSERT_HEAD(head, fp, fm_link);
1279 
1280 	/*
1281 	 * If added to an empty hash table, then set the
1282 	 * callout timer to go off after nfsrv_fmod_pendtime.
1283 	 */
1284 	nfsrv_fmod_pending++;
1285 	if (!nfsrv_fmod_timer_on) {
1286 		nfsrv_fmod_timer_on = 1;
1287 		nfs_interval_timer_start(nfsrv_fmod_timer_call,
1288 		    nfsrv_fmod_pendtime);
1289 	}
1290 done:
1291 	lck_mtx_unlock(&nfsrv_fmod_mutex);
1292 	return;
1293 }
1294 #endif /* CONFIG_FSE */
1295 
1296 /*
1297  * nfs write service
1298  */
1299 int
nfsrv_write(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1300 nfsrv_write(
1301 	struct nfsrv_descript *nd,
1302 	struct nfsrv_sock *slp,
1303 	vfs_context_t ctx,
1304 	mbuf_t *mrepp)
1305 {
1306 	struct vnode_attr preattr, postattr;
1307 	int error, preattrerr, postattrerr;
1308 	int ioflags, len, retlen;
1309 	int mlen, mcount;
1310 	int stable = NFS_WRITE_FILESYNC;
1311 	mbuf_t m;
1312 	vnode_t vp;
1313 	struct nfs_filehandle nfh;
1314 	struct nfs_export *nx = NULL;
1315 	struct nfs_export_options *nxo;
1316 	uio_t auio = NULL;
1317 	off_t off;
1318 	uid_t saved_uid;
1319 	struct nfsm_chain *nmreq, nmrep;
1320 
1321 	if (nd->nd_nmreq.nmc_mhead == NULL) {
1322 		*mrepp = NULL;
1323 		return 0;
1324 	}
1325 
1326 	error = 0;
1327 	preattrerr = postattrerr = ENOENT;
1328 	saved_uid = kauth_cred_getuid(nd->nd_cr);
1329 	nmreq = &nd->nd_nmreq;
1330 	nfsm_chain_null(&nmrep);
1331 	vp = NULL;
1332 	len = retlen = 0;
1333 
1334 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
1335 	nfsmerr_if(error);
1336 	if (nd->nd_vers == NFS_VER3) {
1337 		nfsm_chain_get_64(error, nmreq, off);
1338 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1339 		nfsm_chain_get_32(error, nmreq, stable);
1340 	} else {
1341 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1342 		nfsm_chain_get_32(error, nmreq, off);
1343 		nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1344 		if (nfsrv_async) {
1345 			stable = NFS_WRITE_UNSTABLE;
1346 		}
1347 	}
1348 	nfsm_chain_get_32(error, nmreq, len);
1349 	nfsmerr_if(error);
1350 	retlen = len;
1351 
1352 	/*
1353 	 * For NFS Version 2, it is not obvious what a write of zero length
1354 	 * should do, but I might as well be consistent with Version 3,
1355 	 * which is to return ok so long as there are no permission problems.
1356 	 */
1357 
1358 	if (len > 0) {
1359 		error = nfsm_chain_trim_data(nmreq, len, &mlen);
1360 		nfsmerr_if(error);
1361 	} else {
1362 		mlen = 0;
1363 	}
1364 	if ((len > NFSRV_MAXDATA) || (len < 0) || (mlen < len)) {
1365 		error = EIO;
1366 		goto nfsmerr;
1367 	}
1368 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
1369 	nfsmerr_if(error);
1370 
1371 	/* update export stats */
1372 	NFSStatAdd64(&nx->nx_stats.ops, 1);
1373 
1374 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
1375 	nfsmerr_if(error);
1376 
1377 	if (nd->nd_vers == NFS_VER3) {
1378 		nfsm_srv_pre_vattr_init(&preattr);
1379 		preattrerr = vnode_getattr(vp, &preattr, ctx);
1380 	}
1381 	if (vnode_vtype(vp) != VREG) {
1382 		if (nd->nd_vers == NFS_VER3) {
1383 			error = EINVAL;
1384 		} else {
1385 			error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1386 		}
1387 	}
1388 	if (!error) {
1389 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1);
1390 	}
1391 	nfsmerr_if(error);
1392 
1393 #if CONFIG_MACF
1394 	if (!error) {
1395 		error = mac_vnode_check_open(ctx, vp, FWRITE);
1396 		if (error) {
1397 			error = EACCES;
1398 		} else {
1399 			/* XXXab: Do we need to do this?! */
1400 			error = mac_vnode_check_write(ctx, vfs_context_ucred(ctx), vp);
1401 			if (error) {
1402 				error = EACCES;
1403 			}
1404 		}
1405 	}
1406 	nfsmerr_if(error);
1407 #endif
1408 
1409 	if (len > 0) {
1410 		for (mcount = 0, m = nmreq->nmc_mcur; m; m = mbuf_next(m)) {
1411 			if (mbuf_len(m) > 0) {
1412 				mcount++;
1413 			}
1414 		}
1415 		auio = uio_create(mcount, off, UIO_SYSSPACE, UIO_WRITE);
1416 		if (!auio) {
1417 			error = ENOMEM;
1418 		}
1419 		nfsmerr_if(error);
1420 		for (m = nmreq->nmc_mcur; m; m = mbuf_next(m)) {
1421 			if ((mlen = (int)mbuf_len(m)) > 0) {
1422 				uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen);
1423 			}
1424 		}
1425 		/*
1426 		 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1427 		 * enough to ensure data integrity) mus be written to stable storage
1428 		 * synchronously.  (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1429 		 */
1430 		if (stable == NFS_WRITE_UNSTABLE) {
1431 			ioflags = IO_NODELOCKED;
1432 		} else if (stable == NFS_WRITE_DATASYNC) {
1433 			ioflags = (IO_SYNC | IO_NODELOCKED);
1434 		} else {
1435 			ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
1436 		}
1437 
1438 		error = VNOP_WRITE(vp, auio, ioflags, ctx);
1439 		OSAddAtomic64(1, &nfsrvstats.srvvop_writes);
1440 
1441 		/* update export stats */
1442 		NFSStatAdd64(&nx->nx_stats.bytes_written, len);
1443 
1444 		/* update active user stats */
1445 		nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, len);
1446 
1447 #if CONFIG_FSE
1448 		if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) {
1449 			nfsrv_modified(vp, ctx);
1450 		}
1451 #endif
1452 	}
1453 	nfsm_srv_vattr_init(&postattr, nd->nd_vers);
1454 	postattrerr = vnode_getattr(vp, &postattr, ctx);
1455 	if (!error && (nd->nd_vers == NFS_VER2)) {
1456 		error = postattrerr; /* NFSv2 must have attributes to return */
1457 	}
1458 	vnode_put(vp);
1459 	vp = NULL;
1460 
1461 nfsmerr:
1462 	/* assemble reply */
1463 	nd->nd_repstat = error;
1464 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) +
1465 	    NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED +
1466 	    NFSX_WRITEVERF(nd->nd_vers));
1467 	nfsmout_if(error);
1468 	*mrepp = nmrep.nmc_mhead;
1469 	nfsmout_on_status(nd, error);
1470 	if (nd->nd_vers == NFS_VER3) {
1471 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
1472 		    preattrerr, &preattr, postattrerr, &postattr);
1473 		nfsmout_if(error || nd->nd_repstat);
1474 		nfsm_chain_add_32(error, &nmrep, retlen);
1475 		/* If nfsrv_async is set, then pretend the write was FILESYNC. */
1476 		if ((stable == NFS_WRITE_UNSTABLE) && !nfsrv_async) {
1477 			nfsm_chain_add_32(error, &nmrep, stable);
1478 		} else {
1479 			nfsm_chain_add_32(error, &nmrep, NFS_WRITE_FILESYNC);
1480 		}
1481 		/* write verifier */
1482 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
1483 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
1484 	} else {
1485 		error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
1486 	}
1487 nfsmout:
1488 	nfsm_chain_build_done(error, &nmrep);
1489 	if (vp) {
1490 		vnode_put(vp);
1491 	}
1492 	if (auio) {
1493 		uio_free(auio);
1494 	}
1495 	if (error) {
1496 		nfsm_chain_cleanup(&nmrep);
1497 		*mrepp = NULL;
1498 	}
1499 	return error;
1500 }
1501 
1502 /*
1503  * NFS write service with write gathering support. Called when
1504  * nfsrv_wg_delay > 0.
1505  * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1506  * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1507  * Jan. 1994.
1508  */
1509 
1510 #define NWDELAYHASH(sock, f) \
1511 	(&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
1512 /* These macros compare nfsrv_descript structures.  */
1513 #define NFSW_CONTIG(o, n) \
1514 	        (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1515 /*
1516  * XXX The following is an incorrect comparison; it fails to take into account
1517  * XXX scoping of MAC labels, but we currently lack KPI for credential
1518  * XXX comparisons.
1519  */
1520 #define NFSW_SAMECRED(o, n) \
1521 	(!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1522 	        sizeof (struct ucred)))
1523 
1524 int
nfsrv_writegather(struct nfsrv_descript ** ndp,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1525 nfsrv_writegather(
1526 	struct nfsrv_descript **ndp,
1527 	struct nfsrv_sock *slp,
1528 	vfs_context_t ctx,
1529 	mbuf_t *mrepp)
1530 {
1531 	struct nfsrv_descript *nd, *wp, *owp, *swp;
1532 	struct nfs_export *nx;
1533 	struct nfs_export_options *nxo;
1534 	struct nfsrv_wg_delayhash *wpp;
1535 	uid_t saved_uid;
1536 	struct vnode_attr preattr, postattr;
1537 	int error, mlen, i, ioflags;
1538 	size_t tlen;
1539 	int preattrerr, postattrerr;
1540 	vnode_t vp;
1541 	mbuf_t m;
1542 	uio_t auio = NULL;
1543 	time_t cur_usec;
1544 	struct timeval now;
1545 	struct nfsm_chain *nmreq, nmrep;
1546 
1547 	error = 0;
1548 	preattrerr = postattrerr = ENOENT;
1549 	nfsm_chain_null(&nmrep);
1550 	vp = NULL;
1551 
1552 	*mrepp = NULL;
1553 	if (*ndp) {
1554 		nd = *ndp;
1555 		*ndp = NULL;
1556 		nmreq = &nd->nd_nmreq;
1557 		LIST_INIT(&nd->nd_coalesce);
1558 		nd->nd_mrep = NULL;
1559 		nd->nd_stable = NFS_WRITE_FILESYNC;
1560 		microuptime(&now);
1561 		cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1562 		nd->nd_time = cur_usec +
1563 		    ((nd->nd_vers == NFS_VER3) ? nfsrv_wg_delay_v3 : nfsrv_wg_delay);
1564 
1565 		/* Now, get the write header... */
1566 		nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nd->nd_fh.nfh_fhp, nd->nd_fh.nfh_len);
1567 		/* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1568 		nfsmerr_if(error);
1569 		if (nd->nd_vers == NFS_VER3) {
1570 			nfsm_chain_get_64(error, nmreq, nd->nd_off);
1571 			nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1572 			nfsm_chain_get_32(error, nmreq, nd->nd_stable);
1573 		} else {
1574 			nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1575 			nfsm_chain_get_32(error, nmreq, nd->nd_off);
1576 			nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1577 			if (nfsrv_async) {
1578 				nd->nd_stable = NFS_WRITE_UNSTABLE;
1579 			}
1580 		}
1581 		nfsm_chain_get_32(error, nmreq, nd->nd_len);
1582 		nfsmerr_if(error);
1583 		nd->nd_eoff = nd->nd_off + nd->nd_len;
1584 
1585 		if (nd->nd_len > 0) {
1586 			error = nfsm_chain_trim_data(nmreq, nd->nd_len, &mlen);
1587 			nfsmerr_if(error);
1588 		} else {
1589 			mlen = 0;
1590 		}
1591 
1592 		if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) {
1593 			error = EIO;
1594 nfsmerr:
1595 			nd->nd_repstat = error;
1596 			error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
1597 			if (!error) {
1598 				nd->nd_mrep = nmrep.nmc_mhead;
1599 				if (nd->nd_vers == NFS_VER3) {
1600 					nfsm_chain_add_wcc_data(error, nd, &nmrep,
1601 					    preattrerr, &preattr, postattrerr, &postattr);
1602 				}
1603 			}
1604 			nfsm_chain_build_done(error, &nmrep);
1605 			nd->nd_time = 1;
1606 		}
1607 
1608 		/*
1609 		 * Add this entry to the hash and time queues.
1610 		 */
1611 		lck_mtx_lock(&slp->ns_wgmutex);
1612 		owp = NULL;
1613 		wp = slp->ns_tq.lh_first;
1614 		while (wp && wp->nd_time < nd->nd_time) {
1615 			owp = wp;
1616 			wp = wp->nd_tq.le_next;
1617 		}
1618 		if (owp) {
1619 			LIST_INSERT_AFTER(owp, nd, nd_tq);
1620 		} else {
1621 			LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq);
1622 		}
1623 		if (!error) {
1624 			wpp = NWDELAYHASH(slp, nd->nd_fh.nfh_fid);
1625 			owp = NULL;
1626 			wp = wpp->lh_first;
1627 			while (wp && !nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) {
1628 				owp = wp;
1629 				wp = wp->nd_hash.le_next;
1630 			}
1631 			while (wp && (wp->nd_off < nd->nd_off) &&
1632 			    nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) {
1633 				owp = wp;
1634 				wp = wp->nd_hash.le_next;
1635 			}
1636 			if (owp) {
1637 				LIST_INSERT_AFTER(owp, nd, nd_hash);
1638 				/*
1639 				 * Search the hash list for overlapping entries and
1640 				 * coalesce.
1641 				 */
1642 				for (; nd && NFSW_CONTIG(owp, nd); nd = wp) {
1643 					wp = nd->nd_hash.le_next;
1644 					if (NFSW_SAMECRED(owp, nd)) {
1645 						nfsrv_wg_coalesce(owp, nd);
1646 					}
1647 				}
1648 			} else {
1649 				LIST_INSERT_HEAD(wpp, nd, nd_hash);
1650 			}
1651 		}
1652 	} else {
1653 		lck_mtx_lock(&slp->ns_wgmutex);
1654 	}
1655 
1656 	/*
1657 	 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1658 	 * and generate the associated reply mbuf list(s).
1659 	 */
1660 loop1:
1661 	microuptime(&now);
1662 	cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1663 	for (nd = slp->ns_tq.lh_first; nd; nd = owp) {
1664 		owp = nd->nd_tq.le_next;
1665 		if (nd->nd_time > cur_usec) {
1666 			break;
1667 		}
1668 		if (nd->nd_mrep) {
1669 			continue;
1670 		}
1671 		LIST_REMOVE(nd, nd_tq);
1672 		LIST_REMOVE(nd, nd_hash);
1673 		nmreq = &nd->nd_nmreq;
1674 		preattrerr = postattrerr = ENOENT;
1675 
1676 		/* save the incoming uid before mapping, */
1677 		/* for updating active user stats later */
1678 		saved_uid = kauth_cred_getuid(nd->nd_cr);
1679 
1680 		error = nfsrv_fhtovp(&nd->nd_fh, nd, &vp, &nx, &nxo);
1681 		if (!error) {
1682 			/* update per-export stats */
1683 			NFSStatAdd64(&nx->nx_stats.ops, 1);
1684 
1685 			error = nfsrv_credcheck(nd, ctx, nx, nxo);
1686 			if (error) {
1687 				vnode_put(vp);
1688 			}
1689 		}
1690 		if (!error) {
1691 			if (nd->nd_vers == NFS_VER3) {
1692 				nfsm_srv_pre_vattr_init(&preattr);
1693 				preattrerr = vnode_getattr(vp, &preattr, ctx);
1694 			}
1695 			if (vnode_vtype(vp) != VREG) {
1696 				if (nd->nd_vers == NFS_VER3) {
1697 					error = EINVAL;
1698 				} else {
1699 					error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1700 				}
1701 			}
1702 		} else {
1703 			vp = NULL;
1704 		}
1705 		if (!error) {
1706 			error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1);
1707 		}
1708 
1709 		if (nd->nd_stable == NFS_WRITE_UNSTABLE) {
1710 			ioflags = IO_NODELOCKED;
1711 		} else if (nd->nd_stable == NFS_WRITE_DATASYNC) {
1712 			ioflags = (IO_SYNC | IO_NODELOCKED);
1713 		} else {
1714 			ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
1715 		}
1716 
1717 		if (!error && ((nd->nd_eoff - nd->nd_off) > 0)) {
1718 			for (i = 0, m = nmreq->nmc_mhead; m; m = mbuf_next(m)) {
1719 				if (mbuf_len(m) > 0) {
1720 					i++;
1721 				}
1722 			}
1723 
1724 			auio = uio_create(i, nd->nd_off, UIO_SYSSPACE, UIO_WRITE);
1725 			if (!auio) {
1726 				error = ENOMEM;
1727 			}
1728 			if (!error) {
1729 				for (m = nmreq->nmc_mhead; m; m = mbuf_next(m)) {
1730 					if ((tlen = mbuf_len(m)) > 0) {
1731 						uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen);
1732 					}
1733 				}
1734 				error = VNOP_WRITE(vp, auio, ioflags, ctx);
1735 				OSAddAtomic64(1, &nfsrvstats.srvvop_writes);
1736 
1737 				/* update export stats */
1738 				NFSStatAdd64(&nx->nx_stats.bytes_written, nd->nd_len);
1739 				/* update active user stats */
1740 				nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, nd->nd_len);
1741 
1742 #if CONFIG_FSE
1743 				if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) {
1744 					nfsrv_modified(vp, ctx);
1745 				}
1746 #endif
1747 			}
1748 			if (auio) {
1749 				uio_free(auio);
1750 				auio = NULL;
1751 			}
1752 		}
1753 		if (vp) {
1754 			nfsm_srv_vattr_init(&postattr, nd->nd_vers);
1755 			postattrerr = vnode_getattr(vp, &postattr, ctx);
1756 			vnode_put(vp);
1757 		}
1758 
1759 		/*
1760 		 * Loop around generating replies for all write rpcs that have
1761 		 * now been completed.
1762 		 */
1763 		swp = nd;
1764 		do {
1765 			if (error) {
1766 				nd->nd_repstat = error;
1767 				error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
1768 				if (!error && (nd->nd_vers == NFS_VER3)) {
1769 					nfsm_chain_add_wcc_data(error, nd, &nmrep,
1770 					    preattrerr, &preattr, postattrerr, &postattr);
1771 				}
1772 			} else {
1773 				nd->nd_repstat = error;
1774 				error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) +
1775 				    NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED +
1776 				    NFSX_WRITEVERF(nd->nd_vers));
1777 				if (!error && (nd->nd_vers == NFS_VER3)) {
1778 					nfsm_chain_add_wcc_data(error, nd, &nmrep,
1779 					    preattrerr, &preattr, postattrerr, &postattr);
1780 					nfsm_chain_add_32(error, &nmrep, nd->nd_len);
1781 					nfsm_chain_add_32(error, &nmrep, nd->nd_stable);
1782 					/* write verifier */
1783 					nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
1784 					nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
1785 				} else if (!error) {
1786 					error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
1787 				}
1788 			}
1789 			nfsm_chain_build_done(error, &nmrep);
1790 			nfsmerr_if(error);
1791 			nd->nd_mrep = nmrep.nmc_mhead;
1792 
1793 			/*
1794 			 * Done. Put it at the head of the timer queue so that
1795 			 * the final phase can return the reply.
1796 			 */
1797 			if (nd != swp) {
1798 				nd->nd_time = 1;
1799 				LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq);
1800 			}
1801 			nd = swp->nd_coalesce.lh_first;
1802 			if (nd) {
1803 				LIST_REMOVE(nd, nd_tq);
1804 			}
1805 		} while (nd);
1806 		swp->nd_time = 1;
1807 		LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq);
1808 		goto loop1;
1809 	}
1810 
1811 	/*
1812 	 * Search for a reply to return.
1813 	 */
1814 	for (nd = slp->ns_tq.lh_first; nd; nd = nd->nd_tq.le_next) {
1815 		if (nd->nd_mrep) {
1816 			LIST_REMOVE(nd, nd_tq);
1817 			*mrepp = nd->nd_mrep;
1818 			*ndp = nd;
1819 			break;
1820 		}
1821 	}
1822 	slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0;
1823 	lck_mtx_unlock(&slp->ns_wgmutex);
1824 
1825 	/*
1826 	 * If we've just created a write pending gather,
1827 	 * start the timer to check on it soon to make sure
1828 	 * the write will be completed.
1829 	 *
1830 	 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1831 	 */
1832 	lck_mtx_lock(&nfsd_mutex);
1833 	if (slp->ns_wgtime) {
1834 		if (slp->ns_wgq.tqe_next == SLPNOLIST) {
1835 			TAILQ_INSERT_HEAD(&nfsrv_sockwg, slp, ns_wgq);
1836 		}
1837 		if (!nfsrv_wg_timer_on) {
1838 			nfsrv_wg_timer_on = 1;
1839 			nfs_interval_timer_start(nfsrv_wg_timer_call,
1840 			    NFSRV_WGATHERDELAY);
1841 		}
1842 	} else if (slp->ns_wgq.tqe_next != SLPNOLIST) {
1843 		TAILQ_REMOVE(&nfsrv_sockwg, slp, ns_wgq);
1844 		slp->ns_wgq.tqe_next = SLPNOLIST;
1845 	}
1846 	lck_mtx_unlock(&nfsd_mutex);
1847 
1848 	return 0;
1849 }
1850 
1851 /*
1852  * Coalesce the write request nd into owp. To do this we must:
1853  * - remove nd from the queues
1854  * - merge nd->nd_nmreq into owp->nd_nmreq
1855  * - update the nd_eoff and nd_stable for owp
1856  * - put nd on owp's nd_coalesce list
1857  */
1858 int
nfsrv_wg_coalesce(struct nfsrv_descript * owp,struct nfsrv_descript * nd)1859 nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd)
1860 {
1861 	int error;
1862 	off_t overlap;
1863 	mbuf_t mp, mpnext;
1864 	struct nfsrv_descript *p;
1865 
1866 	LIST_REMOVE(nd, nd_hash);
1867 	LIST_REMOVE(nd, nd_tq);
1868 	if (owp->nd_eoff < nd->nd_eoff) {
1869 		overlap = owp->nd_eoff - nd->nd_off;
1870 		if (overlap < 0) {
1871 			return EIO;
1872 		}
1873 		if (overlap > 0) {
1874 			mbuf_adj(nd->nd_nmreq.nmc_mhead, (int)overlap);
1875 		}
1876 		mp = owp->nd_nmreq.nmc_mhead;
1877 		while ((mpnext = mbuf_next(mp))) {
1878 			mp = mpnext;
1879 		}
1880 		error = mbuf_setnext(mp, nd->nd_nmreq.nmc_mhead);
1881 		if (error) {
1882 			return error;
1883 		}
1884 		owp->nd_eoff = nd->nd_eoff;
1885 	} else {
1886 		mbuf_freem(nd->nd_nmreq.nmc_mhead);
1887 	}
1888 	nd->nd_nmreq.nmc_mhead = NULL;
1889 	nd->nd_nmreq.nmc_mcur = NULL;
1890 	if (nd->nd_stable == NFS_WRITE_FILESYNC) {
1891 		owp->nd_stable = NFS_WRITE_FILESYNC;
1892 	} else if ((nd->nd_stable == NFS_WRITE_DATASYNC) &&
1893 	    (owp->nd_stable == NFS_WRITE_UNSTABLE)) {
1894 		owp->nd_stable = NFS_WRITE_DATASYNC;
1895 	}
1896 	LIST_INSERT_HEAD(&owp->nd_coalesce, nd, nd_tq);
1897 
1898 	/*
1899 	 * If nd had anything else coalesced into it, transfer them
1900 	 * to owp, otherwise their replies will never get sent.
1901 	 */
1902 	while ((p = nd->nd_coalesce.lh_first)) {
1903 		LIST_REMOVE(p, nd_tq);
1904 		LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq);
1905 	}
1906 	return 0;
1907 }
1908 
1909 /*
1910  * Scan the write gathering queues for writes that need to be
1911  * completed now.
1912  */
1913 void
nfsrv_wg_timer(__unused void * param0,__unused void * param1)1914 nfsrv_wg_timer(__unused void *param0, __unused void *param1)
1915 {
1916 	struct timeval now;
1917 	time_t cur_usec, next_usec;
1918 	time_t interval;
1919 	struct nfsrv_sock *slp;
1920 	int writes_pending = 0;
1921 
1922 	microuptime(&now);
1923 	cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1924 	next_usec = cur_usec + (NFSRV_WGATHERDELAY * 1000);
1925 
1926 	lck_mtx_lock(&nfsd_mutex);
1927 	TAILQ_FOREACH(slp, &nfsrv_sockwg, ns_wgq) {
1928 		if (slp->ns_wgtime) {
1929 			writes_pending++;
1930 			if (slp->ns_wgtime <= cur_usec) {
1931 				lck_rw_lock_exclusive(&slp->ns_rwlock);
1932 				slp->ns_flag |= SLP_DOWRITES;
1933 				lck_rw_done(&slp->ns_rwlock);
1934 				nfsrv_wakenfsd(slp);
1935 				continue;
1936 			}
1937 			if (slp->ns_wgtime < next_usec) {
1938 				next_usec = slp->ns_wgtime;
1939 			}
1940 		}
1941 	}
1942 
1943 	if (writes_pending == 0) {
1944 		nfsrv_wg_timer_on = 0;
1945 		lck_mtx_unlock(&nfsd_mutex);
1946 		return;
1947 	}
1948 	lck_mtx_unlock(&nfsd_mutex);
1949 
1950 	/*
1951 	 * Return the number of msec to wait again
1952 	 */
1953 	interval = (next_usec - cur_usec) / 1000;
1954 	if (interval < 1) {
1955 		interval = 1;
1956 	}
1957 	nfs_interval_timer_start(nfsrv_wg_timer_call, interval);
1958 }
1959 
1960 /*
1961  * Sort the group list in increasing numerical order.
1962  * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1963  *  that used to be here.)
1964  */
1965 void
nfsrv_group_sort(gid_t * list,int num)1966 nfsrv_group_sort(gid_t *list, int num)
1967 {
1968 	int i, j;
1969 	gid_t v;
1970 
1971 	/* Insertion sort. */
1972 	for (i = 1; i < num; i++) {
1973 		v = list[i];
1974 		/* find correct slot for value v, moving others up */
1975 		for (j = i; --j >= 0 && v < list[j];) {
1976 			list[j + 1] = list[j];
1977 		}
1978 		list[j + 1] = v;
1979 	}
1980 }
1981 
1982 /*
1983  * nfs create service
1984  * now does a truncate to 0 length via. setattr if it already exists
1985  */
1986 int
nfsrv_create(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1987 nfsrv_create(
1988 	struct nfsrv_descript *nd,
1989 	struct nfsrv_sock *slp,
1990 	vfs_context_t ctx,
1991 	mbuf_t *mrepp)
1992 {
1993 	struct vnode_attr dpreattr, dpostattr, postattr;
1994 	struct vnode_attr va, *vap = &va;
1995 	struct nameidata ni;
1996 	int error, dpreattrerr, dpostattrerr, postattrerr;
1997 	int how, exclusive_flag;
1998 	uint32_t len = 0, cnflags;
1999 	uint64_t rdev;
2000 	vnode_t vp, dvp, dirp;
2001 	struct nfs_filehandle nfh = {};
2002 	struct nfs_export *nx = NULL;
2003 	struct nfs_export_options *nxo = NULL;
2004 	u_quad_t tempsize;
2005 	u_char cverf[NFSX_V3CREATEVERF];
2006 	uid_t saved_uid;
2007 	struct nfsm_chain *nmreq, nmrep;
2008 	__unused const int nfs_vers = nd->nd_vers;
2009 	assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
2010 
2011 	error = 0;
2012 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
2013 	nmreq = &nd->nd_nmreq;
2014 	nfsm_chain_null(&nmrep);
2015 	vp = dvp = dirp = NULL;
2016 	exclusive_flag = 0;
2017 	ni.ni_cnd.cn_nameiop = 0;
2018 	rdev = 0;
2019 
2020 	saved_uid = kauth_cred_getuid(nd->nd_cr);
2021 
2022 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
2023 	nfsm_chain_get_32(error, nmreq, len);
2024 	nfsm_name_len_check(error, nd, len);
2025 	nfsmerr_if(error);
2026 
2027 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2028 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2029 	if (!error) {
2030 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2031 		if (nx != NULL) {
2032 			/* update export stats */
2033 			NFSStatAdd64(&nx->nx_stats.ops, 1);
2034 
2035 			/* update active user stats */
2036 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2037 		}
2038 	}
2039 	if (dirp) {
2040 		if (nd->nd_vers == NFS_VER3) {
2041 			nfsm_srv_pre_vattr_init(&dpreattr);
2042 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2043 		} else {
2044 			vnode_put(dirp);
2045 			dirp = NULL;
2046 		}
2047 	}
2048 
2049 	if (error) {
2050 		ni.ni_cnd.cn_nameiop = 0;
2051 		goto nfsmerr;
2052 	}
2053 
2054 	dvp = ni.ni_dvp;
2055 	vp = ni.ni_vp;
2056 	VATTR_INIT(vap);
2057 
2058 	if (nd->nd_vers == NFS_VER3) {
2059 		nfsm_chain_get_32(error, nmreq, how);
2060 		nfsmerr_if(error);
2061 		switch (how) {
2062 		case NFS_CREATE_GUARDED:
2063 			if (vp) {
2064 				error = EEXIST;
2065 				break;
2066 			}
2067 			OS_FALLTHROUGH;
2068 		case NFS_CREATE_UNCHECKED:
2069 			error = nfsm_chain_get_sattr(nd, nmreq, vap);
2070 			break;
2071 		case NFS_CREATE_EXCLUSIVE:
2072 			nfsm_chain_get_opaque(error, nmreq, NFSX_V3CREATEVERF, cverf);
2073 			exclusive_flag = 1;
2074 			if (vp == NULL) {
2075 				VATTR_SET(vap, va_mode, 0);
2076 			}
2077 			break;
2078 		}
2079 		;
2080 		VATTR_SET(vap, va_type, VREG);
2081 	} else {
2082 		enum vtype v_type;
2083 
2084 		error = nfsm_chain_get_sattr(nd, nmreq, vap);
2085 		nfsmerr_if(error);
2086 		v_type = vap->va_type;
2087 		if (v_type == VNON) {
2088 			v_type = VREG;
2089 		}
2090 		VATTR_SET(vap, va_type, v_type);
2091 
2092 		switch (v_type) {
2093 		case VCHR:
2094 		case VBLK:
2095 		case VFIFO:
2096 			rdev = vap->va_data_size;
2097 			VATTR_CLEAR_ACTIVE(vap, va_data_size);
2098 			break;
2099 		default:
2100 			break;
2101 		}
2102 		;
2103 	}
2104 	nfsmerr_if(error);
2105 
2106 	/*
2107 	 * If it doesn't exist, create it
2108 	 * otherwise just truncate to 0 length
2109 	 *   should I set the mode too ??
2110 	 */
2111 	if (vp == NULL) {
2112 		kauth_acl_t xacl = NULL;
2113 
2114 		/* authorize before creating */
2115 		error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
2116 
2117 		/* construct ACL and handle inheritance */
2118 		if (!error) {
2119 			error = kauth_acl_inherit(dvp,
2120 			    NULL,
2121 			    &xacl,
2122 			    0 /* !isdir */,
2123 			    ctx);
2124 
2125 			if (!error && xacl != NULL) {
2126 				VATTR_SET(vap, va_acl, xacl);
2127 			}
2128 		}
2129 		VATTR_CLEAR_ACTIVE(vap, va_data_size);
2130 		VATTR_CLEAR_ACTIVE(vap, va_access_time);
2131 		/*
2132 		 * Server policy is to alway use the mapped rpc credential for
2133 		 * file system object creation. This has the nice side effect of
2134 		 * enforcing BSD creation semantics
2135 		 */
2136 		VATTR_CLEAR_ACTIVE(vap, va_uid);
2137 		VATTR_CLEAR_ACTIVE(vap, va_gid);
2138 
2139 		/* validate new-file security information */
2140 		if (!error) {
2141 			error = vnode_authattr_new(dvp, vap, 0, ctx);
2142 		}
2143 
2144 		if (!error) {
2145 			error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
2146 			if (error) {
2147 				error = EACCES;
2148 			}
2149 		}
2150 
2151 		if (vap->va_type == VREG || vap->va_type == VSOCK) {
2152 			if (!error) {
2153 				error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx);
2154 			}
2155 
2156 			if (!error && !VATTR_ALL_SUPPORTED(vap)) {
2157 				/*
2158 				 * If some of the requested attributes weren't handled by the VNOP,
2159 				 * use our fallback code.
2160 				 */
2161 				error = vnode_setattr_fallback(vp, vap, ctx);
2162 			}
2163 
2164 			if (xacl != NULL) {
2165 				kauth_acl_free(xacl);
2166 			}
2167 
2168 			if (!error) {
2169 				if (exclusive_flag) {
2170 					exclusive_flag = 0;
2171 					VATTR_INIT(vap);
2172 					bcopy(cverf, (caddr_t)&vap->va_access_time,
2173 					    NFSX_V3CREATEVERF);
2174 					VATTR_SET_ACTIVE(vap, va_access_time);
2175 					// skip authorization, as this is an
2176 					// NFS internal implementation detail.
2177 					error = vnode_setattr(vp, vap, ctx);
2178 				}
2179 
2180 #if CONFIG_FSE
2181 				if (nfsrv_fsevents_enabled && need_fsevent(FSE_CREATE_FILE, vp)) {
2182 					add_fsevent(FSE_CREATE_FILE, ctx,
2183 					    FSE_ARG_VNODE, vp,
2184 					    FSE_ARG_DONE);
2185 				}
2186 #endif
2187 			}
2188 		} else if (vap->va_type == VCHR || vap->va_type == VBLK ||
2189 		    vap->va_type == VFIFO) {
2190 			if (vap->va_type == VCHR && rdev == 0xffffffff) {
2191 				VATTR_SET(vap, va_type, VFIFO);
2192 			}
2193 			if (vap->va_type != VFIFO) {
2194 				error = suser(nd->nd_cr, NULL);
2195 				nfsmerr_if(error);
2196 			}
2197 			VATTR_SET(vap, va_rdev, (dev_t)rdev);
2198 
2199 			error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx);
2200 
2201 			if (xacl != NULL) {
2202 				kauth_acl_free(xacl);
2203 			}
2204 
2205 			nfsmerr_if(error);
2206 
2207 			if (vp) {
2208 				vnode_recycle(vp);
2209 				vnode_put(vp);
2210 				vp = NULL;
2211 			}
2212 			ni.ni_cnd.cn_nameiop = LOOKUP;
2213 #if CONFIG_TRIGGERS
2214 			ni.ni_op = OP_LOOKUP;
2215 #endif
2216 			ni.ni_cnd.cn_flags &= ~LOCKPARENT;
2217 			ni.ni_cnd.cn_context = ctx;
2218 			ni.ni_startdir = dvp;
2219 			ni.ni_usedvp   = dvp;
2220 			ni.ni_rootdir = rootvnode;
2221 			cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
2222 			while ((error = lookup(&ni)) == ERECYCLE) {
2223 				ni.ni_cnd.cn_flags = cnflags;
2224 				ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
2225 				ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
2226 			}
2227 			if (!error) {
2228 				if (ni.ni_cnd.cn_flags & ISSYMLINK) {
2229 					error = EINVAL;
2230 				}
2231 				vp = ni.ni_vp;
2232 			}
2233 			nfsmerr_if(error);
2234 		} else {
2235 			error = ENXIO;
2236 		}
2237 		/*
2238 		 * nameidone has to happen before we vnode_put(dvp)
2239 		 * since it may need to release the fs_nodelock on the dvp
2240 		 */
2241 		nameidone(&ni);
2242 		ni.ni_cnd.cn_nameiop = 0;
2243 
2244 		vnode_put(dvp);
2245 	} else {
2246 		/*
2247 		 * nameidone has to happen before we vnode_put(dvp)
2248 		 * since it may need to release the fs_nodelock on the dvp
2249 		 */
2250 		nameidone(&ni);
2251 		ni.ni_cnd.cn_nameiop = 0;
2252 
2253 		vnode_put(dvp);
2254 
2255 #if CONFIG_MACF
2256 		if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
2257 			/* NOTE: File has not been open for NFS case, so NOCRED for filecred */
2258 			error = mac_vnode_check_truncate(ctx, NOCRED, vp);
2259 			if (error) {
2260 				error = EACCES;
2261 			}
2262 		}
2263 #endif
2264 		if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
2265 			error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA,
2266 			    ctx, nxo, 0);
2267 			if (!error) {
2268 				tempsize = vap->va_data_size;
2269 				VATTR_INIT(vap);
2270 				VATTR_SET(vap, va_data_size, tempsize);
2271 				error = vnode_setattr(vp, vap, ctx);
2272 			}
2273 		}
2274 	}
2275 	if (!error) {
2276 		error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
2277 		if (!error) {
2278 			nfsm_srv_vattr_init(&postattr, nd->nd_vers);
2279 			postattrerr = vnode_getattr(vp, &postattr, ctx);
2280 			if (nd->nd_vers == NFS_VER2) {
2281 				error = postattrerr;
2282 			}
2283 		}
2284 	}
2285 	if (vp) {
2286 		vnode_put(vp);
2287 	}
2288 
2289 	if (nd->nd_vers == NFS_VER3) {
2290 		if (exclusive_flag && !error &&
2291 		    bcmp(cverf, &postattr.va_access_time, NFSX_V3CREATEVERF)) {
2292 			error = EEXIST;
2293 		}
2294 		nfsm_srv_vattr_init(&dpostattr, NFS_VER3);
2295 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2296 		vnode_put(dirp);
2297 		dirp = NULL;
2298 	}
2299 
2300 nfsmerr:
2301 	/* assemble reply */
2302 	nd->nd_repstat = error;
2303 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
2304 	    NFSX_FATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
2305 	assert(nfs_vers == nd->nd_vers);
2306 	nfsmout_if(error);
2307 	*mrepp = nmrep.nmc_mhead;
2308 	nfsmout_on_status(nd, error);
2309 	if (nd->nd_vers == NFS_VER3) {
2310 		if (!nd->nd_repstat) {
2311 			if (!nfh.nfh_fhp) {
2312 				error = NFSERR_SERVERFAULT;
2313 				goto nfsmerr;
2314 			}
2315 			nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
2316 			nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
2317 		}
2318 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
2319 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2320 	} else {
2321 		nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
2322 		if (!error) {
2323 			error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
2324 		}
2325 	}
2326 nfsmout:
2327 	nfsm_chain_build_done(error, &nmrep);
2328 	if (ni.ni_cnd.cn_nameiop) {
2329 		/*
2330 		 * nameidone has to happen before we vnode_put(dvp)
2331 		 * since it may need to release the fs_nodelock on the dvp
2332 		 */
2333 		nameidone(&ni);
2334 
2335 		if (vp) {
2336 			vnode_put(vp);
2337 		}
2338 		vnode_put(dvp);
2339 	}
2340 	if (dirp) {
2341 		vnode_put(dirp);
2342 	}
2343 	if (error) {
2344 		nfsm_chain_cleanup(&nmrep);
2345 		*mrepp = NULL;
2346 	}
2347 	return error;
2348 }
2349 
2350 /*
2351  * nfs v3 mknod service
2352  */
2353 int
nfsrv_mknod(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2354 nfsrv_mknod(
2355 	struct nfsrv_descript *nd,
2356 	struct nfsrv_sock *slp,
2357 	vfs_context_t ctx,
2358 	mbuf_t *mrepp)
2359 {
2360 	struct vnode_attr dpreattr, dpostattr, postattr;
2361 	struct vnode_attr va, *vap = &va;
2362 	struct nameidata ni;
2363 	int error, dpreattrerr, dpostattrerr, postattrerr;
2364 	uint32_t len = 0, cnflags;
2365 	u_int32_t major = 0, minor = 0;
2366 	enum vtype vtyp;
2367 	nfstype nvtype;
2368 	vnode_t vp, dvp, dirp;
2369 	struct nfs_filehandle nfh = {};
2370 	struct nfs_export *nx = NULL;
2371 	struct nfs_export_options *nxo = NULL;
2372 	uid_t saved_uid;
2373 	kauth_acl_t xacl = NULL;
2374 	struct nfsm_chain *nmreq, nmrep;
2375 
2376 	error = 0;
2377 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
2378 	nmreq = &nd->nd_nmreq;
2379 	nfsm_chain_null(&nmrep);
2380 	vp = dvp = dirp = NULL;
2381 	ni.ni_cnd.cn_nameiop = 0;
2382 
2383 	saved_uid = kauth_cred_getuid(nd->nd_cr);
2384 
2385 	nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
2386 	nfsm_chain_get_32(error, nmreq, len);
2387 	nfsm_name_len_check(error, nd, len);
2388 	nfsmerr_if(error);
2389 
2390 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2391 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2392 	if (!error) {
2393 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2394 		if (nx != NULL) {
2395 			/* update export stats */
2396 			NFSStatAdd64(&nx->nx_stats.ops, 1);
2397 
2398 			/* update active user stats */
2399 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2400 		}
2401 	}
2402 	if (dirp) {
2403 		nfsm_srv_pre_vattr_init(&dpreattr);
2404 		dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2405 	}
2406 	if (error) {
2407 		ni.ni_cnd.cn_nameiop = 0;
2408 		goto nfsmerr;
2409 	}
2410 
2411 	dvp = ni.ni_dvp;
2412 	vp = ni.ni_vp;
2413 
2414 	nfsm_chain_get_32(error, nmreq, nvtype);
2415 	nfsmerr_if(error);
2416 	vtyp = nfstov_type(nvtype, NFS_VER3);
2417 	if (!error && (vtyp != VCHR) && (vtyp != VBLK) && (vtyp != VSOCK) && (vtyp != VFIFO)) {
2418 		error = NFSERR_BADTYPE;
2419 		goto out;
2420 	}
2421 
2422 	VATTR_INIT(vap);
2423 	error = nfsm_chain_get_sattr(nd, nmreq, vap);
2424 	if ((vtyp == VCHR) || (vtyp == VBLK)) {
2425 		nfsm_chain_get_32(error, nmreq, major);
2426 		nfsm_chain_get_32(error, nmreq, minor);
2427 		nfsmerr_if(error);
2428 		VATTR_SET(vap, va_rdev, makedev(major, minor));
2429 	}
2430 	nfsmerr_if(error);
2431 
2432 	/*
2433 	 * If it doesn't exist, create it.
2434 	 */
2435 	if (vp) {
2436 		error = EEXIST;
2437 		goto out;
2438 	}
2439 	VATTR_SET(vap, va_type, vtyp);
2440 
2441 	/* authorize before creating */
2442 	error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
2443 
2444 	/* construct ACL and handle inheritance */
2445 	if (!error) {
2446 		error = kauth_acl_inherit(dvp,
2447 		    NULL,
2448 		    &xacl,
2449 		    0 /* !isdir */,
2450 		    ctx);
2451 
2452 		if (!error && xacl != NULL) {
2453 			VATTR_SET(vap, va_acl, xacl);
2454 		}
2455 	}
2456 	VATTR_CLEAR_ACTIVE(vap, va_data_size);
2457 	VATTR_CLEAR_ACTIVE(vap, va_access_time);
2458 	/*
2459 	 * Server policy is to alway use the mapped rpc credential for
2460 	 * file system object creation. This has the nice side effect of
2461 	 * enforcing BSD creation semantics
2462 	 */
2463 	VATTR_CLEAR_ACTIVE(vap, va_uid);
2464 	VATTR_CLEAR_ACTIVE(vap, va_gid);
2465 
2466 	/* validate new-file security information */
2467 	if (!error) {
2468 		error = vnode_authattr_new(dvp, vap, 0, ctx);
2469 	}
2470 	if (!error) {
2471 		error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
2472 		if (error) {
2473 			error = EACCES;
2474 		}
2475 	}
2476 	if (error) {
2477 		goto out1;
2478 	}
2479 
2480 	if (vtyp == VSOCK) {
2481 		error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx);
2482 
2483 		if (!error && !VATTR_ALL_SUPPORTED(vap)) {
2484 			/*
2485 			 * If some of the requested attributes weren't handled by the VNOP,
2486 			 * use our fallback code.
2487 			 */
2488 			error = vnode_setattr_fallback(vp, vap, ctx);
2489 		}
2490 	} else {
2491 		if (vtyp != VFIFO && (error = suser(nd->nd_cr, (u_short *)0))) {
2492 			goto out1;
2493 		}
2494 		if ((error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx))) {
2495 			goto out1;
2496 		}
2497 		if (vp) {
2498 			vnode_recycle(vp);
2499 			vnode_put(vp);
2500 			vp = NULL;
2501 		}
2502 		ni.ni_cnd.cn_nameiop = LOOKUP;
2503 #if CONFIG_TRIGGERS
2504 		ni.ni_op = OP_LOOKUP;
2505 #endif
2506 		ni.ni_cnd.cn_flags &= ~LOCKPARENT;
2507 		ni.ni_cnd.cn_context = vfs_context_current();
2508 		ni.ni_startdir = dvp;
2509 		ni.ni_usedvp   = dvp;
2510 		ni.ni_rootdir = rootvnode;
2511 		cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
2512 		while ((error = lookup(&ni)) == ERECYCLE) {
2513 			ni.ni_cnd.cn_flags = cnflags;
2514 			ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
2515 			ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
2516 		}
2517 		if (!error) {
2518 			vp = ni.ni_vp;
2519 			if (ni.ni_cnd.cn_flags & ISSYMLINK) {
2520 				error = EINVAL;
2521 			}
2522 		}
2523 	}
2524 out1:
2525 	if (xacl != NULL) {
2526 		kauth_acl_free(xacl);
2527 	}
2528 out:
2529 	/*
2530 	 * nameidone has to happen before we vnode_put(dvp)
2531 	 * since it may need to release the fs_nodelock on the dvp
2532 	 */
2533 	nameidone(&ni);
2534 	ni.ni_cnd.cn_nameiop = 0;
2535 
2536 	vnode_put(dvp);
2537 	dvp = NULL;
2538 
2539 	if (!error) {
2540 		error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
2541 		if (!error) {
2542 			nfsm_srv_vattr_init(&postattr, NFS_VER3);
2543 			postattrerr = vnode_getattr(vp, &postattr, ctx);
2544 		}
2545 	}
2546 	if (vp) {
2547 		vnode_put(vp);
2548 		vp = NULL;
2549 	}
2550 
2551 	nfsm_srv_vattr_init(&dpostattr, NFS_VER3);
2552 	dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2553 	vnode_put(dirp);
2554 	dirp = NULL;
2555 
2556 nfsmerr:
2557 	/* assemble reply */
2558 	nd->nd_repstat = error;
2559 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(NFS_VER3, &nfh) +
2560 	    NFSX_POSTOPATTR(NFS_VER3) + NFSX_WCCDATA(NFS_VER3));
2561 	nfsmout_if(error);
2562 	*mrepp = nmrep.nmc_mhead;
2563 	nfsmout_on_status(nd, error);
2564 	if (!nd->nd_repstat) {
2565 		if (!nfh.nfh_fhp) {
2566 			error = NFSERR_SERVERFAULT;
2567 			goto nfsmerr;
2568 		}
2569 		nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
2570 		nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
2571 	}
2572 	nfsm_chain_add_wcc_data(error, nd, &nmrep,
2573 	    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2574 nfsmout:
2575 	nfsm_chain_build_done(error, &nmrep);
2576 	if (ni.ni_cnd.cn_nameiop) {
2577 		/*
2578 		 * nameidone has to happen before we vnode_put(dvp)
2579 		 * since it may need to release the fs_nodelock on the dvp
2580 		 */
2581 		nameidone(&ni);
2582 
2583 		if (vp) {
2584 			vnode_put(vp);
2585 		}
2586 		vnode_put(dvp);
2587 	}
2588 	if (dvp) {
2589 		vnode_put(dvp);
2590 	}
2591 	if (vp) {
2592 		vnode_put(vp);
2593 	}
2594 	if (dirp) {
2595 		vnode_put(dirp);
2596 	}
2597 	if (error) {
2598 		nfsm_chain_cleanup(&nmrep);
2599 		*mrepp = NULL;
2600 	}
2601 	return error;
2602 }
2603 
2604 /*
2605  * nfs remove service
2606  */
2607 int
nfsrv_remove(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2608 nfsrv_remove(
2609 	struct nfsrv_descript *nd,
2610 	struct nfsrv_sock *slp,
2611 	vfs_context_t ctx,
2612 	mbuf_t *mrepp)
2613 {
2614 	struct nameidata ni;
2615 	int error, dpreattrerr, dpostattrerr;
2616 	uint32_t len = 0;
2617 	uid_t saved_uid;
2618 	vnode_t vp, dvp, dirp = NULL;
2619 	struct vnode_attr dpreattr, dpostattr;
2620 	struct nfs_filehandle nfh;
2621 	struct nfs_export *nx = NULL;
2622 	struct nfs_export_options *nxo = NULL;
2623 	struct nfsm_chain *nmreq, nmrep;
2624 
2625 	error = 0;
2626 	dpreattrerr = dpostattrerr = ENOENT;
2627 	saved_uid = kauth_cred_getuid(nd->nd_cr);
2628 	dvp = vp = dirp = NULL;
2629 	nmreq = &nd->nd_nmreq;
2630 	nfsm_chain_null(&nmrep);
2631 
2632 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
2633 	nfsm_chain_get_32(error, nmreq, len);
2634 	nfsm_name_len_check(error, nd, len);
2635 	nfsmerr_if(error);
2636 
2637 	NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2638 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2639 	if (!error) {
2640 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2641 		if (nx != NULL) {
2642 			/* update export stats */
2643 			NFSStatAdd64(&nx->nx_stats.ops, 1);
2644 
2645 			/* update active user stats */
2646 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2647 		}
2648 	}
2649 	if (dirp) {
2650 		if (nd->nd_vers == NFS_VER3) {
2651 			nfsm_srv_pre_vattr_init(&dpreattr);
2652 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2653 		} else {
2654 			vnode_put(dirp);
2655 			dirp = NULL;
2656 		}
2657 	}
2658 
2659 	if (!error) {
2660 		dvp = ni.ni_dvp;
2661 		vp = ni.ni_vp;
2662 
2663 		if (vnode_vtype(vp) == VDIR) {
2664 			error = NFSERR_ISDIR;
2665 		} else if (vnode_isvroot(vp)) {
2666 			/*
2667 			 * The root of a mounted filesystem cannot be deleted.
2668 			 */
2669 			error = EBUSY;
2670 		} else {
2671 			error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
2672 		}
2673 
2674 		if (!error) {
2675 			error = vn_authorize_unlink(dvp, vp, &ni.ni_cnd, ctx, NULL);
2676 			if (error) {
2677 				error = EACCES;
2678 			}
2679 		}
2680 
2681 		if (!error) {
2682 #if CONFIG_FSE
2683 			char     *path = NULL;
2684 			int       plen = 0;
2685 			fse_info  finfo;
2686 
2687 			if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
2688 				plen = MAXPATHLEN;
2689 				if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
2690 					get_fse_info(vp, &finfo, ctx);
2691 				} else if (path) {
2692 					release_pathbuff(path);
2693 					path = NULL;
2694 				}
2695 			}
2696 #endif
2697 			error = VNOP_REMOVE(dvp, vp, &ni.ni_cnd, 0, ctx);
2698 
2699 #if CONFIG_FSE
2700 			if (path) {
2701 				if (!error) {
2702 					add_fsevent(FSE_DELETE, ctx,
2703 					    FSE_ARG_STRING, plen, path,
2704 					    FSE_ARG_FINFO, &finfo,
2705 					    FSE_ARG_DONE);
2706 				}
2707 				release_pathbuff(path);
2708 			}
2709 #endif
2710 		}
2711 
2712 		/*
2713 		 * nameidone has to happen before we vnode_put(dvp)
2714 		 * since it may need to release the fs_nodelock on the dvp
2715 		 */
2716 		nameidone(&ni);
2717 
2718 		vnode_put(vp);
2719 		vnode_put(dvp);
2720 	}
2721 
2722 nfsmerr:
2723 	if (dirp) {
2724 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
2725 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2726 		vnode_put(dirp);
2727 	}
2728 
2729 	/* assemble reply */
2730 	nd->nd_repstat = error;
2731 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
2732 	nfsmout_if(error);
2733 	*mrepp = nmrep.nmc_mhead;
2734 	nfsmout_on_status(nd, error);
2735 	if (nd->nd_vers == NFS_VER3) {
2736 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
2737 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2738 	}
2739 nfsmout:
2740 	nfsm_chain_build_done(error, &nmrep);
2741 	if (error) {
2742 		nfsm_chain_cleanup(&nmrep);
2743 		*mrepp = NULL;
2744 	}
2745 	return error;
2746 }
2747 
2748 /*
2749  * nfs rename service
2750  */
2751 int
nfsrv_rename(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2752 nfsrv_rename(
2753 	struct nfsrv_descript *nd,
2754 	struct nfsrv_sock *slp,
2755 	vfs_context_t ctx,
2756 	mbuf_t *mrepp)
2757 {
2758 	kauth_cred_t saved_cred = NULL;
2759 	uid_t saved_uid;
2760 	int error;
2761 	uint32_t fromlen, tolen;
2762 	int fdpreattrerr, fdpostattrerr;
2763 	int tdpreattrerr, tdpostattrerr;
2764 	char *frompath = NULL, *topath = NULL;
2765 	struct nameidata fromni, toni;
2766 	vnode_t fvp, tvp, tdvp, fdvp, fdirp, tdirp;
2767 	struct vnode_attr fdpreattr, fdpostattr;
2768 	struct vnode_attr tdpreattr, tdpostattr;
2769 	struct nfs_filehandle fnfh, tnfh;
2770 	struct nfs_export *fnx, *tnx;
2771 	struct nfs_export_options *fnxo, *tnxo;
2772 	enum vtype fvtype, tvtype;
2773 	int holding_mntlock;
2774 	mount_t locked_mp;
2775 	struct nfsm_chain *nmreq, nmrep;
2776 	char *from_name, *to_name;
2777 #if CONFIG_FSE
2778 	int from_len = 0, to_len = 0;
2779 	fse_info from_finfo, to_finfo;
2780 #endif
2781 	u_char didstats = 0;
2782 	const char *oname;
2783 
2784 	error = 0;
2785 	fdpreattrerr = fdpostattrerr = ENOENT;
2786 	tdpreattrerr = tdpostattrerr = ENOENT;
2787 	saved_uid = kauth_cred_getuid(nd->nd_cr);
2788 	fromlen = tolen = 0;
2789 	frompath = topath = NULL;
2790 	fdirp = tdirp = NULL;
2791 	nmreq = &nd->nd_nmreq;
2792 	nfsm_chain_null(&nmrep);
2793 
2794 	/*
2795 	 * these need to be set before calling any code
2796 	 * that they may take us out through the error path.
2797 	 */
2798 	holding_mntlock = 0;
2799 	fvp = tvp = NULL;
2800 	fdvp = tdvp = NULL;
2801 	locked_mp = NULL;
2802 
2803 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, fnfh.nfh_fhp, fnfh.nfh_len);
2804 	nfsm_chain_get_32(error, nmreq, fromlen);
2805 	nfsm_name_len_check(error, nd, fromlen);
2806 	nfsmerr_if(error);
2807 	error = nfsm_chain_get_path_namei(nmreq, fromlen, &fromni);
2808 	nfsmerr_if(error);
2809 	frompath = fromni.ni_cnd.cn_pnbuf;
2810 
2811 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, tnfh.nfh_fhp, tnfh.nfh_len);
2812 	nfsm_chain_get_32(error, nmreq, tolen);
2813 	nfsm_name_len_check(error, nd, tolen);
2814 	nfsmerr_if(error);
2815 	error = nfsm_chain_get_path_namei(nmreq, tolen, &toni);
2816 	nfsmerr_if(error);
2817 	topath = toni.ni_cnd.cn_pnbuf;
2818 
2819 	/*
2820 	 * Remember our original uid so that we can reset cr_uid before
2821 	 * the second nfsrv_namei() call, in case it is remapped.
2822 	 */
2823 	saved_cred = nd->nd_cr;
2824 	kauth_cred_ref(saved_cred);
2825 retry:
2826 	NDINIT(&fromni, DELETE, OP_UNLINK, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(frompath), ctx);
2827 	fromni.ni_cnd.cn_pnbuf = frompath;
2828 	frompath = NULL;
2829 	fromni.ni_cnd.cn_pnlen = MAXPATHLEN;
2830 	fromni.ni_cnd.cn_flags |= HASBUF;
2831 
2832 	error = nfsrv_namei(nd, ctx, &fromni, &fnfh, &fdirp, &fnx, &fnxo);
2833 	if (error) {
2834 		goto out;
2835 	}
2836 	fdvp = fromni.ni_dvp;
2837 	fvp  = fromni.ni_vp;
2838 
2839 	if (fdirp) {
2840 		if (nd->nd_vers == NFS_VER3) {
2841 			nfsm_srv_pre_vattr_init(&fdpreattr);
2842 			fdpreattrerr = vnode_getattr(fdirp, &fdpreattr, ctx);
2843 		} else {
2844 			vnode_put(fdirp);
2845 			fdirp = NULL;
2846 		}
2847 	}
2848 	fvtype = vnode_vtype(fvp);
2849 
2850 	/* reset credential if it was remapped */
2851 	if (nd->nd_cr != saved_cred) {
2852 		kauth_cred_ref(saved_cred);
2853 		kauth_cred_unref(&nd->nd_cr);
2854 		ctx->vc_ucred = nd->nd_cr = saved_cred;
2855 	}
2856 
2857 	NDINIT(&toni, RENAME, OP_RENAME, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(topath), ctx);
2858 	toni.ni_cnd.cn_pnbuf = topath;
2859 	topath = NULL;
2860 	toni.ni_cnd.cn_pnlen = MAXPATHLEN;
2861 	toni.ni_cnd.cn_flags |= HASBUF;
2862 
2863 	if (fvtype == VDIR) {
2864 		toni.ni_cnd.cn_flags |= WILLBEDIR;
2865 	}
2866 
2867 	tnx = NULL;
2868 	error = nfsrv_namei(nd, ctx, &toni, &tnfh, &tdirp, &tnx, &tnxo);
2869 	if (error) {
2870 		/*
2871 		 * Translate error code for rename("dir1", "dir2/.").
2872 		 */
2873 		if (error == EISDIR && fvtype == VDIR) {
2874 			if (nd->nd_vers == NFS_VER3) {
2875 				error = EINVAL;
2876 			} else {
2877 				error = ENOTEMPTY;
2878 			}
2879 		}
2880 		goto out;
2881 	}
2882 	tdvp = toni.ni_dvp;
2883 	tvp  = toni.ni_vp;
2884 
2885 	if (!didstats) {
2886 		/* update export stats once only */
2887 		if (tnx != NULL) {
2888 			/* update export stats */
2889 			NFSStatAdd64(&tnx->nx_stats.ops, 1);
2890 
2891 			/* update active user stats */
2892 			nfsrv_update_user_stat(tnx, nd, saved_uid, 1, 0, 0);
2893 			didstats = 1;
2894 		}
2895 	}
2896 
2897 	if (tdirp) {
2898 		if (nd->nd_vers == NFS_VER3) {
2899 			nfsm_srv_pre_vattr_init(&tdpreattr);
2900 			tdpreattrerr = vnode_getattr(tdirp, &tdpreattr, ctx);
2901 		} else {
2902 			vnode_put(tdirp);
2903 			tdirp = NULL;
2904 		}
2905 	}
2906 
2907 	if (tvp != NULL) {
2908 		tvtype = vnode_vtype(tvp);
2909 
2910 		if (fvtype == VDIR && tvtype != VDIR) {
2911 			if (nd->nd_vers == NFS_VER3) {
2912 				error = EEXIST;
2913 			} else {
2914 				error = EISDIR;
2915 			}
2916 			goto out;
2917 		} else if (fvtype != VDIR && tvtype == VDIR) {
2918 			if (nd->nd_vers == NFS_VER3) {
2919 				error = EEXIST;
2920 			} else {
2921 				error = ENOTDIR;
2922 			}
2923 			goto out;
2924 		}
2925 		if (tvtype == VDIR && vnode_mountedhere(tvp)) {
2926 			if (nd->nd_vers == NFS_VER3) {
2927 				error = EXDEV;
2928 			} else {
2929 				error = ENOTEMPTY;
2930 			}
2931 			goto out;
2932 		}
2933 	}
2934 	if (fvp == tdvp) {
2935 		if (nd->nd_vers == NFS_VER3) {
2936 			error = EINVAL;
2937 		} else {
2938 			error = ENOTEMPTY;
2939 		}
2940 		goto out;
2941 	}
2942 
2943 	/*
2944 	 * Authorization.
2945 	 *
2946 	 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2947 	 * the node is moving between directories and we need rights to remove from the
2948 	 * old and add to the new.
2949 	 *
2950 	 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2951 	 *
2952 	 * Note that we do not inherit when renaming.  XXX this needs to be revisited to
2953 	 * implement the deferred-inherit bit.
2954 	 */
2955 	{
2956 		int moving = 0;
2957 
2958 		error = 0;
2959 		if ((tvp != NULL) && vnode_isdir(tvp)) {
2960 			if (tvp != fdvp) {
2961 				moving = 1;
2962 			}
2963 		} else if (tdvp != fdvp) {
2964 			moving = 1;
2965 		}
2966 		if (moving) {
2967 			/* moving out of fdvp, must have delete rights */
2968 			if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx, fnxo, 0)) != 0) {
2969 				goto auth_exit;
2970 			}
2971 			/* moving into tdvp or tvp, must have rights to add */
2972 			if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp,
2973 			    NULL,
2974 			    vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2975 			    ctx, tnxo, 0)) != 0) {
2976 				goto auth_exit;
2977 			}
2978 		} else {
2979 			/* node staying in same directory, must be allowed to add new name */
2980 			if ((error = nfsrv_authorize(fdvp, NULL,
2981 			    vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2982 			    ctx, fnxo, 0)) != 0) {
2983 				goto auth_exit;
2984 			}
2985 		}
2986 		/* overwriting tvp */
2987 		if ((tvp != NULL) && !vnode_isdir(tvp) &&
2988 		    ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) {
2989 			goto auth_exit;
2990 		}
2991 
2992 		if (!error &&
2993 		    ((error = vn_authorize_rename(fdvp, fvp, &fromni.ni_cnd, tdvp, tvp, &toni.ni_cnd, ctx, NULL)) != 0)) {
2994 			if (error) {
2995 				error = EACCES;
2996 			}
2997 			goto auth_exit;
2998 		}
2999 		/* XXX more checks? */
3000 
3001 auth_exit:
3002 		/* authorization denied */
3003 		if (error != 0) {
3004 			goto out;
3005 		}
3006 	}
3007 
3008 	if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
3009 	    (tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) {
3010 		if (nd->nd_vers == NFS_VER3) {
3011 			error = EXDEV;
3012 		} else {
3013 			error = ENOTEMPTY;
3014 		}
3015 		goto out;
3016 	}
3017 	/*
3018 	 * The following edge case is caught here:
3019 	 * (to cannot be a descendent of from)
3020 	 *
3021 	 *       o fdvp
3022 	 *      /
3023 	 *     /
3024 	 *    o fvp
3025 	 *     \
3026 	 *      \
3027 	 *       o tdvp
3028 	 *      /
3029 	 *     /
3030 	 *    o tvp
3031 	 */
3032 	if (tdvp->v_parent == fvp) {
3033 		if (nd->nd_vers == NFS_VER3) {
3034 			error = EXDEV;
3035 		} else {
3036 			error = ENOTEMPTY;
3037 		}
3038 		goto out;
3039 	}
3040 	if (fvtype == VDIR && vnode_mountedhere(fvp)) {
3041 		if (nd->nd_vers == NFS_VER3) {
3042 			error = EXDEV;
3043 		} else {
3044 			error = ENOTEMPTY;
3045 		}
3046 		goto out;
3047 	}
3048 	/*
3049 	 * If source is the same as the destination (that is the
3050 	 * same vnode) then there is nothing to do...
3051 	 * EXCEPT if the underlying file system supports case
3052 	 * insensitivity and is case preserving.  In this case
3053 	 * the file system needs to handle the special case of
3054 	 * getting the same vnode as target (fvp) and source (tvp).
3055 	 *
3056 	 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3057 	 * and _PC_CASE_PRESERVING can have this exception, and they need to
3058 	 * handle the special case of getting the same vnode as target and
3059 	 * source.  NOTE: Then the target is unlocked going into vnop_rename,
3060 	 * so not to cause locking problems. There is a single reference on tvp.
3061 	 *
3062 	 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3063 	 * that correct behaviour then is just to remove the source (link)
3064 	 */
3065 	if ((fvp == tvp) && (fdvp == tdvp)) {
3066 		if (fromni.ni_cnd.cn_namelen == toni.ni_cnd.cn_namelen &&
3067 		    !bcmp(fromni.ni_cnd.cn_nameptr, toni.ni_cnd.cn_nameptr,
3068 		    fromni.ni_cnd.cn_namelen)) {
3069 			goto out;
3070 		}
3071 	}
3072 
3073 	if (holding_mntlock && vnode_mount(fvp) != locked_mp) {
3074 		/*
3075 		 * we're holding a reference and lock
3076 		 * on locked_mp, but it no longer matches
3077 		 * what we want to do... so drop our hold
3078 		 */
3079 		mount_unlock_renames(locked_mp);
3080 		mount_drop(locked_mp, 0);
3081 		holding_mntlock = 0;
3082 	}
3083 	if (tdvp != fdvp && fvtype == VDIR) {
3084 		/*
3085 		 * serialize renames that re-shape
3086 		 * the tree... if holding_mntlock is
3087 		 * set, then we're ready to go...
3088 		 * otherwise we
3089 		 * first need to drop the iocounts
3090 		 * we picked up, second take the
3091 		 * lock to serialize the access,
3092 		 * then finally start the lookup
3093 		 * process over with the lock held
3094 		 */
3095 		if (!holding_mntlock) {
3096 			/*
3097 			 * need to grab a reference on
3098 			 * the mount point before we
3099 			 * drop all the iocounts... once
3100 			 * the iocounts are gone, the mount
3101 			 * could follow
3102 			 */
3103 			locked_mp = vnode_mount(fvp);
3104 			mount_ref(locked_mp, 0);
3105 
3106 			/* make a copy of to path to pass to nfsrv_namei() again */
3107 			topath = zalloc(ZV_NAMEI);
3108 			bcopy(toni.ni_cnd.cn_pnbuf, topath, tolen + 1);
3109 
3110 			/*
3111 			 * nameidone has to happen before we vnode_put(tdvp)
3112 			 * since it may need to release the fs_nodelock on the tdvp
3113 			 */
3114 			nameidone(&toni);
3115 
3116 			if (tvp) {
3117 				vnode_put(tvp);
3118 			}
3119 			vnode_put(tdvp);
3120 
3121 			/* make a copy of from path to pass to nfsrv_namei() again */
3122 			frompath = zalloc(ZV_NAMEI);
3123 			bcopy(fromni.ni_cnd.cn_pnbuf, frompath, fromlen + 1);
3124 
3125 			/*
3126 			 * nameidone has to happen before we vnode_put(fdvp)
3127 			 * since it may need to release the fs_nodelock on the fdvp
3128 			 */
3129 			nameidone(&fromni);
3130 
3131 			vnode_put(fvp);
3132 			vnode_put(fdvp);
3133 
3134 			if (fdirp) {
3135 				vnode_put(fdirp);
3136 				fdirp = NULL;
3137 			}
3138 			if (tdirp) {
3139 				vnode_put(tdirp);
3140 				tdirp = NULL;
3141 			}
3142 			mount_lock_renames(locked_mp);
3143 			holding_mntlock = 1;
3144 
3145 			fvp = tvp = NULL;
3146 			fdvp = tdvp = NULL;
3147 
3148 			fdpreattrerr = tdpreattrerr = ENOENT;
3149 
3150 			if (!topath || !frompath) {
3151 				/* we couldn't allocate a path, so bail */
3152 				error = ENOMEM;
3153 				goto out;
3154 			}
3155 
3156 			/* reset credential if it was remapped */
3157 			if (nd->nd_cr != saved_cred) {
3158 				kauth_cred_ref(saved_cred);
3159 				kauth_cred_unref(&nd->nd_cr);
3160 				ctx->vc_ucred = nd->nd_cr = saved_cred;
3161 			}
3162 
3163 			goto retry;
3164 		}
3165 	} else {
3166 		/*
3167 		 * when we dropped the iocounts to take
3168 		 * the lock, we allowed the identity of
3169 		 * the various vnodes to change... if they did,
3170 		 * we may no longer be dealing with a rename
3171 		 * that reshapes the tree... once we're holding
3172 		 * the iocounts, the vnodes can't change type
3173 		 * so we're free to drop the lock at this point
3174 		 * and continue on
3175 		 */
3176 		if (holding_mntlock) {
3177 			mount_unlock_renames(locked_mp);
3178 			mount_drop(locked_mp, 0);
3179 			holding_mntlock = 0;
3180 		}
3181 	}
3182 
3183 	// save these off so we can later verify that fvp is the same
3184 	vnode_t oparent;
3185 	oname   = fvp->v_name;
3186 	oparent = fvp->v_parent;
3187 
3188 	/*
3189 	 * If generating an fsevent, then
3190 	 * stash any pre-rename info we may need.
3191 	 */
3192 #if CONFIG_FSE
3193 	if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, fvp)) {
3194 		int from_truncated = 0, to_truncated = 0;
3195 
3196 		get_fse_info(fvp, &from_finfo, ctx);
3197 		if (tvp) {
3198 			get_fse_info(tvp, &to_finfo, ctx);
3199 		}
3200 
3201 		from_name = get_pathbuff();
3202 		if (from_name) {
3203 			from_len = safe_getpath(fdvp, fromni.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated);
3204 		}
3205 
3206 		to_name = from_name ? get_pathbuff() : NULL;
3207 		if (to_name) {
3208 			to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated);
3209 		}
3210 
3211 		if (from_truncated || to_truncated) {
3212 			from_finfo.mode |= FSE_TRUNCATED_PATH;
3213 		}
3214 	} else {
3215 		from_name = NULL;
3216 		to_name   = NULL;
3217 	}
3218 #else /* CONFIG_FSE */
3219 	from_name = NULL;
3220 	to_name   = NULL;
3221 #endif /* CONFIG_FSE */
3222 
3223 	error = VNOP_RENAME(fromni.ni_dvp, fromni.ni_vp, &fromni.ni_cnd,
3224 	    toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx);
3225 	/*
3226 	 * fix up name & parent pointers.  note that we first
3227 	 * check that fvp has the same name/parent pointers it
3228 	 * had before the rename call... this is a 'weak' check
3229 	 * at best...
3230 	 */
3231 	if (oname == fvp->v_name && oparent == fvp->v_parent) {
3232 		int update_flags;
3233 		update_flags = VNODE_UPDATE_NAME;
3234 		if (fdvp != tdvp) {
3235 			update_flags |= VNODE_UPDATE_PARENT;
3236 		}
3237 		vnode_update_identity(fvp, tdvp, toni.ni_cnd.cn_nameptr,
3238 		    toni.ni_cnd.cn_namelen, toni.ni_cnd.cn_hash, update_flags);
3239 	}
3240 
3241 	/*
3242 	 * If the rename is OK and we've got the paths
3243 	 * then add an fsevent.
3244 	 */
3245 #if CONFIG_FSE
3246 	if (nfsrv_fsevents_enabled && !error && from_name && to_name) {
3247 		if (tvp) {
3248 			add_fsevent(FSE_RENAME, ctx,
3249 			    FSE_ARG_STRING, from_len, from_name,
3250 			    FSE_ARG_FINFO, &from_finfo,
3251 			    FSE_ARG_STRING, to_len, to_name,
3252 			    FSE_ARG_FINFO, &to_finfo,
3253 			    FSE_ARG_DONE);
3254 		} else {
3255 			add_fsevent(FSE_RENAME, ctx,
3256 			    FSE_ARG_STRING, from_len, from_name,
3257 			    FSE_ARG_FINFO, &from_finfo,
3258 			    FSE_ARG_STRING, to_len, to_name,
3259 			    FSE_ARG_DONE);
3260 		}
3261 	}
3262 	if (from_name) {
3263 		release_pathbuff(from_name);
3264 	}
3265 	if (to_name) {
3266 		release_pathbuff(to_name);
3267 	}
3268 #endif /* CONFIG_FSE */
3269 	from_name = to_name = NULL;
3270 
3271 out:
3272 	if (holding_mntlock) {
3273 		mount_unlock_renames(locked_mp);
3274 		mount_drop(locked_mp, 0);
3275 		holding_mntlock = 0;
3276 	}
3277 	if (tdvp) {
3278 		/*
3279 		 * nameidone has to happen before we vnode_put(tdvp)
3280 		 * since it may need to release the fs_nodelock on the tdvp
3281 		 */
3282 		nameidone(&toni);
3283 		if (tvp) {
3284 			vnode_put(tvp);
3285 		}
3286 		vnode_put(tdvp);
3287 
3288 		tdvp = NULL;
3289 	}
3290 	if (fdvp) {
3291 		/*
3292 		 * nameidone has to happen before we vnode_put(fdvp)
3293 		 * since it may need to release the fs_nodelock on the fdvp
3294 		 */
3295 		nameidone(&fromni);
3296 
3297 		if (fvp) {
3298 			vnode_put(fvp);
3299 		}
3300 		vnode_put(fdvp);
3301 
3302 		fdvp = NULL;
3303 	}
3304 	if (fdirp) {
3305 		nfsm_srv_vattr_init(&fdpostattr, nd->nd_vers);
3306 		fdpostattrerr = vnode_getattr(fdirp, &fdpostattr, ctx);
3307 		vnode_put(fdirp);
3308 		fdirp = NULL;
3309 	}
3310 	if (tdirp) {
3311 		nfsm_srv_vattr_init(&tdpostattr, nd->nd_vers);
3312 		tdpostattrerr = vnode_getattr(tdirp, &tdpostattr, ctx);
3313 		vnode_put(tdirp);
3314 		tdirp = NULL;
3315 	}
3316 
3317 nfsmerr:
3318 	/* assemble reply */
3319 	nd->nd_repstat = error;
3320 	error = nfsrv_rephead(nd, slp, &nmrep, 2 * NFSX_WCCDATA(nd->nd_vers));
3321 	nfsmout_if(error);
3322 	*mrepp = nmrep.nmc_mhead;
3323 	nfsmout_on_status(nd, error);
3324 	if (nd->nd_vers == NFS_VER3) {
3325 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3326 		    fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr);
3327 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3328 		    tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr);
3329 	}
3330 nfsmout:
3331 	nfsm_chain_build_done(error, &nmrep);
3332 	if (holding_mntlock) {
3333 		mount_unlock_renames(locked_mp);
3334 		mount_drop(locked_mp, 0);
3335 	}
3336 	if (tdvp) {
3337 		/*
3338 		 * nameidone has to happen before we vnode_put(tdvp)
3339 		 * since it may need to release the fs_nodelock on the tdvp
3340 		 */
3341 		nameidone(&toni);
3342 
3343 		if (tvp) {
3344 			vnode_put(tvp);
3345 		}
3346 		vnode_put(tdvp);
3347 	}
3348 	if (fdvp) {
3349 		/*
3350 		 * nameidone has to happen before we vnode_put(fdvp)
3351 		 * since it may need to release the fs_nodelock on the fdvp
3352 		 */
3353 		nameidone(&fromni);
3354 
3355 		if (fvp) {
3356 			vnode_put(fvp);
3357 		}
3358 		vnode_put(fdvp);
3359 	}
3360 	if (fdirp) {
3361 		vnode_put(fdirp);
3362 	}
3363 	if (tdirp) {
3364 		vnode_put(tdirp);
3365 	}
3366 	if (frompath) {
3367 		NFS_ZFREE(ZV_NAMEI, frompath);
3368 	}
3369 	if (topath) {
3370 		NFS_ZFREE(ZV_NAMEI, topath);
3371 	}
3372 	if (saved_cred) {
3373 		kauth_cred_unref(&saved_cred);
3374 	}
3375 	if (error) {
3376 		nfsm_chain_cleanup(&nmrep);
3377 		*mrepp = NULL;
3378 	}
3379 	return error;
3380 }
3381 
3382 /*
3383  * nfs link service
3384  */
3385 int
nfsrv_link(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3386 nfsrv_link(
3387 	struct nfsrv_descript *nd,
3388 	struct nfsrv_sock *slp,
3389 	vfs_context_t ctx,
3390 	mbuf_t *mrepp)
3391 {
3392 	struct nameidata ni;
3393 	int error, dpreattrerr, dpostattrerr, attrerr;
3394 	uint32_t len = 0;
3395 	vnode_t vp, xp, dvp, dirp;
3396 	struct vnode_attr dpreattr, dpostattr, attr;
3397 	struct nfs_filehandle nfh, dnfh;
3398 	struct nfs_export *nx;
3399 	struct nfs_export_options *nxo;
3400 	struct nfsm_chain *nmreq, nmrep;
3401 
3402 	error = 0;
3403 	dpreattrerr = dpostattrerr = attrerr = ENOENT;
3404 	vp = xp = dvp = dirp = NULL;
3405 	nmreq = &nd->nd_nmreq;
3406 	nfsm_chain_null(&nmrep);
3407 
3408 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3409 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
3410 	nfsm_chain_get_32(error, nmreq, len);
3411 	nfsm_name_len_check(error, nd, len);
3412 	nfsmerr_if(error);
3413 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
3414 	nfsmerr_if(error);
3415 
3416 	/* update export stats */
3417 	NFSStatAdd64(&nx->nx_stats.ops, 1);
3418 
3419 	/* update active user stats */
3420 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
3421 
3422 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
3423 	nfsmerr_if(error);
3424 
3425 	/* we're not allowed to link to directories... */
3426 	if (vnode_vtype(vp) == VDIR) {
3427 		error = NFSERR_ISDIR;
3428 		goto out;
3429 	}
3430 
3431 	/* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3432 	if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) {
3433 		goto out;
3434 	}
3435 
3436 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(vnode_getname(vp)), ctx);
3437 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3438 	if (!error) {
3439 		error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
3440 	}
3441 	if (dirp) {
3442 		if (nd->nd_vers == NFS_VER3) {
3443 			nfsm_srv_pre_vattr_init(&dpreattr);
3444 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3445 		} else {
3446 			vnode_put(dirp);
3447 			dirp = NULL;
3448 		}
3449 	}
3450 	if (error) {
3451 		goto out;
3452 	}
3453 	dvp = ni.ni_dvp;
3454 	xp = ni.ni_vp;
3455 
3456 	if (xp != NULL) {
3457 		error = EEXIST;
3458 	} else if (vnode_mount(vp) != vnode_mount(dvp)) {
3459 		error = EXDEV;
3460 	} else {
3461 		error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3462 	}
3463 
3464 #if CONFIG_MACF
3465 	if (!error) {
3466 		error = mac_vnode_check_link(ctx, dvp, vp, &ni.ni_cnd);
3467 		if (error) {
3468 			error = EACCES;
3469 		}
3470 	}
3471 #endif
3472 	if (!error) {
3473 		error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx);
3474 	}
3475 
3476 #if CONFIG_FSE
3477 	if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
3478 		char *target_path = NULL;
3479 		int plen, truncated = 0;
3480 		fse_info finfo;
3481 
3482 		/* build the path to the new link file */
3483 		target_path = get_pathbuff();
3484 		if (target_path) {
3485 			plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
3486 
3487 			if (get_fse_info(vp, &finfo, ctx) == 0) {
3488 				if (truncated) {
3489 					finfo.mode |= FSE_TRUNCATED_PATH;
3490 				}
3491 				add_fsevent(FSE_CREATE_FILE, ctx,
3492 				    FSE_ARG_STRING, plen, target_path,
3493 				    FSE_ARG_FINFO, &finfo,
3494 				    FSE_ARG_DONE);
3495 			}
3496 
3497 			release_pathbuff(target_path);
3498 		}
3499 	}
3500 #endif
3501 
3502 	/*
3503 	 * nameidone has to happen before we vnode_put(dvp)
3504 	 * since it may need to release the fs_nodelock on the dvp
3505 	 */
3506 	nameidone(&ni);
3507 
3508 	if (xp) {
3509 		vnode_put(xp);
3510 	}
3511 	vnode_put(dvp);
3512 out:
3513 	if (nd->nd_vers == NFS_VER3) {
3514 		nfsm_srv_vattr_init(&attr, NFS_VER3);
3515 		attrerr = vnode_getattr(vp, &attr, ctx);
3516 	}
3517 	if (dirp) {
3518 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3519 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3520 		vnode_put(dirp);
3521 		dirp = NULL;
3522 	}
3523 	vnode_put(vp);
3524 	vp = NULL;
3525 
3526 nfsmerr:
3527 	/* assemble reply */
3528 	nd->nd_repstat = error;
3529 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3530 	nfsmout_if(error);
3531 	*mrepp = nmrep.nmc_mhead;
3532 	nfsmout_on_status(nd, error);
3533 	if (nd->nd_vers == NFS_VER3) {
3534 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
3535 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3536 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3537 	}
3538 nfsmout:
3539 	nfsm_chain_build_done(error, &nmrep);
3540 	if (vp) {
3541 		vnode_put(vp);
3542 	}
3543 	if (error) {
3544 		nfsm_chain_cleanup(&nmrep);
3545 		*mrepp = NULL;
3546 	}
3547 	return error;
3548 }
3549 
3550 /*
3551  * nfs symbolic link service
3552  */
3553 int
nfsrv_symlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3554 nfsrv_symlink(
3555 	struct nfsrv_descript *nd,
3556 	struct nfsrv_sock *slp,
3557 	vfs_context_t ctx,
3558 	mbuf_t *mrepp)
3559 {
3560 	struct vnode_attr dpreattr, dpostattr, postattr;
3561 	struct vnode_attr va, *vap = &va;
3562 	struct nameidata ni;
3563 	int error, dpreattrerr, dpostattrerr, postattrerr;
3564 	uint32_t len = 0, linkdatalen = 0, cnflags;
3565 	uid_t saved_uid;
3566 	char *linkdata;
3567 	vnode_t vp, dvp, dirp;
3568 	struct nfs_filehandle nfh = {};
3569 	struct nfs_export *nx = NULL;
3570 	struct nfs_export_options *nxo = NULL;
3571 	uio_t auio = NULL;
3572 	UIO_STACKBUF(uio_buf, 1);
3573 	struct nfsm_chain *nmreq, nmrep;
3574 	__unused const int nfs_vers = nd->nd_vers;
3575 	assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3576 
3577 	error = 0;
3578 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3579 	nmreq = &nd->nd_nmreq;
3580 	nfsm_chain_null(&nmrep);
3581 	linkdata = NULL;
3582 	dirp = NULL;
3583 
3584 	saved_uid = kauth_cred_getuid(nd->nd_cr);
3585 
3586 	ni.ni_cnd.cn_nameiop = 0;
3587 	vp = dvp = NULL;
3588 
3589 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3590 	nfsm_chain_get_32(error, nmreq, len);
3591 	nfsm_name_len_check(error, nd, len);
3592 	nfsmerr_if(error);
3593 
3594 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, 0, ctx);
3595 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3596 	if (!error) {
3597 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3598 		if (nx != NULL) {
3599 			/* update export stats */
3600 			NFSStatAdd64(&nx->nx_stats.ops, 1);
3601 
3602 			/* update active user stats */
3603 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3604 		}
3605 	}
3606 	if (dirp) {
3607 		if (nd->nd_vers == NFS_VER3) {
3608 			nfsm_srv_pre_vattr_init(&dpreattr);
3609 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3610 		} else {
3611 			vnode_put(dirp);
3612 			dirp = NULL;
3613 		}
3614 	}
3615 	if (error) {
3616 		ni.ni_cnd.cn_nameiop = 0;
3617 		goto out1;
3618 	}
3619 	dvp = ni.ni_dvp;
3620 	vp = ni.ni_vp;
3621 
3622 	VATTR_INIT(vap);
3623 	if (nd->nd_vers == NFS_VER3) {
3624 		error = nfsm_chain_get_sattr(nd, nmreq, vap);
3625 	}
3626 	nfsm_chain_get_32(error, nmreq, linkdatalen);
3627 	if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) ||
3628 	    ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) {
3629 		error = NFSERR_NAMETOL;
3630 	}
3631 	nfsmerr_if(error);
3632 	linkdata = kalloc_data(linkdatalen + 1, Z_WAITOK);
3633 	if (linkdata) {
3634 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3635 		    &uio_buf[0], sizeof(uio_buf));
3636 	}
3637 	if (!linkdata || !auio) {
3638 		error = ENOMEM;
3639 		goto out;
3640 	}
3641 	uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
3642 	error = nfsm_chain_get_uio(nmreq, linkdatalen, auio);
3643 	if (!error && (nd->nd_vers == NFS_VER2)) {
3644 		error = nfsm_chain_get_sattr(nd, nmreq, vap);
3645 	}
3646 	nfsmerr_if(error);
3647 	*(linkdata + linkdatalen) = '\0';
3648 	if (vp) {
3649 		error = EEXIST;
3650 		goto out;
3651 	}
3652 
3653 	VATTR_SET(vap, va_type, VLNK);
3654 	VATTR_CLEAR_ACTIVE(vap, va_data_size);
3655 	VATTR_CLEAR_ACTIVE(vap, va_access_time);
3656 	/*
3657 	 * Server policy is to alway use the mapped rpc credential for
3658 	 * file system object creation. This has the nice side effect of
3659 	 * enforcing BSD creation semantics
3660 	 */
3661 	VATTR_CLEAR_ACTIVE(vap, va_uid);
3662 	VATTR_CLEAR_ACTIVE(vap, va_gid);
3663 
3664 	/* authorize before creating */
3665 	error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3666 
3667 	/* validate given attributes */
3668 	if (!error) {
3669 		error = vnode_authattr_new(dvp, vap, 0, ctx);
3670 	}
3671 	if (!error) {
3672 		error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
3673 		if (error) {
3674 			error = EACCES;
3675 		}
3676 	}
3677 
3678 	if (!error) {
3679 		error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
3680 	}
3681 
3682 	if (!error && (nd->nd_vers == NFS_VER3)) {
3683 		if (vp == NULL) {
3684 			ni.ni_cnd.cn_nameiop = LOOKUP;
3685 #if CONFIG_TRIGGERS
3686 			ni.ni_op = OP_LOOKUP;
3687 #endif
3688 			ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3689 			ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3690 			ni.ni_cnd.cn_context = ctx;
3691 			ni.ni_startdir = dvp;
3692 			ni.ni_usedvp   = dvp;
3693 			ni.ni_rootdir = rootvnode;
3694 			cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
3695 			while ((error = lookup(&ni)) == ERECYCLE) {
3696 				ni.ni_cnd.cn_flags = cnflags;
3697 				ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
3698 				ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
3699 			}
3700 			if (!error) {
3701 				vp = ni.ni_vp;
3702 			}
3703 		}
3704 		if (!error) {
3705 			error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
3706 			if (!error) {
3707 				nfsm_srv_vattr_init(&postattr, NFS_VER3);
3708 				postattrerr = vnode_getattr(vp, &postattr, ctx);
3709 			}
3710 		}
3711 	}
3712 
3713 #if CONFIG_FSE
3714 	if (nfsrv_fsevents_enabled && !error && vp) {
3715 		add_fsevent(FSE_CREATE_FILE, ctx,
3716 		    FSE_ARG_VNODE, vp,
3717 		    FSE_ARG_DONE);
3718 	}
3719 #endif
3720 out:
3721 	/*
3722 	 * nameidone has to happen before we vnode_put(dvp)
3723 	 * since it may need to release the fs_nodelock on the dvp
3724 	 */
3725 	nameidone(&ni);
3726 	ni.ni_cnd.cn_nameiop = 0;
3727 	if (vp) {
3728 		vnode_put(vp);
3729 	}
3730 	vnode_put(dvp);
3731 out1:
3732 	if (linkdata) {
3733 		kfree_data(linkdata, linkdatalen + 1);
3734 	}
3735 	if (dirp) {
3736 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3737 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3738 		vnode_put(dirp);
3739 		dirp = NULL;
3740 	}
3741 
3742 nfsmerr:
3743 	/* assemble reply */
3744 	nd->nd_repstat = error;
3745 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3746 	    NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3747 	assert(nfs_vers == nd->nd_vers);
3748 	nfsmout_if(error);
3749 	*mrepp = nmrep.nmc_mhead;
3750 	nfsmout_on_status(nd, error);
3751 	if (nd->nd_vers == NFS_VER3) {
3752 		if (!nd->nd_repstat) {
3753 			if (!nfh.nfh_fhp) {
3754 				error = NFSERR_SERVERFAULT;
3755 				goto nfsmerr;
3756 			}
3757 			nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3758 			nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3759 		}
3760 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3761 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3762 	}
3763 nfsmout:
3764 	nfsm_chain_build_done(error, &nmrep);
3765 	if (ni.ni_cnd.cn_nameiop) {
3766 		/*
3767 		 * nameidone has to happen before we vnode_put(dvp)
3768 		 * since it may need to release the fs_nodelock on the dvp
3769 		 */
3770 		nameidone(&ni);
3771 
3772 		if (vp) {
3773 			vnode_put(vp);
3774 		}
3775 		vnode_put(dvp);
3776 	}
3777 	if (dirp) {
3778 		vnode_put(dirp);
3779 	}
3780 	if (linkdata) {
3781 		kfree_data(linkdata, linkdatalen + 1);
3782 	}
3783 	if (error) {
3784 		nfsm_chain_cleanup(&nmrep);
3785 		*mrepp = NULL;
3786 	}
3787 	return error;
3788 }
3789 
3790 /*
3791  * nfs mkdir service
3792  */
3793 
3794 int
nfsrv_mkdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3795 nfsrv_mkdir(
3796 	struct nfsrv_descript *nd,
3797 	struct nfsrv_sock *slp,
3798 	vfs_context_t ctx,
3799 	mbuf_t *mrepp)
3800 {
3801 	struct vnode_attr dpreattr, dpostattr, postattr;
3802 	struct vnode_attr va, *vap = &va;
3803 	struct nameidata ni;
3804 	int error, dpreattrerr, dpostattrerr, postattrerr;
3805 	uint32_t len = 0;
3806 	vnode_t vp, dvp, dirp;
3807 	struct nfs_filehandle nfh = {};
3808 	struct nfs_export *nx = NULL;
3809 	struct nfs_export_options *nxo = NULL;
3810 	uid_t saved_uid;
3811 	kauth_acl_t xacl = NULL;
3812 	struct nfsm_chain *nmreq, nmrep;
3813 	__unused const int nfs_vers = nd->nd_vers;
3814 	assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3815 
3816 	error = 0;
3817 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3818 	nmreq = &nd->nd_nmreq;
3819 	nfsm_chain_null(&nmrep);
3820 
3821 	saved_uid = kauth_cred_getuid(nd->nd_cr);
3822 
3823 	ni.ni_cnd.cn_nameiop = 0;
3824 	vp = dvp = dirp = NULL;
3825 
3826 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3827 	nfsm_chain_get_32(error, nmreq, len);
3828 	nfsm_name_len_check(error, nd, len);
3829 	nfsmerr_if(error);
3830 
3831 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | WILLBEDIR, UIO_SYSSPACE, 0, ctx);
3832 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3833 	if (!error) {
3834 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3835 		if (nx != NULL) {
3836 			/* update export stats */
3837 			NFSStatAdd64(&nx->nx_stats.ops, 1);
3838 
3839 			/* update active user stats */
3840 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3841 		}
3842 	}
3843 	if (dirp) {
3844 		if (nd->nd_vers == NFS_VER3) {
3845 			nfsm_srv_pre_vattr_init(&dpreattr);
3846 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3847 		} else {
3848 			vnode_put(dirp);
3849 			dirp = NULL;
3850 		}
3851 	}
3852 	if (error) {
3853 		ni.ni_cnd.cn_nameiop = 0;
3854 		goto nfsmerr;
3855 	}
3856 	dvp = ni.ni_dvp;
3857 	vp = ni.ni_vp;
3858 
3859 	VATTR_INIT(vap);
3860 	error = nfsm_chain_get_sattr(nd, nmreq, vap);
3861 	nfsmerr_if(error);
3862 	VATTR_SET(vap, va_type, VDIR);
3863 
3864 	if (vp != NULL) {
3865 		/*
3866 		 * nameidone has to happen before we vnode_put(dvp)
3867 		 * since it may need to release the fs_nodelock on the dvp
3868 		 */
3869 		nameidone(&ni);
3870 		vnode_put(dvp);
3871 		vnode_put(vp);
3872 		error = EEXIST;
3873 		goto out;
3874 	}
3875 
3876 	error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
3877 
3878 	/* construct ACL and handle inheritance */
3879 	if (!error) {
3880 		error = kauth_acl_inherit(dvp,
3881 		    NULL,
3882 		    &xacl,      /* isdir */
3883 		    1,
3884 		    ctx);
3885 
3886 		if (!error && xacl != NULL) {
3887 			VATTR_SET(vap, va_acl, xacl);
3888 		}
3889 	}
3890 
3891 	VATTR_CLEAR_ACTIVE(vap, va_data_size);
3892 	VATTR_CLEAR_ACTIVE(vap, va_access_time);
3893 	/*
3894 	 * We don't support the S_ISGID bit for directories. Solaris and other
3895 	 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3896 	 * any ways.
3897 	 */
3898 	if (VATTR_IS_ACTIVE(vap, va_mode)) {
3899 		vap->va_mode &= ~S_ISGID;
3900 	}
3901 	/*
3902 	 * Server policy is to alway use the mapped rpc credential for
3903 	 * file system object creation. This has the nice side effect of
3904 	 * enforcing BSD creation semantics
3905 	 */
3906 	VATTR_CLEAR_ACTIVE(vap, va_uid);
3907 	VATTR_CLEAR_ACTIVE(vap, va_gid);
3908 
3909 	/* validate new-file security information */
3910 	if (!error) {
3911 		error = vnode_authattr_new(dvp, vap, 0, ctx);
3912 	}
3913 	/*
3914 	 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3915 	 * sit well with our clients so we map all errors to EPERM.
3916 	 */
3917 	if (error) {
3918 		error = EPERM;
3919 	}
3920 
3921 	if (!error) {
3922 		error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL);
3923 		if (error) {
3924 			error = EACCES;
3925 		}
3926 	}
3927 
3928 	if (!error) {
3929 		error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
3930 	}
3931 
3932 #if CONFIG_FSE
3933 	if (nfsrv_fsevents_enabled && !error) {
3934 		add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3935 	}
3936 #endif
3937 
3938 	if (!error && !VATTR_ALL_SUPPORTED(vap)) {
3939 		/*
3940 		 * If some of the requested attributes weren't handled by the VNOP,
3941 		 * use our fallback code.
3942 		 */
3943 		error = vnode_setattr_fallback(vp, vap, ctx);
3944 	}
3945 
3946 	if (xacl != NULL) {
3947 		kauth_acl_free(xacl);
3948 	}
3949 
3950 	if (!error) {
3951 		error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
3952 		if (!error) {
3953 			nfsm_srv_vattr_init(&postattr, nd->nd_vers);
3954 			postattrerr = vnode_getattr(vp, &postattr, ctx);
3955 			if (nd->nd_vers == NFS_VER2) {
3956 				error = postattrerr;
3957 			}
3958 		}
3959 		vnode_put(vp);
3960 		vp = NULL;
3961 	}
3962 	/*
3963 	 * nameidone has to happen before we vnode_put(dvp)
3964 	 * since it may need to release the fs_nodelock on the dvp
3965 	 */
3966 	nameidone(&ni);
3967 	vnode_put(dvp);
3968 out:
3969 	ni.ni_cnd.cn_nameiop = 0;
3970 
3971 	if (dirp) {
3972 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3973 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3974 		vnode_put(dirp);
3975 		dirp = NULL;
3976 	}
3977 
3978 nfsmerr:
3979 	/* assemble reply */
3980 	nd->nd_repstat = error;
3981 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3982 	    NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3983 	assert(nfs_vers == nd->nd_vers);
3984 	nfsmout_if(error);
3985 	*mrepp = nmrep.nmc_mhead;
3986 	nfsmout_on_status(nd, error);
3987 	if (nd->nd_vers == NFS_VER3) {
3988 		if (!nd->nd_repstat) {
3989 			if (!nfh.nfh_fhp) {
3990 				error = NFSERR_SERVERFAULT;
3991 				goto nfsmerr;
3992 			}
3993 			nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3994 			nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3995 		}
3996 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3997 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3998 	} else {
3999 		nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
4000 		if (!error) {
4001 			error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
4002 		}
4003 	}
4004 nfsmout:
4005 	nfsm_chain_build_done(error, &nmrep);
4006 	if (ni.ni_cnd.cn_nameiop) {
4007 		/*
4008 		 * nameidone has to happen before we vnode_put(dvp)
4009 		 * since it may need to release the fs_nodelock on the dvp
4010 		 */
4011 		nameidone(&ni);
4012 		vnode_put(dvp);
4013 		if (vp) {
4014 			vnode_put(vp);
4015 		}
4016 	}
4017 	if (dirp) {
4018 		vnode_put(dirp);
4019 	}
4020 	if (error) {
4021 		nfsm_chain_cleanup(&nmrep);
4022 		*mrepp = NULL;
4023 	}
4024 	return error;
4025 }
4026 
4027 /*
4028  * nfs rmdir service
4029  */
4030 int
nfsrv_rmdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4031 nfsrv_rmdir(
4032 	struct nfsrv_descript *nd,
4033 	struct nfsrv_sock *slp,
4034 	vfs_context_t ctx,
4035 	mbuf_t *mrepp)
4036 {
4037 	int error, dpreattrerr, dpostattrerr;
4038 	uint32_t len = 0;
4039 	uid_t saved_uid;
4040 	vnode_t vp, dvp, dirp;
4041 	struct vnode_attr dpreattr, dpostattr;
4042 	struct nfs_filehandle nfh;
4043 	struct nfs_export *nx = NULL;
4044 	struct nfs_export_options *nxo = NULL;
4045 	struct nameidata ni;
4046 	struct nfsm_chain *nmreq, nmrep;
4047 
4048 	error = 0;
4049 	dpreattrerr = dpostattrerr = ENOENT;
4050 	saved_uid = kauth_cred_getuid(nd->nd_cr);
4051 	nmreq = &nd->nd_nmreq;
4052 	nfsm_chain_null(&nmrep);
4053 
4054 	vp = dvp = dirp = NULL;
4055 
4056 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4057 	nfsm_chain_get_32(error, nmreq, len);
4058 	nfsm_name_len_check(error, nd, len);
4059 	nfsmerr_if(error);
4060 
4061 	NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
4062 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
4063 	if (!error) {
4064 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
4065 		if (nx != NULL) {
4066 			/* update export stats */
4067 			NFSStatAdd64(&nx->nx_stats.ops, 1);
4068 
4069 			/* update active user stats */
4070 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
4071 		}
4072 	}
4073 	if (dirp) {
4074 		if (nd->nd_vers == NFS_VER3) {
4075 			nfsm_srv_pre_vattr_init(&dpreattr);
4076 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
4077 		} else {
4078 			vnode_put(dirp);
4079 			dirp = NULL;
4080 		}
4081 	}
4082 	nfsmerr_if(error);
4083 
4084 	dvp = ni.ni_dvp;
4085 	vp = ni.ni_vp;
4086 
4087 	if (vnode_vtype(vp) != VDIR) {
4088 		error = ENOTDIR;
4089 		goto out;
4090 	}
4091 	/*
4092 	 * No rmdir "." please.
4093 	 */
4094 	if (dvp == vp) {
4095 		error = EINVAL;
4096 		goto out;
4097 	}
4098 	/*
4099 	 * No rmdir ".." please.
4100 	 */
4101 	if (vnode_parent(dvp) == vp) {
4102 		error = EINVAL;
4103 		goto out;
4104 	}
4105 	/*
4106 	 * The root of a mounted filesystem cannot be deleted.
4107 	 */
4108 	if (vnode_isvroot(vp)) {
4109 		error = EBUSY;
4110 	}
4111 	if (!error) {
4112 		error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
4113 	}
4114 	if (!error) {
4115 		error = vn_authorize_rmdir(dvp, vp, &ni.ni_cnd, ctx, NULL);
4116 		if (error) {
4117 			error = EACCES;
4118 		}
4119 	}
4120 
4121 	if (!error) {
4122 #if CONFIG_FSE
4123 		char     *path = NULL;
4124 		int       plen = 0;
4125 		fse_info  finfo;
4126 
4127 		if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
4128 			plen = MAXPATHLEN;
4129 			if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
4130 				get_fse_info(vp, &finfo, ctx);
4131 			} else if (path) {
4132 				release_pathbuff(path);
4133 				path = NULL;
4134 			}
4135 		}
4136 #endif /* CONFIG_FSE */
4137 
4138 		error = VNOP_RMDIR(dvp, vp, &ni.ni_cnd, ctx);
4139 
4140 #if CONFIG_FSE
4141 		if (path) {
4142 			if (!error) {
4143 				add_fsevent(FSE_DELETE, ctx,
4144 				    FSE_ARG_STRING, plen, path,
4145 				    FSE_ARG_FINFO, &finfo,
4146 				    FSE_ARG_DONE);
4147 			}
4148 			release_pathbuff(path);
4149 		}
4150 #endif /* CONFIG_FSE */
4151 	}
4152 out:
4153 	/*
4154 	 * nameidone has to happen before we vnode_put(dvp)
4155 	 * since it may need to release the fs_nodelock on the dvp
4156 	 */
4157 	nameidone(&ni);
4158 
4159 	vnode_put(dvp);
4160 	vnode_put(vp);
4161 
4162 	if (dirp) {
4163 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
4164 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
4165 		vnode_put(dirp);
4166 		dirp = NULL;
4167 	}
4168 
4169 nfsmerr:
4170 	/* assemble reply */
4171 	nd->nd_repstat = error;
4172 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
4173 	nfsmout_if(error);
4174 	*mrepp = nmrep.nmc_mhead;
4175 	nfsmout_on_status(nd, error);
4176 	if (nd->nd_vers == NFS_VER3) {
4177 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
4178 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4179 	}
4180 nfsmout:
4181 	nfsm_chain_build_done(error, &nmrep);
4182 	if (dirp) {
4183 		vnode_put(dirp);
4184 	}
4185 	if (error) {
4186 		nfsm_chain_cleanup(&nmrep);
4187 		*mrepp = NULL;
4188 	}
4189 	return error;
4190 }
4191 
4192 /*
4193  * nfs readdir service
4194  * - mallocs what it thinks is enough to read
4195  *	count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4196  * - calls VNOP_READDIR()
4197  * - loops around building the reply
4198  *	if the output generated exceeds count break out of loop
4199  *	The nfsm_clget macro is used here so that the reply will be packed
4200  *	tightly in mbuf clusters.
4201  * - it only knows that it has encountered eof when the VNOP_READDIR()
4202  *	reads nothing
4203  * - as such one readdir rpc will return eof false although you are there
4204  *	and then the next will return eof
4205  * - it trims out records with d_fileno == 0
4206  *	this doesn't matter for Unix clients, but they might confuse clients
4207  *	for other os'.
4208  * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4209  *	than requested, but this may not apply to all filesystems. For
4210  *	example, client NFS does not { although it is never remote mounted
4211  *	anyhow }
4212  *     The alternate call nfsrv_readdirplus() does lookups as well.
4213  * PS:  The XNFS protocol spec clearly describes what the "count"s arguments
4214  *      are supposed to cover.  For readdir, the count is the total number of
4215  *      bytes included in everything from the directory's postopattr through
4216  *      the EOF flag.  For readdirplus, the maxcount is the same, and the
4217  *      dircount includes all that except for the entry attributes and handles.
4218  */
4219 int
nfsrv_readdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4220 nfsrv_readdir(
4221 	struct nfsrv_descript *nd,
4222 	struct nfsrv_sock *slp,
4223 	vfs_context_t ctx,
4224 	mbuf_t *mrepp)
4225 {
4226 	struct direntry *dp;
4227 	char *cpos, *cend, *rbuf;
4228 	size_t rbuf_siz;
4229 	vnode_t vp;
4230 	struct vnode_attr attr = {};
4231 	struct nfs_filehandle nfh;
4232 	struct nfs_export *nx;
4233 	struct nfs_export_options *nxo;
4234 	uio_t auio = NULL;
4235 	UIO_STACKBUF(uio_buf, 1);
4236 	int len, nlen, rem, xfer, error, attrerr;
4237 	int siz, count, fullsiz, eofflag, nentries;
4238 	u_quad_t off, toff, verf = 0;
4239 	int vnopflag;
4240 	struct nfsm_chain *nmreq, nmrep;
4241 
4242 	error = 0;
4243 	attrerr = ENOENT;
4244 	count = nentries = 0;
4245 	nmreq = &nd->nd_nmreq;
4246 	nfsm_chain_null(&nmrep);
4247 	rbuf = NULL;
4248 	rbuf_siz = 0;
4249 	vp = NULL;
4250 
4251 	vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4252 
4253 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4254 	if (nd->nd_vers == NFS_VER3) {
4255 		nfsm_chain_get_64(error, nmreq, toff);
4256 		nfsm_chain_get_64(error, nmreq, verf);
4257 	} else {
4258 		nfsm_chain_get_32(error, nmreq, toff);
4259 	}
4260 	nfsm_chain_get_32(error, nmreq, count);
4261 	nfsmerr_if(error);
4262 
4263 	off = toff;
4264 	siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4265 	xfer = NFSRV_NDMAXDATA(nd);
4266 	if (siz > xfer) {
4267 		siz = xfer;
4268 	}
4269 	fullsiz = siz;
4270 
4271 	if (fullsiz == 0) {
4272 		error = NFSERR_TOOSMALL;
4273 		goto nfsmerr;
4274 	}
4275 
4276 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4277 	nfsmerr_if(error);
4278 
4279 	/* update export stats */
4280 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4281 
4282 	/* update active user stats */
4283 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4284 
4285 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4286 	nfsmerr_if(error);
4287 
4288 	if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) {
4289 		vnopflag |= VNODE_READDIR_NAMEMAX;
4290 	}
4291 
4292 	if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) {
4293 		vnopflag |= VNODE_READDIR_SEEKOFF32;
4294 	}
4295 
4296 	if (nd->nd_vers == NFS_VER3) {
4297 		nfsm_srv_vattr_init(&attr, NFS_VER3);
4298 		error = attrerr = vnode_getattr(vp, &attr, ctx);
4299 		if (!error && verf && (verf != attr.va_filerev)) {
4300 			error = NFSERR_BAD_COOKIE;
4301 		}
4302 	}
4303 	if (!error) {
4304 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4305 	}
4306 #if CONFIG_MACF
4307 	if (!error) {
4308 		if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4309 			error = EACCES;
4310 		}
4311 
4312 		if (!error) {
4313 			error = mac_vnode_check_readdir(ctx, vp);
4314 		}
4315 	}
4316 #endif
4317 	nfsmerr_if(error);
4318 
4319 	rbuf_siz = siz;
4320 	rbuf = kalloc_data(rbuf_siz, Z_WAITOK);
4321 	if (rbuf) {
4322 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4323 		    &uio_buf[0], sizeof(uio_buf));
4324 	}
4325 	if (!rbuf || !auio) {
4326 		error = ENOMEM;
4327 		goto nfsmerr;
4328 	}
4329 again:
4330 	uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4331 	uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4332 	eofflag = 0;
4333 	error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4334 	off = uio_offset(auio);
4335 
4336 	if (nd->nd_vers == NFS_VER3) {
4337 		nfsm_srv_vattr_init(&attr, NFS_VER3);
4338 		attrerr = vnode_getattr(vp, &attr, ctx);
4339 	}
4340 	nfsmerr_if(error);
4341 
4342 	if (uio_resid(auio) != 0) {
4343 		siz -= uio_resid(auio);
4344 
4345 		/* If nothing read, return empty reply with eof set */
4346 		if (siz == 0) {
4347 			vnode_put(vp);
4348 			vp = NULL;
4349 			kfree_data(rbuf, rbuf_siz);
4350 			/* assemble reply */
4351 			nd->nd_repstat = error;
4352 			error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4353 			    NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED);
4354 			nfsmout_if(error);
4355 			*mrepp = nmrep.nmc_mhead;
4356 			nfsmout_on_status(nd, error);
4357 			if (nd->nd_vers == NFS_VER3) {
4358 				nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4359 				nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4360 			}
4361 			nfsm_chain_add_32(error, &nmrep, FALSE);
4362 			nfsm_chain_add_32(error, &nmrep, TRUE);
4363 			nfsm_chain_build_done(error, &nmrep);
4364 			return error;
4365 		}
4366 	}
4367 
4368 	/*
4369 	 * Check for degenerate cases of nothing useful read.
4370 	 * If so go try again
4371 	 */
4372 	cpos = rbuf;
4373 	cend = rbuf + siz;
4374 	dp = (struct direntry *)cpos;
4375 	while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4376 		cpos += dp->d_reclen;
4377 		dp = (struct direntry *)cpos;
4378 		nentries--;
4379 	}
4380 	if ((cpos >= cend) || (nentries == 0)) {
4381 		toff = off;
4382 		siz = fullsiz;
4383 		goto again;
4384 	}
4385 
4386 	vnode_put(vp);
4387 	vp = NULL;
4388 
4389 	/* assemble reply */
4390 	nd->nd_repstat = error;
4391 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4392 	    NFSX_COOKIEVERF(nd->nd_vers) + siz);
4393 	nfsmout_if(error);
4394 	*mrepp = nmrep.nmc_mhead;
4395 	nfsmout_on_status(nd, error);
4396 	nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4397 
4398 	len = 2 * NFSX_UNSIGNED;
4399 	if (nd->nd_vers == NFS_VER3) {
4400 		len += NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF;
4401 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4402 		nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4403 		nfsmerr_if(error);
4404 	}
4405 
4406 	/* Loop through the records and build reply */
4407 	while ((cpos < cend) && (nentries > 0)) {
4408 		if (dp->d_fileno != 0) {
4409 			nlen = dp->d_namlen;
4410 			if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) {
4411 				nlen = NFS_MAXNAMLEN;
4412 			}
4413 			rem = nfsm_rndup(nlen) - nlen;
4414 			len += (4 * NFSX_UNSIGNED + nlen + rem);
4415 			if (nd->nd_vers == NFS_VER3) {
4416 				len += 2 * NFSX_UNSIGNED;
4417 			}
4418 			if (len > count) {
4419 				eofflag = 0;
4420 				break;
4421 			}
4422 			/* Build the directory record xdr from the direntry. */
4423 			nfsm_chain_add_32(error, &nmrep, TRUE);
4424 			if (nd->nd_vers == NFS_VER3) {
4425 				nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4426 			} else {
4427 				nfsm_chain_add_32(error, &nmrep, dp->d_fileno);
4428 			}
4429 			nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4430 			if (nd->nd_vers == NFS_VER3) {
4431 				if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4432 					dp->d_seekoff &= 0x00000000ffffffffULL;
4433 				}
4434 				nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4435 			} else {
4436 				nfsm_chain_add_32(error, &nmrep, dp->d_seekoff);
4437 			}
4438 			nfsmerr_if(error);
4439 		}
4440 		cpos += dp->d_reclen;
4441 		dp = (struct direntry *)cpos;
4442 		nentries--;
4443 	}
4444 	nfsm_chain_add_32(error, &nmrep, FALSE);
4445 	nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4446 	kfree_data(rbuf, rbuf_siz);
4447 	goto nfsmout;
4448 nfsmerr:
4449 	if (rbuf) {
4450 		kfree_data(rbuf, rbuf_siz);
4451 	}
4452 	if (vp) {
4453 		vnode_put(vp);
4454 	}
4455 	nd->nd_repstat = error;
4456 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers));
4457 	nfsmout_if(error);
4458 	*mrepp = nmrep.nmc_mhead;
4459 	nfsmout_on_status(nd, error);
4460 	if (nd->nd_vers == NFS_VER3) {
4461 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4462 	}
4463 nfsmout:
4464 	nfsm_chain_build_done(error, &nmrep);
4465 	if (error) {
4466 		nfsm_chain_cleanup(&nmrep);
4467 		*mrepp = NULL;
4468 	}
4469 	return error;
4470 }
4471 
4472 int
nfsrv_readdirplus(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4473 nfsrv_readdirplus(
4474 	struct nfsrv_descript *nd,
4475 	struct nfsrv_sock *slp,
4476 	vfs_context_t ctx,
4477 	mbuf_t *mrepp)
4478 {
4479 	struct direntry *dp;
4480 	char *cpos, *cend, *rbuf;
4481 	size_t rbuf_siz;
4482 	vnode_t vp, nvp;
4483 	struct nfs_filehandle dnfh, nfh;
4484 	struct nfs_export *nx;
4485 	struct nfs_export_options *nxo;
4486 	uio_t auio = NULL;
4487 	UIO_STACKBUF(uio_buf, 1);
4488 	struct vnode_attr attr, va, *vap = &va;
4489 	int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr;
4490 	int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot;
4491 	u_quad_t off, toff, verf;
4492 	int vnopflag;
4493 	struct nfsm_chain *nmreq, nmrep;
4494 
4495 	error = 0;
4496 	attrerr = ENOENT;
4497 	nentries = 0;
4498 	nmreq = &nd->nd_nmreq;
4499 	nfsm_chain_null(&nmrep);
4500 	rbuf = NULL;
4501 	rbuf_siz = 0;
4502 	vp = NULL;
4503 	dircount = maxcount = 0;
4504 
4505 	vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4506 
4507 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
4508 	nfsm_chain_get_64(error, nmreq, toff);
4509 	nfsm_chain_get_64(error, nmreq, verf);
4510 	nfsm_chain_get_32(error, nmreq, dircount);
4511 	nfsm_chain_get_32(error, nmreq, maxcount);
4512 	nfsmerr_if(error);
4513 
4514 	off = toff;
4515 	xfer = NFSRV_NDMAXDATA(nd);
4516 	dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4517 	if (dircount > xfer) {
4518 		dircount = xfer;
4519 	}
4520 	fullsiz = siz = dircount;
4521 	maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4522 	if (maxcount > xfer) {
4523 		maxcount = xfer;
4524 	}
4525 
4526 	if (maxcount == 0) {
4527 		error = NFSERR_TOOSMALL;
4528 		goto nfsmerr;
4529 	}
4530 
4531 	error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo);
4532 	nfsmerr_if(error);
4533 
4534 	/* update export stats */
4535 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4536 
4537 	/* update active user stats */
4538 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4539 
4540 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4541 	nfsmerr_if(error);
4542 
4543 	if (nxo->nxo_flags & NX_32BITCLIENTS) {
4544 		vnopflag |= VNODE_READDIR_SEEKOFF32;
4545 	}
4546 
4547 	if (nxo->nxo_flags & NX_MANGLEDNAMES) {
4548 		vnopflag |= VNODE_READDIR_NAMEMAX;
4549 	}
4550 
4551 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4552 	error = attrerr = vnode_getattr(vp, &attr, ctx);
4553 	if (!error && verf && (verf != attr.va_filerev)) {
4554 		error = NFSERR_BAD_COOKIE;
4555 	}
4556 	if (!error) {
4557 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4558 	}
4559 #if CONFIG_MACF
4560 	if (!error) {
4561 		if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4562 			error = EACCES;
4563 		}
4564 
4565 		if (!error) {
4566 			error = mac_vnode_check_readdir(ctx, vp);
4567 		}
4568 	}
4569 #endif
4570 	nfsmerr_if(error);
4571 
4572 	rbuf_siz = siz;
4573 	rbuf = kalloc_data(siz, Z_WAITOK);
4574 	if (rbuf) {
4575 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4576 		    &uio_buf[0], sizeof(uio_buf));
4577 	}
4578 	if (!rbuf || !auio) {
4579 		error = ENOMEM;
4580 		goto nfsmerr;
4581 	}
4582 
4583 again:
4584 	uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4585 	uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4586 	eofflag = 0;
4587 	error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4588 	off = uio_offset(auio);
4589 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4590 	attrerr = vnode_getattr(vp, &attr, ctx);
4591 	nfsmerr_if(error);
4592 
4593 	if (uio_resid(auio) != 0) {
4594 		siz -= uio_resid(auio);
4595 
4596 		/* If nothing read, return empty reply with eof set */
4597 		if (siz == 0) {
4598 			vnode_put(vp);
4599 			vp = NULL;
4600 			kfree_data(rbuf, rbuf_siz);
4601 			/* assemble reply */
4602 			nd->nd_repstat = error;
4603 			error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR +
4604 			    NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED);
4605 			nfsmout_if(error);
4606 			*mrepp = nmrep.nmc_mhead;
4607 			nfsmout_on_status(nd, error);
4608 			nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4609 			nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4610 			nfsm_chain_add_32(error, &nmrep, FALSE);
4611 			nfsm_chain_add_32(error, &nmrep, TRUE);
4612 			nfsm_chain_build_done(error, &nmrep);
4613 			return error;
4614 		}
4615 	}
4616 
4617 	/*
4618 	 * Check for degenerate cases of nothing useful read.
4619 	 * If so go try again
4620 	 */
4621 	cpos = rbuf;
4622 	cend = rbuf + siz;
4623 	dp = (struct direntry *)cpos;
4624 	while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4625 		cpos += dp->d_reclen;
4626 		dp = (struct direntry *)cpos;
4627 		nentries--;
4628 	}
4629 	if ((cpos >= cend) || (nentries == 0)) {
4630 		toff = off;
4631 		siz = fullsiz;
4632 		goto again;
4633 	}
4634 
4635 	/*
4636 	 * Probe the directory to see if the filesystem supports VGET.
4637 	 */
4638 	if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)attr.va_fileid, &nvp, ctx))) {
4639 		if (error == ENOTSUP) { /* let others get passed back */
4640 			error = NFSERR_NOTSUPP;
4641 		}
4642 		goto nfsmerr;
4643 	}
4644 	vnode_put(nvp);
4645 
4646 	/* assemble reply */
4647 	nd->nd_repstat = error;
4648 	error = nfsrv_rephead(nd, slp, &nmrep, maxcount);
4649 	nfsmout_if(error);
4650 	*mrepp = nmrep.nmc_mhead;
4651 	nfsmout_on_status(nd, error);
4652 	nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4653 
4654 	dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
4655 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4656 	nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4657 	nfsmerr_if(error);
4658 
4659 	/* Loop through the records and build reply */
4660 	while ((cpos < cend) && (nentries > 0)) {
4661 		if (dp->d_fileno != 0) {
4662 			nlen = dp->d_namlen;
4663 			rem = nfsm_rndup(nlen) - nlen;
4664 			gotfh = gotattr = 1;
4665 
4666 			/* Got to get the vnode for lookup per entry. */
4667 			if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx)) {
4668 				/* Can't get the vnode... so no fh or attrs */
4669 				gotfh = gotattr = 0;
4670 			} else {
4671 				isdotdot = ((dp->d_namlen == 2) &&
4672 				    (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
4673 				if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) {
4674 					gotfh = 0;
4675 				}
4676 				nfsm_srv_vattr_init(vap, NFS_VER3);
4677 				if (vnode_getattr(nvp, vap, ctx)) {
4678 					gotattr = 0;
4679 				}
4680 				vnode_put(nvp);
4681 			}
4682 
4683 			/*
4684 			 * If either the dircount or maxcount will be
4685 			 * exceeded, get out now. Both of these lengths
4686 			 * are calculated conservatively, including all
4687 			 * XDR overheads.
4688 			 */
4689 			len += 8 * NFSX_UNSIGNED + nlen + rem;
4690 			if (gotattr) {
4691 				len += NFSX_V3FATTR;
4692 			}
4693 			if (gotfh) {
4694 				len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len);
4695 			}
4696 			dirlen += 6 * NFSX_UNSIGNED + nlen + rem;
4697 			if ((len > maxcount) || (dirlen > dircount)) {
4698 				eofflag = 0;
4699 				break;
4700 			}
4701 
4702 			/* Build the directory record xdr from the direntry. */
4703 			nfsm_chain_add_32(error, &nmrep, TRUE);
4704 			nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4705 			nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4706 			if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4707 				dp->d_seekoff &= 0x00000000ffffffffULL;
4708 			}
4709 			nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4710 			nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap);
4711 			if (gotfh) {
4712 				nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4713 			} else {
4714 				nfsm_chain_add_32(error, &nmrep, FALSE);
4715 			}
4716 			nfsmerr_if(error);
4717 		}
4718 		cpos += dp->d_reclen;
4719 		dp = (struct direntry *)cpos;
4720 		nentries--;
4721 	}
4722 	vnode_put(vp);
4723 	vp = NULL;
4724 	nfsm_chain_add_32(error, &nmrep, FALSE);
4725 	nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4726 	kfree_data(rbuf, rbuf_siz);
4727 	goto nfsmout;
4728 nfsmerr:
4729 	if (rbuf) {
4730 		kfree_data(rbuf, rbuf_siz);
4731 	}
4732 	nd->nd_repstat = error;
4733 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR);
4734 	nfsmout_if(error);
4735 	*mrepp = nmrep.nmc_mhead;
4736 	nfsmout_on_status(nd, error);
4737 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4738 nfsmout:
4739 	nfsm_chain_build_done(error, &nmrep);
4740 	if (vp) {
4741 		vnode_put(vp);
4742 	}
4743 	if (error) {
4744 		nfsm_chain_cleanup(&nmrep);
4745 		*mrepp = NULL;
4746 	}
4747 	return error;
4748 }
4749 
4750 /*
4751  * nfs commit service
4752  */
4753 int
nfsrv_commit(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4754 nfsrv_commit(
4755 	struct nfsrv_descript *nd,
4756 	struct nfsrv_sock *slp,
4757 	vfs_context_t ctx,
4758 	mbuf_t *mrepp)
4759 {
4760 	vnode_t vp;
4761 	struct nfs_filehandle nfh;
4762 	struct nfs_export *nx = NULL;
4763 	struct nfs_export_options *nxo;
4764 	int error, preattrerr, postattrerr, count;
4765 	struct vnode_attr preattr, postattr;
4766 	u_quad_t off;
4767 	struct nfsm_chain *nmreq, nmrep;
4768 
4769 	error = 0;
4770 	preattrerr = postattrerr = ENOENT;
4771 	nmreq = &nd->nd_nmreq;
4772 	nfsm_chain_null(&nmrep);
4773 	vp = NULL;
4774 
4775 	/*
4776 	 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4777 	 * count parameters, so those arguments are useless (someday maybe).
4778 	 */
4779 
4780 	nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
4781 	nfsm_chain_get_64(error, nmreq, off);
4782 	nfsm_chain_get_32(error, nmreq, count);
4783 	nfsmerr_if(error);
4784 
4785 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4786 	nfsmerr_if(error);
4787 
4788 	/* update export stats */
4789 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4790 
4791 	/* update active user stats */
4792 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4793 
4794 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4795 	nfsmerr_if(error);
4796 
4797 	/* This must identify a file system object of type, NF3REG */
4798 	if (vnode_vtype(vp) != VREG) {
4799 		error = NFSERR_BADTYPE;
4800 		goto nfsmerr;
4801 	}
4802 
4803 	nfsm_srv_pre_vattr_init(&preattr);
4804 	preattrerr = vnode_getattr(vp, &preattr, ctx);
4805 
4806 	error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
4807 
4808 	nfsm_srv_vattr_init(&postattr, 1);
4809 	postattrerr = vnode_getattr(vp, &postattr, ctx);
4810 
4811 nfsmerr:
4812 	if (vp) {
4813 		vnode_put(vp);
4814 	}
4815 
4816 	/* assemble reply */
4817 	nd->nd_repstat = error;
4818 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4819 	nfsmout_if(error);
4820 	*mrepp = nmrep.nmc_mhead;
4821 	nfsmout_on_status(nd, error);
4822 	nfsm_chain_add_wcc_data(error, nd, &nmrep,
4823 	    preattrerr, &preattr, postattrerr, &postattr);
4824 	if (!nd->nd_repstat) {
4825 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
4826 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
4827 	}
4828 nfsmout:
4829 	nfsm_chain_build_done(error, &nmrep);
4830 	if (error) {
4831 		nfsm_chain_cleanup(&nmrep);
4832 		*mrepp = NULL;
4833 	}
4834 	return error;
4835 }
4836 
4837 /*
4838  * nfs statfs service
4839  */
4840 int
nfsrv_statfs(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4841 nfsrv_statfs(
4842 	struct nfsrv_descript *nd,
4843 	struct nfsrv_sock *slp,
4844 	vfs_context_t ctx,
4845 	mbuf_t *mrepp)
4846 {
4847 	struct vfs_attr va = {};
4848 	int error, attrerr;
4849 	vnode_t vp;
4850 	struct vnode_attr attr;
4851 	struct nfs_filehandle nfh;
4852 	struct nfs_export *nx;
4853 	struct nfs_export_options *nxo;
4854 	off_t blksize;
4855 	struct nfsm_chain *nmreq, nmrep;
4856 
4857 	error = 0;
4858 	attrerr = ENOENT;
4859 	nmreq = &nd->nd_nmreq;
4860 	nfsm_chain_null(&nmrep);
4861 	vp = NULL;
4862 	blksize = 512;
4863 
4864 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4865 	nfsmerr_if(error);
4866 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4867 	nfsmerr_if(error);
4868 
4869 	/* update export stats */
4870 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4871 
4872 	/* update active user stats */
4873 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4874 
4875 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4876 	nfsmerr_if(error);
4877 
4878 	VFSATTR_INIT(&va);
4879 	VFSATTR_WANTED(&va, f_blocks);
4880 	VFSATTR_WANTED(&va, f_bfree);
4881 	VFSATTR_WANTED(&va, f_bavail);
4882 	VFSATTR_WANTED(&va, f_files);
4883 	VFSATTR_WANTED(&va, f_ffree);
4884 	error = vfs_getattr(vnode_mount(vp), &va, ctx);
4885 	blksize = vfs_statfs(vnode_mount(vp))->f_bsize;
4886 
4887 	if (nd->nd_vers == NFS_VER3) {
4888 		nfsm_srv_vattr_init(&attr, nd->nd_vers);
4889 		attrerr = vnode_getattr(vp, &attr, ctx);
4890 	}
4891 
4892 nfsmerr:
4893 	if (vp) {
4894 		vnode_put(vp);
4895 	}
4896 
4897 	/* assemble reply */
4898 	nd->nd_repstat = error;
4899 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_STATFS(nd->nd_vers));
4900 	nfsmout_if(error);
4901 	*mrepp = nmrep.nmc_mhead;
4902 	nfsmout_on_status(nd, error);
4903 	if (nd->nd_vers == NFS_VER3) {
4904 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4905 	}
4906 	nfsmout_if(nd->nd_repstat);
4907 
4908 	if (nd->nd_vers == NFS_VER3) {
4909 		nfsm_chain_add_64(error, &nmrep, va.f_blocks * blksize);
4910 		nfsm_chain_add_64(error, &nmrep, va.f_bfree * blksize);
4911 		nfsm_chain_add_64(error, &nmrep, va.f_bavail * blksize);
4912 		nfsm_chain_add_64(error, &nmrep, va.f_files);
4913 		nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4914 		nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4915 		nfsm_chain_add_32(error, &nmrep, 0); /* invarsec */
4916 	} else {
4917 		nfsm_chain_add_32(error, &nmrep, NFS_V2MAXDATA);
4918 		nfsm_chain_add_32(error, &nmrep, blksize);
4919 		nfsm_chain_add_32(error, &nmrep, va.f_blocks);
4920 		nfsm_chain_add_32(error, &nmrep, va.f_bfree);
4921 		nfsm_chain_add_32(error, &nmrep, va.f_bavail);
4922 	}
4923 nfsmout:
4924 	nfsm_chain_build_done(error, &nmrep);
4925 	if (error) {
4926 		nfsm_chain_cleanup(&nmrep);
4927 		*mrepp = NULL;
4928 	}
4929 	return error;
4930 }
4931 
4932 /*
4933  * nfs fsinfo service
4934  */
4935 int
nfsrv_fsinfo(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4936 nfsrv_fsinfo(
4937 	struct nfsrv_descript *nd,
4938 	struct nfsrv_sock *slp,
4939 	vfs_context_t ctx,
4940 	mbuf_t *mrepp)
4941 {
4942 	int error, attrerr, prefsize, maxsize;
4943 	vnode_t vp;
4944 	struct vnode_attr attr;
4945 	struct nfs_filehandle nfh;
4946 	struct nfs_export *nx;
4947 	struct nfs_export_options *nxo;
4948 	struct nfsm_chain *nmreq, nmrep;
4949 
4950 	error = 0;
4951 	attrerr = ENOENT;
4952 	nmreq = &nd->nd_nmreq;
4953 	nfsm_chain_null(&nmrep);
4954 	vp = NULL;
4955 
4956 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4957 	nfsmerr_if(error);
4958 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4959 	nfsmerr_if(error);
4960 
4961 	/* update export stats */
4962 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4963 
4964 	/* update active user stats */
4965 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4966 
4967 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4968 	nfsmerr_if(error);
4969 
4970 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4971 	attrerr = vnode_getattr(vp, &attr, ctx);
4972 
4973 nfsmerr:
4974 	if (vp) {
4975 		vnode_put(vp);
4976 	}
4977 
4978 	/* assemble reply */
4979 	nd->nd_repstat = error;
4980 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4981 	nfsmout_if(error);
4982 	*mrepp = nmrep.nmc_mhead;
4983 	nfsmout_on_status(nd, error);
4984 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4985 	nfsmout_if(nd->nd_repstat);
4986 
4987 	/*
4988 	 * XXX There should be file system VFS OP(s) to get this information.
4989 	 * For now, assume our usual NFS defaults.
4990 	 */
4991 	if (slp->ns_sotype == SOCK_DGRAM) {
4992 		maxsize = NFS_MAXDGRAMDATA;
4993 		prefsize = NFS_PREFDGRAMDATA;
4994 	} else {
4995 		maxsize = prefsize = NFSRV_MAXDATA;
4996 	}
4997 
4998 	nfsm_chain_add_32(error, &nmrep, maxsize);
4999 	nfsm_chain_add_32(error, &nmrep, prefsize);
5000 	nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5001 	nfsm_chain_add_32(error, &nmrep, maxsize);
5002 	nfsm_chain_add_32(error, &nmrep, prefsize);
5003 	nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5004 	nfsm_chain_add_32(error, &nmrep, prefsize);
5005 	nfsm_chain_add_64(error, &nmrep, 0xffffffffffffffffULL);
5006 	nfsm_chain_add_32(error, &nmrep, 0);
5007 	nfsm_chain_add_32(error, &nmrep, 1);
5008 	/* XXX link/symlink support should be taken from volume capabilities */
5009 	nfsm_chain_add_32(error, &nmrep,
5010 	    NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK |
5011 	    NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME);
5012 
5013 nfsmout:
5014 	nfsm_chain_build_done(error, &nmrep);
5015 	if (error) {
5016 		nfsm_chain_cleanup(&nmrep);
5017 		*mrepp = NULL;
5018 	}
5019 	return error;
5020 }
5021 
5022 /*
5023  * nfs pathconf service
5024  */
5025 int
nfsrv_pathconf(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)5026 nfsrv_pathconf(
5027 	struct nfsrv_descript *nd,
5028 	struct nfsrv_sock *slp,
5029 	vfs_context_t ctx,
5030 	mbuf_t *mrepp)
5031 {
5032 	int error, attrerr, linkmax = 0, namemax = 0;
5033 	int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0;
5034 	vnode_t vp;
5035 	struct vnode_attr attr;
5036 	struct nfs_filehandle nfh;
5037 	struct nfs_export *nx;
5038 	struct nfs_export_options *nxo;
5039 	struct nfsm_chain *nmreq, nmrep;
5040 
5041 	error = 0;
5042 	attrerr = ENOENT;
5043 	nmreq = &nd->nd_nmreq;
5044 	nfsm_chain_null(&nmrep);
5045 	vp = NULL;
5046 
5047 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
5048 	nfsmerr_if(error);
5049 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
5050 	nfsmerr_if(error);
5051 
5052 	/* update export stats */
5053 	NFSStatAdd64(&nx->nx_stats.ops, 1);
5054 
5055 	/* update active user stats */
5056 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
5057 
5058 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
5059 	nfsmerr_if(error);
5060 
5061 	error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx);
5062 	if (!error) {
5063 		error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx);
5064 	}
5065 	if (!error) {
5066 		error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx);
5067 	}
5068 	if (!error) {
5069 		error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, &notrunc, ctx);
5070 	}
5071 	if (!error) {
5072 		error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx);
5073 	}
5074 	if (!error) {
5075 		error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx);
5076 	}
5077 
5078 	nfsm_srv_vattr_init(&attr, NFS_VER3);
5079 	attrerr = vnode_getattr(vp, &attr, ctx);
5080 
5081 nfsmerr:
5082 	if (vp) {
5083 		vnode_put(vp);
5084 	}
5085 
5086 	/* assemble reply */
5087 	nd->nd_repstat = error;
5088 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
5089 	nfsmout_if(error);
5090 	*mrepp = nmrep.nmc_mhead;
5091 	nfsmout_on_status(nd, error);
5092 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
5093 	nfsmout_if(nd->nd_repstat);
5094 
5095 	nfsm_chain_add_32(error, &nmrep, linkmax);
5096 	nfsm_chain_add_32(error, &nmrep, namemax);
5097 	nfsm_chain_add_32(error, &nmrep, notrunc);
5098 	nfsm_chain_add_32(error, &nmrep, chownres);
5099 	nfsm_chain_add_32(error, &nmrep, !case_sensitive);
5100 	nfsm_chain_add_32(error, &nmrep, case_preserving);
5101 
5102 nfsmout:
5103 	nfsm_chain_build_done(error, &nmrep);
5104 	if (error) {
5105 		nfsm_chain_cleanup(&nmrep);
5106 		*mrepp = NULL;
5107 	}
5108 	return error;
5109 }
5110 
5111 /*
5112  * Null operation, used by clients to ping server
5113  */
5114 /* ARGSUSED */
5115 int
nfsrv_null(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5116 nfsrv_null(
5117 	struct nfsrv_descript *nd,
5118 	struct nfsrv_sock *slp,
5119 	__unused vfs_context_t ctx,
5120 	mbuf_t *mrepp)
5121 {
5122 	int error = NFSERR_RETVOID;
5123 	struct nfsm_chain nmrep;
5124 
5125 	/*
5126 	 * RPCSEC_GSS context setup ?
5127 	 */
5128 	if (nd->nd_gss_context) {
5129 		return nfs_gss_svc_ctx_init(nd, slp, mrepp);
5130 	}
5131 
5132 	nfsm_chain_null(&nmrep);
5133 
5134 	/* assemble reply */
5135 	nd->nd_repstat = error;
5136 	error = nfsrv_rephead(nd, slp, &nmrep, 0);
5137 	nfsmout_if(error);
5138 	*mrepp = nmrep.nmc_mhead;
5139 nfsmout:
5140 	nfsm_chain_build_done(error, &nmrep);
5141 	if (error) {
5142 		nfsm_chain_cleanup(&nmrep);
5143 		*mrepp = NULL;
5144 	}
5145 	return error;
5146 }
5147 
5148 /*
5149  * No operation, used for obsolete procedures
5150  */
5151 /* ARGSUSED */
5152 int
nfsrv_noop(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5153 nfsrv_noop(
5154 	struct nfsrv_descript *nd,
5155 	struct nfsrv_sock *slp,
5156 	__unused vfs_context_t ctx,
5157 	mbuf_t *mrepp)
5158 {
5159 	int error;
5160 	struct nfsm_chain nmrep;
5161 
5162 	nfsm_chain_null(&nmrep);
5163 
5164 	if (nd->nd_repstat) {
5165 		error = nd->nd_repstat;
5166 	} else {
5167 		error = EPROCUNAVAIL;
5168 	}
5169 
5170 	/* assemble reply */
5171 	nd->nd_repstat = error;
5172 	error = nfsrv_rephead(nd, slp, &nmrep, 0);
5173 	nfsmout_if(error);
5174 	*mrepp = nmrep.nmc_mhead;
5175 nfsmout:
5176 	nfsm_chain_build_done(error, &nmrep);
5177 	if (error) {
5178 		nfsm_chain_cleanup(&nmrep);
5179 		*mrepp = NULL;
5180 	}
5181 	return error;
5182 }
5183 
5184 const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = {
5185 	nfsrv_null,
5186 	nfsrv_getattr,
5187 	nfsrv_setattr,
5188 	nfsrv_lookup,
5189 	nfsrv_access,
5190 	nfsrv_readlink,
5191 	nfsrv_read,
5192 	nfsrv_write,
5193 	nfsrv_create,
5194 	nfsrv_mkdir,
5195 	nfsrv_symlink,
5196 	nfsrv_mknod,
5197 	nfsrv_remove,
5198 	nfsrv_rmdir,
5199 	nfsrv_rename,
5200 	nfsrv_link,
5201 	nfsrv_readdir,
5202 	nfsrv_readdirplus,
5203 	nfsrv_statfs,
5204 	nfsrv_fsinfo,
5205 	nfsrv_pathconf,
5206 	nfsrv_commit,
5207 	nfsrv_noop
5208 };
5209 
5210 /*
5211  * Perform access checking for vnodes obtained from file handles that would
5212  * refer to files already opened by a Unix client. You cannot just use
5213  * vnode_authorize() for two reasons.
5214  * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5215  * 2 - The owner is to be given access irrespective of mode bits so that
5216  *     processes that chmod after opening a file don't break. I don't like
5217  *     this because it opens a security hole, but since the nfs server opens
5218  *     a security hole the size of a barn door anyhow, what the heck.
5219  *
5220  * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5221  * will return EPERM instead of EACCESS. EPERM is always an error.
5222  */
5223 
5224 int
nfsrv_authorize(vnode_t vp,vnode_t dvp,kauth_action_t action,vfs_context_t ctx,struct nfs_export_options * nxo,int override)5225 nfsrv_authorize(
5226 	vnode_t vp,
5227 	vnode_t dvp,
5228 	kauth_action_t action,
5229 	vfs_context_t ctx,
5230 	struct nfs_export_options *nxo,
5231 	int override)
5232 {
5233 	struct vnode_attr vattr;
5234 	int error;
5235 
5236 	if (action & KAUTH_VNODE_WRITE_RIGHTS) {
5237 		/*
5238 		 * Disallow write attempts on read-only exports;
5239 		 * unless the file is a socket or a block or character
5240 		 * device resident on the file system.
5241 		 */
5242 		if (nxo->nxo_flags & NX_READONLY) {
5243 			switch (vnode_vtype(vp)) {
5244 			case VREG: case VDIR: case VLNK: case VCPLX:
5245 				return EROFS;
5246 			default:
5247 				break;
5248 			}
5249 		}
5250 	}
5251 	error = vnode_authorize(vp, dvp, action, ctx);
5252 	/*
5253 	 * Allow certain operations for the owner (reads and writes
5254 	 * on files that are already open). Picking up from FreeBSD.
5255 	 */
5256 	if (override && (error == EACCES)) {
5257 		VATTR_INIT(&vattr);
5258 		VATTR_WANTED(&vattr, va_uid);
5259 		if ((vnode_getattr(vp, &vattr, ctx) == 0) &&
5260 		    (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) {
5261 			error = 0;
5262 		}
5263 	}
5264 	return error;
5265 }
5266 
5267 #endif /* CONFIG_NFS_SERVER */
5268