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