xref: /xnu-11215.41.3/bsd/nfs/nfs_serv.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
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 	int is_subdir = 0;
2775 	mount_t locked_mp;
2776 	struct nfsm_chain *nmreq, nmrep;
2777 	char *from_name, *to_name;
2778 #if CONFIG_FSE
2779 	int from_len = 0, to_len = 0;
2780 	fse_info from_finfo, to_finfo;
2781 #endif
2782 	u_char didstats = 0;
2783 	const char *oname;
2784 
2785 	error = 0;
2786 	fdpreattrerr = fdpostattrerr = ENOENT;
2787 	tdpreattrerr = tdpostattrerr = ENOENT;
2788 	saved_uid = kauth_cred_getuid(nd->nd_cr);
2789 	fromlen = tolen = 0;
2790 	frompath = topath = NULL;
2791 	fdirp = tdirp = NULL;
2792 	nmreq = &nd->nd_nmreq;
2793 	nfsm_chain_null(&nmrep);
2794 
2795 	/*
2796 	 * these need to be set before calling any code
2797 	 * that they may take us out through the error path.
2798 	 */
2799 	holding_mntlock = 0;
2800 	fvp = tvp = NULL;
2801 	fdvp = tdvp = NULL;
2802 	locked_mp = NULL;
2803 
2804 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, fnfh.nfh_fhp, fnfh.nfh_len);
2805 	nfsm_chain_get_32(error, nmreq, fromlen);
2806 	nfsm_name_len_check(error, nd, fromlen);
2807 	nfsmerr_if(error);
2808 	error = nfsm_chain_get_path_namei(nmreq, fromlen, &fromni);
2809 	nfsmerr_if(error);
2810 	frompath = fromni.ni_cnd.cn_pnbuf;
2811 
2812 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, tnfh.nfh_fhp, tnfh.nfh_len);
2813 	nfsm_chain_get_32(error, nmreq, tolen);
2814 	nfsm_name_len_check(error, nd, tolen);
2815 	nfsmerr_if(error);
2816 	error = nfsm_chain_get_path_namei(nmreq, tolen, &toni);
2817 	nfsmerr_if(error);
2818 	topath = toni.ni_cnd.cn_pnbuf;
2819 
2820 	/*
2821 	 * Remember our original uid so that we can reset cr_uid before
2822 	 * the second nfsrv_namei() call, in case it is remapped.
2823 	 */
2824 	saved_cred = nd->nd_cr;
2825 	kauth_cred_ref(saved_cred);
2826 retry:
2827 	NDINIT(&fromni, DELETE, OP_UNLINK, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(frompath), ctx);
2828 	fromni.ni_cnd.cn_pnbuf = frompath;
2829 	frompath = NULL;
2830 	fromni.ni_cnd.cn_pnlen = MAXPATHLEN;
2831 	fromni.ni_cnd.cn_flags |= HASBUF;
2832 
2833 	error = nfsrv_namei(nd, ctx, &fromni, &fnfh, &fdirp, &fnx, &fnxo);
2834 	if (error) {
2835 		goto out;
2836 	}
2837 	fdvp = fromni.ni_dvp;
2838 	fvp  = fromni.ni_vp;
2839 
2840 	if (fdirp) {
2841 		if (nd->nd_vers == NFS_VER3) {
2842 			nfsm_srv_pre_vattr_init(&fdpreattr);
2843 			fdpreattrerr = vnode_getattr(fdirp, &fdpreattr, ctx);
2844 		} else {
2845 			vnode_put(fdirp);
2846 			fdirp = NULL;
2847 		}
2848 	}
2849 	fvtype = vnode_vtype(fvp);
2850 
2851 	/* reset credential if it was remapped */
2852 	if (nd->nd_cr != saved_cred) {
2853 		kauth_cred_ref(saved_cred);
2854 		kauth_cred_unref(&nd->nd_cr);
2855 		ctx->vc_ucred = nd->nd_cr = saved_cred;
2856 	}
2857 
2858 	NDINIT(&toni, RENAME, OP_RENAME, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(topath), ctx);
2859 	toni.ni_cnd.cn_pnbuf = topath;
2860 	topath = NULL;
2861 	toni.ni_cnd.cn_pnlen = MAXPATHLEN;
2862 	toni.ni_cnd.cn_flags |= HASBUF;
2863 
2864 	if (fvtype == VDIR) {
2865 		toni.ni_cnd.cn_flags |= WILLBEDIR;
2866 	}
2867 
2868 	tnx = NULL;
2869 	error = nfsrv_namei(nd, ctx, &toni, &tnfh, &tdirp, &tnx, &tnxo);
2870 	if (error) {
2871 		/*
2872 		 * Translate error code for rename("dir1", "dir2/.").
2873 		 */
2874 		if (error == EISDIR && fvtype == VDIR) {
2875 			if (nd->nd_vers == NFS_VER3) {
2876 				error = EINVAL;
2877 			} else {
2878 				error = ENOTEMPTY;
2879 			}
2880 		}
2881 		goto out;
2882 	}
2883 	tdvp = toni.ni_dvp;
2884 	tvp  = toni.ni_vp;
2885 
2886 	if (!didstats) {
2887 		/* update export stats once only */
2888 		if (tnx != NULL) {
2889 			/* update export stats */
2890 			NFSStatAdd64(&tnx->nx_stats.ops, 1);
2891 
2892 			/* update active user stats */
2893 			nfsrv_update_user_stat(tnx, nd, saved_uid, 1, 0, 0);
2894 			didstats = 1;
2895 		}
2896 	}
2897 
2898 	if (tdirp) {
2899 		if (nd->nd_vers == NFS_VER3) {
2900 			nfsm_srv_pre_vattr_init(&tdpreattr);
2901 			tdpreattrerr = vnode_getattr(tdirp, &tdpreattr, ctx);
2902 		} else {
2903 			vnode_put(tdirp);
2904 			tdirp = NULL;
2905 		}
2906 	}
2907 
2908 	if (tvp != NULL) {
2909 		tvtype = vnode_vtype(tvp);
2910 
2911 		if (fvtype == VDIR && tvtype != VDIR) {
2912 			if (nd->nd_vers == NFS_VER3) {
2913 				error = EEXIST;
2914 			} else {
2915 				error = EISDIR;
2916 			}
2917 			goto out;
2918 		} else if (fvtype != VDIR && tvtype == VDIR) {
2919 			if (nd->nd_vers == NFS_VER3) {
2920 				error = EEXIST;
2921 			} else {
2922 				error = ENOTDIR;
2923 			}
2924 			goto out;
2925 		}
2926 		if (tvtype == VDIR && vnode_mountedhere(tvp)) {
2927 			if (nd->nd_vers == NFS_VER3) {
2928 				error = EXDEV;
2929 			} else {
2930 				error = ENOTEMPTY;
2931 			}
2932 			goto out;
2933 		}
2934 	}
2935 	if (fvp == tdvp) {
2936 		if (nd->nd_vers == NFS_VER3) {
2937 			error = EINVAL;
2938 		} else {
2939 			error = ENOTEMPTY;
2940 		}
2941 		goto out;
2942 	}
2943 	if (fvtype == VDIR) {
2944 		error = vnode_issubdir(tdvp, fvp, &is_subdir, ctx);
2945 		if (error) {
2946 			goto out;
2947 		}
2948 		if (is_subdir) {
2949 			error = EINVAL;
2950 			goto out;
2951 		}
2952 	}
2953 
2954 	/*
2955 	 * Authorization.
2956 	 *
2957 	 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2958 	 * the node is moving between directories and we need rights to remove from the
2959 	 * old and add to the new.
2960 	 *
2961 	 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2962 	 *
2963 	 * Note that we do not inherit when renaming.  XXX this needs to be revisited to
2964 	 * implement the deferred-inherit bit.
2965 	 */
2966 	{
2967 		int moving = 0;
2968 
2969 		error = 0;
2970 		if ((tvp != NULL) && vnode_isdir(tvp)) {
2971 			if (tvp != fdvp) {
2972 				moving = 1;
2973 			}
2974 		} else if (tdvp != fdvp) {
2975 			moving = 1;
2976 		}
2977 		if (moving) {
2978 			/* moving out of fdvp, must have delete rights */
2979 			if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx, fnxo, 0)) != 0) {
2980 				goto auth_exit;
2981 			}
2982 			/* moving into tdvp or tvp, must have rights to add */
2983 			if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp,
2984 			    NULL,
2985 			    vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2986 			    ctx, tnxo, 0)) != 0) {
2987 				goto auth_exit;
2988 			}
2989 		} else {
2990 			/* node staying in same directory, must be allowed to add new name */
2991 			if ((error = nfsrv_authorize(fdvp, NULL,
2992 			    vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2993 			    ctx, fnxo, 0)) != 0) {
2994 				goto auth_exit;
2995 			}
2996 		}
2997 		/* overwriting tvp */
2998 		if ((tvp != NULL) && !vnode_isdir(tvp) &&
2999 		    ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) {
3000 			goto auth_exit;
3001 		}
3002 
3003 		if (!error &&
3004 		    ((error = vn_authorize_rename(fdvp, fvp, &fromni.ni_cnd, tdvp, tvp, &toni.ni_cnd, ctx, NULL)) != 0)) {
3005 			if (error) {
3006 				error = EACCES;
3007 			}
3008 			goto auth_exit;
3009 		}
3010 		/* XXX more checks? */
3011 
3012 auth_exit:
3013 		/* authorization denied */
3014 		if (error != 0) {
3015 			goto out;
3016 		}
3017 	}
3018 
3019 	if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
3020 	    (tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) {
3021 		if (nd->nd_vers == NFS_VER3) {
3022 			error = EXDEV;
3023 		} else {
3024 			error = ENOTEMPTY;
3025 		}
3026 		goto out;
3027 	}
3028 	/*
3029 	 * The following edge case is caught here:
3030 	 * (to cannot be a descendent of from)
3031 	 *
3032 	 *       o fdvp
3033 	 *      /
3034 	 *     /
3035 	 *    o fvp
3036 	 *     \
3037 	 *      \
3038 	 *       o tdvp
3039 	 *      /
3040 	 *     /
3041 	 *    o tvp
3042 	 */
3043 	if (tdvp->v_parent == fvp) {
3044 		if (nd->nd_vers == NFS_VER3) {
3045 			error = EXDEV;
3046 		} else {
3047 			error = ENOTEMPTY;
3048 		}
3049 		goto out;
3050 	}
3051 	if (fvtype == VDIR && vnode_mountedhere(fvp)) {
3052 		if (nd->nd_vers == NFS_VER3) {
3053 			error = EXDEV;
3054 		} else {
3055 			error = ENOTEMPTY;
3056 		}
3057 		goto out;
3058 	}
3059 	/*
3060 	 * If source is the same as the destination (that is the
3061 	 * same vnode) then there is nothing to do...
3062 	 * EXCEPT if the underlying file system supports case
3063 	 * insensitivity and is case preserving.  In this case
3064 	 * the file system needs to handle the special case of
3065 	 * getting the same vnode as target (fvp) and source (tvp).
3066 	 *
3067 	 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3068 	 * and _PC_CASE_PRESERVING can have this exception, and they need to
3069 	 * handle the special case of getting the same vnode as target and
3070 	 * source.  NOTE: Then the target is unlocked going into vnop_rename,
3071 	 * so not to cause locking problems. There is a single reference on tvp.
3072 	 *
3073 	 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3074 	 * that correct behaviour then is just to remove the source (link)
3075 	 */
3076 	if ((fvp == tvp) && (fdvp == tdvp)) {
3077 		if (fromni.ni_cnd.cn_namelen == toni.ni_cnd.cn_namelen &&
3078 		    !bcmp(fromni.ni_cnd.cn_nameptr, toni.ni_cnd.cn_nameptr,
3079 		    fromni.ni_cnd.cn_namelen)) {
3080 			goto out;
3081 		}
3082 	}
3083 
3084 	if (holding_mntlock && vnode_mount(fvp) != locked_mp) {
3085 		/*
3086 		 * we're holding a reference and lock
3087 		 * on locked_mp, but it no longer matches
3088 		 * what we want to do... so drop our hold
3089 		 */
3090 		mount_unlock_renames(locked_mp);
3091 		mount_drop(locked_mp, 0);
3092 		holding_mntlock = 0;
3093 	}
3094 	if (tdvp != fdvp && fvtype == VDIR) {
3095 		/*
3096 		 * serialize renames that re-shape
3097 		 * the tree... if holding_mntlock is
3098 		 * set, then we're ready to go...
3099 		 * otherwise we
3100 		 * first need to drop the iocounts
3101 		 * we picked up, second take the
3102 		 * lock to serialize the access,
3103 		 * then finally start the lookup
3104 		 * process over with the lock held
3105 		 */
3106 		if (!holding_mntlock) {
3107 			/*
3108 			 * need to grab a reference on
3109 			 * the mount point before we
3110 			 * drop all the iocounts... once
3111 			 * the iocounts are gone, the mount
3112 			 * could follow
3113 			 */
3114 			locked_mp = vnode_mount(fvp);
3115 			mount_ref(locked_mp, 0);
3116 
3117 			/* make a copy of to path to pass to nfsrv_namei() again */
3118 			topath = zalloc(ZV_NAMEI);
3119 			bcopy(toni.ni_cnd.cn_pnbuf, topath, tolen + 1);
3120 
3121 			/*
3122 			 * nameidone has to happen before we vnode_put(tdvp)
3123 			 * since it may need to release the fs_nodelock on the tdvp
3124 			 */
3125 			nameidone(&toni);
3126 
3127 			if (tvp) {
3128 				vnode_put(tvp);
3129 			}
3130 			vnode_put(tdvp);
3131 
3132 			/* make a copy of from path to pass to nfsrv_namei() again */
3133 			frompath = zalloc(ZV_NAMEI);
3134 			bcopy(fromni.ni_cnd.cn_pnbuf, frompath, fromlen + 1);
3135 
3136 			/*
3137 			 * nameidone has to happen before we vnode_put(fdvp)
3138 			 * since it may need to release the fs_nodelock on the fdvp
3139 			 */
3140 			nameidone(&fromni);
3141 
3142 			vnode_put(fvp);
3143 			vnode_put(fdvp);
3144 
3145 			if (fdirp) {
3146 				vnode_put(fdirp);
3147 				fdirp = NULL;
3148 			}
3149 			if (tdirp) {
3150 				vnode_put(tdirp);
3151 				tdirp = NULL;
3152 			}
3153 			mount_lock_renames(locked_mp);
3154 			holding_mntlock = 1;
3155 
3156 			fvp = tvp = NULL;
3157 			fdvp = tdvp = NULL;
3158 
3159 			fdpreattrerr = tdpreattrerr = ENOENT;
3160 
3161 			if (!topath || !frompath) {
3162 				/* we couldn't allocate a path, so bail */
3163 				error = ENOMEM;
3164 				goto out;
3165 			}
3166 
3167 			/* reset credential if it was remapped */
3168 			if (nd->nd_cr != saved_cred) {
3169 				kauth_cred_ref(saved_cred);
3170 				kauth_cred_unref(&nd->nd_cr);
3171 				ctx->vc_ucred = nd->nd_cr = saved_cred;
3172 			}
3173 
3174 			goto retry;
3175 		}
3176 	} else {
3177 		/*
3178 		 * when we dropped the iocounts to take
3179 		 * the lock, we allowed the identity of
3180 		 * the various vnodes to change... if they did,
3181 		 * we may no longer be dealing with a rename
3182 		 * that reshapes the tree... once we're holding
3183 		 * the iocounts, the vnodes can't change type
3184 		 * so we're free to drop the lock at this point
3185 		 * and continue on
3186 		 */
3187 		if (holding_mntlock) {
3188 			mount_unlock_renames(locked_mp);
3189 			mount_drop(locked_mp, 0);
3190 			holding_mntlock = 0;
3191 		}
3192 	}
3193 
3194 	// save these off so we can later verify that fvp is the same
3195 	vnode_t oparent;
3196 	oname   = fvp->v_name;
3197 	oparent = fvp->v_parent;
3198 
3199 	/*
3200 	 * If generating an fsevent, then
3201 	 * stash any pre-rename info we may need.
3202 	 */
3203 #if CONFIG_FSE
3204 	if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, fvp)) {
3205 		int from_truncated = 0, to_truncated = 0;
3206 
3207 		get_fse_info(fvp, &from_finfo, ctx);
3208 		if (tvp) {
3209 			get_fse_info(tvp, &to_finfo, ctx);
3210 		}
3211 
3212 		from_name = get_pathbuff();
3213 		if (from_name) {
3214 			from_len = safe_getpath(fdvp, fromni.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated);
3215 		}
3216 
3217 		to_name = from_name ? get_pathbuff() : NULL;
3218 		if (to_name) {
3219 			to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated);
3220 		}
3221 
3222 		if (from_truncated || to_truncated) {
3223 			from_finfo.mode |= FSE_TRUNCATED_PATH;
3224 		}
3225 	} else {
3226 		from_name = NULL;
3227 		to_name   = NULL;
3228 	}
3229 #else /* CONFIG_FSE */
3230 	from_name = NULL;
3231 	to_name   = NULL;
3232 #endif /* CONFIG_FSE */
3233 
3234 	error = VNOP_RENAME(fromni.ni_dvp, fromni.ni_vp, &fromni.ni_cnd,
3235 	    toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx);
3236 	/*
3237 	 * fix up name & parent pointers.  note that we first
3238 	 * check that fvp has the same name/parent pointers it
3239 	 * had before the rename call... this is a 'weak' check
3240 	 * at best...
3241 	 */
3242 	if (oname == fvp->v_name && oparent == fvp->v_parent) {
3243 		int update_flags;
3244 		update_flags = VNODE_UPDATE_NAME;
3245 		if (fdvp != tdvp) {
3246 			update_flags |= VNODE_UPDATE_PARENT;
3247 		}
3248 		vnode_update_identity(fvp, tdvp, toni.ni_cnd.cn_nameptr,
3249 		    toni.ni_cnd.cn_namelen, toni.ni_cnd.cn_hash, update_flags);
3250 	}
3251 
3252 	/*
3253 	 * If the rename is OK and we've got the paths
3254 	 * then add an fsevent.
3255 	 */
3256 #if CONFIG_FSE
3257 	if (nfsrv_fsevents_enabled && !error && from_name && to_name) {
3258 		if (tvp) {
3259 			add_fsevent(FSE_RENAME, ctx,
3260 			    FSE_ARG_STRING, from_len, from_name,
3261 			    FSE_ARG_FINFO, &from_finfo,
3262 			    FSE_ARG_STRING, to_len, to_name,
3263 			    FSE_ARG_FINFO, &to_finfo,
3264 			    FSE_ARG_DONE);
3265 		} else {
3266 			add_fsevent(FSE_RENAME, ctx,
3267 			    FSE_ARG_STRING, from_len, from_name,
3268 			    FSE_ARG_FINFO, &from_finfo,
3269 			    FSE_ARG_STRING, to_len, to_name,
3270 			    FSE_ARG_DONE);
3271 		}
3272 	}
3273 	if (from_name) {
3274 		release_pathbuff(from_name);
3275 	}
3276 	if (to_name) {
3277 		release_pathbuff(to_name);
3278 	}
3279 #endif /* CONFIG_FSE */
3280 	from_name = to_name = NULL;
3281 
3282 out:
3283 	if (holding_mntlock) {
3284 		mount_unlock_renames(locked_mp);
3285 		mount_drop(locked_mp, 0);
3286 		holding_mntlock = 0;
3287 	}
3288 	if (tdvp) {
3289 		/*
3290 		 * nameidone has to happen before we vnode_put(tdvp)
3291 		 * since it may need to release the fs_nodelock on the tdvp
3292 		 */
3293 		nameidone(&toni);
3294 		if (tvp) {
3295 			vnode_put(tvp);
3296 		}
3297 		vnode_put(tdvp);
3298 
3299 		tdvp = NULL;
3300 	}
3301 	if (fdvp) {
3302 		/*
3303 		 * nameidone has to happen before we vnode_put(fdvp)
3304 		 * since it may need to release the fs_nodelock on the fdvp
3305 		 */
3306 		nameidone(&fromni);
3307 
3308 		if (fvp) {
3309 			vnode_put(fvp);
3310 		}
3311 		vnode_put(fdvp);
3312 
3313 		fdvp = NULL;
3314 	}
3315 	if (fdirp) {
3316 		nfsm_srv_vattr_init(&fdpostattr, nd->nd_vers);
3317 		fdpostattrerr = vnode_getattr(fdirp, &fdpostattr, ctx);
3318 		vnode_put(fdirp);
3319 		fdirp = NULL;
3320 	}
3321 	if (tdirp) {
3322 		nfsm_srv_vattr_init(&tdpostattr, nd->nd_vers);
3323 		tdpostattrerr = vnode_getattr(tdirp, &tdpostattr, ctx);
3324 		vnode_put(tdirp);
3325 		tdirp = NULL;
3326 	}
3327 
3328 nfsmerr:
3329 	/* assemble reply */
3330 	nd->nd_repstat = error;
3331 	error = nfsrv_rephead(nd, slp, &nmrep, 2 * NFSX_WCCDATA(nd->nd_vers));
3332 	nfsmout_if(error);
3333 	*mrepp = nmrep.nmc_mhead;
3334 	nfsmout_on_status(nd, error);
3335 	if (nd->nd_vers == NFS_VER3) {
3336 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3337 		    fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr);
3338 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3339 		    tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr);
3340 	}
3341 nfsmout:
3342 	nfsm_chain_build_done(error, &nmrep);
3343 	if (holding_mntlock) {
3344 		mount_unlock_renames(locked_mp);
3345 		mount_drop(locked_mp, 0);
3346 	}
3347 	if (tdvp) {
3348 		/*
3349 		 * nameidone has to happen before we vnode_put(tdvp)
3350 		 * since it may need to release the fs_nodelock on the tdvp
3351 		 */
3352 		nameidone(&toni);
3353 
3354 		if (tvp) {
3355 			vnode_put(tvp);
3356 		}
3357 		vnode_put(tdvp);
3358 	}
3359 	if (fdvp) {
3360 		/*
3361 		 * nameidone has to happen before we vnode_put(fdvp)
3362 		 * since it may need to release the fs_nodelock on the fdvp
3363 		 */
3364 		nameidone(&fromni);
3365 
3366 		if (fvp) {
3367 			vnode_put(fvp);
3368 		}
3369 		vnode_put(fdvp);
3370 	}
3371 	if (fdirp) {
3372 		vnode_put(fdirp);
3373 	}
3374 	if (tdirp) {
3375 		vnode_put(tdirp);
3376 	}
3377 	if (frompath) {
3378 		NFS_ZFREE(ZV_NAMEI, frompath);
3379 	}
3380 	if (topath) {
3381 		NFS_ZFREE(ZV_NAMEI, topath);
3382 	}
3383 	if (saved_cred) {
3384 		kauth_cred_unref(&saved_cred);
3385 	}
3386 	if (error) {
3387 		nfsm_chain_cleanup(&nmrep);
3388 		*mrepp = NULL;
3389 	}
3390 	return error;
3391 }
3392 
3393 /*
3394  * nfs link service
3395  */
3396 int
nfsrv_link(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3397 nfsrv_link(
3398 	struct nfsrv_descript *nd,
3399 	struct nfsrv_sock *slp,
3400 	vfs_context_t ctx,
3401 	mbuf_t *mrepp)
3402 {
3403 	struct nameidata ni;
3404 	int error, dpreattrerr, dpostattrerr, attrerr;
3405 	uint32_t len = 0;
3406 	vnode_t vp, xp, dvp, dirp;
3407 	struct vnode_attr dpreattr, dpostattr, attr;
3408 	struct nfs_filehandle nfh, dnfh;
3409 	struct nfs_export *nx;
3410 	struct nfs_export_options *nxo;
3411 	struct nfsm_chain *nmreq, nmrep;
3412 
3413 	error = 0;
3414 	dpreattrerr = dpostattrerr = attrerr = ENOENT;
3415 	vp = xp = dvp = dirp = NULL;
3416 	nmreq = &nd->nd_nmreq;
3417 	nfsm_chain_null(&nmrep);
3418 
3419 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3420 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
3421 	nfsm_chain_get_32(error, nmreq, len);
3422 	nfsm_name_len_check(error, nd, len);
3423 	nfsmerr_if(error);
3424 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
3425 	nfsmerr_if(error);
3426 
3427 	/* update export stats */
3428 	NFSStatAdd64(&nx->nx_stats.ops, 1);
3429 
3430 	/* update active user stats */
3431 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
3432 
3433 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
3434 	nfsmerr_if(error);
3435 
3436 	/* we're not allowed to link to directories... */
3437 	if (vnode_vtype(vp) == VDIR) {
3438 		error = NFSERR_ISDIR;
3439 		goto out;
3440 	}
3441 
3442 	/* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3443 	if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) {
3444 		goto out;
3445 	}
3446 
3447 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(vnode_getname(vp)), ctx);
3448 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3449 	if (!error) {
3450 		error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
3451 	}
3452 	if (dirp) {
3453 		if (nd->nd_vers == NFS_VER3) {
3454 			nfsm_srv_pre_vattr_init(&dpreattr);
3455 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3456 		} else {
3457 			vnode_put(dirp);
3458 			dirp = NULL;
3459 		}
3460 	}
3461 	if (error) {
3462 		goto out;
3463 	}
3464 	dvp = ni.ni_dvp;
3465 	xp = ni.ni_vp;
3466 
3467 	if (xp != NULL) {
3468 		error = EEXIST;
3469 	} else if (vnode_mount(vp) != vnode_mount(dvp)) {
3470 		error = EXDEV;
3471 	} else {
3472 		error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3473 	}
3474 
3475 #if CONFIG_MACF
3476 	if (!error) {
3477 		error = mac_vnode_check_link(ctx, dvp, vp, &ni.ni_cnd);
3478 		if (error) {
3479 			error = EACCES;
3480 		}
3481 	}
3482 #endif
3483 	if (!error) {
3484 		error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx);
3485 	}
3486 
3487 #if CONFIG_FSE
3488 	if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
3489 		char *target_path = NULL;
3490 		int plen, truncated = 0;
3491 		fse_info finfo;
3492 
3493 		/* build the path to the new link file */
3494 		target_path = get_pathbuff();
3495 		if (target_path) {
3496 			plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
3497 
3498 			if (get_fse_info(vp, &finfo, ctx) == 0) {
3499 				if (truncated) {
3500 					finfo.mode |= FSE_TRUNCATED_PATH;
3501 				}
3502 				add_fsevent(FSE_CREATE_FILE, ctx,
3503 				    FSE_ARG_STRING, plen, target_path,
3504 				    FSE_ARG_FINFO, &finfo,
3505 				    FSE_ARG_DONE);
3506 			}
3507 
3508 			release_pathbuff(target_path);
3509 		}
3510 	}
3511 #endif
3512 
3513 	/*
3514 	 * nameidone has to happen before we vnode_put(dvp)
3515 	 * since it may need to release the fs_nodelock on the dvp
3516 	 */
3517 	nameidone(&ni);
3518 
3519 	if (xp) {
3520 		vnode_put(xp);
3521 	}
3522 	vnode_put(dvp);
3523 out:
3524 	if (nd->nd_vers == NFS_VER3) {
3525 		nfsm_srv_vattr_init(&attr, NFS_VER3);
3526 		attrerr = vnode_getattr(vp, &attr, ctx);
3527 	}
3528 	if (dirp) {
3529 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3530 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3531 		vnode_put(dirp);
3532 		dirp = NULL;
3533 	}
3534 	vnode_put(vp);
3535 	vp = NULL;
3536 
3537 nfsmerr:
3538 	/* assemble reply */
3539 	nd->nd_repstat = error;
3540 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3541 	nfsmout_if(error);
3542 	*mrepp = nmrep.nmc_mhead;
3543 	nfsmout_on_status(nd, error);
3544 	if (nd->nd_vers == NFS_VER3) {
3545 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
3546 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3547 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3548 	}
3549 nfsmout:
3550 	nfsm_chain_build_done(error, &nmrep);
3551 	if (vp) {
3552 		vnode_put(vp);
3553 	}
3554 	if (error) {
3555 		nfsm_chain_cleanup(&nmrep);
3556 		*mrepp = NULL;
3557 	}
3558 	return error;
3559 }
3560 
3561 /*
3562  * nfs symbolic link service
3563  */
3564 int
nfsrv_symlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3565 nfsrv_symlink(
3566 	struct nfsrv_descript *nd,
3567 	struct nfsrv_sock *slp,
3568 	vfs_context_t ctx,
3569 	mbuf_t *mrepp)
3570 {
3571 	struct vnode_attr dpreattr, dpostattr, postattr;
3572 	struct vnode_attr va, *vap = &va;
3573 	struct nameidata ni;
3574 	int error, dpreattrerr, dpostattrerr, postattrerr;
3575 	uint32_t len = 0, linkdatalen = 0, cnflags;
3576 	uid_t saved_uid;
3577 	char *linkdata;
3578 	vnode_t vp, dvp, dirp;
3579 	struct nfs_filehandle nfh = {};
3580 	struct nfs_export *nx = NULL;
3581 	struct nfs_export_options *nxo = NULL;
3582 	uio_t auio = NULL;
3583 	UIO_STACKBUF(uio_buf, 1);
3584 	struct nfsm_chain *nmreq, nmrep;
3585 	__unused const int nfs_vers = nd->nd_vers;
3586 	assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3587 
3588 	error = 0;
3589 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3590 	nmreq = &nd->nd_nmreq;
3591 	nfsm_chain_null(&nmrep);
3592 	linkdata = NULL;
3593 	dirp = NULL;
3594 
3595 	saved_uid = kauth_cred_getuid(nd->nd_cr);
3596 
3597 	ni.ni_cnd.cn_nameiop = 0;
3598 	vp = dvp = NULL;
3599 
3600 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3601 	nfsm_chain_get_32(error, nmreq, len);
3602 	nfsm_name_len_check(error, nd, len);
3603 	nfsmerr_if(error);
3604 
3605 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, 0, ctx);
3606 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3607 	if (!error) {
3608 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3609 		if (nx != NULL) {
3610 			/* update export stats */
3611 			NFSStatAdd64(&nx->nx_stats.ops, 1);
3612 
3613 			/* update active user stats */
3614 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3615 		}
3616 	}
3617 	if (dirp) {
3618 		if (nd->nd_vers == NFS_VER3) {
3619 			nfsm_srv_pre_vattr_init(&dpreattr);
3620 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3621 		} else {
3622 			vnode_put(dirp);
3623 			dirp = NULL;
3624 		}
3625 	}
3626 	if (error) {
3627 		ni.ni_cnd.cn_nameiop = 0;
3628 		goto out1;
3629 	}
3630 	dvp = ni.ni_dvp;
3631 	vp = ni.ni_vp;
3632 
3633 	VATTR_INIT(vap);
3634 	if (nd->nd_vers == NFS_VER3) {
3635 		error = nfsm_chain_get_sattr(nd, nmreq, vap);
3636 	}
3637 	nfsm_chain_get_32(error, nmreq, linkdatalen);
3638 	if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) ||
3639 	    ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) {
3640 		error = NFSERR_NAMETOL;
3641 	}
3642 	nfsmerr_if(error);
3643 	linkdata = kalloc_data(linkdatalen + 1, Z_WAITOK);
3644 	if (linkdata) {
3645 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3646 		    &uio_buf[0], sizeof(uio_buf));
3647 	}
3648 	if (!linkdata || !auio) {
3649 		error = ENOMEM;
3650 		goto out;
3651 	}
3652 	uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
3653 	error = nfsm_chain_get_uio(nmreq, linkdatalen, auio);
3654 	if (!error && (nd->nd_vers == NFS_VER2)) {
3655 		error = nfsm_chain_get_sattr(nd, nmreq, vap);
3656 	}
3657 	nfsmerr_if(error);
3658 	*(linkdata + linkdatalen) = '\0';
3659 	if (vp) {
3660 		error = EEXIST;
3661 		goto out;
3662 	}
3663 
3664 	VATTR_SET(vap, va_type, VLNK);
3665 	VATTR_CLEAR_ACTIVE(vap, va_data_size);
3666 	VATTR_CLEAR_ACTIVE(vap, va_access_time);
3667 	/*
3668 	 * Server policy is to alway use the mapped rpc credential for
3669 	 * file system object creation. This has the nice side effect of
3670 	 * enforcing BSD creation semantics
3671 	 */
3672 	VATTR_CLEAR_ACTIVE(vap, va_uid);
3673 	VATTR_CLEAR_ACTIVE(vap, va_gid);
3674 
3675 	/* authorize before creating */
3676 	error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3677 
3678 	/* validate given attributes */
3679 	if (!error) {
3680 		error = vnode_authattr_new(dvp, vap, 0, ctx);
3681 	}
3682 	if (!error) {
3683 		error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
3684 		if (error) {
3685 			error = EACCES;
3686 		}
3687 	}
3688 
3689 	if (!error) {
3690 		error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
3691 	}
3692 
3693 	if (!error && (nd->nd_vers == NFS_VER3)) {
3694 		if (vp == NULL) {
3695 			ni.ni_cnd.cn_nameiop = LOOKUP;
3696 #if CONFIG_TRIGGERS
3697 			ni.ni_op = OP_LOOKUP;
3698 #endif
3699 			ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3700 			ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3701 			ni.ni_cnd.cn_context = ctx;
3702 			ni.ni_startdir = dvp;
3703 			ni.ni_usedvp   = dvp;
3704 			ni.ni_rootdir = rootvnode;
3705 			cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
3706 			while ((error = lookup(&ni)) == ERECYCLE) {
3707 				ni.ni_cnd.cn_flags = cnflags;
3708 				ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
3709 				ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
3710 			}
3711 			if (!error) {
3712 				vp = ni.ni_vp;
3713 			}
3714 		}
3715 		if (!error) {
3716 			error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
3717 			if (!error) {
3718 				nfsm_srv_vattr_init(&postattr, NFS_VER3);
3719 				postattrerr = vnode_getattr(vp, &postattr, ctx);
3720 			}
3721 		}
3722 	}
3723 
3724 #if CONFIG_FSE
3725 	if (nfsrv_fsevents_enabled && !error && vp) {
3726 		add_fsevent(FSE_CREATE_FILE, ctx,
3727 		    FSE_ARG_VNODE, vp,
3728 		    FSE_ARG_DONE);
3729 	}
3730 #endif
3731 out:
3732 	/*
3733 	 * nameidone has to happen before we vnode_put(dvp)
3734 	 * since it may need to release the fs_nodelock on the dvp
3735 	 */
3736 	nameidone(&ni);
3737 	ni.ni_cnd.cn_nameiop = 0;
3738 	if (vp) {
3739 		vnode_put(vp);
3740 	}
3741 	vnode_put(dvp);
3742 out1:
3743 	if (linkdata) {
3744 		kfree_data(linkdata, linkdatalen + 1);
3745 	}
3746 	if (dirp) {
3747 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3748 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3749 		vnode_put(dirp);
3750 		dirp = NULL;
3751 	}
3752 
3753 nfsmerr:
3754 	/* assemble reply */
3755 	nd->nd_repstat = error;
3756 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3757 	    NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3758 	assert(nfs_vers == nd->nd_vers);
3759 	nfsmout_if(error);
3760 	*mrepp = nmrep.nmc_mhead;
3761 	nfsmout_on_status(nd, error);
3762 	if (nd->nd_vers == NFS_VER3) {
3763 		if (!nd->nd_repstat) {
3764 			if (!nfh.nfh_fhp) {
3765 				error = NFSERR_SERVERFAULT;
3766 				goto nfsmerr;
3767 			}
3768 			nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3769 			nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3770 		}
3771 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
3772 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3773 	}
3774 nfsmout:
3775 	nfsm_chain_build_done(error, &nmrep);
3776 	if (ni.ni_cnd.cn_nameiop) {
3777 		/*
3778 		 * nameidone has to happen before we vnode_put(dvp)
3779 		 * since it may need to release the fs_nodelock on the dvp
3780 		 */
3781 		nameidone(&ni);
3782 
3783 		if (vp) {
3784 			vnode_put(vp);
3785 		}
3786 		vnode_put(dvp);
3787 	}
3788 	if (dirp) {
3789 		vnode_put(dirp);
3790 	}
3791 	if (linkdata) {
3792 		kfree_data(linkdata, linkdatalen + 1);
3793 	}
3794 	if (error) {
3795 		nfsm_chain_cleanup(&nmrep);
3796 		*mrepp = NULL;
3797 	}
3798 	return error;
3799 }
3800 
3801 /*
3802  * nfs mkdir service
3803  */
3804 
3805 int
nfsrv_mkdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3806 nfsrv_mkdir(
3807 	struct nfsrv_descript *nd,
3808 	struct nfsrv_sock *slp,
3809 	vfs_context_t ctx,
3810 	mbuf_t *mrepp)
3811 {
3812 	struct vnode_attr dpreattr, dpostattr, postattr;
3813 	struct vnode_attr va, *vap = &va;
3814 	struct nameidata ni;
3815 	int error, dpreattrerr, dpostattrerr, postattrerr;
3816 	uint32_t len = 0;
3817 	vnode_t vp, dvp, dirp;
3818 	struct nfs_filehandle nfh = {};
3819 	struct nfs_export *nx = NULL;
3820 	struct nfs_export_options *nxo = NULL;
3821 	uid_t saved_uid;
3822 	kauth_acl_t xacl = NULL;
3823 	struct nfsm_chain *nmreq, nmrep;
3824 	__unused const int nfs_vers = nd->nd_vers;
3825 	assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3826 
3827 	error = 0;
3828 	dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3829 	nmreq = &nd->nd_nmreq;
3830 	nfsm_chain_null(&nmrep);
3831 
3832 	saved_uid = kauth_cred_getuid(nd->nd_cr);
3833 
3834 	ni.ni_cnd.cn_nameiop = 0;
3835 	vp = dvp = dirp = NULL;
3836 
3837 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3838 	nfsm_chain_get_32(error, nmreq, len);
3839 	nfsm_name_len_check(error, nd, len);
3840 	nfsmerr_if(error);
3841 
3842 	NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | WILLBEDIR, UIO_SYSSPACE, 0, ctx);
3843 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3844 	if (!error) {
3845 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3846 		if (nx != NULL) {
3847 			/* update export stats */
3848 			NFSStatAdd64(&nx->nx_stats.ops, 1);
3849 
3850 			/* update active user stats */
3851 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3852 		}
3853 	}
3854 	if (dirp) {
3855 		if (nd->nd_vers == NFS_VER3) {
3856 			nfsm_srv_pre_vattr_init(&dpreattr);
3857 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3858 		} else {
3859 			vnode_put(dirp);
3860 			dirp = NULL;
3861 		}
3862 	}
3863 	if (error) {
3864 		ni.ni_cnd.cn_nameiop = 0;
3865 		goto nfsmerr;
3866 	}
3867 	dvp = ni.ni_dvp;
3868 	vp = ni.ni_vp;
3869 
3870 	VATTR_INIT(vap);
3871 	error = nfsm_chain_get_sattr(nd, nmreq, vap);
3872 	nfsmerr_if(error);
3873 	VATTR_SET(vap, va_type, VDIR);
3874 
3875 	if (vp != NULL) {
3876 		/*
3877 		 * nameidone has to happen before we vnode_put(dvp)
3878 		 * since it may need to release the fs_nodelock on the dvp
3879 		 */
3880 		nameidone(&ni);
3881 		vnode_put(dvp);
3882 		vnode_put(vp);
3883 		error = EEXIST;
3884 		goto out;
3885 	}
3886 
3887 	error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
3888 
3889 	/* construct ACL and handle inheritance */
3890 	if (!error) {
3891 		error = kauth_acl_inherit(dvp,
3892 		    NULL,
3893 		    &xacl,      /* isdir */
3894 		    1,
3895 		    ctx);
3896 
3897 		if (!error && xacl != NULL) {
3898 			VATTR_SET(vap, va_acl, xacl);
3899 		}
3900 	}
3901 
3902 	VATTR_CLEAR_ACTIVE(vap, va_data_size);
3903 	VATTR_CLEAR_ACTIVE(vap, va_access_time);
3904 	/*
3905 	 * We don't support the S_ISGID bit for directories. Solaris and other
3906 	 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3907 	 * any ways.
3908 	 */
3909 	if (VATTR_IS_ACTIVE(vap, va_mode)) {
3910 		vap->va_mode &= ~S_ISGID;
3911 	}
3912 	/*
3913 	 * Server policy is to alway use the mapped rpc credential for
3914 	 * file system object creation. This has the nice side effect of
3915 	 * enforcing BSD creation semantics
3916 	 */
3917 	VATTR_CLEAR_ACTIVE(vap, va_uid);
3918 	VATTR_CLEAR_ACTIVE(vap, va_gid);
3919 
3920 	/* validate new-file security information */
3921 	if (!error) {
3922 		error = vnode_authattr_new(dvp, vap, 0, ctx);
3923 	}
3924 	/*
3925 	 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3926 	 * sit well with our clients so we map all errors to EPERM.
3927 	 */
3928 	if (error) {
3929 		error = EPERM;
3930 	}
3931 
3932 	if (!error) {
3933 		error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL);
3934 		if (error) {
3935 			error = EACCES;
3936 		}
3937 	}
3938 
3939 	if (!error) {
3940 		error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
3941 	}
3942 
3943 #if CONFIG_FSE
3944 	if (nfsrv_fsevents_enabled && !error) {
3945 		add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3946 	}
3947 #endif
3948 
3949 	if (!error && !VATTR_ALL_SUPPORTED(vap)) {
3950 		/*
3951 		 * If some of the requested attributes weren't handled by the VNOP,
3952 		 * use our fallback code.
3953 		 */
3954 		error = vnode_setattr_fallback(vp, vap, ctx);
3955 	}
3956 
3957 	if (xacl != NULL) {
3958 		kauth_acl_free(xacl);
3959 	}
3960 
3961 	if (!error) {
3962 		error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
3963 		if (!error) {
3964 			nfsm_srv_vattr_init(&postattr, nd->nd_vers);
3965 			postattrerr = vnode_getattr(vp, &postattr, ctx);
3966 			if (nd->nd_vers == NFS_VER2) {
3967 				error = postattrerr;
3968 			}
3969 		}
3970 		vnode_put(vp);
3971 		vp = NULL;
3972 	}
3973 	/*
3974 	 * nameidone has to happen before we vnode_put(dvp)
3975 	 * since it may need to release the fs_nodelock on the dvp
3976 	 */
3977 	nameidone(&ni);
3978 	vnode_put(dvp);
3979 out:
3980 	ni.ni_cnd.cn_nameiop = 0;
3981 
3982 	if (dirp) {
3983 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3984 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3985 		vnode_put(dirp);
3986 		dirp = NULL;
3987 	}
3988 
3989 nfsmerr:
3990 	/* assemble reply */
3991 	nd->nd_repstat = error;
3992 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3993 	    NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3994 	assert(nfs_vers == nd->nd_vers);
3995 	nfsmout_if(error);
3996 	*mrepp = nmrep.nmc_mhead;
3997 	nfsmout_on_status(nd, error);
3998 	if (nd->nd_vers == NFS_VER3) {
3999 		if (!nd->nd_repstat) {
4000 			if (!nfh.nfh_fhp) {
4001 				error = NFSERR_SERVERFAULT;
4002 				goto nfsmerr;
4003 			}
4004 			nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4005 			nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
4006 		}
4007 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
4008 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4009 	} else {
4010 		nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
4011 		if (!error) {
4012 			error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
4013 		}
4014 	}
4015 nfsmout:
4016 	nfsm_chain_build_done(error, &nmrep);
4017 	if (ni.ni_cnd.cn_nameiop) {
4018 		/*
4019 		 * nameidone has to happen before we vnode_put(dvp)
4020 		 * since it may need to release the fs_nodelock on the dvp
4021 		 */
4022 		nameidone(&ni);
4023 		vnode_put(dvp);
4024 		if (vp) {
4025 			vnode_put(vp);
4026 		}
4027 	}
4028 	if (dirp) {
4029 		vnode_put(dirp);
4030 	}
4031 	if (error) {
4032 		nfsm_chain_cleanup(&nmrep);
4033 		*mrepp = NULL;
4034 	}
4035 	return error;
4036 }
4037 
4038 /*
4039  * nfs rmdir service
4040  */
4041 int
nfsrv_rmdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4042 nfsrv_rmdir(
4043 	struct nfsrv_descript *nd,
4044 	struct nfsrv_sock *slp,
4045 	vfs_context_t ctx,
4046 	mbuf_t *mrepp)
4047 {
4048 	int error, dpreattrerr, dpostattrerr;
4049 	uint32_t len = 0;
4050 	uid_t saved_uid;
4051 	vnode_t vp, dvp, dirp;
4052 	struct vnode_attr dpreattr, dpostattr;
4053 	struct nfs_filehandle nfh;
4054 	struct nfs_export *nx = NULL;
4055 	struct nfs_export_options *nxo = NULL;
4056 	struct nameidata ni;
4057 	struct nfsm_chain *nmreq, nmrep;
4058 
4059 	error = 0;
4060 	dpreattrerr = dpostattrerr = ENOENT;
4061 	saved_uid = kauth_cred_getuid(nd->nd_cr);
4062 	nmreq = &nd->nd_nmreq;
4063 	nfsm_chain_null(&nmrep);
4064 
4065 	vp = dvp = dirp = NULL;
4066 
4067 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4068 	nfsm_chain_get_32(error, nmreq, len);
4069 	nfsm_name_len_check(error, nd, len);
4070 	nfsmerr_if(error);
4071 
4072 	NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
4073 	error = nfsm_chain_get_path_namei(nmreq, len, &ni);
4074 	if (!error) {
4075 		error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
4076 		if (nx != NULL) {
4077 			/* update export stats */
4078 			NFSStatAdd64(&nx->nx_stats.ops, 1);
4079 
4080 			/* update active user stats */
4081 			nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
4082 		}
4083 	}
4084 	if (dirp) {
4085 		if (nd->nd_vers == NFS_VER3) {
4086 			nfsm_srv_pre_vattr_init(&dpreattr);
4087 			dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
4088 		} else {
4089 			vnode_put(dirp);
4090 			dirp = NULL;
4091 		}
4092 	}
4093 	nfsmerr_if(error);
4094 
4095 	dvp = ni.ni_dvp;
4096 	vp = ni.ni_vp;
4097 
4098 	if (vnode_vtype(vp) != VDIR) {
4099 		error = ENOTDIR;
4100 		goto out;
4101 	}
4102 	/*
4103 	 * No rmdir "." please.
4104 	 */
4105 	if (dvp == vp) {
4106 		error = EINVAL;
4107 		goto out;
4108 	}
4109 	/*
4110 	 * No rmdir ".." please.
4111 	 */
4112 	if (vnode_parent(dvp) == vp) {
4113 		error = EINVAL;
4114 		goto out;
4115 	}
4116 	/*
4117 	 * The root of a mounted filesystem cannot be deleted.
4118 	 */
4119 	if (vnode_isvroot(vp)) {
4120 		error = EBUSY;
4121 	}
4122 	if (!error) {
4123 		error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
4124 	}
4125 	if (!error) {
4126 		error = vn_authorize_rmdir(dvp, vp, &ni.ni_cnd, ctx, NULL);
4127 		if (error) {
4128 			error = EACCES;
4129 		}
4130 	}
4131 
4132 	if (!error) {
4133 #if CONFIG_FSE
4134 		char     *path = NULL;
4135 		int       plen = 0;
4136 		fse_info  finfo;
4137 
4138 		if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
4139 			plen = MAXPATHLEN;
4140 			if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
4141 				get_fse_info(vp, &finfo, ctx);
4142 			} else if (path) {
4143 				release_pathbuff(path);
4144 				path = NULL;
4145 			}
4146 		}
4147 #endif /* CONFIG_FSE */
4148 
4149 		error = VNOP_RMDIR(dvp, vp, &ni.ni_cnd, ctx);
4150 
4151 #if CONFIG_FSE
4152 		if (path) {
4153 			if (!error) {
4154 				add_fsevent(FSE_DELETE, ctx,
4155 				    FSE_ARG_STRING, plen, path,
4156 				    FSE_ARG_FINFO, &finfo,
4157 				    FSE_ARG_DONE);
4158 			}
4159 			release_pathbuff(path);
4160 		}
4161 #endif /* CONFIG_FSE */
4162 	}
4163 out:
4164 	/*
4165 	 * nameidone has to happen before we vnode_put(dvp)
4166 	 * since it may need to release the fs_nodelock on the dvp
4167 	 */
4168 	nameidone(&ni);
4169 
4170 	vnode_put(dvp);
4171 	vnode_put(vp);
4172 
4173 	if (dirp) {
4174 		nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
4175 		dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
4176 		vnode_put(dirp);
4177 		dirp = NULL;
4178 	}
4179 
4180 nfsmerr:
4181 	/* assemble reply */
4182 	nd->nd_repstat = error;
4183 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
4184 	nfsmout_if(error);
4185 	*mrepp = nmrep.nmc_mhead;
4186 	nfsmout_on_status(nd, error);
4187 	if (nd->nd_vers == NFS_VER3) {
4188 		nfsm_chain_add_wcc_data(error, nd, &nmrep,
4189 		    dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4190 	}
4191 nfsmout:
4192 	nfsm_chain_build_done(error, &nmrep);
4193 	if (dirp) {
4194 		vnode_put(dirp);
4195 	}
4196 	if (error) {
4197 		nfsm_chain_cleanup(&nmrep);
4198 		*mrepp = NULL;
4199 	}
4200 	return error;
4201 }
4202 
4203 /*
4204  * nfs readdir service
4205  * - mallocs what it thinks is enough to read
4206  *	count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4207  * - calls VNOP_READDIR()
4208  * - loops around building the reply
4209  *	if the output generated exceeds count break out of loop
4210  *	The nfsm_clget macro is used here so that the reply will be packed
4211  *	tightly in mbuf clusters.
4212  * - it only knows that it has encountered eof when the VNOP_READDIR()
4213  *	reads nothing
4214  * - as such one readdir rpc will return eof false although you are there
4215  *	and then the next will return eof
4216  * - it trims out records with d_fileno == 0
4217  *	this doesn't matter for Unix clients, but they might confuse clients
4218  *	for other os'.
4219  * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4220  *	than requested, but this may not apply to all filesystems. For
4221  *	example, client NFS does not { although it is never remote mounted
4222  *	anyhow }
4223  *     The alternate call nfsrv_readdirplus() does lookups as well.
4224  * PS:  The XNFS protocol spec clearly describes what the "count"s arguments
4225  *      are supposed to cover.  For readdir, the count is the total number of
4226  *      bytes included in everything from the directory's postopattr through
4227  *      the EOF flag.  For readdirplus, the maxcount is the same, and the
4228  *      dircount includes all that except for the entry attributes and handles.
4229  */
4230 int
nfsrv_readdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4231 nfsrv_readdir(
4232 	struct nfsrv_descript *nd,
4233 	struct nfsrv_sock *slp,
4234 	vfs_context_t ctx,
4235 	mbuf_t *mrepp)
4236 {
4237 	struct direntry *dp;
4238 	char *cpos, *cend, *rbuf;
4239 	size_t rbuf_siz;
4240 	vnode_t vp;
4241 	struct vnode_attr attr = {};
4242 	struct nfs_filehandle nfh;
4243 	struct nfs_export *nx;
4244 	struct nfs_export_options *nxo;
4245 	uio_t auio = NULL;
4246 	UIO_STACKBUF(uio_buf, 1);
4247 	int len, nlen, rem, xfer, error, attrerr;
4248 	int siz, count, fullsiz, eofflag, nentries;
4249 	u_quad_t off, toff, verf = 0;
4250 	int vnopflag;
4251 	struct nfsm_chain *nmreq, nmrep;
4252 
4253 	error = 0;
4254 	attrerr = ENOENT;
4255 	count = nentries = 0;
4256 	nmreq = &nd->nd_nmreq;
4257 	nfsm_chain_null(&nmrep);
4258 	rbuf = NULL;
4259 	rbuf_siz = 0;
4260 	vp = NULL;
4261 
4262 	vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4263 
4264 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4265 	if (nd->nd_vers == NFS_VER3) {
4266 		nfsm_chain_get_64(error, nmreq, toff);
4267 		nfsm_chain_get_64(error, nmreq, verf);
4268 	} else {
4269 		nfsm_chain_get_32(error, nmreq, toff);
4270 	}
4271 	nfsm_chain_get_32(error, nmreq, count);
4272 	nfsmerr_if(error);
4273 
4274 	off = toff;
4275 	siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4276 	xfer = NFSRV_NDMAXDATA(nd);
4277 	if (siz > xfer) {
4278 		siz = xfer;
4279 	}
4280 	fullsiz = siz;
4281 
4282 	if (fullsiz == 0) {
4283 		error = NFSERR_TOOSMALL;
4284 		goto nfsmerr;
4285 	}
4286 
4287 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4288 	nfsmerr_if(error);
4289 
4290 	/* update export stats */
4291 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4292 
4293 	/* update active user stats */
4294 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4295 
4296 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4297 	nfsmerr_if(error);
4298 
4299 	if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) {
4300 		vnopflag |= VNODE_READDIR_NAMEMAX;
4301 	}
4302 
4303 	if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) {
4304 		vnopflag |= VNODE_READDIR_SEEKOFF32;
4305 	}
4306 
4307 	if (nd->nd_vers == NFS_VER3) {
4308 		nfsm_srv_vattr_init(&attr, NFS_VER3);
4309 		error = attrerr = vnode_getattr(vp, &attr, ctx);
4310 		if (!error && verf && (verf != attr.va_filerev)) {
4311 			error = NFSERR_BAD_COOKIE;
4312 		}
4313 	}
4314 	if (!error) {
4315 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4316 	}
4317 #if CONFIG_MACF
4318 	if (!error) {
4319 		if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4320 			error = EACCES;
4321 		}
4322 
4323 		if (!error) {
4324 			error = mac_vnode_check_readdir(ctx, vp);
4325 		}
4326 	}
4327 #endif
4328 	nfsmerr_if(error);
4329 
4330 	rbuf_siz = siz;
4331 	rbuf = kalloc_data(rbuf_siz, Z_WAITOK);
4332 	if (rbuf) {
4333 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4334 		    &uio_buf[0], sizeof(uio_buf));
4335 	}
4336 	if (!rbuf || !auio) {
4337 		error = ENOMEM;
4338 		goto nfsmerr;
4339 	}
4340 again:
4341 	uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4342 	uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4343 	eofflag = 0;
4344 	error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4345 	off = uio_offset(auio);
4346 
4347 	if (nd->nd_vers == NFS_VER3) {
4348 		nfsm_srv_vattr_init(&attr, NFS_VER3);
4349 		attrerr = vnode_getattr(vp, &attr, ctx);
4350 	}
4351 	nfsmerr_if(error);
4352 
4353 	if (uio_resid(auio) != 0) {
4354 		siz -= uio_resid(auio);
4355 
4356 		/* If nothing read, return empty reply with eof set */
4357 		if (siz == 0) {
4358 			vnode_put(vp);
4359 			vp = NULL;
4360 			kfree_data(rbuf, rbuf_siz);
4361 			/* assemble reply */
4362 			nd->nd_repstat = error;
4363 			error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4364 			    NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED);
4365 			nfsmout_if(error);
4366 			*mrepp = nmrep.nmc_mhead;
4367 			nfsmout_on_status(nd, error);
4368 			if (nd->nd_vers == NFS_VER3) {
4369 				nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4370 				nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4371 			}
4372 			nfsm_chain_add_32(error, &nmrep, FALSE);
4373 			nfsm_chain_add_32(error, &nmrep, TRUE);
4374 			nfsm_chain_build_done(error, &nmrep);
4375 			return error;
4376 		}
4377 	}
4378 
4379 	/*
4380 	 * Check for degenerate cases of nothing useful read.
4381 	 * If so go try again
4382 	 */
4383 	cpos = rbuf;
4384 	cend = rbuf + siz;
4385 	dp = (struct direntry *)cpos;
4386 	while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4387 		cpos += dp->d_reclen;
4388 		dp = (struct direntry *)cpos;
4389 		nentries--;
4390 	}
4391 	if ((cpos >= cend) || (nentries == 0)) {
4392 		toff = off;
4393 		siz = fullsiz;
4394 		goto again;
4395 	}
4396 
4397 	vnode_put(vp);
4398 	vp = NULL;
4399 
4400 	/* assemble reply */
4401 	nd->nd_repstat = error;
4402 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4403 	    NFSX_COOKIEVERF(nd->nd_vers) + siz);
4404 	nfsmout_if(error);
4405 	*mrepp = nmrep.nmc_mhead;
4406 	nfsmout_on_status(nd, error);
4407 	nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4408 
4409 	len = 2 * NFSX_UNSIGNED;
4410 	if (nd->nd_vers == NFS_VER3) {
4411 		len += NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF;
4412 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4413 		nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4414 		nfsmerr_if(error);
4415 	}
4416 
4417 	/* Loop through the records and build reply */
4418 	while ((cpos < cend) && (nentries > 0)) {
4419 		if (dp->d_fileno != 0) {
4420 			nlen = dp->d_namlen;
4421 			if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) {
4422 				nlen = NFS_MAXNAMLEN;
4423 			}
4424 			rem = nfsm_rndup(nlen) - nlen;
4425 			len += (4 * NFSX_UNSIGNED + nlen + rem);
4426 			if (nd->nd_vers == NFS_VER3) {
4427 				len += 2 * NFSX_UNSIGNED;
4428 			}
4429 			if (len > count) {
4430 				eofflag = 0;
4431 				break;
4432 			}
4433 			/* Build the directory record xdr from the direntry. */
4434 			nfsm_chain_add_32(error, &nmrep, TRUE);
4435 			if (nd->nd_vers == NFS_VER3) {
4436 				nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4437 			} else {
4438 				nfsm_chain_add_32(error, &nmrep, dp->d_fileno);
4439 			}
4440 			nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4441 			if (nd->nd_vers == NFS_VER3) {
4442 				if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4443 					dp->d_seekoff &= 0x00000000ffffffffULL;
4444 				}
4445 				nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4446 			} else {
4447 				nfsm_chain_add_32(error, &nmrep, dp->d_seekoff);
4448 			}
4449 			nfsmerr_if(error);
4450 		}
4451 		cpos += dp->d_reclen;
4452 		dp = (struct direntry *)cpos;
4453 		nentries--;
4454 	}
4455 	nfsm_chain_add_32(error, &nmrep, FALSE);
4456 	nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4457 	kfree_data(rbuf, rbuf_siz);
4458 	goto nfsmout;
4459 nfsmerr:
4460 	if (rbuf) {
4461 		kfree_data(rbuf, rbuf_siz);
4462 	}
4463 	if (vp) {
4464 		vnode_put(vp);
4465 	}
4466 	nd->nd_repstat = error;
4467 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers));
4468 	nfsmout_if(error);
4469 	*mrepp = nmrep.nmc_mhead;
4470 	nfsmout_on_status(nd, error);
4471 	if (nd->nd_vers == NFS_VER3) {
4472 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4473 	}
4474 nfsmout:
4475 	nfsm_chain_build_done(error, &nmrep);
4476 	if (error) {
4477 		nfsm_chain_cleanup(&nmrep);
4478 		*mrepp = NULL;
4479 	}
4480 	return error;
4481 }
4482 
4483 int
nfsrv_readdirplus(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4484 nfsrv_readdirplus(
4485 	struct nfsrv_descript *nd,
4486 	struct nfsrv_sock *slp,
4487 	vfs_context_t ctx,
4488 	mbuf_t *mrepp)
4489 {
4490 	struct direntry *dp;
4491 	char *cpos, *cend, *rbuf;
4492 	size_t rbuf_siz;
4493 	vnode_t vp, nvp;
4494 	struct nfs_filehandle dnfh, nfh;
4495 	struct nfs_export *nx;
4496 	struct nfs_export_options *nxo;
4497 	uio_t auio = NULL;
4498 	UIO_STACKBUF(uio_buf, 1);
4499 	struct vnode_attr attr, va, *vap = &va;
4500 	int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr;
4501 	int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot;
4502 	u_quad_t off, toff, verf;
4503 	int vnopflag;
4504 	struct nfsm_chain *nmreq, nmrep;
4505 
4506 	error = 0;
4507 	attrerr = ENOENT;
4508 	nentries = 0;
4509 	nmreq = &nd->nd_nmreq;
4510 	nfsm_chain_null(&nmrep);
4511 	rbuf = NULL;
4512 	rbuf_siz = 0;
4513 	vp = NULL;
4514 	dircount = maxcount = 0;
4515 
4516 	vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4517 
4518 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
4519 	nfsm_chain_get_64(error, nmreq, toff);
4520 	nfsm_chain_get_64(error, nmreq, verf);
4521 	nfsm_chain_get_32(error, nmreq, dircount);
4522 	nfsm_chain_get_32(error, nmreq, maxcount);
4523 	nfsmerr_if(error);
4524 
4525 	off = toff;
4526 	xfer = NFSRV_NDMAXDATA(nd);
4527 	dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4528 	if (dircount > xfer) {
4529 		dircount = xfer;
4530 	}
4531 	fullsiz = siz = dircount;
4532 	maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4533 	if (maxcount > xfer) {
4534 		maxcount = xfer;
4535 	}
4536 
4537 	if (maxcount == 0) {
4538 		error = NFSERR_TOOSMALL;
4539 		goto nfsmerr;
4540 	}
4541 
4542 	error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo);
4543 	nfsmerr_if(error);
4544 
4545 	/* update export stats */
4546 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4547 
4548 	/* update active user stats */
4549 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4550 
4551 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4552 	nfsmerr_if(error);
4553 
4554 	if (nxo->nxo_flags & NX_32BITCLIENTS) {
4555 		vnopflag |= VNODE_READDIR_SEEKOFF32;
4556 	}
4557 
4558 	if (nxo->nxo_flags & NX_MANGLEDNAMES) {
4559 		vnopflag |= VNODE_READDIR_NAMEMAX;
4560 	}
4561 
4562 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4563 	error = attrerr = vnode_getattr(vp, &attr, ctx);
4564 	if (!error && verf && (verf != attr.va_filerev)) {
4565 		error = NFSERR_BAD_COOKIE;
4566 	}
4567 	if (!error) {
4568 		error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4569 	}
4570 #if CONFIG_MACF
4571 	if (!error) {
4572 		if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4573 			error = EACCES;
4574 		}
4575 
4576 		if (!error) {
4577 			error = mac_vnode_check_readdir(ctx, vp);
4578 		}
4579 	}
4580 #endif
4581 	nfsmerr_if(error);
4582 
4583 	rbuf_siz = siz;
4584 	rbuf = kalloc_data(siz, Z_WAITOK);
4585 	if (rbuf) {
4586 		auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4587 		    &uio_buf[0], sizeof(uio_buf));
4588 	}
4589 	if (!rbuf || !auio) {
4590 		error = ENOMEM;
4591 		goto nfsmerr;
4592 	}
4593 
4594 again:
4595 	uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4596 	uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4597 	eofflag = 0;
4598 	error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4599 	off = uio_offset(auio);
4600 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4601 	attrerr = vnode_getattr(vp, &attr, ctx);
4602 	nfsmerr_if(error);
4603 
4604 	if (uio_resid(auio) != 0) {
4605 		siz -= uio_resid(auio);
4606 
4607 		/* If nothing read, return empty reply with eof set */
4608 		if (siz == 0) {
4609 			vnode_put(vp);
4610 			vp = NULL;
4611 			kfree_data(rbuf, rbuf_siz);
4612 			/* assemble reply */
4613 			nd->nd_repstat = error;
4614 			error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR +
4615 			    NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED);
4616 			nfsmout_if(error);
4617 			*mrepp = nmrep.nmc_mhead;
4618 			nfsmout_on_status(nd, error);
4619 			nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4620 			nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4621 			nfsm_chain_add_32(error, &nmrep, FALSE);
4622 			nfsm_chain_add_32(error, &nmrep, TRUE);
4623 			nfsm_chain_build_done(error, &nmrep);
4624 			return error;
4625 		}
4626 	}
4627 
4628 	/*
4629 	 * Check for degenerate cases of nothing useful read.
4630 	 * If so go try again
4631 	 */
4632 	cpos = rbuf;
4633 	cend = rbuf + siz;
4634 	dp = (struct direntry *)cpos;
4635 	while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4636 		cpos += dp->d_reclen;
4637 		dp = (struct direntry *)cpos;
4638 		nentries--;
4639 	}
4640 	if ((cpos >= cend) || (nentries == 0)) {
4641 		toff = off;
4642 		siz = fullsiz;
4643 		goto again;
4644 	}
4645 
4646 	/*
4647 	 * Probe the directory to see if the filesystem supports VGET.
4648 	 */
4649 	if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)attr.va_fileid, &nvp, ctx))) {
4650 		if (error == ENOTSUP) { /* let others get passed back */
4651 			error = NFSERR_NOTSUPP;
4652 		}
4653 		goto nfsmerr;
4654 	}
4655 	vnode_put(nvp);
4656 
4657 	/* assemble reply */
4658 	nd->nd_repstat = error;
4659 	error = nfsrv_rephead(nd, slp, &nmrep, maxcount);
4660 	nfsmout_if(error);
4661 	*mrepp = nmrep.nmc_mhead;
4662 	nfsmout_on_status(nd, error);
4663 	nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4664 
4665 	dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
4666 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4667 	nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4668 	nfsmerr_if(error);
4669 
4670 	/* Loop through the records and build reply */
4671 	while ((cpos < cend) && (nentries > 0)) {
4672 		if (dp->d_fileno != 0) {
4673 			nlen = dp->d_namlen;
4674 			rem = nfsm_rndup(nlen) - nlen;
4675 			gotfh = gotattr = 1;
4676 
4677 			/* Got to get the vnode for lookup per entry. */
4678 			if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx)) {
4679 				/* Can't get the vnode... so no fh or attrs */
4680 				gotfh = gotattr = 0;
4681 			} else {
4682 				isdotdot = ((dp->d_namlen == 2) &&
4683 				    (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
4684 				if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) {
4685 					gotfh = 0;
4686 				}
4687 				nfsm_srv_vattr_init(vap, NFS_VER3);
4688 				if (vnode_getattr(nvp, vap, ctx)) {
4689 					gotattr = 0;
4690 				}
4691 				vnode_put(nvp);
4692 			}
4693 
4694 			/*
4695 			 * If either the dircount or maxcount will be
4696 			 * exceeded, get out now. Both of these lengths
4697 			 * are calculated conservatively, including all
4698 			 * XDR overheads.
4699 			 */
4700 			len += 8 * NFSX_UNSIGNED + nlen + rem;
4701 			if (gotattr) {
4702 				len += NFSX_V3FATTR;
4703 			}
4704 			if (gotfh) {
4705 				len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len);
4706 			}
4707 			dirlen += 6 * NFSX_UNSIGNED + nlen + rem;
4708 			if ((len > maxcount) || (dirlen > dircount)) {
4709 				eofflag = 0;
4710 				break;
4711 			}
4712 
4713 			/* Build the directory record xdr from the direntry. */
4714 			nfsm_chain_add_32(error, &nmrep, TRUE);
4715 			nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4716 			nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4717 			if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4718 				dp->d_seekoff &= 0x00000000ffffffffULL;
4719 			}
4720 			nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4721 			nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap);
4722 			if (gotfh) {
4723 				nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4724 			} else {
4725 				nfsm_chain_add_32(error, &nmrep, FALSE);
4726 			}
4727 			nfsmerr_if(error);
4728 		}
4729 		cpos += dp->d_reclen;
4730 		dp = (struct direntry *)cpos;
4731 		nentries--;
4732 	}
4733 	vnode_put(vp);
4734 	vp = NULL;
4735 	nfsm_chain_add_32(error, &nmrep, FALSE);
4736 	nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4737 	kfree_data(rbuf, rbuf_siz);
4738 	goto nfsmout;
4739 nfsmerr:
4740 	if (rbuf) {
4741 		kfree_data(rbuf, rbuf_siz);
4742 	}
4743 	nd->nd_repstat = error;
4744 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR);
4745 	nfsmout_if(error);
4746 	*mrepp = nmrep.nmc_mhead;
4747 	nfsmout_on_status(nd, error);
4748 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4749 nfsmout:
4750 	nfsm_chain_build_done(error, &nmrep);
4751 	if (vp) {
4752 		vnode_put(vp);
4753 	}
4754 	if (error) {
4755 		nfsm_chain_cleanup(&nmrep);
4756 		*mrepp = NULL;
4757 	}
4758 	return error;
4759 }
4760 
4761 /*
4762  * nfs commit service
4763  */
4764 int
nfsrv_commit(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4765 nfsrv_commit(
4766 	struct nfsrv_descript *nd,
4767 	struct nfsrv_sock *slp,
4768 	vfs_context_t ctx,
4769 	mbuf_t *mrepp)
4770 {
4771 	vnode_t vp;
4772 	struct nfs_filehandle nfh;
4773 	struct nfs_export *nx = NULL;
4774 	struct nfs_export_options *nxo;
4775 	int error, preattrerr, postattrerr, count;
4776 	struct vnode_attr preattr, postattr;
4777 	u_quad_t off;
4778 	struct nfsm_chain *nmreq, nmrep;
4779 
4780 	error = 0;
4781 	preattrerr = postattrerr = ENOENT;
4782 	nmreq = &nd->nd_nmreq;
4783 	nfsm_chain_null(&nmrep);
4784 	vp = NULL;
4785 
4786 	/*
4787 	 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4788 	 * count parameters, so those arguments are useless (someday maybe).
4789 	 */
4790 
4791 	nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
4792 	nfsm_chain_get_64(error, nmreq, off);
4793 	nfsm_chain_get_32(error, nmreq, count);
4794 	nfsmerr_if(error);
4795 
4796 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4797 	nfsmerr_if(error);
4798 
4799 	/* update export stats */
4800 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4801 
4802 	/* update active user stats */
4803 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4804 
4805 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4806 	nfsmerr_if(error);
4807 
4808 	/* This must identify a file system object of type, NF3REG */
4809 	if (vnode_vtype(vp) != VREG) {
4810 		error = NFSERR_BADTYPE;
4811 		goto nfsmerr;
4812 	}
4813 
4814 	nfsm_srv_pre_vattr_init(&preattr);
4815 	preattrerr = vnode_getattr(vp, &preattr, ctx);
4816 
4817 	error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
4818 
4819 	nfsm_srv_vattr_init(&postattr, 1);
4820 	postattrerr = vnode_getattr(vp, &postattr, ctx);
4821 
4822 nfsmerr:
4823 	if (vp) {
4824 		vnode_put(vp);
4825 	}
4826 
4827 	/* assemble reply */
4828 	nd->nd_repstat = error;
4829 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4830 	nfsmout_if(error);
4831 	*mrepp = nmrep.nmc_mhead;
4832 	nfsmout_on_status(nd, error);
4833 	nfsm_chain_add_wcc_data(error, nd, &nmrep,
4834 	    preattrerr, &preattr, postattrerr, &postattr);
4835 	if (!nd->nd_repstat) {
4836 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
4837 		nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
4838 	}
4839 nfsmout:
4840 	nfsm_chain_build_done(error, &nmrep);
4841 	if (error) {
4842 		nfsm_chain_cleanup(&nmrep);
4843 		*mrepp = NULL;
4844 	}
4845 	return error;
4846 }
4847 
4848 /*
4849  * nfs statfs service
4850  */
4851 int
nfsrv_statfs(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4852 nfsrv_statfs(
4853 	struct nfsrv_descript *nd,
4854 	struct nfsrv_sock *slp,
4855 	vfs_context_t ctx,
4856 	mbuf_t *mrepp)
4857 {
4858 	struct vfs_attr va = {};
4859 	int error, attrerr;
4860 	vnode_t vp;
4861 	struct vnode_attr attr;
4862 	struct nfs_filehandle nfh;
4863 	struct nfs_export *nx;
4864 	struct nfs_export_options *nxo;
4865 	off_t blksize;
4866 	struct nfsm_chain *nmreq, nmrep;
4867 
4868 	error = 0;
4869 	attrerr = ENOENT;
4870 	nmreq = &nd->nd_nmreq;
4871 	nfsm_chain_null(&nmrep);
4872 	vp = NULL;
4873 	blksize = 512;
4874 
4875 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4876 	nfsmerr_if(error);
4877 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4878 	nfsmerr_if(error);
4879 
4880 	/* update export stats */
4881 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4882 
4883 	/* update active user stats */
4884 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4885 
4886 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4887 	nfsmerr_if(error);
4888 
4889 	VFSATTR_INIT(&va);
4890 	VFSATTR_WANTED(&va, f_blocks);
4891 	VFSATTR_WANTED(&va, f_bfree);
4892 	VFSATTR_WANTED(&va, f_bavail);
4893 	VFSATTR_WANTED(&va, f_files);
4894 	VFSATTR_WANTED(&va, f_ffree);
4895 	error = vfs_getattr(vnode_mount(vp), &va, ctx);
4896 	blksize = vfs_statfs(vnode_mount(vp))->f_bsize;
4897 
4898 	if (nd->nd_vers == NFS_VER3) {
4899 		nfsm_srv_vattr_init(&attr, nd->nd_vers);
4900 		attrerr = vnode_getattr(vp, &attr, ctx);
4901 	}
4902 
4903 nfsmerr:
4904 	if (vp) {
4905 		vnode_put(vp);
4906 	}
4907 
4908 	/* assemble reply */
4909 	nd->nd_repstat = error;
4910 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_STATFS(nd->nd_vers));
4911 	nfsmout_if(error);
4912 	*mrepp = nmrep.nmc_mhead;
4913 	nfsmout_on_status(nd, error);
4914 	if (nd->nd_vers == NFS_VER3) {
4915 		nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4916 	}
4917 	nfsmout_if(nd->nd_repstat);
4918 
4919 	if (nd->nd_vers == NFS_VER3) {
4920 		nfsm_chain_add_64(error, &nmrep, va.f_blocks * blksize);
4921 		nfsm_chain_add_64(error, &nmrep, va.f_bfree * blksize);
4922 		nfsm_chain_add_64(error, &nmrep, va.f_bavail * blksize);
4923 		nfsm_chain_add_64(error, &nmrep, va.f_files);
4924 		nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4925 		nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4926 		nfsm_chain_add_32(error, &nmrep, 0); /* invarsec */
4927 	} else {
4928 		nfsm_chain_add_32(error, &nmrep, NFS_V2MAXDATA);
4929 		nfsm_chain_add_32(error, &nmrep, blksize);
4930 		nfsm_chain_add_32(error, &nmrep, va.f_blocks);
4931 		nfsm_chain_add_32(error, &nmrep, va.f_bfree);
4932 		nfsm_chain_add_32(error, &nmrep, va.f_bavail);
4933 	}
4934 nfsmout:
4935 	nfsm_chain_build_done(error, &nmrep);
4936 	if (error) {
4937 		nfsm_chain_cleanup(&nmrep);
4938 		*mrepp = NULL;
4939 	}
4940 	return error;
4941 }
4942 
4943 /*
4944  * nfs fsinfo service
4945  */
4946 int
nfsrv_fsinfo(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4947 nfsrv_fsinfo(
4948 	struct nfsrv_descript *nd,
4949 	struct nfsrv_sock *slp,
4950 	vfs_context_t ctx,
4951 	mbuf_t *mrepp)
4952 {
4953 	int error, attrerr, prefsize, maxsize;
4954 	vnode_t vp;
4955 	struct vnode_attr attr;
4956 	struct nfs_filehandle nfh;
4957 	struct nfs_export *nx;
4958 	struct nfs_export_options *nxo;
4959 	struct nfsm_chain *nmreq, nmrep;
4960 
4961 	error = 0;
4962 	attrerr = ENOENT;
4963 	nmreq = &nd->nd_nmreq;
4964 	nfsm_chain_null(&nmrep);
4965 	vp = NULL;
4966 
4967 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4968 	nfsmerr_if(error);
4969 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4970 	nfsmerr_if(error);
4971 
4972 	/* update export stats */
4973 	NFSStatAdd64(&nx->nx_stats.ops, 1);
4974 
4975 	/* update active user stats */
4976 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4977 
4978 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
4979 	nfsmerr_if(error);
4980 
4981 	nfsm_srv_vattr_init(&attr, NFS_VER3);
4982 	attrerr = vnode_getattr(vp, &attr, ctx);
4983 
4984 nfsmerr:
4985 	if (vp) {
4986 		vnode_put(vp);
4987 	}
4988 
4989 	/* assemble reply */
4990 	nd->nd_repstat = error;
4991 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4992 	nfsmout_if(error);
4993 	*mrepp = nmrep.nmc_mhead;
4994 	nfsmout_on_status(nd, error);
4995 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4996 	nfsmout_if(nd->nd_repstat);
4997 
4998 	/*
4999 	 * XXX There should be file system VFS OP(s) to get this information.
5000 	 * For now, assume our usual NFS defaults.
5001 	 */
5002 	if (slp->ns_sotype == SOCK_DGRAM) {
5003 		maxsize = NFS_MAXDGRAMDATA;
5004 		prefsize = NFS_PREFDGRAMDATA;
5005 	} else {
5006 		maxsize = prefsize = NFSRV_MAXDATA;
5007 	}
5008 
5009 	nfsm_chain_add_32(error, &nmrep, maxsize);
5010 	nfsm_chain_add_32(error, &nmrep, prefsize);
5011 	nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5012 	nfsm_chain_add_32(error, &nmrep, maxsize);
5013 	nfsm_chain_add_32(error, &nmrep, prefsize);
5014 	nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5015 	nfsm_chain_add_32(error, &nmrep, prefsize);
5016 	nfsm_chain_add_64(error, &nmrep, 0xffffffffffffffffULL);
5017 	nfsm_chain_add_32(error, &nmrep, 0);
5018 	nfsm_chain_add_32(error, &nmrep, 1);
5019 	/* XXX link/symlink support should be taken from volume capabilities */
5020 	nfsm_chain_add_32(error, &nmrep,
5021 	    NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK |
5022 	    NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME);
5023 
5024 nfsmout:
5025 	nfsm_chain_build_done(error, &nmrep);
5026 	if (error) {
5027 		nfsm_chain_cleanup(&nmrep);
5028 		*mrepp = NULL;
5029 	}
5030 	return error;
5031 }
5032 
5033 /*
5034  * nfs pathconf service
5035  */
5036 int
nfsrv_pathconf(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)5037 nfsrv_pathconf(
5038 	struct nfsrv_descript *nd,
5039 	struct nfsrv_sock *slp,
5040 	vfs_context_t ctx,
5041 	mbuf_t *mrepp)
5042 {
5043 	int error, attrerr, linkmax = 0, namemax = 0;
5044 	int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0;
5045 	vnode_t vp;
5046 	struct vnode_attr attr;
5047 	struct nfs_filehandle nfh;
5048 	struct nfs_export *nx;
5049 	struct nfs_export_options *nxo;
5050 	struct nfsm_chain *nmreq, nmrep;
5051 
5052 	error = 0;
5053 	attrerr = ENOENT;
5054 	nmreq = &nd->nd_nmreq;
5055 	nfsm_chain_null(&nmrep);
5056 	vp = NULL;
5057 
5058 	nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
5059 	nfsmerr_if(error);
5060 	error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
5061 	nfsmerr_if(error);
5062 
5063 	/* update export stats */
5064 	NFSStatAdd64(&nx->nx_stats.ops, 1);
5065 
5066 	/* update active user stats */
5067 	nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
5068 
5069 	error = nfsrv_credcheck(nd, ctx, nx, nxo);
5070 	nfsmerr_if(error);
5071 
5072 	error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx);
5073 	if (!error) {
5074 		error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx);
5075 	}
5076 	if (!error) {
5077 		error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx);
5078 	}
5079 	if (!error) {
5080 		error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, &notrunc, ctx);
5081 	}
5082 	if (!error) {
5083 		error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx);
5084 	}
5085 	if (!error) {
5086 		error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx);
5087 	}
5088 
5089 	nfsm_srv_vattr_init(&attr, NFS_VER3);
5090 	attrerr = vnode_getattr(vp, &attr, ctx);
5091 
5092 nfsmerr:
5093 	if (vp) {
5094 		vnode_put(vp);
5095 	}
5096 
5097 	/* assemble reply */
5098 	nd->nd_repstat = error;
5099 	error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
5100 	nfsmout_if(error);
5101 	*mrepp = nmrep.nmc_mhead;
5102 	nfsmout_on_status(nd, error);
5103 	nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
5104 	nfsmout_if(nd->nd_repstat);
5105 
5106 	nfsm_chain_add_32(error, &nmrep, linkmax);
5107 	nfsm_chain_add_32(error, &nmrep, namemax);
5108 	nfsm_chain_add_32(error, &nmrep, notrunc);
5109 	nfsm_chain_add_32(error, &nmrep, chownres);
5110 	nfsm_chain_add_32(error, &nmrep, !case_sensitive);
5111 	nfsm_chain_add_32(error, &nmrep, case_preserving);
5112 
5113 nfsmout:
5114 	nfsm_chain_build_done(error, &nmrep);
5115 	if (error) {
5116 		nfsm_chain_cleanup(&nmrep);
5117 		*mrepp = NULL;
5118 	}
5119 	return error;
5120 }
5121 
5122 /*
5123  * Null operation, used by clients to ping server
5124  */
5125 /* ARGSUSED */
5126 int
nfsrv_null(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5127 nfsrv_null(
5128 	struct nfsrv_descript *nd,
5129 	struct nfsrv_sock *slp,
5130 	__unused vfs_context_t ctx,
5131 	mbuf_t *mrepp)
5132 {
5133 	int error = NFSERR_RETVOID;
5134 	struct nfsm_chain nmrep;
5135 
5136 	/*
5137 	 * RPCSEC_GSS context setup ?
5138 	 */
5139 	if (nd->nd_gss_context) {
5140 		return nfs_gss_svc_ctx_init(nd, slp, mrepp);
5141 	}
5142 
5143 	nfsm_chain_null(&nmrep);
5144 
5145 	/* assemble reply */
5146 	nd->nd_repstat = error;
5147 	error = nfsrv_rephead(nd, slp, &nmrep, 0);
5148 	nfsmout_if(error);
5149 	*mrepp = nmrep.nmc_mhead;
5150 nfsmout:
5151 	nfsm_chain_build_done(error, &nmrep);
5152 	if (error) {
5153 		nfsm_chain_cleanup(&nmrep);
5154 		*mrepp = NULL;
5155 	}
5156 	return error;
5157 }
5158 
5159 /*
5160  * No operation, used for obsolete procedures
5161  */
5162 /* ARGSUSED */
5163 int
nfsrv_noop(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5164 nfsrv_noop(
5165 	struct nfsrv_descript *nd,
5166 	struct nfsrv_sock *slp,
5167 	__unused vfs_context_t ctx,
5168 	mbuf_t *mrepp)
5169 {
5170 	int error;
5171 	struct nfsm_chain nmrep;
5172 
5173 	nfsm_chain_null(&nmrep);
5174 
5175 	if (nd->nd_repstat) {
5176 		error = nd->nd_repstat;
5177 	} else {
5178 		error = EPROCUNAVAIL;
5179 	}
5180 
5181 	/* assemble reply */
5182 	nd->nd_repstat = error;
5183 	error = nfsrv_rephead(nd, slp, &nmrep, 0);
5184 	nfsmout_if(error);
5185 	*mrepp = nmrep.nmc_mhead;
5186 nfsmout:
5187 	nfsm_chain_build_done(error, &nmrep);
5188 	if (error) {
5189 		nfsm_chain_cleanup(&nmrep);
5190 		*mrepp = NULL;
5191 	}
5192 	return error;
5193 }
5194 
5195 const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = {
5196 	nfsrv_null,
5197 	nfsrv_getattr,
5198 	nfsrv_setattr,
5199 	nfsrv_lookup,
5200 	nfsrv_access,
5201 	nfsrv_readlink,
5202 	nfsrv_read,
5203 	nfsrv_write,
5204 	nfsrv_create,
5205 	nfsrv_mkdir,
5206 	nfsrv_symlink,
5207 	nfsrv_mknod,
5208 	nfsrv_remove,
5209 	nfsrv_rmdir,
5210 	nfsrv_rename,
5211 	nfsrv_link,
5212 	nfsrv_readdir,
5213 	nfsrv_readdirplus,
5214 	nfsrv_statfs,
5215 	nfsrv_fsinfo,
5216 	nfsrv_pathconf,
5217 	nfsrv_commit,
5218 	nfsrv_noop
5219 };
5220 
5221 /*
5222  * Perform access checking for vnodes obtained from file handles that would
5223  * refer to files already opened by a Unix client. You cannot just use
5224  * vnode_authorize() for two reasons.
5225  * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5226  * 2 - The owner is to be given access irrespective of mode bits so that
5227  *     processes that chmod after opening a file don't break. I don't like
5228  *     this because it opens a security hole, but since the nfs server opens
5229  *     a security hole the size of a barn door anyhow, what the heck.
5230  *
5231  * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5232  * will return EPERM instead of EACCESS. EPERM is always an error.
5233  */
5234 
5235 int
nfsrv_authorize(vnode_t vp,vnode_t dvp,kauth_action_t action,vfs_context_t ctx,struct nfs_export_options * nxo,int override)5236 nfsrv_authorize(
5237 	vnode_t vp,
5238 	vnode_t dvp,
5239 	kauth_action_t action,
5240 	vfs_context_t ctx,
5241 	struct nfs_export_options *nxo,
5242 	int override)
5243 {
5244 	struct vnode_attr vattr;
5245 	int error;
5246 
5247 	if (action & KAUTH_VNODE_WRITE_RIGHTS) {
5248 		/*
5249 		 * Disallow write attempts on read-only exports;
5250 		 * unless the file is a socket or a block or character
5251 		 * device resident on the file system.
5252 		 */
5253 		if (nxo->nxo_flags & NX_READONLY) {
5254 			switch (vnode_vtype(vp)) {
5255 			case VREG: case VDIR: case VLNK: case VCPLX:
5256 				return EROFS;
5257 			default:
5258 				break;
5259 			}
5260 		}
5261 	}
5262 	error = vnode_authorize(vp, dvp, action, ctx);
5263 	/*
5264 	 * Allow certain operations for the owner (reads and writes
5265 	 * on files that are already open). Picking up from FreeBSD.
5266 	 */
5267 	if (override && (error == EACCES)) {
5268 		VATTR_INIT(&vattr);
5269 		VATTR_WANTED(&vattr, va_uid);
5270 		if ((vnode_getattr(vp, &vattr, ctx) == 0) &&
5271 		    (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) {
5272 			error = 0;
5273 		}
5274 	}
5275 	return error;
5276 }
5277 
5278 #endif /* CONFIG_NFS_SERVER */
5279