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