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