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