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