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