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