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