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