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