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