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