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