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