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