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