]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_serv.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_serv.c
CommitLineData
1c79356b 1/*
5d5c5d0d
A
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
6601e61a 4 * @APPLE_LICENSE_HEADER_START@
1c79356b 5 *
6601e61a
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
8f6c56a5 11 *
6601e61a
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
6601e61a
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
8f6c56a5 19 *
6601e61a 20 * @APPLE_LICENSE_HEADER_END@
1c79356b
A
21 */
22/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23/*
24 * Copyright (c) 1989, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 *
58 * @(#)nfs_serv.c 8.7 (Berkeley) 5/14/95
59 * FreeBSD-Id: nfs_serv.c,v 1.52 1997/10/28 15:59:05 bde Exp $
60 */
61
62/*
63 * nfs version 2 and 3 server calls to vnode ops
64 * - these routines generally have 3 phases
65 * 1 - break down and validate rpc request in mbuf list
66 * 2 - do the vnode ops for the request
67 * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c)
68 * 3 - build the rpc reply in an mbuf list
69 * nb:
70 * - do not mix the phases, since the nfsm_?? macros can return failures
91447636 71 * on a bad rpc or similar and do not do any vnode_rele()s or vnode_put()s
1c79356b
A
72 *
73 * - the nfsm_reply() macro generates an nfs rpc reply with the nfs
74 * error number iff error != 0 whereas
75 * returning an error from the server function implies a fatal error
76 * such as a badly constructed rpc request that should be dropped without
77 * a reply.
78 * For Version 3, nfsm_reply() does not return for the error case, since
79 * most version 3 rpcs return more than the status for error cases.
80 */
81
82#include <sys/param.h>
83#include <sys/systm.h>
84#include <sys/proc.h>
91447636 85#include <sys/kauth.h>
1c79356b
A
86#include <sys/unistd.h>
87#include <sys/malloc.h>
88#include <sys/vnode.h>
91447636 89#include <sys/mount_internal.h>
1c79356b
A
90#include <sys/socket.h>
91#include <sys/socketvar.h>
91447636 92#include <sys/kpi_mbuf.h>
1c79356b
A
93#include <sys/dirent.h>
94#include <sys/stat.h>
95#include <sys/kernel.h>
96#include <sys/sysctl.h>
97#include <sys/ubc.h>
91447636
A
98#include <sys/vnode_internal.h>
99#include <sys/uio_internal.h>
100#include <libkern/OSAtomic.h>
1c79356b 101
1c79356b
A
102#include <sys/vm.h>
103#include <sys/vmparam.h>
1c79356b
A
104
105#include <nfs/nfsproto.h>
106#include <nfs/rpcv2.h>
107#include <nfs/nfs.h>
108#include <nfs/xdr_subs.h>
109#include <nfs/nfsm_subs.h>
1c79356b
A
110
111nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
112 NFFIFO, NFNON };
113#ifndef NFS_NOSERVER
114nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
115 NFCHR, NFNON };
116/* Global vars */
117extern u_long nfs_xdrneg1;
118extern u_long nfs_false, nfs_true;
119extern enum vtype nv3tov_type[8];
120extern struct nfsstats nfsstats;
121
122int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000;
123int nfsrvw_procrastinate_v3 = 0;
124
125int nfs_async = 0;
126#ifdef notyet
127/* XXX CSM 11/25/97 Upgrade sysctl.h someday */
128SYSCTL_INT(_vfs_nfs, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, "");
129#endif
130
91447636
A
131static int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int);
132static void nfsrvw_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
133
134#define THREAD_SAFE_FS(VP) \
135 ((VP)->v_mount ? (VP)->v_mount->mnt_vtable->vfc_threadsafe : 0)
1c79356b
A
136
137/*
138 * nfs v3 access service
139 */
140int
141nfsrv3_access(nfsd, slp, procp, mrq)
142 struct nfsrv_descript *nfsd;
143 struct nfssvc_sock *slp;
91447636
A
144 proc_t procp;
145 mbuf_t *mrq;
1c79356b 146{
91447636
A
147 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
148 mbuf_t nam = nfsd->nd_nam;
1c79356b 149 caddr_t dpos = nfsd->nd_dpos;
cc9f6e38 150 vnode_t vp;
91447636
A
151 struct nfs_filehandle nfh;
152 u_long *tl;
153 long t1;
1c79356b 154 caddr_t bpos;
91447636 155 int error = 0, getret;
1c79356b 156 char *cp2;
91447636
A
157 mbuf_t mb, mreq, mb2;
158 struct vnode_attr vattr, *vap = &vattr;
159 u_long nfsmode;
160 kauth_action_t testaction;
161 struct vfs_context context;
162 struct nfs_export *nx;
163 struct nfs_export_options *nxo;
1c79356b 164
91447636 165 nfsm_srvmtofh(&nfh);
1c79356b 166 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
91447636
A
167 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
168 nfsm_reply(NFSX_UNSIGNED);
169 nfsm_srvpostop_attr(1, NULL);
170 return (0);
171 }
172 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
173 vnode_put(vp);
1c79356b 174 nfsm_reply(NFSX_UNSIGNED);
91447636 175 nfsm_srvpostop_attr(1, NULL);
1c79356b
A
176 return (0);
177 }
178 nfsmode = fxdr_unsigned(u_long, *tl);
91447636
A
179
180 context.vc_proc = procp;
181 context.vc_ucred = nfsd->nd_cr;
182
183 /*
184 * Each NFS mode bit is tested separately.
185 *
186 * XXX this code is nominally correct, but returns a pessimistic
187 * rather than optimistic result. It will be necessary to add
188 * an NFS-specific interface to the vnode_authorize code to
189 * obtain good performance in the optimistic mode.
190 */
191 if (nfsmode & NFSV3ACCESS_READ) {
192 if (vnode_isdir(vp)) {
193 testaction =
194 KAUTH_VNODE_LIST_DIRECTORY |
195 KAUTH_VNODE_READ_EXTATTRIBUTES;
196 } else {
197 testaction =
198 KAUTH_VNODE_READ_DATA |
199 KAUTH_VNODE_READ_EXTATTRIBUTES;
200 }
201 if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
202 nfsmode &= ~NFSV3ACCESS_READ;
203 }
204 if ((nfsmode & NFSV3ACCESS_LOOKUP) &&
205 (!vnode_isdir(vp) ||
206 nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, &context, nxo, 0)))
207 nfsmode &= ~NFSV3ACCESS_LOOKUP;
208 if (nfsmode & NFSV3ACCESS_MODIFY) {
209 if (vnode_isdir(vp)) {
210 testaction =
211 KAUTH_VNODE_ADD_FILE |
212 KAUTH_VNODE_ADD_SUBDIRECTORY |
213 KAUTH_VNODE_DELETE_CHILD;
214 } else {
215 testaction =
743b1565 216 KAUTH_VNODE_WRITE_DATA;
91447636
A
217 }
218 if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
219 nfsmode &= ~NFSV3ACCESS_MODIFY;
220 }
221 if (nfsmode & NFSV3ACCESS_EXTEND) {
222 if (vnode_isdir(vp)) {
223 testaction =
224 KAUTH_VNODE_ADD_FILE |
225 KAUTH_VNODE_ADD_SUBDIRECTORY;
226 } else {
227 testaction =
228 KAUTH_VNODE_WRITE_DATA |
229 KAUTH_VNODE_APPEND_DATA;
230 }
231 if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
232 nfsmode &= ~NFSV3ACCESS_EXTEND;
233 }
cc9f6e38 234
91447636 235 /*
cc9f6e38
A
236 * Note concerning NFSV3ACCESS_DELETE:
237 * For hard links, the answer may be wrong if the vnode
91447636 238 * has multiple parents with different permissions.
cc9f6e38
A
239 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
240 * interpret the missing/cleared DELETE bit.
241 * So we'll just leave the DELETE bit alone. At worst,
242 * we're telling the client it might be able to do
243 * something it really can't.
91447636 244 */
91447636
A
245
246 if ((nfsmode & NFSV3ACCESS_EXECUTE) &&
247 (vnode_isdir(vp) ||
248 nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context, nxo, 0)))
249 nfsmode &= ~NFSV3ACCESS_EXECUTE;
250
251 nfsm_srv_vattr_init(vap, 1);
252 getret = vnode_getattr(vp, vap, &context);
253 vnode_put(vp);
1c79356b
A
254 nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED);
255 nfsm_srvpostop_attr(getret, vap);
256 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
257 *tl = txdr_unsigned(nfsmode);
91447636
A
258nfsmout:
259 return (error);
1c79356b
A
260}
261
262/*
263 * nfs getattr service
264 */
265int
266nfsrv_getattr(nfsd, slp, procp, mrq)
267 struct nfsrv_descript *nfsd;
268 struct nfssvc_sock *slp;
91447636
A
269 proc_t procp;
270 mbuf_t *mrq;
1c79356b 271{
91447636
A
272 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
273 mbuf_t nam = nfsd->nd_nam;
1c79356b 274 caddr_t dpos = nfsd->nd_dpos;
91447636
A
275 struct nfs_fattr *fp;
276 struct vnode_attr va;
277 struct vnode_attr *vap = &va;
278 vnode_t vp;
279 struct nfs_filehandle nfh;
280 u_long *tl;
281 long t1;
1c79356b 282 caddr_t bpos;
91447636 283 int error = 0;
1c79356b 284 char *cp2;
91447636
A
285 mbuf_t mb, mb2, mreq;
286 struct vfs_context context;
287 struct nfs_export *nx;
288 struct nfs_export_options *nxo;
289 int v3 = (nfsd->nd_flag & ND_NFSV3);
1c79356b 290
91447636
A
291 nfsm_srvmtofh(&nfh);
292 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b
A
293 nfsm_reply(0);
294 return (0);
295 }
91447636
A
296 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
297 vnode_put(vp);
298 nfsm_reply(0);
299 return (0);
300 }
301 context.vc_proc = procp;
302 context.vc_ucred = nfsd->nd_cr;
303
304 nfsm_srv_vattr_init(vap, v3);
305 error = vnode_getattr(vp, vap, &context);
306 vnode_put(vp);
307 nfsm_reply(NFSX_FATTR(v3));
1c79356b
A
308 if (error)
309 return (0);
91447636 310 nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(v3));
1c79356b 311 nfsm_srvfillattr(vap, fp);
91447636
A
312nfsmout:
313 return (error);
1c79356b
A
314}
315
316/*
317 * nfs setattr service
318 */
319int
320nfsrv_setattr(nfsd, slp, procp, mrq)
321 struct nfsrv_descript *nfsd;
322 struct nfssvc_sock *slp;
91447636
A
323 proc_t procp;
324 mbuf_t *mrq;
1c79356b 325{
91447636
A
326 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
327 mbuf_t nam = nfsd->nd_nam;
1c79356b 328 caddr_t dpos = nfsd->nd_dpos;
91447636
A
329 struct vnode_attr preat;
330 struct vnode_attr postat;
331 struct vnode_attr va;
332 struct vnode_attr *vap = &va;
333 struct nfsv2_sattr *sp;
334 struct nfs_fattr *fp;
335 vnode_t vp;
336 struct nfs_filehandle nfh;
337 struct nfs_export *nx;
338 struct nfs_export_options *nxo;
339 u_long *tl;
340 long t1;
1c79356b 341 caddr_t bpos;
91447636 342 int error = 0, preat_ret = 1, postat_ret = 1;
1c79356b
A
343 int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0;
344 char *cp2;
91447636 345 mbuf_t mb, mb2, mreq;
1c79356b 346 struct timespec guard;
91447636
A
347 struct vfs_context context;
348 kauth_action_t action;
349 uid_t saved_uid;
1c79356b 350
91447636
A
351 nfsm_srvmtofh(&nfh);
352 VATTR_INIT(vap);
1c79356b
A
353 if (v3) {
354 nfsm_srvsattr(vap);
355 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
356 gcheck = fxdr_unsigned(int, *tl);
357 if (gcheck) {
358 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
359 fxdr_nfsv3time(tl, &guard);
360 }
361 } else {
362 nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
363 /*
364 * Nah nah nah nah na nah
365 * There is a bug in the Sun client that puts 0xffff in the mode
366 * field of sattr when it should put in 0xffffffff. The u_short
367 * doesn't sign extend.
368 * --> check the low order 2 bytes for 0xffff
369 */
370 if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
91447636 371 VATTR_SET(vap, va_mode, nfstov_mode(sp->sa_mode));
1c79356b 372 if (sp->sa_uid != nfs_xdrneg1)
91447636 373 VATTR_SET(vap, va_uid, fxdr_unsigned(uid_t, sp->sa_uid));
1c79356b 374 if (sp->sa_gid != nfs_xdrneg1)
91447636 375 VATTR_SET(vap, va_gid, fxdr_unsigned(gid_t, sp->sa_gid));
1c79356b 376 if (sp->sa_size != nfs_xdrneg1)
91447636 377 VATTR_SET(vap, va_data_size, fxdr_unsigned(u_quad_t, sp->sa_size));
1c79356b 378 if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) {
91447636
A
379 fxdr_nfsv2time(&sp->sa_atime, &vap->va_access_time);
380 VATTR_SET_ACTIVE(vap, va_access_time);
381 }
382 if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) {
383 fxdr_nfsv2time(&sp->sa_mtime, &vap->va_modify_time);
384 VATTR_SET_ACTIVE(vap, va_modify_time);
1c79356b 385 }
1c79356b
A
386 }
387
91447636
A
388 /*
389 * Save the original credential UID in case they are
390 * mapped and we need to map the IDs in the attributes.
391 */
392 saved_uid = kauth_cred_getuid(nfsd->nd_cr);
393
1c79356b
A
394 /*
395 * Now that we have all the fields, lets do it.
396 */
91447636 397 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b 398 nfsm_reply(2 * NFSX_UNSIGNED);
91447636 399 nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
1c79356b
A
400 return (0);
401 }
91447636
A
402 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
403 vnode_put(vp);
404 nfsm_reply(2 * NFSX_UNSIGNED);
405 nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
406 return (0);
407 }
408
409 context.vc_proc = procp;
410 context.vc_ucred = nfsd->nd_cr;
411
1c79356b 412 if (v3) {
91447636
A
413 nfsm_srv_pre_vattr_init(&preat, v3);
414 error = preat_ret = vnode_getattr(vp, &preat, &context);
415 if (!error && gcheck && VATTR_IS_SUPPORTED(&preat, va_change_time) &&
416 (preat.va_change_time.tv_sec != guard.tv_sec ||
417 preat.va_change_time.tv_nsec != guard.tv_nsec))
1c79356b 418 error = NFSERR_NOT_SYNC;
91447636
A
419 if (!preat_ret && !VATTR_ALL_SUPPORTED(&preat))
420 preat_ret = 1;
1c79356b 421 if (error) {
91447636 422 vnode_put(vp);
1c79356b 423 nfsm_reply(NFSX_WCCDATA(v3));
91447636 424 nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
1c79356b
A
425 return (0);
426 }
427 }
428
429 /*
91447636
A
430 * If the credentials were mapped, we should
431 * map the same values in the attributes.
1c79356b 432 */
91447636
A
433 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
434 int ismember;
435 VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
436 if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
437 VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
1c79356b 438 }
91447636
A
439
440 /*
441 * Authorize the attribute changes.
442 */
443 if (((error = vnode_authattr(vp, vap, &action, &context))) ||
444 ((error = nfsrv_authorize(vp, NULL, action, &context, nxo, 0))))
445 goto out;
446 error = vnode_setattr(vp, vap, &context);
447
448 nfsm_srv_vattr_init(&postat, v3);
449 postat_ret = vnode_getattr(vp, &postat, &context);
1c79356b
A
450 if (!error)
451 error = postat_ret;
452out:
91447636 453 vnode_put(vp);
1c79356b
A
454 nfsm_reply(NFSX_WCCORFATTR(v3));
455 if (v3) {
91447636 456 nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
1c79356b
A
457 return (0);
458 } else {
459 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
91447636 460 nfsm_srvfillattr(&postat, fp);
1c79356b 461 }
91447636
A
462nfsmout:
463 return (error);
1c79356b
A
464}
465
466/*
467 * nfs lookup rpc
468 */
469int
470nfsrv_lookup(nfsd, slp, procp, mrq)
471 struct nfsrv_descript *nfsd;
472 struct nfssvc_sock *slp;
91447636
A
473 proc_t procp;
474 mbuf_t *mrq;
1c79356b 475{
91447636
A
476 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
477 mbuf_t nam = nfsd->nd_nam;
1c79356b 478 caddr_t dpos = nfsd->nd_dpos;
91447636 479 struct nfs_fattr *fp;
1c79356b 480 struct nameidata nd, *ndp = &nd;
91447636
A
481/* XXX Revisit when enabling WebNFS */
482#ifdef WEBNFS_ENABLED
1c79356b
A
483 struct nameidata ind;
484#endif
91447636
A
485 vnode_t vp, dirp = NULL;
486 struct nfs_filehandle dnfh, nfh;
487 struct nfs_export *nx;
488 struct nfs_export_options *nxo;
489 caddr_t cp;
490 u_long *tl;
491 long t1;
1c79356b 492 caddr_t bpos;
91447636 493 int error = 0, len, dirattr_ret = 1, isdotdot;
1c79356b
A
494 int v3 = (nfsd->nd_flag & ND_NFSV3), pubflag;
495 char *cp2;
91447636
A
496 mbuf_t mb, mb2, mreq;
497 struct vnode_attr va, dirattr, *vap = &va;
498 struct vfs_context context;
499
500 context.vc_proc = procp;
501 context.vc_ucred = nfsd->nd_cr;
1c79356b 502
91447636
A
503 nfsm_srvmtofh(&dnfh);
504 nfsm_srvnamesiz(len, v3);
1c79356b 505
91447636 506 pubflag = nfs_ispublicfh(&dnfh);
1c79356b 507
1c79356b 508 nd.ni_cnd.cn_nameiop = LOOKUP;
91447636
A
509 nd.ni_cnd.cn_flags = LOCKLEAF;
510 error = nfsm_path_mbuftond(&md, &dpos, v3, pubflag, &len, &nd);
511 isdotdot = ((len == 2) && (nd.ni_cnd.cn_pnbuf[0] == '.') && (nd.ni_cnd.cn_pnbuf[1] == '.'));
512 if (!error)
513 error = nfs_namei(nfsd, &context, &nd, &dnfh, nam, pubflag, &dirp, &nx, &nxo);
1c79356b 514
91447636
A
515/* XXX Revisit when enabling WebNFS */
516#ifdef WEBNFS_ENABLED
1c79356b 517 if (!error && pubflag) {
91447636 518 if (vnode_vtype(nd.ni_vp) == VDIR && nfs_pub.np_index != NULL) {
1c79356b
A
519 /*
520 * Setup call to lookup() to see if we can find
521 * the index file. Arguably, this doesn't belong
522 * in a kernel.. Ugh.
523 */
524 ind = nd;
1c79356b
A
525 ind.ni_pathlen = strlen(nfs_pub.np_index);
526 ind.ni_cnd.cn_nameptr = ind.ni_cnd.cn_pnbuf =
527 nfs_pub.np_index;
528 ind.ni_startdir = nd.ni_vp;
91447636
A
529 ind.ni_usedvp = nd.ni_vp;
530
531 if (!(error = lookup(&ind))) {
1c79356b
A
532 /*
533 * Found an index file. Get rid of
534 * the old references.
535 */
536 if (dirp)
91447636 537 vnode_put(dirp);
1c79356b 538 dirp = nd.ni_vp;
91447636 539 vnode_put(nd.ni_startdir);
1c79356b
A
540 ndp = &ind;
541 } else
542 error = 0;
543 }
544 /*
545 * If the public filehandle was used, check that this lookup
546 * didn't result in a filehandle outside the publicly exported
547 * filesystem.
548 */
549
91447636
A
550 if (!error && vnode_mount(ndp->ni_vp) != nfs_pub.np_mount) {
551 vnode_put(nd.ni_vp);
552 nameidone(&nd);
1c79356b
A
553 error = EPERM;
554 }
555 }
556#endif
557
558 if (dirp) {
91447636
A
559 if (v3) {
560 nfsm_srv_vattr_init(&dirattr, v3);
561 dirattr_ret = vnode_getattr(dirp, &dirattr, &context);
562 }
563 vnode_put(dirp);
1c79356b
A
564 }
565
566 if (error) {
567 nfsm_reply(NFSX_POSTOPATTR(v3));
568 nfsm_srvpostop_attr(dirattr_ret, &dirattr);
569 return (0);
570 }
91447636 571 nameidone(&nd);
1c79356b 572
1c79356b 573 vp = ndp->ni_vp;
91447636
A
574 error = nfsrv_vptofh(nx, !v3, (isdotdot ? &dnfh : NULL), vp, &context, &nfh);
575 if (!error) {
576 nfsm_srv_vattr_init(vap, v3);
577 error = vnode_getattr(vp, vap, &context);
578 }
579 vnode_put(vp);
580 nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3));
1c79356b
A
581 if (error) {
582 nfsm_srvpostop_attr(dirattr_ret, &dirattr);
583 return (0);
584 }
91447636 585 nfsm_srvfhtom(&nfh, v3);
1c79356b
A
586 if (v3) {
587 nfsm_srvpostop_attr(0, vap);
588 nfsm_srvpostop_attr(dirattr_ret, &dirattr);
589 } else {
590 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
591 nfsm_srvfillattr(vap, fp);
592 }
91447636
A
593nfsmout:
594 return (error);
1c79356b
A
595}
596
597/*
598 * nfs readlink service
599 */
600int
601nfsrv_readlink(nfsd, slp, procp, mrq)
602 struct nfsrv_descript *nfsd;
603 struct nfssvc_sock *slp;
91447636
A
604 proc_t procp;
605 mbuf_t *mrq;
1c79356b 606{
91447636
A
607 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
608 mbuf_t nam = nfsd->nd_nam;
1c79356b 609 caddr_t dpos = nfsd->nd_dpos;
91447636
A
610 mbuf_t mp;
611 u_long *tl;
612 long t1;
1c79356b 613 caddr_t bpos;
91447636 614 int error = 0, i, tlen, len, getret = 1;
1c79356b
A
615 int v3 = (nfsd->nd_flag & ND_NFSV3);
616 char *cp2;
91447636
A
617 mbuf_t mb, mb2, mp2, mp3, mreq;
618 vnode_t vp;
619 struct vnode_attr attr;
620 struct nfs_filehandle nfh;
621 struct nfs_export *nx;
622 struct nfs_export_options *nxo;
623 uio_t uiop = NULL;
624 char uio_buf[ UIO_SIZEOF(4) ];
625 char *uio_bufp = &uio_buf[0];
626 int uio_buflen = UIO_SIZEOF(4);
627 int mblen;
628 struct vfs_context context;
1c79356b 629
91447636 630 nfsm_srvmtofh(&nfh);
1c79356b
A
631 len = 0;
632 i = 0;
91447636
A
633
634 mp2 = mp3 = NULL;
635 vp = NULL;
1c79356b 636 while (len < NFS_MAXPATHLEN) {
91447636
A
637 mp = NULL;
638 if ((error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mp)))
639 goto out;
640 mblen = mbuf_maxlen(mp);
641 mbuf_setlen(mp, mblen);
1c79356b
A
642 if (len == 0)
643 mp3 = mp2 = mp;
644 else {
91447636
A
645 if ((error = mbuf_setnext(mp2, mp))) {
646 mbuf_free(mp);
647 goto out;
648 }
1c79356b
A
649 mp2 = mp;
650 }
91447636
A
651 if ((len + mblen) > NFS_MAXPATHLEN) {
652 mbuf_setlen(mp, NFS_MAXPATHLEN - len);
1c79356b
A
653 len = NFS_MAXPATHLEN;
654 } else
91447636
A
655 len += mblen;
656 i++;
657 }
658 if (i > 4) {
659 uio_buflen = UIO_SIZEOF(i);
660 MALLOC(uio_bufp, char*, uio_buflen, M_TEMP, M_WAITOK);
661 if (!uio_bufp) {
662 error = ENOMEM;
663 mbuf_freem(mp3);
664 nfsm_reply(2 * NFSX_UNSIGNED);
665 nfsm_srvpostop_attr(1, NULL);
666 return (0);
667 }
668 }
669 uiop = uio_createwithbuffer(i, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
670 if (!uiop) {
671 error = ENOMEM;
672 mbuf_freem(mp3);
673 if (uio_bufp != &uio_buf[0]) {
674 FREE(uio_bufp, M_TEMP);
675 uio_bufp = &uio_buf[0];
676 }
677 nfsm_reply(2 * NFSX_UNSIGNED);
678 nfsm_srvpostop_attr(1, NULL);
679 return (0);
680 }
681 mp = mp3;
682 while (mp) {
683 uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
684 mp = mbuf_next(mp);
685 }
686
687 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
688 mbuf_freem(mp3);
689 if (uio_bufp != &uio_buf[0]) {
690 FREE(uio_bufp, M_TEMP);
691 uio_bufp = &uio_buf[0];
692 }
693 nfsm_reply(2 * NFSX_UNSIGNED);
694 nfsm_srvpostop_attr(1, NULL);
695 return (0);
696 }
697 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
698 vnode_put(vp);
699 mbuf_freem(mp3);
700 if (uio_bufp != &uio_buf[0]) {
701 FREE(uio_bufp, M_TEMP);
702 uio_bufp = &uio_buf[0];
703 }
1c79356b 704 nfsm_reply(2 * NFSX_UNSIGNED);
91447636 705 nfsm_srvpostop_attr(1, NULL);
1c79356b
A
706 return (0);
707 }
91447636 708 if (vnode_vtype(vp) != VLNK) {
1c79356b
A
709 if (v3)
710 error = EINVAL;
711 else
712 error = ENXIO;
713 goto out;
714 }
91447636
A
715
716 context.vc_proc = procp;
717 context.vc_ucred = nfsd->nd_cr;
718
719 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context, nxo, 0)))
720 goto out;
721 error = VNOP_READLINK(vp, uiop, &context);
1c79356b 722out:
91447636
A
723 if (vp) {
724 if (v3) {
725 nfsm_srv_vattr_init(&attr, v3);
726 getret = vnode_getattr(vp, &attr, &context);
727 }
728 vnode_put(vp);
729 }
e5568f75 730 if (error) {
91447636 731 mbuf_freem(mp3);
e5568f75
A
732 mp3 = NULL;
733 }
1c79356b
A
734 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED);
735 if (v3) {
736 nfsm_srvpostop_attr(getret, &attr);
91447636
A
737 if (error) {
738 if (uio_bufp != &uio_buf[0])
739 FREE(uio_bufp, M_TEMP);
1c79356b 740 return (0);
91447636 741 }
1c79356b 742 }
e5568f75 743 if (!error) {
91447636
A
744 if (uiop && (uio_resid(uiop) > 0)) {
745 // LP64todo - fix this
746 len -= uio_resid(uiop);
e5568f75
A
747 tlen = nfsm_rndup(len);
748 nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len);
749 }
750 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
751 *tl = txdr_unsigned(len);
91447636 752 mbuf_setnext(mb, mp3);
1c79356b 753 }
91447636
A
754nfsmout:
755 if (uio_bufp != &uio_buf[0])
756 FREE(uio_bufp, M_TEMP);
757 return (error);
1c79356b
A
758}
759
760/*
761 * nfs read service
762 */
763int
764nfsrv_read(nfsd, slp, procp, mrq)
765 struct nfsrv_descript *nfsd;
766 struct nfssvc_sock *slp;
91447636
A
767 proc_t procp;
768 mbuf_t *mrq;
1c79356b 769{
91447636
A
770 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
771 mbuf_t nam = nfsd->nd_nam;
1c79356b 772 caddr_t dpos = nfsd->nd_dpos;
91447636
A
773 mbuf_t m;
774 struct nfs_fattr *fp;
775 u_long *tl;
776 long t1;
777 int i;
1c79356b 778 caddr_t bpos;
91447636 779 int error = 0, count, len, left, siz, tlen, getret;
743b1565 780 int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen, maxlen;
1c79356b 781 char *cp2;
91447636
A
782 mbuf_t mb, mb2, mreq;
783 mbuf_t m2;
784 vnode_t vp;
785 struct nfs_filehandle nfh;
786 struct nfs_export *nx;
787 struct nfs_export_options *nxo;
788 uio_t uiop = NULL;
789 char *uio_bufp = NULL;
790 struct vnode_attr va, *vap = &va;
1c79356b 791 off_t off;
91447636
A
792 char uio_buf[ UIO_SIZEOF(0) ];
793 struct vfs_context context;
1c79356b 794
91447636 795 nfsm_srvmtofh(&nfh);
1c79356b
A
796 if (v3) {
797 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
798 fxdr_hyper(tl, &off);
799 } else {
800 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
801 off = (off_t)fxdr_unsigned(u_long, *tl);
802 }
743b1565
A
803 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
804 reqlen = fxdr_unsigned(u_long, *tl);
805 maxlen = NFS_SRVMAXDATA(nfsd);
806 if (reqlen > maxlen)
807 reqlen = maxlen;
808
91447636 809 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b 810 nfsm_reply(2 * NFSX_UNSIGNED);
91447636 811 nfsm_srvpostop_attr(1, NULL);
1c79356b
A
812 return (0);
813 }
91447636
A
814 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
815 vnode_put(vp);
816 nfsm_reply(2 * NFSX_UNSIGNED);
817 nfsm_srvpostop_attr(1, NULL);
818 return (0);
819 }
820 if (vnode_vtype(vp) != VREG) {
1c79356b
A
821 if (v3)
822 error = EINVAL;
823 else
91447636 824 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1c79356b 825 }
91447636
A
826
827 context.vc_proc = procp;
828 context.vc_ucred = nfsd->nd_cr;
829
1c79356b 830 if (!error) {
91447636
A
831 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context, nxo, 1)))
832 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context, nxo, 1);
1c79356b 833 }
91447636
A
834 nfsm_srv_vattr_init(vap, v3);
835 getret = vnode_getattr(vp, vap, &context);
1c79356b
A
836 if (!error)
837 error = getret;
838 if (error) {
91447636 839 vnode_put(vp);
1c79356b
A
840 nfsm_reply(NFSX_POSTOPATTR(v3));
841 nfsm_srvpostop_attr(getret, vap);
842 return (0);
843 }
91447636
A
844 if ((u_quad_t)off >= vap->va_data_size)
845 count = 0;
846 else if (((u_quad_t)off + reqlen) > vap->va_data_size)
847 count = nfsm_rndup(vap->va_data_size - off);
1c79356b 848 else
91447636
A
849 count = reqlen;
850 nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(count));
1c79356b
A
851 if (v3) {
852 nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
853 *tl++ = nfs_true;
854 fp = (struct nfs_fattr *)tl;
855 tl += (NFSX_V3FATTR / sizeof (u_long));
856 } else {
857 nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED);
858 fp = (struct nfs_fattr *)tl;
859 tl += (NFSX_V2FATTR / sizeof (u_long));
860 }
91447636
A
861 len = left = count;
862 if (count > 0) {
1c79356b
A
863 /*
864 * Generate the mbuf list with the uio_iov ref. to it.
865 */
866 i = 0;
867 m = m2 = mb;
868 while (left > 0) {
91447636 869 siz = min(mbuf_trailingspace(m), left);
1c79356b
A
870 if (siz > 0) {
871 left -= siz;
872 i++;
873 }
874 if (left > 0) {
91447636
A
875 m = NULL;
876 if ((error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &m)))
877 goto errorexit;
878 mbuf_setnext(m2, m);
1c79356b
A
879 m2 = m;
880 }
881 }
91447636
A
882 MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
883 if (!uio_bufp) {
884 error = ENOMEM;
885 goto errorexit;
886 }
887 uiop = uio_createwithbuffer(i, off, UIO_SYSSPACE, UIO_READ,
888 uio_bufp, UIO_SIZEOF(i));
889 if (!uiop) {
890 error = ENOMEM;
891 goto errorexit;
892 }
1c79356b 893 m = mb;
91447636 894 left = count;
1c79356b
A
895 i = 0;
896 while (left > 0) {
897 if (m == NULL)
898 panic("nfsrv_read iov");
91447636 899 siz = min(mbuf_trailingspace(m), left);
1c79356b 900 if (siz > 0) {
91447636
A
901 tlen = mbuf_len(m);
902 uio_addiov(uiop, CAST_USER_ADDR_T((char *)mbuf_data(m) + tlen), siz);
903 mbuf_setlen(m, tlen + siz);
1c79356b 904 left -= siz;
1c79356b
A
905 i++;
906 }
91447636
A
907 m = mbuf_next(m);
908 }
909 error = VNOP_READ(vp, uiop, IO_NODELOCKED, &context);
910 off = uio_offset(uiop);
911errorexit:
e5568f75
A
912 /*
913 * This may seem a little weird that we drop the whole
914 * successful read if we get an error on the getattr.
915 * The reason is because we've already set up the reply
916 * to have postop attrs and omitting these optional bits
917 * would require shifting all the data in the reply.
918 *
919 * It would be more correct if we would simply drop the
920 * postop attrs if the getattr fails. We might be able to
921 * do that easier if we allocated separate mbufs for the data.
922 */
91447636
A
923 nfsm_srv_vattr_init(vap, v3);
924 if (error || (getret = vnode_getattr(vp, vap, &context))) {
1c79356b
A
925 if (!error)
926 error = getret;
91447636
A
927 mbuf_freem(mreq);
928 vnode_put(vp);
1c79356b
A
929 nfsm_reply(NFSX_POSTOPATTR(v3));
930 nfsm_srvpostop_attr(getret, vap);
91447636
A
931 if (uio_bufp != NULL) {
932 FREE(uio_bufp, M_TEMP);
933 }
1c79356b
A
934 return (0);
935 }
55e303ae 936 } else {
91447636
A
937 uiop = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
938 if (!uiop) {
939 error = ENOMEM;
940 goto errorexit;
941 }
55e303ae 942 }
91447636 943 vnode_put(vp);
1c79356b 944 nfsm_srvfillattr(vap, fp);
91447636
A
945 // LP64todo - fix this
946 len -= uio_resid(uiop);
1c79356b 947 tlen = nfsm_rndup(len);
91447636
A
948 if (count != tlen || tlen != len)
949 nfsm_adj(mb, count - tlen, tlen - len);
1c79356b
A
950 if (v3) {
951 *tl++ = txdr_unsigned(len);
952 if (len < reqlen)
953 *tl++ = nfs_true;
954 else
955 *tl++ = nfs_false;
956 }
957 *tl = txdr_unsigned(len);
91447636
A
958nfsmout:
959 if (uio_bufp != NULL) {
960 FREE(uio_bufp, M_TEMP);
961 }
962 return (error);
1c79356b
A
963}
964
965/*
966 * nfs write service
967 */
968int
969nfsrv_write(nfsd, slp, procp, mrq)
970 struct nfsrv_descript *nfsd;
971 struct nfssvc_sock *slp;
91447636
A
972 proc_t procp;
973 mbuf_t *mrq;
1c79356b 974{
91447636
A
975 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
976 mbuf_t nam = nfsd->nd_nam;
1c79356b 977 caddr_t dpos = nfsd->nd_dpos;
91447636
A
978 int i, count;
979 mbuf_t mp;
980 struct nfs_fattr *fp;
981 struct vnode_attr va, forat;
982 struct vnode_attr *vap = &va;
983 u_long *tl;
984 long t1;
985 caddr_t bpos, tpos;
986 int error = 0, len, forat_ret = 1;
987 int ioflags, aftat_ret = 1, retlen, zeroing, adjust, tlen;
1c79356b
A
988 int stable = NFSV3WRITE_FILESYNC;
989 int v3 = (nfsd->nd_flag & ND_NFSV3);
990 char *cp2;
91447636
A
991 mbuf_t mb, mb2, mreq;
992 vnode_t vp;
993 struct nfs_filehandle nfh;
994 struct nfs_export *nx;
995 struct nfs_export_options *nxo;
996 uio_t uiop;
1c79356b 997 off_t off;
91447636
A
998 char *uio_bufp = NULL;
999 struct vfs_context context;
1c79356b
A
1000
1001 if (mrep == NULL) {
1002 *mrq = NULL;
1003 return (0);
1004 }
91447636 1005 nfsm_srvmtofh(&nfh);
1c79356b
A
1006 if (v3) {
1007 nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
1008 fxdr_hyper(tl, &off);
1009 tl += 3;
1010 stable = fxdr_unsigned(int, *tl++);
1011 } else {
1012 nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
1013 off = (off_t)fxdr_unsigned(u_long, *++tl);
1014 tl += 2;
1015 if (nfs_async)
1016 stable = NFSV3WRITE_UNSTABLE;
1017 }
1018 retlen = len = fxdr_unsigned(long, *tl);
91447636 1019 count = i = 0;
1c79356b
A
1020
1021 /*
1022 * For NFS Version 2, it is not obvious what a write of zero length
1023 * should do, but I might as well be consistent with Version 3,
1024 * which is to return ok so long as there are no permission problems.
1025 */
1026 if (len > 0) {
1027 zeroing = 1;
1028 mp = mrep;
1029 while (mp) {
1030 if (mp == md) {
1031 zeroing = 0;
91447636
A
1032 tpos = mbuf_data(mp);
1033 tlen = mbuf_len(mp);
1034 adjust = dpos - tpos;
1035 tlen -= adjust;
1036 mbuf_setlen(mp, tlen);
1037 if (tlen > 0 && adjust > 0) {
1038 tpos += adjust;
1039 if ((error = mbuf_setdata(mp, tpos, tlen))) {
1040 nfsm_reply(2 * NFSX_UNSIGNED);
1041 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1042 return (0);
1043 }
1044 }
1c79356b
A
1045 }
1046 if (zeroing)
91447636
A
1047 mbuf_setlen(mp, 0);
1048 else if ((tlen = mbuf_len(mp)) > 0) {
1049 i += tlen;
1c79356b 1050 if (i > len) {
91447636 1051 mbuf_setlen(mp, tlen - (i - len));
1c79356b
A
1052 zeroing = 1;
1053 }
91447636
A
1054 if (mbuf_len(mp) > 0)
1055 count++;
1c79356b 1056 }
91447636 1057 mp = mbuf_next(mp);
1c79356b
A
1058 }
1059 }
1060 if (len > NFS_MAXDATA || len < 0 || i < len) {
1061 error = EIO;
1062 nfsm_reply(2 * NFSX_UNSIGNED);
1063 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1064 return (0);
1065 }
91447636 1066 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b
A
1067 nfsm_reply(2 * NFSX_UNSIGNED);
1068 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1069 return (0);
1070 }
91447636
A
1071 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
1072 vnode_put(vp);
1073 nfsm_reply(2 * NFSX_UNSIGNED);
1074 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1075 return (0);
1076 }
1077 context.vc_proc = procp;
1078 context.vc_ucred = nfsd->nd_cr;
1079
1080 if (v3) {
1081 nfsm_srv_pre_vattr_init(&forat, v3);
1082 forat_ret = vnode_getattr(vp, &forat, &context);
1083 }
1084 if (vnode_vtype(vp) != VREG) {
1c79356b
A
1085 if (v3)
1086 error = EINVAL;
1087 else
91447636 1088 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1c79356b
A
1089 }
1090 if (!error) {
91447636 1091 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1);
1c79356b
A
1092 }
1093 if (error) {
91447636 1094 vnode_put(vp);
1c79356b
A
1095 nfsm_reply(NFSX_WCCDATA(v3));
1096 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1097 return (0);
1098 }
1099
1100 if (len > 0) {
91447636
A
1101 MALLOC(uio_bufp, char *, UIO_SIZEOF(count), M_TEMP, M_WAITOK);
1102 if (!uio_bufp) {
1103 error = ENOMEM;
1104 vnode_put(vp);
1105 nfsm_reply(NFSX_WCCDATA(v3));
1106 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1107 return (0);
1108 }
1109 uiop = uio_createwithbuffer(count, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(count));
1110 if (!uiop) {
1111 error = ENOMEM;
1112 vnode_put(vp);
1113 nfsm_reply(NFSX_WCCDATA(v3));
1114 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
1115 if (uio_bufp != NULL) {
1116 FREE(uio_bufp, M_TEMP);
1117 }
1118 return (0);
1119 }
1c79356b
A
1120 mp = mrep;
1121 while (mp) {
91447636
A
1122 if ((tlen = mbuf_len(mp)) > 0)
1123 uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), tlen);
1124 mp = mbuf_next(mp);
1c79356b
A
1125 }
1126
1127 /*
1128 * XXX
1129 * The IO_METASYNC flag indicates that all metadata (and not just
1130 * enough to ensure data integrity) mus be written to stable storage
1131 * synchronously.
1132 * (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1133 */
1134 if (stable == NFSV3WRITE_UNSTABLE)
1135 ioflags = IO_NODELOCKED;
1136 else if (stable == NFSV3WRITE_DATASYNC)
1137 ioflags = (IO_SYNC | IO_NODELOCKED);
1138 else
1139 ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
91447636
A
1140
1141 error = VNOP_WRITE(vp, uiop, ioflags, &context);
1142 OSAddAtomic(1, (SInt32*)(SInt32*)&nfsstats.srvvop_writes);
1143 }
1144 nfsm_srv_vattr_init(vap, v3);
1145 aftat_ret = vnode_getattr(vp, vap, &context);
1146 vnode_put(vp);
1c79356b
A
1147 if (!error)
1148 error = aftat_ret;
1149 nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) +
1150 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3));
1151 if (v3) {
1152 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
91447636
A
1153 if (error) {
1154 if (uio_bufp != NULL) {
1155 FREE(uio_bufp, M_TEMP);
1156 }
1c79356b 1157 return (0);
91447636 1158 }
1c79356b
A
1159 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
1160 *tl++ = txdr_unsigned(retlen);
1161 /*
1162 * If nfs_async is set, then pretend the write was FILESYNC.
1163 */
1164 if (stable == NFSV3WRITE_UNSTABLE && !nfs_async)
1165 *tl++ = txdr_unsigned(stable);
1166 else
1167 *tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC);
0c530ab8 1168 /* write verifier */
91447636
A
1169 *tl++ = txdr_unsigned(boottime_sec());
1170 *tl = txdr_unsigned(0);
1c79356b
A
1171 } else {
1172 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
1173 nfsm_srvfillattr(vap, fp);
1174 }
91447636
A
1175nfsmout:
1176 if (uio_bufp != NULL) {
1177 FREE(uio_bufp, M_TEMP);
1178 }
1179 return (error);
1c79356b
A
1180}
1181
1182/*
1183 * NFS write service with write gathering support. Called when
1184 * nfsrvw_procrastinate > 0.
1185 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1186 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1187 * Jan. 1994.
1188 */
1189int
1190nfsrv_writegather(ndp, slp, procp, mrq)
1191 struct nfsrv_descript **ndp;
1192 struct nfssvc_sock *slp;
91447636
A
1193 proc_t procp;
1194 mbuf_t *mrq;
1c79356b 1195{
91447636
A
1196 mbuf_t mp;
1197 struct nfsrv_descript *wp, *nfsd, *owp, *swp;
1198 struct nfs_export *nx;
1199 struct nfs_export_options *nxo;
1200 struct nfs_fattr *fp;
1201 int i;
1c79356b 1202 struct nfsrvw_delayhash *wpp;
91447636
A
1203 kauth_cred_t cred;
1204 struct vnode_attr va, forat;
1205 u_long *tl;
1206 long t1;
1207 caddr_t bpos, dpos, tpos;
1208 int error = 0, len, forat_ret = 1;
1209 int ioflags, aftat_ret = 1, adjust, v3, zeroing, tlen;
1c79356b 1210 char *cp2;
91447636
A
1211 mbuf_t mb, mb2, mreq, mrep, md;
1212 vnode_t vp;
1213 uio_t uiop = NULL;
1214 char *uio_bufp = NULL;
1215 u_quad_t cur_usec;
55e303ae 1216 struct timeval now;
91447636
A
1217 struct vfs_context context;
1218
1219 context.vc_proc = procp;
1c79356b
A
1220
1221#ifndef nolint
1222 i = 0;
1223 len = 0;
1224#endif
91447636 1225
1c79356b
A
1226 *mrq = NULL;
1227 if (*ndp) {
1228 nfsd = *ndp;
1229 *ndp = NULL;
1230 mrep = nfsd->nd_mrep;
1231 md = nfsd->nd_md;
1232 dpos = nfsd->nd_dpos;
91447636
A
1233 cred = nfsd->nd_cr;
1234 context.vc_ucred = cred;
1c79356b
A
1235 v3 = (nfsd->nd_flag & ND_NFSV3);
1236 LIST_INIT(&nfsd->nd_coalesce);
1237 nfsd->nd_mreq = NULL;
1238 nfsd->nd_stable = NFSV3WRITE_FILESYNC;
55e303ae
A
1239 microuptime(&now);
1240 cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec;
1c79356b
A
1241 nfsd->nd_time = cur_usec +
1242 (v3 ? nfsrvw_procrastinate_v3 : nfsrvw_procrastinate);
1243
1244 /*
1245 * Now, get the write header..
1246 */
1247 nfsm_srvmtofh(&nfsd->nd_fh);
91447636 1248 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1c79356b
A
1249 if (v3) {
1250 nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
1251 fxdr_hyper(tl, &nfsd->nd_off);
1252 tl += 3;
1253 nfsd->nd_stable = fxdr_unsigned(int, *tl++);
1254 } else {
1255 nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
1256 nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl);
1257 tl += 2;
1258 if (nfs_async)
1259 nfsd->nd_stable = NFSV3WRITE_UNSTABLE;
1260 }
1261 len = fxdr_unsigned(long, *tl);
1262 nfsd->nd_len = len;
1263 nfsd->nd_eoff = nfsd->nd_off + len;
1264
1265 /*
1266 * Trim the header out of the mbuf list and trim off any trailing
1267 * junk so that the mbuf list has only the write data.
1268 */
1269 zeroing = 1;
1270 i = 0;
1271 mp = mrep;
1272 while (mp) {
1273 if (mp == md) {
1274 zeroing = 0;
91447636
A
1275 tpos = mbuf_data(mp);
1276 tlen = mbuf_len(mp);
1277 adjust = dpos - tpos;
1278 tlen -= adjust;
1279 mbuf_setlen(mp, tlen);
1280 if (tlen > 0 && adjust > 0) {
1281 tpos += adjust;
1282 if ((error = mbuf_setdata(mp, tpos, tlen)))
1283 goto nfsmout;
1284 }
1c79356b
A
1285 }
1286 if (zeroing)
91447636 1287 mbuf_setlen(mp, 0);
1c79356b 1288 else {
91447636
A
1289 tlen = mbuf_len(mp);
1290 i += tlen;
1c79356b 1291 if (i > len) {
91447636 1292 mbuf_setlen(mp, tlen - (i - len));
1c79356b
A
1293 zeroing = 1;
1294 }
1295 }
91447636 1296 mp = mbuf_next(mp);
1c79356b
A
1297 }
1298 if (len > NFS_MAXDATA || len < 0 || i < len) {
1299nfsmout:
91447636 1300 mbuf_freem(mrep);
e5568f75 1301 mrep = NULL;
1c79356b
A
1302 error = EIO;
1303 nfsm_writereply(2 * NFSX_UNSIGNED, v3);
1304 if (v3)
1305 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
1306 nfsd->nd_mreq = mreq;
1307 nfsd->nd_mrep = NULL;
91447636 1308 nfsd->nd_time = 1;
1c79356b
A
1309 }
1310
1311 /*
1312 * Add this entry to the hash and time queues.
1313 */
91447636 1314 lck_mtx_lock(&slp->ns_wgmutex);
1c79356b
A
1315 owp = NULL;
1316 wp = slp->ns_tq.lh_first;
1317 while (wp && wp->nd_time < nfsd->nd_time) {
1318 owp = wp;
1319 wp = wp->nd_tq.le_next;
1320 }
1c79356b
A
1321 if (owp) {
1322 LIST_INSERT_AFTER(owp, nfsd, nd_tq);
1323 } else {
1324 LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
1325 }
1326 if (nfsd->nd_mrep) {
91447636 1327 wpp = NWDELAYHASH(slp, nfsd->nd_fh.nfh_fid);
1c79356b
A
1328 owp = NULL;
1329 wp = wpp->lh_first;
91447636 1330 while (wp && !nfsrv_fhmatch(&nfsd->nd_fh, &wp->nd_fh)) {
1c79356b
A
1331 owp = wp;
1332 wp = wp->nd_hash.le_next;
1333 }
91447636
A
1334 while (wp && (wp->nd_off < nfsd->nd_off) &&
1335 nfsrv_fhmatch(&nfsd->nd_fh, &wp->nd_fh)) {
1c79356b
A
1336 owp = wp;
1337 wp = wp->nd_hash.le_next;
1338 }
1339 if (owp) {
1340 LIST_INSERT_AFTER(owp, nfsd, nd_hash);
1341
1342 /*
1343 * Search the hash list for overlapping entries and
1344 * coalesce.
1345 */
1346 for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) {
1347 wp = nfsd->nd_hash.le_next;
1348 if (NFSW_SAMECRED(owp, nfsd))
1349 nfsrvw_coalesce(owp, nfsd);
1350 }
1351 } else {
1352 LIST_INSERT_HEAD(wpp, nfsd, nd_hash);
1353 }
1354 }
91447636
A
1355 } else {
1356 lck_mtx_lock(&slp->ns_wgmutex);
1c79356b
A
1357 }
1358
1359 /*
91447636 1360 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1c79356b
A
1361 * and generate the associated reply mbuf list(s).
1362 */
1363loop1:
55e303ae
A
1364 microuptime(&now);
1365 cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec;
1c79356b
A
1366 for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) {
1367 owp = nfsd->nd_tq.le_next;
1368 if (nfsd->nd_time > cur_usec)
1369 break;
1370 if (nfsd->nd_mreq)
1371 continue;
1c79356b
A
1372 LIST_REMOVE(nfsd, nd_tq);
1373 LIST_REMOVE(nfsd, nd_hash);
1c79356b
A
1374 mrep = nfsd->nd_mrep;
1375 nfsd->nd_mrep = NULL;
1c79356b
A
1376 v3 = (nfsd->nd_flag & ND_NFSV3);
1377 forat_ret = aftat_ret = 1;
91447636 1378 error = nfsrv_fhtovp(&nfsd->nd_fh, nfsd->nd_nam, TRUE, &vp, &nx, &nxo);
1c79356b 1379 if (!error) {
91447636
A
1380 error = nfsrv_credcheck(nfsd, nx, nxo);
1381 if (error)
1382 vnode_put(vp);
1383 }
1384 cred = nfsd->nd_cr;
1385 context.vc_ucred = cred;
1386 if (!error) {
1387 if (v3) {
1388 nfsm_srv_pre_vattr_init(&forat, v3);
1389 forat_ret = vnode_getattr(vp, &forat, &context);
1390 }
1391 if (vnode_vtype(vp) != VREG) {
1c79356b
A
1392 if (v3)
1393 error = EINVAL;
1394 else
91447636 1395 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1c79356b
A
1396 }
1397 } else
1398 vp = NULL;
1399 if (!error) {
91447636 1400 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1);
1c79356b
A
1401 }
1402
1403 if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE)
1404 ioflags = IO_NODELOCKED;
1405 else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC)
1406 ioflags = (IO_SYNC | IO_NODELOCKED);
1407 else
1408 ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
91447636
A
1409
1410 if (!error && ((nfsd->nd_eoff - nfsd->nd_off) > 0)) {
1c79356b
A
1411 mp = mrep;
1412 i = 0;
1413 while (mp) {
91447636 1414 if (mbuf_len(mp) > 0)
1c79356b 1415 i++;
91447636 1416 mp = mbuf_next(mp);
1c79356b 1417 }
91447636
A
1418
1419 MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
1420 if (uio_bufp)
1421 uiop = uio_createwithbuffer(i, nfsd->nd_off, UIO_SYSSPACE,
1422 UIO_WRITE, uio_bufp, UIO_SIZEOF(i));
1423 if (!uio_bufp || !uiop)
1424 error = ENOMEM;
1425 if (!error) {
1426 mp = mrep;
1427 while (mp) {
1428 if ((tlen = mbuf_len(mp)) > 0)
1429 uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), tlen);
1430 mp = mbuf_next(mp);
1c79356b 1431 }
91447636
A
1432 error = VNOP_WRITE(vp, uiop, ioflags, &context);
1433 OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
1c79356b 1434 }
91447636
A
1435 if (uio_bufp) {
1436 FREE(uio_bufp, M_TEMP);
1437 uio_bufp = NULL;
1c79356b 1438 }
1c79356b 1439 }
91447636 1440 mbuf_freem(mrep);
e5568f75 1441 mrep = NULL;
1c79356b 1442 if (vp) {
91447636
A
1443 nfsm_srv_pre_vattr_init(&va, v3);
1444 aftat_ret = vnode_getattr(vp, &va, &context);
1445 vnode_put(vp);
1c79356b
A
1446 }
1447
1448 /*
1449 * Loop around generating replies for all write rpcs that have
1450 * now been completed.
1451 */
1452 swp = nfsd;
1453 do {
1c79356b
A
1454 if (error) {
1455 nfsm_writereply(NFSX_WCCDATA(v3), v3);
1456 if (v3) {
1457 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
1458 }
1459 } else {
1460 nfsm_writereply(NFSX_PREOPATTR(v3) +
1461 NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED +
1462 NFSX_WRITEVERF(v3), v3);
1463 if (v3) {
1464 nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
1465 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
1466 *tl++ = txdr_unsigned(nfsd->nd_len);
1467 *tl++ = txdr_unsigned(swp->nd_stable);
0c530ab8 1468 /* write verifier */
91447636
A
1469 *tl++ = txdr_unsigned(boottime_sec());
1470 *tl = txdr_unsigned(0);
1c79356b
A
1471 } else {
1472 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
1473 nfsm_srvfillattr(&va, fp);
1474 }
1475 }
1476 nfsd->nd_mreq = mreq;
1477 if (nfsd->nd_mrep)
1478 panic("nfsrv_write: nd_mrep not free");
1479
1480 /*
1481 * Done. Put it at the head of the timer queue so that
1482 * the final phase can return the reply.
1483 */
1c79356b 1484 if (nfsd != swp) {
91447636 1485 nfsd->nd_time = 1;
1c79356b
A
1486 LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
1487 }
1488 nfsd = swp->nd_coalesce.lh_first;
1489 if (nfsd) {
1490 LIST_REMOVE(nfsd, nd_tq);
1491 }
1c79356b 1492 } while (nfsd);
91447636 1493 swp->nd_time = 1;
1c79356b 1494 LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq);
1c79356b
A
1495 goto loop1;
1496 }
1c79356b
A
1497
1498 /*
1499 * Search for a reply to return.
1500 */
1c79356b
A
1501 for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next)
1502 if (nfsd->nd_mreq) {
1c79356b
A
1503 LIST_REMOVE(nfsd, nd_tq);
1504 *mrq = nfsd->nd_mreq;
1505 *ndp = nfsd;
1506 break;
1507 }
91447636
A
1508 slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0;
1509 lck_mtx_unlock(&slp->ns_wgmutex);
1c79356b
A
1510 return (0);
1511}
1512
1513/*
1514 * Coalesce the write request nfsd into owp. To do this we must:
1515 * - remove nfsd from the queues
1516 * - merge nfsd->nd_mrep into owp->nd_mrep
1517 * - update the nd_eoff and nd_stable for owp
1518 * - put nfsd on owp's nd_coalesce list
1c79356b
A
1519 */
1520static void
91447636
A
1521nfsrvw_coalesce(
1522 struct nfsrv_descript *owp,
1523 struct nfsrv_descript *nfsd)
1c79356b 1524{
91447636
A
1525 int overlap, error;
1526 mbuf_t mp, mpnext;
1c79356b
A
1527 struct nfsrv_descript *p;
1528
1c79356b
A
1529 LIST_REMOVE(nfsd, nd_hash);
1530 LIST_REMOVE(nfsd, nd_tq);
1531 if (owp->nd_eoff < nfsd->nd_eoff) {
1532 overlap = owp->nd_eoff - nfsd->nd_off;
1533 if (overlap < 0)
1534 panic("nfsrv_coalesce: bad off");
1535 if (overlap > 0)
91447636 1536 mbuf_adj(nfsd->nd_mrep, overlap);
1c79356b 1537 mp = owp->nd_mrep;
91447636
A
1538 while ((mpnext = mbuf_next(mp)))
1539 mp = mpnext;
1540 error = mbuf_setnext(mp, nfsd->nd_mrep);
1541 if (error)
1542 panic("nfsrvw_coalesce: mbuf_setnext failed: %d", error);
1c79356b 1543 owp->nd_eoff = nfsd->nd_eoff;
91447636
A
1544 } else {
1545 mbuf_freem(nfsd->nd_mrep);
1546 }
1c79356b
A
1547 nfsd->nd_mrep = NULL;
1548 if (nfsd->nd_stable == NFSV3WRITE_FILESYNC)
1549 owp->nd_stable = NFSV3WRITE_FILESYNC;
1550 else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC &&
1551 owp->nd_stable == NFSV3WRITE_UNSTABLE)
1552 owp->nd_stable = NFSV3WRITE_DATASYNC;
1553 LIST_INSERT_HEAD(&owp->nd_coalesce, nfsd, nd_tq);
1554
1555 /*
1556 * If nfsd had anything else coalesced into it, transfer them
1557 * to owp, otherwise their replies will never get sent.
1558 */
1559 for (p = nfsd->nd_coalesce.lh_first; p;
1560 p = nfsd->nd_coalesce.lh_first) {
1561 LIST_REMOVE(p, nd_tq);
1562 LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq);
1563 }
1564}
1565
1566/*
1567 * Sort the group list in increasing numerical order.
1568 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1569 * that used to be here.)
91447636
A
1570 *
1571 * XXX ILLEGAL
1c79356b
A
1572 */
1573void
1574nfsrvw_sort(list, num)
91447636
A
1575 gid_t *list;
1576 int num;
1c79356b 1577{
91447636 1578 int i, j;
1c79356b
A
1579 gid_t v;
1580
1581 /* Insertion sort. */
1582 for (i = 1; i < num; i++) {
1583 v = list[i];
1584 /* find correct slot for value v, moving others up */
1585 for (j = i; --j >= 0 && v < list[j];)
1586 list[j + 1] = list[j];
1587 list[j + 1] = v;
1588 }
1589}
1590
1591/*
1592 * copy credentials making sure that the result can be compared with bcmp().
91447636 1593 *
0c530ab8
A
1594 * NOTE: This function is only intended to operate on a real input
1595 * credential and a template output credential; the template
1596 * ouptut credential is intended to then be used as an argument
1597 * to kauth_cred_create() - AND NEVER REFERENCED OTHERWISE.
1c79356b
A
1598 */
1599void
91447636 1600nfsrv_setcred(kauth_cred_t incred, kauth_cred_t outcred)
1c79356b 1601{
91447636 1602 int i;
1c79356b 1603
91447636 1604 bzero((caddr_t)outcred, sizeof (*outcred));
91447636 1605 outcred->cr_uid = kauth_cred_getuid(incred);
1c79356b
A
1606 outcred->cr_ngroups = incred->cr_ngroups;
1607 for (i = 0; i < incred->cr_ngroups; i++)
1608 outcred->cr_groups[i] = incred->cr_groups[i];
1609 nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
1610}
1611
1612/*
1613 * nfs create service
1614 * now does a truncate to 0 length via. setattr if it already exists
1615 */
1616int
1617nfsrv_create(nfsd, slp, procp, mrq)
1618 struct nfsrv_descript *nfsd;
1619 struct nfssvc_sock *slp;
91447636
A
1620 proc_t procp;
1621 mbuf_t *mrq;
1c79356b 1622{
91447636
A
1623 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
1624 mbuf_t nam = nfsd->nd_nam;
1c79356b 1625 caddr_t dpos = nfsd->nd_dpos;
91447636
A
1626 struct nfs_fattr *fp;
1627 struct vnode_attr dirfor, diraft, postat;
1628 struct vnode_attr va;
1629 struct vnode_attr *vap = &va;
1630 struct nfsv2_sattr *sp;
1631 u_long *tl;
1c79356b 1632 struct nameidata nd;
91447636
A
1633 caddr_t cp;
1634 long t1;
1c79356b 1635 caddr_t bpos;
91447636 1636 int error = 0, rdev, len, tsize, dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
1637 int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0;
1638 char *cp2;
91447636
A
1639 mbuf_t mb, mb2, mreq;
1640 vnode_t vp, dvp, dirp = NULL;
1641 struct nfs_filehandle nfh;
1642 struct nfs_export *nx;
1643 struct nfs_export_options *nxo;
1644 u_quad_t tempsize;
1c79356b 1645 u_char cverf[NFSX_V3CREATEVERF];
91447636
A
1646 struct vfs_context context;
1647 uid_t saved_uid;
1648
1649 context.vc_proc = procp;
1650 context.vc_ucred = nfsd->nd_cr;
1651
1652 /*
1653 * Save the original credential UID in case they are
1654 * mapped and we need to map the IDs in the attributes.
1655 */
1656 saved_uid = kauth_cred_getuid(nfsd->nd_cr);
1c79356b
A
1657
1658#ifndef nolint
1659 rdev = 0;
1660#endif
1661 nd.ni_cnd.cn_nameiop = 0;
91447636
A
1662 vp = dvp = NULL;
1663 nfsm_srvmtofh(&nfh);
1664 nfsm_srvnamesiz(len, v3);
1665
1c79356b 1666 nd.ni_cnd.cn_nameiop = CREATE;
91447636
A
1667 nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
1668 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
1669 if (!error)
1670 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 1671 if (dirp) {
91447636
A
1672 if (v3) {
1673 nfsm_srv_pre_vattr_init(&dirfor, v3);
1674 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
1675 } else {
1676 vnode_put(dirp);
1677 dirp = NULL;
1c79356b
A
1678 }
1679 }
1680 if (error) {
91447636 1681 nd.ni_cnd.cn_nameiop = 0;
1c79356b
A
1682 nfsm_reply(NFSX_WCCDATA(v3));
1683 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
1684 if (dirp)
91447636 1685 vnode_put(dirp);
1c79356b
A
1686 return (0);
1687 }
91447636
A
1688 dvp = nd.ni_dvp;
1689 vp = nd.ni_vp;
1690
1691 VATTR_INIT(vap);
1692
1c79356b
A
1693 if (v3) {
1694 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
1695 how = fxdr_unsigned(int, *tl);
1696 switch (how) {
1697 case NFSV3CREATE_GUARDED:
91447636 1698 if (vp) {
1c79356b
A
1699 error = EEXIST;
1700 break;
1701 }
1702 case NFSV3CREATE_UNCHECKED:
1703 nfsm_srvsattr(vap);
1704 break;
1705 case NFSV3CREATE_EXCLUSIVE:
1706 nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF);
1707 bcopy(cp, cverf, NFSX_V3CREATEVERF);
1708 exclusive_flag = 1;
91447636
A
1709 if (vp == NULL)
1710 VATTR_SET(vap, va_mode, 0);
1c79356b
A
1711 break;
1712 };
91447636 1713 VATTR_SET(vap, va_type, VREG);
1c79356b 1714 } else {
91447636
A
1715 enum vtype v_type;
1716
1c79356b 1717 nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
91447636
A
1718 v_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
1719 if (v_type == VNON)
1720 v_type = VREG;
1721 VATTR_SET(vap, va_type, v_type);
1722 VATTR_SET(vap, va_mode, nfstov_mode(sp->sa_mode));
1723
1724 switch (v_type) {
1c79356b
A
1725 case VREG:
1726 tsize = fxdr_unsigned(long, sp->sa_size);
1727 if (tsize != -1)
91447636 1728 VATTR_SET(vap, va_data_size, (u_quad_t)tsize);
1c79356b
A
1729 break;
1730 case VCHR:
1731 case VBLK:
1732 case VFIFO:
1733 rdev = fxdr_unsigned(long, sp->sa_size);
1734 break;
91447636
A
1735 default:
1736 break;
1c79356b
A
1737 };
1738 }
1739
1740 /*
91447636 1741 * If it doesn't exist, create it
1c79356b
A
1742 * otherwise just truncate to 0 length
1743 * should I set the mode too ??
1744 */
91447636
A
1745 if (vp == NULL) {
1746 kauth_acl_t xacl = NULL;
1747
1748 /*
1749 * If the credentials were mapped, we should
1750 * map the same values in the attributes.
1751 */
1752 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
1753 int ismember;
1754 VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
1755 if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
1756 VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
1757 }
1758
1759 /* authorize before creating */
1760 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
1761
1762 /* construct ACL and handle inheritance */
1763 if (!error) {
1764 error = kauth_acl_inherit(dvp,
1765 NULL,
1766 &xacl,
1767 0 /* !isdir */,
1768 &context);
1769
1770 if (!error && xacl != NULL)
1771 VATTR_SET(vap, va_acl, xacl);
1772 }
1773 VATTR_CLEAR_ACTIVE(vap, va_data_size);
1774 VATTR_CLEAR_ACTIVE(vap, va_access_time);
1775
1776 /* validate new-file security information */
1777 if (!error) {
1778 error = vnode_authattr_new(dvp, vap, 0, &context);
1779 if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
1780 /*
1781 * Most NFS servers just ignore the UID/GID attributes, so we
1782 * try ignoring them if that'll help the request succeed.
1783 */
1784 VATTR_CLEAR_ACTIVE(vap, va_uid);
1785 VATTR_CLEAR_ACTIVE(vap, va_gid);
1786 error = vnode_authattr_new(dvp, vap, 0, &context);
1787 }
1788 }
1789
1c79356b 1790 if (vap->va_type == VREG || vap->va_type == VSOCK) {
91447636
A
1791
1792 if (!error)
1793 error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context);
1794
1795 if (!error && !VATTR_ALL_SUPPORTED(vap))
1796 /*
1797 * If some of the requested attributes weren't handled by the VNOP,
1798 * use our fallback code.
1799 */
1800 error = vnode_setattr_fallback(vp, vap, &context);
1801
1802 if (xacl != NULL)
1803 kauth_acl_free(xacl);
1804
1c79356b 1805 if (!error) {
1c79356b
A
1806 if (exclusive_flag) {
1807 exclusive_flag = 0;
91447636
A
1808 VATTR_INIT(vap);
1809 bcopy(cverf, (caddr_t)&vap->va_access_time,
1c79356b 1810 NFSX_V3CREATEVERF);
91447636
A
1811 VATTR_SET_ACTIVE(vap, va_access_time);
1812 // skip authorization, as this is an
1813 // NFS internal implementation detail.
1814 error = vnode_setattr(vp, vap, &context);
1c79356b
A
1815 }
1816 }
91447636 1817
1c79356b
A
1818 } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
1819 vap->va_type == VFIFO) {
91447636
A
1820 if (vap->va_type == VCHR && rdev == (int)0xffffffff)
1821 VATTR_SET(vap, va_type, VFIFO);
1c79356b 1822 if (vap->va_type != VFIFO &&
91447636 1823 (error = suser(nfsd->nd_cr, (u_short *)0))) {
1c79356b 1824 nfsm_reply(0);
1c79356b 1825 } else
91447636
A
1826 VATTR_SET(vap, va_rdev, (dev_t)rdev);
1827
1828 error = VNOP_MKNOD(dvp, &vp, &nd.ni_cnd, vap, &context);
1829
1830 if (xacl != NULL)
1831 kauth_acl_free(xacl);
1832
1833 if (error) {
1c79356b
A
1834 nfsm_reply(0);
1835 }
91447636
A
1836 if (vp) {
1837 vnode_recycle(vp);
1838 vnode_put(vp);
1839 vp = NULL;
1840 }
1c79356b 1841 nd.ni_cnd.cn_nameiop = LOOKUP;
91447636
A
1842 nd.ni_cnd.cn_flags &= ~LOCKPARENT;
1843 nd.ni_cnd.cn_context = &context;
1844 nd.ni_startdir = dvp;
1845 nd.ni_usedvp = dvp;
1846 error = lookup(&nd);
1847 if (!error) {
1848 if (nd.ni_cnd.cn_flags & ISSYMLINK)
1849 error = EINVAL;
743b1565 1850 vp = nd.ni_vp;
1c79356b 1851 }
91447636 1852 if (error)
1c79356b 1853 nfsm_reply(0);
1c79356b 1854 } else {
1c79356b
A
1855 error = ENXIO;
1856 }
91447636
A
1857 /*
1858 * nameidone has to happen before we vnode_put(dvp)
1859 * since it may need to release the fs_nodelock on the dvp
1860 */
1861 nameidone(&nd);
1862 nd.ni_cnd.cn_nameiop = 0;
1863
1864 vnode_put(dvp);
1c79356b 1865 } else {
91447636
A
1866 /*
1867 * nameidone has to happen before we vnode_put(dvp)
1868 * since it may need to release the fs_nodelock on the dvp
1869 */
1870 nameidone(&nd);
1871 nd.ni_cnd.cn_nameiop = 0;
1872
1873 vnode_put(dvp);
1874
1875 if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
1876 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA,
1877 &context, nxo, 0);
1c79356b 1878 if (!error) {
91447636
A
1879 tempsize = vap->va_data_size;
1880 VATTR_INIT(vap);
1881 VATTR_SET(vap, va_data_size, tempsize);
1882 error = vnode_setattr(vp, vap, &context);
1c79356b 1883 }
1c79356b
A
1884 }
1885 }
1886 if (!error) {
91447636
A
1887 error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
1888 if (!error) {
1889 nfsm_srv_vattr_init(&postat, v3);
1890 error = vnode_getattr(vp, &postat, &context);
1891 }
1c79356b 1892 }
91447636
A
1893 if (vp)
1894 vnode_put(vp);
1895
1c79356b
A
1896 if (v3) {
1897 if (exclusive_flag && !error &&
91447636 1898 bcmp(cverf, (caddr_t)&postat.va_access_time, NFSX_V3CREATEVERF))
1c79356b 1899 error = EEXIST;
91447636
A
1900 nfsm_srv_vattr_init(&diraft, v3);
1901 diraft_ret = vnode_getattr(dirp, &diraft, &context);
1902 vnode_put(dirp);
1903 dirp = NULL;
1c79356b 1904 }
91447636
A
1905 nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3));
1906
1c79356b
A
1907 if (v3) {
1908 if (!error) {
91447636
A
1909 nfsm_srvpostop_fh(&nfh);
1910 nfsm_srvpostop_attr(0, &postat);
1c79356b
A
1911 }
1912 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
1913 } else {
91447636 1914 nfsm_srvfhtom(&nfh, v3);
1c79356b 1915 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
91447636 1916 nfsm_srvfillattr(&postat, fp);
1c79356b 1917 }
55e303ae 1918 return (0);
1c79356b 1919nfsmout:
1c79356b 1920 if (nd.ni_cnd.cn_nameiop) {
91447636
A
1921 /*
1922 * nameidone has to happen before we vnode_put(dvp)
1923 * since it may need to release the fs_nodelock on the dvp
1924 */
1925 nameidone(&nd);
1926
1927 if (vp)
1928 vnode_put(vp);
1929 vnode_put(dvp);
1930 }
1931 if (dirp)
1932 vnode_put(dirp);
1c79356b
A
1933 return (error);
1934}
1935
1936/*
1937 * nfs v3 mknod service
1938 */
1939int
1940nfsrv_mknod(nfsd, slp, procp, mrq)
1941 struct nfsrv_descript *nfsd;
1942 struct nfssvc_sock *slp;
91447636
A
1943 proc_t procp;
1944 mbuf_t *mrq;
1c79356b 1945{
91447636
A
1946 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
1947 mbuf_t nam = nfsd->nd_nam;
1c79356b 1948 caddr_t dpos = nfsd->nd_dpos;
91447636
A
1949 struct vnode_attr dirfor, diraft, postat;
1950 struct vnode_attr va;
1951 struct vnode_attr *vap = &va;
1952 u_long *tl;
1c79356b 1953 struct nameidata nd;
91447636 1954 long t1;
1c79356b 1955 caddr_t bpos;
91447636 1956 int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
1957 u_long major, minor;
1958 enum vtype vtyp;
1959 char *cp2;
91447636
A
1960 mbuf_t mb, mb2, mreq;
1961 vnode_t vp, dvp, dirp = NULL;
1962 struct nfs_filehandle nfh;
1963 struct nfs_export *nx;
1964 struct nfs_export_options *nxo;
1965 struct vfs_context hacked_context; /* XXX should we have this? */
1966 struct vfs_context context;
1967 uid_t saved_uid;
1968 kauth_acl_t xacl = NULL;
1969
1970 context.vc_proc = procp;
1971 context.vc_ucred = nfsd->nd_cr;
91447636
A
1972
1973 /*
1974 * Save the original credential UID in case they are
1975 * mapped and we need to map the IDs in the attributes.
1976 */
1977 saved_uid = kauth_cred_getuid(nfsd->nd_cr);
1c79356b 1978
91447636 1979 vp = dvp = NULL;
1c79356b 1980 nd.ni_cnd.cn_nameiop = 0;
91447636
A
1981 nfsm_srvmtofh(&nfh);
1982 nfsm_srvnamesiz(len, 1);
1983
1c79356b 1984 nd.ni_cnd.cn_nameiop = CREATE;
91447636
A
1985 nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
1986 error = nfsm_path_mbuftond(&md, &dpos, 1, FALSE, &len, &nd);
1987 if (!error)
1988 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1989 if (dirp) {
1990 nfsm_srv_pre_vattr_init(&dirfor, 1);
1991 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
1992 }
1c79356b 1993 if (error) {
91447636 1994 nd.ni_cnd.cn_nameiop = 0;
1c79356b
A
1995 nfsm_reply(NFSX_WCCDATA(1));
1996 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
1997 if (dirp)
91447636 1998 vnode_put(dirp);
1c79356b
A
1999 return (0);
2000 }
91447636
A
2001 dvp = nd.ni_dvp;
2002 vp = nd.ni_vp;
2003
1c79356b
A
2004 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
2005 vtyp = nfsv3tov_type(*tl);
2006 if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) {
1c79356b 2007 error = NFSERR_BADTYPE;
1c79356b
A
2008 goto out;
2009 }
91447636 2010 VATTR_INIT(vap);
1c79356b 2011 nfsm_srvsattr(vap);
91447636 2012
1c79356b
A
2013 if (vtyp == VCHR || vtyp == VBLK) {
2014 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
2015 major = fxdr_unsigned(u_long, *tl++);
2016 minor = fxdr_unsigned(u_long, *tl);
91447636 2017 VATTR_SET(vap, va_rdev, makedev(major, minor));
1c79356b
A
2018 }
2019
2020 /*
91447636 2021 * If it doesn't exist, create it.
1c79356b 2022 */
91447636 2023 if (vp) {
1c79356b 2024 error = EEXIST;
1c79356b
A
2025 goto out;
2026 }
91447636
A
2027 VATTR_SET(vap, va_type, vtyp);
2028
2029 /*
2030 * If the credentials were mapped, we should
2031 * map the same values in the attributes.
2032 */
2033 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
2034 int ismember;
2035 VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
2036 if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
2037 VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
2038 }
2039
2040 /* authorize before creating */
2041 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
2042
2043 /* construct ACL and handle inheritance */
2044 if (!error) {
2045 error = kauth_acl_inherit(dvp,
2046 NULL,
2047 &xacl,
2048 0 /* !isdir */,
2049 &context);
2050
2051 if (!error && xacl != NULL)
2052 VATTR_SET(vap, va_acl, xacl);
2053 }
2054 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2055 VATTR_CLEAR_ACTIVE(vap, va_access_time);
2056
2057 /* validate new-file security information */
2058 if (!error) {
2059 error = vnode_authattr_new(dvp, vap, 0, &context);
2060 if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
2061 /*
2062 * Most NFS servers just ignore the UID/GID attributes, so we
2063 * try ignoring them if that'll help the request succeed.
2064 */
2065 VATTR_CLEAR_ACTIVE(vap, va_uid);
2066 VATTR_CLEAR_ACTIVE(vap, va_gid);
2067 error = vnode_authattr_new(dvp, vap, 0, &context);
2068 }
2069 }
2070
2071 if (vtyp == VSOCK) {
2072 error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context);
2073
2074 if (!error && !VATTR_ALL_SUPPORTED(vap))
2075 /*
2076 * If some of the requested attributes weren't handled by the VNOP,
2077 * use our fallback code.
2078 */
2079 error = vnode_setattr_fallback(vp, vap, &context);
2080 } else {
2081 if (vtyp != VFIFO && (error = suser(nfsd->nd_cr, (u_short *)0))) {
2082 goto out1;
2083 }
2084 if ((error = VNOP_MKNOD(dvp, &vp, &nd.ni_cnd, vap, &context))) {
2085 goto out1;
2086 }
2087 if (vp) {
2088 vnode_recycle(vp);
2089 vnode_put(vp);
2090 vp = NULL;
1c79356b 2091 }
0c530ab8
A
2092 hacked_context.vc_proc = procp;
2093 hacked_context.vc_ucred = kauth_cred_proc_ref(procp);
2094
1c79356b 2095 nd.ni_cnd.cn_nameiop = LOOKUP;
91447636
A
2096 nd.ni_cnd.cn_flags &= ~LOCKPARENT;
2097 nd.ni_cnd.cn_context = &hacked_context;
2098 nd.ni_startdir = dvp;
2099 nd.ni_usedvp = dvp;
1c79356b 2100 error = lookup(&nd);
91447636
A
2101 if (!error) {
2102 vp = nd.ni_vp;
2103 if (nd.ni_cnd.cn_flags & ISSYMLINK)
2104 error = EINVAL;
1c79356b 2105 }
0c530ab8 2106 kauth_cred_unref(&hacked_context.vc_ucred);
1c79356b 2107 }
91447636
A
2108out1:
2109 if (xacl != NULL)
2110 kauth_acl_free(xacl);
1c79356b 2111out:
91447636
A
2112 /*
2113 * nameidone has to happen before we vnode_put(dvp)
2114 * since it may need to release the fs_nodelock on the dvp
2115 */
2116 nameidone(&nd);
2117 nd.ni_cnd.cn_nameiop = 0;
2118
2119 vnode_put(dvp);
2120
1c79356b 2121 if (!error) {
91447636
A
2122 error = nfsrv_vptofh(nx, 0, NULL, vp, &context, &nfh);
2123 if (!error) {
2124 nfsm_srv_vattr_init(&postat, 1);
2125 error = vnode_getattr(vp, &postat, &context);
2126 }
1c79356b 2127 }
91447636
A
2128 if (vp)
2129 vnode_put(vp);
2130
2131 nfsm_srv_vattr_init(&diraft, 1);
2132 diraft_ret = vnode_getattr(dirp, &diraft, &context);
2133 vnode_put(dirp);
2134 dirp = NULL;
2135
2136 nfsm_reply(NFSX_SRVFH(1, &nfh) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1));
1c79356b 2137 if (!error) {
91447636
A
2138 nfsm_srvpostop_fh(&nfh);
2139 nfsm_srvpostop_attr(0, &postat);
1c79356b
A
2140 }
2141 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
2142 return (0);
2143nfsmout:
1c79356b 2144 if (nd.ni_cnd.cn_nameiop) {
91447636
A
2145 /*
2146 * nameidone has to happen before we vnode_put(dvp)
2147 * since it may need to release the fs_nodelock on the dvp
2148 */
2149 nameidone(&nd);
2150
2151 if (vp)
2152 vnode_put(vp);
2153 vnode_put(dvp);
2154 }
2155 if (dirp)
2156 vnode_put(dirp);
1c79356b
A
2157 return (error);
2158}
2159
2160/*
2161 * nfs remove service
2162 */
2163int
2164nfsrv_remove(nfsd, slp, procp, mrq)
2165 struct nfsrv_descript *nfsd;
2166 struct nfssvc_sock *slp;
91447636
A
2167 proc_t procp;
2168 mbuf_t *mrq;
1c79356b 2169{
91447636
A
2170 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
2171 mbuf_t nam = nfsd->nd_nam;
1c79356b 2172 caddr_t dpos = nfsd->nd_dpos;
1c79356b 2173 struct nameidata nd;
91447636
A
2174 u_long *tl;
2175 long t1;
1c79356b 2176 caddr_t bpos;
91447636 2177 int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
2178 int v3 = (nfsd->nd_flag & ND_NFSV3);
2179 char *cp2;
91447636
A
2180 mbuf_t mb, mreq;
2181 vnode_t vp, dvp, dirp = NULL;
2182 struct vnode_attr dirfor, diraft;
2183 struct nfs_filehandle nfh;
2184 struct nfs_export *nx;
2185 struct nfs_export_options *nxo;
2186 struct vfs_context context;
2187
2188 context.vc_proc = procp;
2189 context.vc_ucred = nfsd->nd_cr;
2190
2191 dvp = vp = NULL;
2192 nfsm_srvmtofh(&nfh);
2193 nfsm_srvnamesiz(len, v3);
1c79356b 2194
1c79356b
A
2195 nd.ni_cnd.cn_nameiop = DELETE;
2196 nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
91447636
A
2197 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
2198 if (!error)
2199 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 2200 if (dirp) {
91447636
A
2201 if (v3) {
2202 nfsm_srv_pre_vattr_init(&dirfor, v3);
2203 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
2204 } else {
2205 vnode_put(dirp);
2206 dirp = NULL;
2207 }
1c79356b
A
2208 }
2209 if (!error) {
91447636 2210 dvp = nd.ni_dvp;
1c79356b 2211 vp = nd.ni_vp;
91447636
A
2212
2213 if (vnode_vtype(vp) == VDIR)
1c79356b 2214 error = EPERM; /* POSIX */
91447636
A
2215 else if (vnode_isvroot(vp))
2216 /*
2217 * The root of a mounted filesystem cannot be deleted.
2218 */
1c79356b 2219 error = EBUSY;
91447636
A
2220 else
2221 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, &context, nxo, 0);
1c79356b 2222
91447636
A
2223 if (!error)
2224 error = VNOP_REMOVE(dvp, vp, &nd.ni_cnd, 0, &context);
1c79356b 2225
91447636
A
2226 /*
2227 * nameidone has to happen before we vnode_put(dvp)
2228 * since it may need to release the fs_nodelock on the dvp
2229 */
2230 nameidone(&nd);
2231
2232 vnode_put(vp);
2233 vnode_put(dvp);
1c79356b 2234 }
91447636
A
2235 if (dirp) {
2236 nfsm_srv_vattr_init(&diraft, v3);
2237 diraft_ret = vnode_getattr(dirp, &diraft, &context);
2238 vnode_put(dirp);
1c79356b
A
2239 }
2240 nfsm_reply(NFSX_WCCDATA(v3));
2241 if (v3) {
2242 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
2243 return (0);
2244 }
91447636
A
2245nfsmout:
2246 return (error);
1c79356b
A
2247}
2248
2249/*
2250 * nfs rename service
2251 */
2252int
2253nfsrv_rename(nfsd, slp, procp, mrq)
2254 struct nfsrv_descript *nfsd;
2255 struct nfssvc_sock *slp;
91447636
A
2256 proc_t procp;
2257 mbuf_t *mrq;
1c79356b 2258{
91447636
A
2259 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
2260 mbuf_t nam = nfsd->nd_nam;
1c79356b 2261 caddr_t dpos = nfsd->nd_dpos;
91447636
A
2262 kauth_cred_t saved_cred = NULL;
2263 u_long *tl;
2264 long t1;
1c79356b 2265 caddr_t bpos;
91447636
A
2266 int error = 0, fromlen, tolen;
2267 int fdirfor_ret = 1, fdiraft_ret = 1;
1c79356b
A
2268 int tdirfor_ret = 1, tdiraft_ret = 1;
2269 int v3 = (nfsd->nd_flag & ND_NFSV3);
91447636
A
2270 char *cp2, *frompath = NULL, *topath = NULL;
2271 mbuf_t mb, mreq;
1c79356b 2272 struct nameidata fromnd, tond;
91447636
A
2273 vnode_t fvp, tvp, tdvp, fdvp, fdirp = NULL;
2274 vnode_t tdirp = NULL;
2275 struct vnode_attr fdirfor, fdiraft, tdirfor, tdiraft;
2276 struct nfs_filehandle fnfh, tnfh;
2277 struct nfs_export *fnx, *tnx;
2278 struct nfs_export_options *fnxo, *tnxo;
2279 enum vtype fvtype, tvtype;
2280 int holding_mntlock;
2281 mount_t locked_mp;
2282 struct vfs_context context;
2283
2284 context.vc_proc = procp;
2285 context.vc_ucred = nfsd->nd_cr;
1c79356b
A
2286
2287#ifndef nolint
91447636 2288 fvp = (vnode_t)0;
1c79356b 2289#endif
91447636
A
2290
2291 /*
2292 * these need to be set before
2293 * calling any nfsm_xxxx macros
2294 * since they may take us out
2295 * through the error path
2296 */
2297 holding_mntlock = 0;
2298 fvp = tvp = NULL;
2299 fdvp = tdvp = NULL;
2300 locked_mp = NULL;
2301
2302 nfsm_srvmtofh(&fnfh);
2303 nfsm_srvnamesiz(fromlen, v3);
2304 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &fromlen, &fromnd);
2305 if (error) {
2306 nfsm_reply(0);
2307 return (0);
2308 }
2309 frompath = fromnd.ni_cnd.cn_pnbuf;
2310 nfsm_srvmtofh(&tnfh);
2311 nfsm_strsiz(tolen, NFS_MAXNAMLEN, v3);
2312 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &tolen, &tond);
2313 if (error) {
2314 nfsm_reply(0);
2315 FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
2316 return (0);
2317 }
2318 topath = tond.ni_cnd.cn_pnbuf;
2319
1c79356b
A
2320 /*
2321 * Remember our original uid so that we can reset cr_uid before
2322 * the second nfs_namei() call, in case it is remapped.
2323 */
91447636
A
2324 saved_cred = nfsd->nd_cr;
2325 kauth_cred_ref(saved_cred);
2326retry:
1c79356b 2327 fromnd.ni_cnd.cn_nameiop = DELETE;
91447636
A
2328 fromnd.ni_cnd.cn_flags = WANTPARENT;
2329
2330 fromnd.ni_cnd.cn_pnbuf = frompath;
2331 frompath = NULL;
2332 fromnd.ni_cnd.cn_pnlen = MAXPATHLEN;
2333 fromnd.ni_cnd.cn_flags |= HASBUF;
2334
2335 error = nfs_namei(nfsd, &context, &fromnd, &fnfh, nam, FALSE, &fdirp, &fnx, &fnxo);
2336 if (error)
2337 goto out;
2338 fdvp = fromnd.ni_dvp;
2339 fvp = fromnd.ni_vp;
2340
1c79356b 2341 if (fdirp) {
91447636
A
2342 if (v3) {
2343 nfsm_srv_pre_vattr_init(&fdirfor, v3);
2344 fdirfor_ret = vnode_getattr(fdirp, &fdirfor, &context);
2345 } else {
2346 vnode_put(fdirp);
2347 fdirp = NULL;
1c79356b
A
2348 }
2349 }
91447636
A
2350 fvtype = vnode_vtype(fvp);
2351
2352 /* reset credential if it was remapped */
2353 if (nfsd->nd_cr != saved_cred) {
0c530ab8
A
2354 kauth_cred_ref(saved_cred);
2355 kauth_cred_unref(&nfsd->nd_cr);
91447636 2356 nfsd->nd_cr = saved_cred;
1c79356b 2357 }
91447636 2358
1c79356b 2359 tond.ni_cnd.cn_nameiop = RENAME;
91447636
A
2360 tond.ni_cnd.cn_flags = WANTPARENT;
2361
2362 tond.ni_cnd.cn_pnbuf = topath;
2363 topath = NULL;
2364 tond.ni_cnd.cn_pnlen = MAXPATHLEN;
2365 tond.ni_cnd.cn_flags |= HASBUF;
2366
2367 if (fvtype == VDIR)
2368 tond.ni_cnd.cn_flags |= WILLBEDIR;
2369
2370 error = nfs_namei(nfsd, &context, &tond, &tnfh, nam, FALSE, &tdirp, &tnx, &tnxo);
1c79356b 2371 if (error) {
91447636
A
2372 /*
2373 * Translate error code for rename("dir1", "dir2/.").
2374 */
2375 if (error == EISDIR && fvtype == VDIR) {
2376 if (v3)
2377 error = EINVAL;
2378 else
2379 error = ENOTEMPTY;
2380 }
2381 goto out;
1c79356b
A
2382 }
2383 tdvp = tond.ni_dvp;
91447636
A
2384 tvp = tond.ni_vp;
2385
2386 if (tdirp) {
2387 if (v3) {
2388 nfsm_srv_pre_vattr_init(&tdirfor, v3);
2389 tdirfor_ret = vnode_getattr(tdirp, &tdirfor, &context);
2390 } else {
2391 vnode_put(tdirp);
2392 tdirp = NULL;
2393 }
2394 }
2395
1c79356b 2396 if (tvp != NULL) {
91447636
A
2397 tvtype = vnode_vtype(tvp);
2398
2399 if (fvtype == VDIR && tvtype != VDIR) {
1c79356b
A
2400 if (v3)
2401 error = EEXIST;
2402 else
2403 error = EISDIR;
2404 goto out;
91447636 2405 } else if (fvtype != VDIR && tvtype == VDIR) {
1c79356b
A
2406 if (v3)
2407 error = EEXIST;
2408 else
2409 error = ENOTDIR;
2410 goto out;
2411 }
91447636 2412 if (tvtype == VDIR && vnode_mountedhere(tvp)) {
1c79356b
A
2413 if (v3)
2414 error = EXDEV;
2415 else
2416 error = ENOTEMPTY;
2417 goto out;
2418 }
2419 }
91447636
A
2420 if (fvp == tdvp) {
2421 if (v3)
2422 error = EINVAL;
2423 else
2424 error = ENOTEMPTY;
2425 goto out;
2426 }
2427
2428 /*
2429 * Authorization.
2430 *
2431 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2432 * the node is moving between directories and we need rights to remove from the
2433 * old and add to the new.
2434 *
2435 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2436 *
2437 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2438 * implement the deferred-inherit bit.
2439 */
2440 {
2441 int moving = 0;
2442
2443 error = 0;
2444 if ((tvp != NULL) && vnode_isdir(tvp)) {
2445 if (tvp != fdvp)
2446 moving = 1;
2447 } else if (tdvp != fdvp) {
2448 moving = 1;
2449 }
2450 if (moving) {
2451 /* moving out of fdvp, must have delete rights */
2452 if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, &context, fnxo, 0)) != 0)
2453 goto auth_exit;
2454 /* moving into tdvp or tvp, must have rights to add */
2455 if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp,
2456 NULL,
2457 vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2458 &context, tnxo, 0)) != 0)
2459 goto auth_exit;
2460 } else {
2461 /* node staying in same directory, must be allowed to add new name */
2462 if ((error = nfsrv_authorize(fdvp, NULL,
2463 vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2464 &context, fnxo, 0)) != 0)
2465 goto auth_exit;
2466 }
2467 /* overwriting tvp */
2468 if ((tvp != NULL) && !vnode_isdir(tvp) &&
2469 ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, &context, tnxo, 0)) != 0))
2470 goto auth_exit;
2471
2472 /* XXX more checks? */
2473
2474auth_exit:
2475 /* authorization denied */
2476 if (error != 0)
2477 goto out;
2478 }
2479
2480 if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
2481 (tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) {
1c79356b
A
2482 if (v3)
2483 error = EXDEV;
2484 else
2485 error = ENOTEMPTY;
2486 goto out;
2487 }
91447636
A
2488 /*
2489 * The following edge case is caught here:
2490 * (to cannot be a descendent of from)
2491 *
2492 * o fdvp
2493 * /
2494 * /
2495 * o fvp
2496 * \
2497 * \
2498 * o tdvp
2499 * /
2500 * /
2501 * o tvp
2502 */
2503 if (tdvp->v_parent == fvp) {
1c79356b
A
2504 if (v3)
2505 error = EXDEV;
2506 else
2507 error = ENOTEMPTY;
2508 goto out;
2509 }
91447636 2510 if (fvtype == VDIR && vnode_mountedhere(fvp)) {
1c79356b 2511 if (v3)
91447636 2512 error = EXDEV;
1c79356b
A
2513 else
2514 error = ENOTEMPTY;
91447636
A
2515 goto out;
2516 }
1c79356b
A
2517 /*
2518 * If source is the same as the destination (that is the
91447636
A
2519 * same vnode) then there is nothing to do...
2520 * EXCEPT if the underlying file system supports case
2521 * insensitivity and is case preserving. In this case
2522 * the file system needs to handle the special case of
2523 * getting the same vnode as target (fvp) and source (tvp).
2524 *
2525 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
2526 * and _PC_CASE_PRESERVING can have this exception, and they need to
2527 * handle the special case of getting the same vnode as target and
2528 * source. NOTE: Then the target is unlocked going into vnop_rename,
2529 * so not to cause locking problems. There is a single reference on tvp.
2530 *
2531 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
2532 * that correct behaviour then is just to remove the source (link)
1c79356b 2533 */
91447636
A
2534 if ((fvp == tvp) && (fdvp == tdvp)) {
2535 if (fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
2536 !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
2537 fromnd.ni_cnd.cn_namelen)) {
2538 goto out;
2539 }
2540 }
2541
2542 if (holding_mntlock && vnode_mount(fvp) != locked_mp) {
2543 /*
2544 * we're holding a reference and lock
2545 * on locked_mp, but it no longer matches
2546 * what we want to do... so drop our hold
2547 */
2548 mount_unlock_renames(locked_mp);
2549 mount_drop(locked_mp, 0);
2550 holding_mntlock = 0;
2551 }
2552 if (tdvp != fdvp && fvtype == VDIR) {
2553 /*
2554 * serialize renames that re-shape
2555 * the tree... if holding_mntlock is
2556 * set, then we're ready to go...
2557 * otherwise we
2558 * first need to drop the iocounts
2559 * we picked up, second take the
2560 * lock to serialize the access,
2561 * then finally start the lookup
2562 * process over with the lock held
2563 */
2564 if (!holding_mntlock) {
2565 /*
2566 * need to grab a reference on
2567 * the mount point before we
2568 * drop all the iocounts... once
2569 * the iocounts are gone, the mount
2570 * could follow
2571 */
2572 locked_mp = vnode_mount(fvp);
2573 mount_ref(locked_mp, 0);
2574
2575 /* make a copy of to path to pass to nfs_namei() again */
2576 MALLOC_ZONE(topath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
2577 if (topath)
2578 bcopy(tond.ni_cnd.cn_pnbuf, topath, tolen + 1);
2579
2580 /*
2581 * nameidone has to happen before we vnode_put(tdvp)
2582 * since it may need to release the fs_nodelock on the tdvp
2583 */
2584 nameidone(&tond);
2585
2586 if (tvp)
2587 vnode_put(tvp);
2588 vnode_put(tdvp);
2589
2590 /* make a copy of from path to pass to nfs_namei() again */
2591 MALLOC_ZONE(frompath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
2592 if (frompath)
2593 bcopy(fromnd.ni_cnd.cn_pnbuf, frompath, fromlen + 1);
2594
2595 /*
2596 * nameidone has to happen before we vnode_put(fdvp)
2597 * since it may need to release the fs_nodelock on the fdvp
2598 */
2599 nameidone(&fromnd);
2600
2601 vnode_put(fvp);
2602 vnode_put(fdvp);
2603
2604 if (fdirp) {
2605 vnode_put(fdirp);
2606 fdirp = NULL;
2607 }
2608 if (tdirp) {
2609 vnode_put(tdirp);
2610 tdirp = NULL;
2611 }
2612 mount_lock_renames(locked_mp);
2613 holding_mntlock = 1;
2614
2615 fvp = tvp = NULL;
2616 fdvp = tdvp = NULL;
2617
2618 fdirfor_ret = tdirfor_ret = 1;
2619
2620 if (!topath || !frompath) {
2621 /* we couldn't allocate a path, so bail */
2622 error = ENOMEM;
2623 goto out;
2624 }
2625
2626 goto retry;
2627 }
1c79356b 2628 } else {
91447636
A
2629 /*
2630 * when we dropped the iocounts to take
2631 * the lock, we allowed the identity of
2632 * the various vnodes to change... if they did,
2633 * we may no longer be dealing with a rename
2634 * that reshapes the tree... once we're holding
2635 * the iocounts, the vnodes can't change type
2636 * so we're free to drop the lock at this point
2637 * and continue on
2638 */
2639 if (holding_mntlock) {
2640 mount_unlock_renames(locked_mp);
2641 mount_drop(locked_mp, 0);
2642 holding_mntlock = 0;
2643 }
2644 }
2645
2646 // save these off so we can later verify that fvp is the same
2647 char *oname;
2648 vnode_t oparent;
2649 oname = fvp->v_name;
2650 oparent = fvp->v_parent;
2651
2652 error = VNOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
2653 tond.ni_dvp, tond.ni_vp, &tond.ni_cnd, &context);
2654 /*
2655 * fix up name & parent pointers. note that we first
2656 * check that fvp has the same name/parent pointers it
2657 * had before the rename call... this is a 'weak' check
2658 * at best...
2659 */
2660 if (oname == fvp->v_name && oparent == fvp->v_parent) {
2661 int update_flags;
2662 update_flags = VNODE_UPDATE_NAME;
2663 if (fdvp != tdvp)
2664 update_flags |= VNODE_UPDATE_PARENT;
2665 vnode_update_identity(fvp, tdvp, tond.ni_cnd.cn_nameptr, tond.ni_cnd.cn_namelen, tond.ni_cnd.cn_hash, update_flags);
2666 }
2667out:
2668 if (holding_mntlock) {
2669 mount_unlock_renames(locked_mp);
2670 mount_drop(locked_mp, 0);
2671 holding_mntlock = 0;
2672 }
2673 if (tdvp) {
2674 /*
2675 * nameidone has to happen before we vnode_put(tdvp)
2676 * since it may need to release the fs_nodelock on the tdvp
2677 */
2678 nameidone(&tond);
1c79356b 2679 if (tvp)
91447636
A
2680 vnode_put(tvp);
2681 vnode_put(tdvp);
2682
2683 tdvp = NULL;
2684 }
2685 if (fdvp) {
2686 /*
2687 * nameidone has to happen before we vnode_put(fdvp)
2688 * since it may need to release the fs_nodelock on the fdvp
2689 */
2690 nameidone(&fromnd);
2691
2692 if (fvp)
2693 vnode_put(fvp);
2694 vnode_put(fdvp);
2695
2696 fdvp = NULL;
1c79356b 2697 }
1c79356b 2698 if (fdirp) {
91447636
A
2699 nfsm_srv_vattr_init(&fdiraft, v3);
2700 fdiraft_ret = vnode_getattr(fdirp, &fdiraft, &context);
2701 vnode_put(fdirp);
2702 fdirp = NULL;
1c79356b
A
2703 }
2704 if (tdirp) {
91447636
A
2705 nfsm_srv_vattr_init(&tdiraft, v3);
2706 tdiraft_ret = vnode_getattr(tdirp, &tdiraft, &context);
2707 vnode_put(tdirp);
2708 tdirp = NULL;
1c79356b 2709 }
1c79356b
A
2710 nfsm_reply(2 * NFSX_WCCDATA(v3));
2711 if (v3) {
2712 nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
2713 nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
2714 }
91447636
A
2715 if (frompath)
2716 FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
2717 if (topath)
2718 FREE_ZONE(topath, MAXPATHLEN, M_NAMEI);
2719 if (saved_cred)
0c530ab8 2720 kauth_cred_unref(&saved_cred);
1c79356b
A
2721 return (0);
2722
2723nfsmout:
91447636
A
2724 if (holding_mntlock) {
2725 mount_unlock_renames(locked_mp);
2726 mount_drop(locked_mp, 0);
2727 }
2728 if (tdvp) {
2729 /*
2730 * nameidone has to happen before we vnode_put(tdvp)
2731 * since it may need to release the fs_nodelock on the tdvp
2732 */
2733 nameidone(&tond);
2734
2735 if (tvp)
2736 vnode_put(tvp);
2737 vnode_put(tdvp);
2738 }
2739 if (fdvp) {
2740 /*
2741 * nameidone has to happen before we vnode_put(fdvp)
2742 * since it may need to release the fs_nodelock on the fdvp
2743 */
2744 nameidone(&fromnd);
2745
2746 if (fvp)
2747 vnode_put(fvp);
2748 vnode_put(fdvp);
2749 }
1c79356b 2750 if (fdirp)
91447636 2751 vnode_put(fdirp);
1c79356b 2752 if (tdirp)
91447636
A
2753 vnode_put(tdirp);
2754 if (frompath)
2755 FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
2756 if (topath)
2757 FREE_ZONE(topath, MAXPATHLEN, M_NAMEI);
2758 if (saved_cred)
0c530ab8 2759 kauth_cred_unref(&saved_cred);
1c79356b
A
2760 return (error);
2761}
2762
2763/*
2764 * nfs link service
2765 */
2766int
2767nfsrv_link(nfsd, slp, procp, mrq)
2768 struct nfsrv_descript *nfsd;
2769 struct nfssvc_sock *slp;
91447636
A
2770 proc_t procp;
2771 mbuf_t *mrq;
1c79356b 2772{
91447636
A
2773 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
2774 mbuf_t nam = nfsd->nd_nam;
1c79356b 2775 caddr_t dpos = nfsd->nd_dpos;
1c79356b 2776 struct nameidata nd;
91447636
A
2777 u_long *tl;
2778 long t1;
1c79356b 2779 caddr_t bpos;
91447636 2780 int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
2781 int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3);
2782 char *cp2;
91447636
A
2783 mbuf_t mb, mreq;
2784 vnode_t vp, xp, dvp, dirp = NULL;
2785 struct vnode_attr dirfor, diraft, at;
2786 struct nfs_filehandle nfh, dnfh;
2787 struct nfs_export *nx;
2788 struct nfs_export_options *nxo;
2789 struct vfs_context context;
2790
2791 vp = xp = dvp = NULL;
2792 nfsm_srvmtofh(&nfh);
2793 nfsm_srvmtofh(&dnfh);
2794 nfsm_srvnamesiz(len, v3);
2795 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b
A
2796 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
2797 nfsm_srvpostop_attr(getret, &at);
2798 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
2799 return (0);
2800 }
91447636
A
2801 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
2802 vnode_put(vp);
2803 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
2804 nfsm_srvpostop_attr(getret, &at);
2805 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
2806 return (0);
2807 }
2808
2809 /* we're not allowed to link to directories... */
2810 if (vnode_vtype(vp) == VDIR) {
1c79356b
A
2811 error = EPERM; /* POSIX */
2812 goto out1;
2813 }
91447636
A
2814
2815 context.vc_proc = procp;
2816 context.vc_ucred = nfsd->nd_cr;
2817
2818 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
2819 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, &context, nxo, 0)) != 0)
2820 goto out1;
2821
1c79356b
A
2822 nd.ni_cnd.cn_nameiop = CREATE;
2823 nd.ni_cnd.cn_flags = LOCKPARENT;
91447636
A
2824 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
2825 if (!error)
2826 error = nfs_namei(nfsd, &context, &nd, &dnfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 2827 if (dirp) {
91447636
A
2828 if (v3) {
2829 nfsm_srv_pre_vattr_init(&dirfor, v3);
2830 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
2831 } else {
2832 vnode_put(dirp);
2833 dirp = NULL;
1c79356b
A
2834 }
2835 }
2836 if (error)
2837 goto out1;
91447636 2838 dvp = nd.ni_dvp;
1c79356b 2839 xp = nd.ni_vp;
91447636
A
2840
2841 if (xp != NULL)
1c79356b 2842 error = EEXIST;
91447636 2843 else if (vnode_mount(vp) != vnode_mount(dvp))
1c79356b 2844 error = EXDEV;
91447636
A
2845 else
2846 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
2847
2848 if (!error)
2849 error = VNOP_LINK(vp, dvp, &nd.ni_cnd, &context);
2850
2851 /*
2852 * nameidone has to happen before we vnode_put(dvp)
2853 * since it may need to release the fs_nodelock on the dvp
2854 */
2855 nameidone(&nd);
2856
2857 if (xp)
2858 vnode_put(xp);
2859 vnode_put(dvp);
1c79356b 2860out1:
91447636
A
2861 if (v3) {
2862 nfsm_srv_vattr_init(&at, v3);
2863 getret = vnode_getattr(vp, &at, &context);
2864 }
1c79356b 2865 if (dirp) {
91447636
A
2866 nfsm_srv_vattr_init(&diraft, v3);
2867 diraft_ret = vnode_getattr(dirp, &diraft, &context);
2868 vnode_put(dirp);
1c79356b 2869 }
91447636
A
2870 vnode_put(vp);
2871
1c79356b
A
2872 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
2873 if (v3) {
2874 nfsm_srvpostop_attr(getret, &at);
2875 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
2876 return (0);
2877 }
91447636
A
2878nfsmout:
2879 return (error);
1c79356b
A
2880}
2881
2882/*
2883 * nfs symbolic link service
2884 */
2885int
2886nfsrv_symlink(nfsd, slp, procp, mrq)
2887 struct nfsrv_descript *nfsd;
2888 struct nfssvc_sock *slp;
91447636
A
2889 proc_t procp;
2890 mbuf_t *mrq;
1c79356b 2891{
91447636
A
2892 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
2893 mbuf_t nam = nfsd->nd_nam;
1c79356b 2894 caddr_t dpos = nfsd->nd_dpos;
91447636 2895 struct vnode_attr dirfor, diraft, postat;
1c79356b 2896 struct nameidata nd;
91447636
A
2897 struct vnode_attr va;
2898 struct vnode_attr *vap = &va;
2899 u_long *tl;
2900 long t1;
1c79356b 2901 struct nfsv2_sattr *sp;
91447636
A
2902 char *bpos, *linkdata = NULL, *cp2;
2903 int error = 0, len, linkdatalen;
2904 int dirfor_ret = 1, diraft_ret = 1;
1c79356b 2905 int v3 = (nfsd->nd_flag & ND_NFSV3);
91447636
A
2906 mbuf_t mb, mreq, mb2;
2907 vnode_t vp, dvp, dirp = NULL;
2908 struct nfs_filehandle nfh;
2909 struct nfs_export *nx;
2910 struct nfs_export_options *nxo;
2911 uio_t auio;
2912 char uio_buf[ UIO_SIZEOF(1) ];
2913 struct vfs_context context;
2914 uid_t saved_uid;
2915
2916 context.vc_proc = procp;
2917 context.vc_ucred = nfsd->nd_cr;
2918
2919 /*
2920 * Save the original credential UID in case they are
2921 * mapped and we need to map the IDs in the attributes.
2922 */
2923 saved_uid = kauth_cred_getuid(nfsd->nd_cr);
1c79356b
A
2924
2925 nd.ni_cnd.cn_nameiop = 0;
91447636
A
2926 vp = dvp = NULL;
2927 nfsm_srvmtofh(&nfh);
2928 nfsm_srvnamesiz(len, v3);
2929
1c79356b 2930 nd.ni_cnd.cn_nameiop = CREATE;
91447636
A
2931 nd.ni_cnd.cn_flags = LOCKPARENT;
2932 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
2933 if (!error)
2934 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 2935 if (dirp) {
91447636
A
2936 if (v3) {
2937 nfsm_srv_pre_vattr_init(&dirfor, v3);
2938 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
2939 } else {
2940 vnode_put(dirp);
2941 dirp = NULL;
1c79356b
A
2942 }
2943 }
91447636
A
2944 if (error) {
2945 nd.ni_cnd.cn_nameiop = 0;
2946 goto out1;
2947 }
2948 dvp = nd.ni_dvp;
2949 vp = nd.ni_vp;
2950
2951 VATTR_INIT(vap);
1c79356b
A
2952 if (v3)
2953 nfsm_srvsattr(vap);
91447636
A
2954 nfsm_strsiz(linkdatalen, NFS_MAXPATHLEN, v3);
2955 MALLOC(linkdata, caddr_t, linkdatalen + 1, M_TEMP, M_WAITOK);
2956 if (!linkdata) {
2957 nameidone(&nd);
2958 nd.ni_cnd.cn_nameiop = 0;
2959 vnode_put(nd.ni_dvp);
2960 vnode_put(nd.ni_vp);
2961 error = ENOMEM;
2962 goto out;
2963 }
2964 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
2965 &uio_buf[0], sizeof(uio_buf));
2966 if (!auio) {
2967 nameidone(&nd);
2968 nd.ni_cnd.cn_nameiop = 0;
2969 vnode_put(nd.ni_dvp);
2970 vnode_put(nd.ni_vp);
2971 error = ENOMEM;
2972 goto out;
2973 }
2974 uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
2975 nfsm_mtouio(auio, linkdatalen);
1c79356b
A
2976 if (!v3) {
2977 nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
91447636
A
2978 VATTR_SET(vap, va_mode, fxdr_unsigned(u_short, sp->sa_mode));
2979 }
2980 *(linkdata + linkdatalen) = '\0';
2981 if (vp) {
1c79356b
A
2982 error = EEXIST;
2983 goto out;
2984 }
91447636
A
2985
2986 /*
2987 * If the credentials were mapped, we should
2988 * map the same values in the attributes.
2989 */
2990 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
2991 int ismember;
2992 VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
2993 if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
2994 VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
2995 }
2996 VATTR_SET(vap, va_type, VLNK);
cc9f6e38
A
2997 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2998 VATTR_CLEAR_ACTIVE(vap, va_access_time);
91447636
A
2999
3000 /* authorize before creating */
3001 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
3002
3003 /* validate given attributes */
3004 if (!error) {
3005 error = vnode_authattr_new(dvp, vap, 0, &context);
3006 if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
3007 /*
3008 * Most NFS servers just ignore the UID/GID attributes, so we
3009 * try ignoring them if that'll help the request succeed.
3010 */
3011 VATTR_CLEAR_ACTIVE(vap, va_uid);
3012 VATTR_CLEAR_ACTIVE(vap, va_gid);
3013 error = vnode_authattr_new(dvp, vap, 0, &context);
3014 }
3015 }
3016 if (!error)
3017 error = VNOP_SYMLINK(dvp, &vp, &nd.ni_cnd, vap, linkdata, &context);
3018
3019 if (!error && v3) {
3020 if (vp == NULL) {
3021 nd.ni_cnd.cn_nameiop = LOOKUP;
3022 nd.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3023 nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3024 nd.ni_cnd.cn_context = &context;
3025 nd.ni_startdir = dvp;
3026 nd.ni_usedvp = dvp;
3027 error = lookup(&nd);
1c79356b 3028 if (!error)
91447636
A
3029 vp = nd.ni_vp;
3030 }
3031 if (!error) {
3032 error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
3033 if (!error) {
3034 nfsm_srv_vattr_init(&postat, v3);
3035 error = vnode_getattr(vp, &postat, &context);
3036 }
1c79356b 3037 }
1c79356b
A
3038 }
3039out:
91447636
A
3040 /*
3041 * nameidone has to happen before we vnode_put(dvp)
3042 * since it may need to release the fs_nodelock on the dvp
3043 */
3044 nameidone(&nd);
3045 nd.ni_cnd.cn_nameiop = 0;
3046
3047 if (vp)
3048 vnode_put(vp);
3049 vnode_put(dvp);
3050out1:
3051 if (linkdata)
3052 FREE(linkdata, M_TEMP);
1c79356b 3053 if (dirp) {
91447636
A
3054 nfsm_srv_vattr_init(&diraft, v3);
3055 diraft_ret = vnode_getattr(dirp, &diraft, &context);
3056 vnode_put(dirp);
1c79356b 3057 }
91447636 3058 nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
1c79356b
A
3059 if (v3) {
3060 if (!error) {
91447636
A
3061 nfsm_srvpostop_fh(&nfh);
3062 nfsm_srvpostop_attr(0, &postat);
1c79356b
A
3063 }
3064 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
3065 }
3066 return (0);
3067nfsmout:
3068 if (nd.ni_cnd.cn_nameiop) {
91447636
A
3069 /*
3070 * nameidone has to happen before we vnode_put(dvp)
3071 * since it may need to release the fs_nodelock on the dvp
3072 */
3073 nameidone(&nd);
3074
3075 if (vp)
3076 vnode_put(vp);
3077 vnode_put(dvp);
1c79356b
A
3078 }
3079 if (dirp)
91447636
A
3080 vnode_put(dirp);
3081 if (linkdata)
3082 FREE(linkdata, M_TEMP);
1c79356b
A
3083 return (error);
3084}
3085
3086/*
3087 * nfs mkdir service
3088 */
3089int
3090nfsrv_mkdir(nfsd, slp, procp, mrq)
3091 struct nfsrv_descript *nfsd;
3092 struct nfssvc_sock *slp;
91447636
A
3093 proc_t procp;
3094 mbuf_t *mrq;
1c79356b 3095{
91447636
A
3096 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
3097 mbuf_t nam = nfsd->nd_nam;
1c79356b 3098 caddr_t dpos = nfsd->nd_dpos;
91447636
A
3099 struct vnode_attr dirfor, diraft, postat;
3100 struct vnode_attr va;
3101 struct vnode_attr *vap = &va;
3102 struct nfs_fattr *fp;
1c79356b 3103 struct nameidata nd;
91447636
A
3104 caddr_t cp;
3105 u_long *tl;
3106 long t1;
1c79356b 3107 caddr_t bpos;
91447636
A
3108 int error = 0, len;
3109 int dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
3110 int v3 = (nfsd->nd_flag & ND_NFSV3);
3111 char *cp2;
91447636
A
3112 mbuf_t mb, mb2, mreq;
3113 vnode_t vp, dvp, dirp = NULL;
3114 struct nfs_filehandle nfh;
3115 struct nfs_export *nx;
3116 struct nfs_export_options *nxo;
3117 struct vfs_context context;
3118 uid_t saved_uid;
3119 kauth_acl_t xacl = NULL;
3120
3121 context.vc_proc = procp;
3122 context.vc_ucred = nfsd->nd_cr;
3123
3124 /*
3125 * Save the original credential UID in case they are
3126 * mapped and we need to map the IDs in the attributes.
3127 */
3128 saved_uid = kauth_cred_getuid(nfsd->nd_cr);
3129
3130 nd.ni_cnd.cn_nameiop = 0;
3131 vp = dvp = NULL;
3132 nfsm_srvmtofh(&nfh);
3133 nfsm_srvnamesiz(len, v3);
3134
1c79356b
A
3135 nd.ni_cnd.cn_nameiop = CREATE;
3136 nd.ni_cnd.cn_flags = LOCKPARENT;
91447636
A
3137 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
3138 if (!error)
3139 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 3140 if (dirp) {
91447636
A
3141 if (v3) {
3142 nfsm_srv_pre_vattr_init(&dirfor, v3);
3143 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
3144 } else {
3145 vnode_put(dirp);
3146 dirp = NULL;
1c79356b
A
3147 }
3148 }
3149 if (error) {
91447636 3150 nd.ni_cnd.cn_nameiop = 0;
1c79356b
A
3151 nfsm_reply(NFSX_WCCDATA(v3));
3152 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
3153 if (dirp)
91447636 3154 vnode_put(dirp);
1c79356b
A
3155 return (0);
3156 }
91447636
A
3157 dvp = nd.ni_dvp;
3158 vp = nd.ni_vp;
3159
3160 VATTR_INIT(vap);
1c79356b
A
3161 if (v3) {
3162 nfsm_srvsattr(vap);
3163 } else {
3164 nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
91447636 3165 VATTR_SET(vap, va_mode, nfstov_mode(*tl++));
1c79356b 3166 }
91447636
A
3167 VATTR_SET(vap, va_type, VDIR);
3168
1c79356b 3169 if (vp != NULL) {
91447636
A
3170 /*
3171 * nameidone has to happen before we vnode_put(dvp)
3172 * since it may need to release the fs_nodelock on the dvp
3173 */
3174 nameidone(&nd);
3175
3176 vnode_put(dvp);
3177 vnode_put(vp);
1c79356b
A
3178 error = EEXIST;
3179 goto out;
3180 }
91447636
A
3181
3182 /*
3183 * If the credentials were mapped, we should
3184 * map the same values in the attributes.
3185 */
3186 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
3187 int ismember;
3188 VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
3189 if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
3190 VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
3191 }
3192
3193 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, &context, nxo, 0);
3194
3195 /* construct ACL and handle inheritance */
1c79356b 3196 if (!error) {
91447636
A
3197 error = kauth_acl_inherit(dvp,
3198 NULL,
3199 &xacl, /* isdir */
3200 1,
3201 &context);
3202
3203 if (!error && xacl != NULL)
3204 VATTR_SET(vap, va_acl, xacl);
3205 }
3206 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3207 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3208
3209 /* validate new-file security information */
3210 if (!error) {
3211 error = vnode_authattr_new(dvp, vap, 0, &context);
3212 if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
3213 /*
3214 * Most NFS servers just ignore the UID/GID attributes, so we
3215 * try ignoring them if that'll help the request succeed.
3216 */
3217 VATTR_CLEAR_ACTIVE(vap, va_uid);
3218 VATTR_CLEAR_ACTIVE(vap, va_gid);
3219 error = vnode_authattr_new(dvp, vap, 0, &context);
3220 }
3221 }
3222
3223 if (!error)
3224 error = VNOP_MKDIR(dvp, &vp, &nd.ni_cnd, vap, &context);
3225
3226 if (!error && !VATTR_ALL_SUPPORTED(vap))
3227 /*
3228 * If some of the requested attributes weren't handled by the VNOP,
3229 * use our fallback code.
3230 */
3231 error = vnode_setattr_fallback(vp, vap, &context);
3232
3233 if (xacl != NULL)
3234 kauth_acl_free(xacl);
3235
3236 if (!error) {
3237 error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
3238 if (!error) {
3239 nfsm_srv_vattr_init(&postat, v3);
3240 error = vnode_getattr(vp, &postat, &context);
3241 }
3242 vnode_put(vp);
3243 vp = NULL;
1c79356b 3244 }
91447636
A
3245 /*
3246 * nameidone has to happen before we vnode_put(dvp)
3247 * since it may need to release the fs_nodelock on the dvp
3248 */
3249 nameidone(&nd);
3250
3251 vnode_put(dvp);
1c79356b 3252out:
91447636
A
3253 nd.ni_cnd.cn_nameiop = 0;
3254
1c79356b 3255 if (dirp) {
91447636
A
3256 nfsm_srv_vattr_init(&diraft, v3);
3257 diraft_ret = vnode_getattr(dirp, &diraft, &context);
3258 vnode_put(dirp);
1c79356b 3259 }
91447636 3260 nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
1c79356b
A
3261 if (v3) {
3262 if (!error) {
91447636
A
3263 nfsm_srvpostop_fh(&nfh);
3264 nfsm_srvpostop_attr(0, &postat);
1c79356b
A
3265 }
3266 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
3267 } else {
91447636 3268 nfsm_srvfhtom(&nfh, v3);
1c79356b 3269 nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
91447636 3270 nfsm_srvfillattr(&postat, fp);
1c79356b
A
3271 }
3272 return (0);
3273nfsmout:
91447636
A
3274 if (nd.ni_cnd.cn_nameiop) {
3275 /*
3276 * nameidone has to happen before we vnode_put(dvp)
3277 * since it may need to release the fs_nodelock on the dvp
3278 */
3279 nameidone(&nd);
3280 vnode_put(dvp);
3281 if (vp)
3282 vnode_put(vp);
3283 }
1c79356b 3284 if (dirp)
91447636 3285 vnode_put(dirp);
1c79356b
A
3286 return (error);
3287}
3288
3289/*
3290 * nfs rmdir service
3291 */
3292int
3293nfsrv_rmdir(nfsd, slp, procp, mrq)
3294 struct nfsrv_descript *nfsd;
3295 struct nfssvc_sock *slp;
91447636
A
3296 proc_t procp;
3297 mbuf_t *mrq;
1c79356b 3298{
91447636
A
3299 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
3300 mbuf_t nam = nfsd->nd_nam;
1c79356b 3301 caddr_t dpos = nfsd->nd_dpos;
91447636
A
3302 u_long *tl;
3303 long t1;
1c79356b 3304 caddr_t bpos;
91447636
A
3305 int error = 0, len;
3306 int dirfor_ret = 1, diraft_ret = 1;
1c79356b
A
3307 int v3 = (nfsd->nd_flag & ND_NFSV3);
3308 char *cp2;
91447636
A
3309 mbuf_t mb, mreq;
3310 vnode_t vp, dvp, dirp = NULL;
3311 struct vnode_attr dirfor, diraft;
3312 struct nfs_filehandle nfh;
3313 struct nfs_export *nx;
3314 struct nfs_export_options *nxo;
1c79356b 3315 struct nameidata nd;
91447636
A
3316 struct vfs_context context;
3317
3318 context.vc_proc = procp;
3319 context.vc_ucred = nfsd->nd_cr;
3320
3321 vp = dvp = NULL;
3322 nfsm_srvmtofh(&nfh);
3323 nfsm_srvnamesiz(len, v3);
1c79356b 3324
1c79356b
A
3325 nd.ni_cnd.cn_nameiop = DELETE;
3326 nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
91447636
A
3327 error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
3328 if (!error)
3329 error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
1c79356b 3330 if (dirp) {
91447636
A
3331 if (v3) {
3332 nfsm_srv_pre_vattr_init(&dirfor, v3);
3333 dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
3334 } else {
3335 vnode_put(dirp);
3336 dirp = NULL;
1c79356b
A
3337 }
3338 }
3339 if (error) {
3340 nfsm_reply(NFSX_WCCDATA(v3));
3341 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
3342 if (dirp)
91447636 3343 vnode_put(dirp);
1c79356b
A
3344 return (0);
3345 }
91447636 3346 dvp = nd.ni_dvp;
1c79356b 3347 vp = nd.ni_vp;
91447636
A
3348
3349 if (vnode_vtype(vp) != VDIR) {
1c79356b
A
3350 error = ENOTDIR;
3351 goto out;
3352 }
3353 /*
3354 * No rmdir "." please.
3355 */
91447636 3356 if (dvp == vp) {
1c79356b
A
3357 error = EINVAL;
3358 goto out;
3359 }
3360 /*
3361 * The root of a mounted filesystem cannot be deleted.
3362 */
91447636 3363 if (vnode_isvroot(vp))
1c79356b 3364 error = EBUSY;
91447636
A
3365 if (!error)
3366 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, &context, nxo, 0);
3367 if (!error)
3368 error = VNOP_RMDIR(dvp, vp, &nd.ni_cnd, &context);
1c79356b 3369out:
91447636
A
3370 /*
3371 * nameidone has to happen before we vnode_put(dvp)
3372 * since it may need to release the fs_nodelock on the dvp
3373 */
3374 nameidone(&nd);
3375
3376 vnode_put(dvp);
3377 vnode_put(vp);
3378
1c79356b 3379 if (dirp) {
91447636
A
3380 nfsm_srv_vattr_init(&diraft, v3);
3381 diraft_ret = vnode_getattr(dirp, &diraft, &context);
3382 vnode_put(dirp);
1c79356b
A
3383 }
3384 nfsm_reply(NFSX_WCCDATA(v3));
3385 if (v3) {
3386 nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
3387 return (0);
3388 }
91447636
A
3389nfsmout:
3390 return (error);
1c79356b
A
3391}
3392
3393/*
3394 * nfs readdir service
3395 * - mallocs what it thinks is enough to read
3396 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
91447636 3397 * - calls VNOP_READDIR()
1c79356b
A
3398 * - loops around building the reply
3399 * if the output generated exceeds count break out of loop
3400 * The nfsm_clget macro is used here so that the reply will be packed
3401 * tightly in mbuf clusters.
91447636 3402 * - it only knows that it has encountered eof when the VNOP_READDIR()
1c79356b
A
3403 * reads nothing
3404 * - as such one readdir rpc will return eof false although you are there
3405 * and then the next will return eof
3406 * - it trims out records with d_fileno == 0
3407 * this doesn't matter for Unix clients, but they might confuse clients
3408 * for other os'.
91447636 3409 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
1c79356b
A
3410 * than requested, but this may not apply to all filesystems. For
3411 * example, client NFS does not { although it is never remote mounted
3412 * anyhow }
3413 * The alternate call nfsrv_readdirplus() does lookups as well.
55e303ae
A
3414 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
3415 * are supposed to cover. For readdir, the count is the total number of
3416 * bytes included in everything from the directory's postopattr through
3417 * the EOF flag. For readdirplus, the maxcount is the same, and the
3418 * dircount includes all that except for the entry attributes and handles.
1c79356b 3419 */
1c79356b
A
3420
3421int
3422nfsrv_readdir(nfsd, slp, procp, mrq)
3423 struct nfsrv_descript *nfsd;
3424 struct nfssvc_sock *slp;
91447636
A
3425 proc_t procp;
3426 mbuf_t *mrq;
1c79356b 3427{
91447636
A
3428 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
3429 mbuf_t nam = nfsd->nd_nam;
1c79356b 3430 caddr_t dpos = nfsd->nd_dpos;
91447636
A
3431 char *bp, *be;
3432 mbuf_t mp;
3433 struct direntry *dp;
3434 caddr_t cp;
3435 u_long *tl;
3436 long t1;
1c79356b 3437 caddr_t bpos;
91447636 3438 mbuf_t mb, mb2, mreq, mp2;
1c79356b 3439 char *cpos, *cend, *cp2, *rbuf;
91447636
A
3440 vnode_t vp;
3441 struct vnode_attr at;
3442 struct nfs_filehandle nfh;
3443 struct nfs_export *nx;
3444 struct nfs_export_options *nxo;
3445 uio_t auio;
3446 char uio_buf[ UIO_SIZEOF(1) ];
1c79356b 3447 int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
91447636 3448 int siz, count, fullsiz, eofflag, nentries = 0;
1c79356b 3449 int v3 = (nfsd->nd_flag & ND_NFSV3);
91447636
A
3450 u_quad_t off, toff, verf;
3451 nfsuint64 tquad;
3452 int vnopflag;
3453 struct vfs_context context;
1c79356b 3454
91447636
A
3455 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
3456
3457 nfsm_srvmtofh(&nfh);
1c79356b
A
3458 if (v3) {
3459 nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
3460 fxdr_hyper(tl, &toff);
3461 tl += 2;
3462 fxdr_hyper(tl, &verf);
3463 tl += 2;
3464 } else {
3465 nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
3466 toff = fxdr_unsigned(u_quad_t, *tl++);
3467 }
3468 off = toff;
91447636
A
3469 count = fxdr_unsigned(int, *tl);
3470 siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
1c79356b
A
3471 xfer = NFS_SRVMAXDATA(nfsd);
3472 if (siz > xfer)
3473 siz = xfer;
3474 fullsiz = siz;
91447636
A
3475 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
3476 nfsm_reply(NFSX_UNSIGNED);
3477 nfsm_srvpostop_attr(getret, &at);
3478 return (0);
3479 }
3480 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
3481 vnode_put(vp);
1c79356b
A
3482 nfsm_reply(NFSX_UNSIGNED);
3483 nfsm_srvpostop_attr(getret, &at);
3484 return (0);
3485 }
91447636
A
3486 context.vc_proc = procp;
3487 context.vc_ucred = nfsd->nd_cr;
3a60a9f5
A
3488 if (!v3 || (nxo->nxo_flags & NX_32BITCLIENTS))
3489 vnopflag |= VNODE_READDIR_SEEKOFF32;
1c79356b 3490 if (v3) {
91447636
A
3491 nfsm_srv_vattr_init(&at, v3);
3492 error = getret = vnode_getattr(vp, &at, &context);
1c79356b
A
3493 if (!error && toff && verf && verf != at.va_filerev)
3494 error = NFSERR_BAD_COOKIE;
3495 }
3496 if (!error)
91447636 3497 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0);
1c79356b 3498 if (error) {
91447636 3499 vnode_put(vp);
1c79356b
A
3500 nfsm_reply(NFSX_POSTOPATTR(v3));
3501 nfsm_srvpostop_attr(getret, &at);
3502 return (0);
3503 }
1c79356b 3504 MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
91447636
A
3505 if (!rbuf) {
3506 error = ENOMEM;
3507 vnode_put(vp);
fa4905b1
A
3508 nfsm_reply(NFSX_POSTOPATTR(v3));
3509 nfsm_srvpostop_attr(getret, &at);
3510 return (0);
1c79356b 3511 }
91447636
A
3512 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3513 &uio_buf[0], sizeof(uio_buf));
3514 if (!auio) {
3515 error = ENOMEM;
3516 FREE(rbuf, M_TEMP);
3517 vnode_put(vp);
3518 nfsm_reply(NFSX_POSTOPATTR(v3));
3519 nfsm_srvpostop_attr(getret, &at);
3520 return (0);
3521 }
3522again:
3523 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
3524 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
3525
3526 eofflag = 0;
3527 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, &context);
3528 off = uio_offset(auio);
3529
1c79356b 3530 if (v3) {
91447636
A
3531 nfsm_srv_vattr_init(&at, v3);
3532 getret = vnode_getattr(vp, &at, &context);
1c79356b
A
3533 if (!error)
3534 error = getret;
3535 }
1c79356b 3536 if (error) {
91447636
A
3537 vnode_put(vp);
3538 FREE(rbuf, M_TEMP);
1c79356b
A
3539 nfsm_reply(NFSX_POSTOPATTR(v3));
3540 nfsm_srvpostop_attr(getret, &at);
3541 return (0);
3542 }
91447636
A
3543 if (uio_resid(auio) != 0) {
3544 // LP64todo - fix this
3545 siz -= uio_resid(auio);
1c79356b
A
3546
3547 /*
3548 * If nothing read, return eof
3549 * rpc reply
3550 */
3551 if (siz == 0) {
91447636 3552 vnode_put(vp);
1c79356b
A
3553 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) +
3554 2 * NFSX_UNSIGNED);
3555 if (v3) {
3556 nfsm_srvpostop_attr(getret, &at);
3557 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
3558 txdr_hyper(&at.va_filerev, tl);
3559 tl += 2;
3560 } else
3561 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
3562 *tl++ = nfs_false;
3563 *tl = nfs_true;
91447636 3564 FREE(rbuf, M_TEMP);
1c79356b
A
3565 return (0);
3566 }
3567 }
3568
3569 /*
3570 * Check for degenerate cases of nothing useful read.
3571 * If so go try again
3572 */
3573 cpos = rbuf;
3574 cend = rbuf + siz;
91447636
A
3575 dp = (struct direntry *)cpos;
3576 while (dp->d_fileno == 0 && cpos < cend && nentries > 0) {
1c79356b 3577 cpos += dp->d_reclen;
91447636
A
3578 dp = (struct direntry *)cpos;
3579 nentries--;
1c79356b 3580 }
91447636 3581 if (cpos >= cend || nentries == 0) {
1c79356b
A
3582 toff = off;
3583 siz = fullsiz;
3584 goto again;
3585 }
3586
91447636 3587 vnode_put(vp);
1c79356b
A
3588 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz);
3589 if (v3) {
55e303ae 3590 len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
1c79356b
A
3591 nfsm_srvpostop_attr(getret, &at);
3592 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
3593 txdr_hyper(&at.va_filerev, tl);
55e303ae
A
3594 } else
3595 len = 2 * NFSX_UNSIGNED;
1c79356b
A
3596 mp = mp2 = mb;
3597 bp = bpos;
91447636 3598 be = bp + mbuf_trailingspace(mp);
1c79356b
A
3599
3600 /* Loop through the records and build reply */
91447636 3601 while (cpos < cend && nentries > 0) {
1c79356b
A
3602 if (dp->d_fileno != 0) {
3603 nlen = dp->d_namlen;
91447636
A
3604 if (!v3 && (nlen > NFS_MAXNAMLEN))
3605 nlen = NFS_MAXNAMLEN;
1c79356b
A
3606 rem = nfsm_rndup(nlen)-nlen;
3607 len += (4 * NFSX_UNSIGNED + nlen + rem);
3608 if (v3)
3609 len += 2 * NFSX_UNSIGNED;
91447636 3610 if (len > count) {
1c79356b
A
3611 eofflag = 0;
3612 break;
3613 }
3614 /*
3615 * Build the directory record xdr from
91447636 3616 * the direntry entry.
1c79356b
A
3617 */
3618 nfsm_clget;
3619 *tl = nfs_true;
3620 bp += NFSX_UNSIGNED;
91447636 3621 nfsm_clget;
1c79356b 3622 if (v3) {
91447636
A
3623 txdr_hyper(&dp->d_fileno, &tquad);
3624 *tl = tquad.nfsuquad[0];
3625 bp += NFSX_UNSIGNED;
1c79356b 3626 nfsm_clget;
91447636
A
3627 *tl = tquad.nfsuquad[1];
3628 bp += NFSX_UNSIGNED;
3629 } else {
3630 *tl = txdr_unsigned(dp->d_fileno);
1c79356b
A
3631 bp += NFSX_UNSIGNED;
3632 }
3633 nfsm_clget;
1c79356b
A
3634 *tl = txdr_unsigned(nlen);
3635 bp += NFSX_UNSIGNED;
3636
3637 /* And loop around copying the name */
3638 xfer = nlen;
3639 cp = dp->d_name;
3640 while (xfer > 0) {
3641 nfsm_clget;
3642 if ((bp+xfer) > be)
3643 tsiz = be-bp;
3644 else
3645 tsiz = xfer;
3646 bcopy(cp, bp, tsiz);
3647 bp += tsiz;
3648 xfer -= tsiz;
3649 if (xfer > 0)
3650 cp += tsiz;
3651 }
3652 /* And null pad to a long boundary */
3653 for (i = 0; i < rem; i++)
3654 *bp++ = '\0';
1c79356b 3655
91447636
A
3656 /* Finish off the record with the cookie */
3657 nfsm_clget;
1c79356b 3658 if (v3) {
3a60a9f5
A
3659 if (vnopflag & VNODE_READDIR_SEEKOFF32)
3660 dp->d_seekoff &= 0x00000000ffffffffULL;
91447636
A
3661 txdr_hyper(&dp->d_seekoff, &tquad);
3662 *tl = tquad.nfsuquad[0];
1c79356b
A
3663 bp += NFSX_UNSIGNED;
3664 nfsm_clget;
91447636
A
3665 *tl = tquad.nfsuquad[1];
3666 bp += NFSX_UNSIGNED;
3667 } else {
3668 *tl = txdr_unsigned(dp->d_seekoff);
3669 bp += NFSX_UNSIGNED;
1c79356b 3670 }
1c79356b
A
3671 }
3672 cpos += dp->d_reclen;
91447636
A
3673 dp = (struct direntry *)cpos;
3674 nentries--;
1c79356b 3675 }
1c79356b
A
3676 nfsm_clget;
3677 *tl = nfs_false;
3678 bp += NFSX_UNSIGNED;
3679 nfsm_clget;
3680 if (eofflag)
3681 *tl = nfs_true;
3682 else
3683 *tl = nfs_false;
3684 bp += NFSX_UNSIGNED;
3685 if (mp != mb) {
3686 if (bp < be)
91447636 3687 mbuf_setlen(mp, bp - (char*)mbuf_data(mp));
1c79356b 3688 } else
91447636
A
3689 mbuf_setlen(mp, mbuf_len(mp) + (bp - bpos));
3690 FREE(rbuf, M_TEMP);
3691nfsmout:
3692 return (error);
1c79356b
A
3693}
3694
91447636
A
3695struct flrep {
3696 nfsuint64 fl_off;
3697 u_long fl_postopok;
3698 u_long fl_fattr[NFSX_V3FATTR / sizeof (u_long)];
3699 u_long fl_fhok;
3700 u_long fl_fhsize;
3701 u_long fl_nfh[NFSX_V3FHMAX / sizeof (u_long)];
3702};
3703
1c79356b
A
3704int
3705nfsrv_readdirplus(nfsd, slp, procp, mrq)
3706 struct nfsrv_descript *nfsd;
3707 struct nfssvc_sock *slp;
91447636
A
3708 proc_t procp;
3709 mbuf_t *mrq;
1c79356b 3710{
91447636
A
3711 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
3712 mbuf_t nam = nfsd->nd_nam;
1c79356b 3713 caddr_t dpos = nfsd->nd_dpos;
91447636
A
3714 char *bp, *be;
3715 mbuf_t mp;
3716 struct direntry *dp;
3717 caddr_t cp;
3718 u_long *tl;
3719 long t1;
1c79356b 3720 caddr_t bpos;
91447636 3721 mbuf_t mb, mb2, mreq, mp2;
1c79356b 3722 char *cpos, *cend, *cp2, *rbuf;
91447636 3723 vnode_t vp, nvp;
1c79356b 3724 struct flrep fl;
91447636 3725 struct nfs_filehandle dnfh, *nfhp = (struct nfs_filehandle *)&fl.fl_fhsize;
0c530ab8 3726 u_long fhsize;
91447636
A
3727 struct nfs_export *nx;
3728 struct nfs_export_options *nxo;
3729 uio_t auio;
3730 char uio_buf[ UIO_SIZEOF(1) ];
3731 struct vnode_attr va, at, *vap = &va;
1c79356b
A
3732 struct nfs_fattr *fp;
3733 int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
6601e61a 3734 int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries = 0, isdotdot;
91447636
A
3735 u_quad_t off, toff, verf;
3736 nfsuint64 tquad;
3737 int vnopflag;
3738 struct vfs_context context;
1c79356b 3739
91447636
A
3740 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
3741 vp = NULL;
3742 nfsm_srvmtofh(&dnfh);
1c79356b
A
3743 nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED);
3744 fxdr_hyper(tl, &toff);
3745 tl += 2;
3746 fxdr_hyper(tl, &verf);
3747 tl += 2;
6601e61a
A
3748 dircount = fxdr_unsigned(int, *tl++);
3749 maxcount = fxdr_unsigned(int, *tl);
1c79356b 3750 off = toff;
1c79356b 3751 xfer = NFS_SRVMAXDATA(nfsd);
6601e61a
A
3752 dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
3753 if (dircount > xfer)
3754 dircount = xfer;
3755 fullsiz = siz = dircount;
3756 maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
3757 if (maxcount > xfer)
3758 maxcount = xfer;
3759
91447636
A
3760 if ((error = nfsrv_fhtovp(&dnfh, nam, TRUE, &vp, &nx, &nxo))) {
3761 nfsm_reply(NFSX_UNSIGNED);
3762 nfsm_srvpostop_attr(getret, &at);
3763 return (0);
3764 }
3765 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
3766 vnode_put(vp);
1c79356b
A
3767 nfsm_reply(NFSX_UNSIGNED);
3768 nfsm_srvpostop_attr(getret, &at);
3769 return (0);
3770 }
91447636
A
3771 context.vc_proc = procp;
3772 context.vc_ucred = nfsd->nd_cr;
3a60a9f5
A
3773 if (nxo->nxo_flags & NX_32BITCLIENTS)
3774 vnopflag |= VNODE_READDIR_SEEKOFF32;
91447636
A
3775 nfsm_srv_vattr_init(&at, 1);
3776 error = getret = vnode_getattr(vp, &at, &context);
1c79356b
A
3777 if (!error && toff && verf && verf != at.va_filerev)
3778 error = NFSERR_BAD_COOKIE;
91447636
A
3779 if (!error)
3780 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0);
1c79356b 3781 if (error) {
91447636
A
3782 vnode_put(vp);
3783 vp = NULL;
1c79356b
A
3784 nfsm_reply(NFSX_V3POSTOPATTR);
3785 nfsm_srvpostop_attr(getret, &at);
3786 return (0);
3787 }
1c79356b 3788 MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
91447636
A
3789 if (!rbuf) {
3790 error = ENOMEM;
3791 vnode_put(vp);
3792 vp = NULL;
3793 nfsm_reply(NFSX_V3POSTOPATTR);
3794 nfsm_srvpostop_attr(getret, &at);
3795 return (0);
3796 }
3797 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3798 &uio_buf[0], sizeof(uio_buf));
3799 if (!auio) {
3800 error = ENOMEM;
3801 FREE(rbuf, M_TEMP);
3802 vnode_put(vp);
3803 vp = NULL;
3804 nfsm_reply(NFSX_V3POSTOPATTR);
3805 nfsm_srvpostop_attr(getret, &at);
3806 return (0);
3807 }
1c79356b 3808again:
91447636
A
3809 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
3810 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
1c79356b 3811 eofflag = 0;
91447636
A
3812 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, &context);
3813 off = uio_offset(auio);
3814 nfsm_srv_vattr_init(&at, 1);
3815 getret = vnode_getattr(vp, &at, &context);
1c79356b
A
3816
3817 if (!error)
3818 error = getret;
3819 if (error) {
91447636
A
3820 vnode_put(vp);
3821 vp = NULL;
3822 FREE(rbuf, M_TEMP);
1c79356b
A
3823 nfsm_reply(NFSX_V3POSTOPATTR);
3824 nfsm_srvpostop_attr(getret, &at);
3825 return (0);
3826 }
91447636
A
3827 if (uio_resid(auio) != 0) {
3828 // LP64todo - fix this
3829 siz -= uio_resid(auio);
1c79356b
A
3830
3831 /*
3832 * If nothing read, return eof
3833 * rpc reply
3834 */
3835 if (siz == 0) {
91447636
A
3836 vnode_put(vp);
3837 vp = NULL;
1c79356b
A
3838 nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF +
3839 2 * NFSX_UNSIGNED);
3840 nfsm_srvpostop_attr(getret, &at);
3841 nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
3842 txdr_hyper(&at.va_filerev, tl);
3843 tl += 2;
3844 *tl++ = nfs_false;
3845 *tl = nfs_true;
91447636 3846 FREE(rbuf, M_TEMP);
1c79356b
A
3847 return (0);
3848 }
3849 }
3850
3851 /*
3852 * Check for degenerate cases of nothing useful read.
3853 * If so go try again
3854 */
3855 cpos = rbuf;
3856 cend = rbuf + siz;
91447636
A
3857 dp = (struct direntry *)cpos;
3858 while (dp->d_fileno == 0 && cpos < cend && nentries > 0) {
1c79356b 3859 cpos += dp->d_reclen;
91447636
A
3860 dp = (struct direntry *)cpos;
3861 nentries--;
1c79356b 3862 }
91447636 3863 if (cpos >= cend || nentries == 0) {
1c79356b
A
3864 toff = off;
3865 siz = fullsiz;
3866 goto again;
3867 }
3868
3869 /*
3870 * Probe one of the directory entries to see if the filesystem
91447636 3871 * supports VGET.
1c79356b 3872 */
91447636
A
3873 if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context))) {
3874 if (error == ENOTSUP) /* let others get passed back */
0b4e3aa0 3875 error = NFSERR_NOTSUPP;
91447636
A
3876 vnode_put(vp);
3877 vp = NULL;
3878 FREE(rbuf, M_TEMP);
1c79356b
A
3879 nfsm_reply(NFSX_V3POSTOPATTR);
3880 nfsm_srvpostop_attr(getret, &at);
3881 return (0);
3882 }
91447636 3883 vnode_put(nvp);
1c79356b
A
3884
3885 dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
6601e61a 3886 nfsm_reply(maxcount);
1c79356b
A
3887 nfsm_srvpostop_attr(getret, &at);
3888 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
3889 txdr_hyper(&at.va_filerev, tl);
3890 mp = mp2 = mb;
3891 bp = bpos;
91447636 3892 be = bp + mbuf_trailingspace(mp);
1c79356b
A
3893
3894 /* Loop through the records and build reply */
91447636 3895 while (cpos < cend && nentries > 0) {
1c79356b
A
3896 if (dp->d_fileno != 0) {
3897 nlen = dp->d_namlen;
3898 rem = nfsm_rndup(nlen)-nlen;
3899
0b4e3aa0
A
3900 /*
3901 * Got to get the vnode for lookup per entry.
1c79356b 3902 */
91447636 3903 if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context))
1c79356b 3904 goto invalid;
91447636
A
3905 isdotdot = ((dp->d_namlen == 2) &&
3906 (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
3907 if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, &context, nfhp)) {
3908 // XXX file handle is optional, so we should be able to
3909 // XXX return this entry without the file handle
3910 vnode_put(nvp);
1c79356b
A
3911 goto invalid;
3912 }
91447636
A
3913 nfsm_srv_vattr_init(vap, 1);
3914 if (vnode_getattr(nvp, vap, &context)) {
3915 // XXX attributes are optional, so we should be able to
3916 // XXX return this entry without the attributes
3917 vnode_put(nvp);
1c79356b
A
3918 goto invalid;
3919 }
91447636 3920 vnode_put(nvp);
1c79356b
A
3921
3922 /*
3923 * If either the dircount or maxcount will be
3924 * exceeded, get out now. Both of these lengths
3925 * are calculated conservatively, including all
3926 * XDR overheads.
3927 */
6601e61a 3928 len += (8 * NFSX_UNSIGNED + nlen + rem + nfsm_rndup(nfhp->nfh_len) +
1c79356b
A
3929 NFSX_V3POSTOPATTR);
3930 dirlen += (6 * NFSX_UNSIGNED + nlen + rem);
6601e61a 3931 if ((len > maxcount) || (dirlen > dircount)) {
1c79356b
A
3932 eofflag = 0;
3933 break;
3934 }
3935
3936 /*
3937 * Build the directory record xdr from
91447636 3938 * the direntry entry.
1c79356b
A
3939 */
3940 fp = (struct nfs_fattr *)&fl.fl_fattr;
3941 nfsm_srvfillattr(vap, fp);
0c530ab8
A
3942 fhsize = nfhp->nfh_len;
3943 fl.fl_fhsize = txdr_unsigned(fhsize);
1c79356b
A
3944 fl.fl_fhok = nfs_true;
3945 fl.fl_postopok = nfs_true;
3a60a9f5
A
3946 if (vnopflag & VNODE_READDIR_SEEKOFF32)
3947 dp->d_seekoff &= 0x00000000ffffffffULL;
91447636 3948 txdr_hyper(&dp->d_seekoff, &fl.fl_off);
1c79356b
A
3949
3950 nfsm_clget;
3951 *tl = nfs_true;
3952 bp += NFSX_UNSIGNED;
91447636 3953
1c79356b 3954 nfsm_clget;
91447636
A
3955 txdr_hyper(&dp->d_fileno, &tquad);
3956 *tl = tquad.nfsuquad[0];
1c79356b
A
3957 bp += NFSX_UNSIGNED;
3958 nfsm_clget;
91447636 3959 *tl = tquad.nfsuquad[1];
1c79356b 3960 bp += NFSX_UNSIGNED;
91447636 3961
1c79356b
A
3962 nfsm_clget;
3963 *tl = txdr_unsigned(nlen);
3964 bp += NFSX_UNSIGNED;
3965
3966 /* And loop around copying the name */
3967 xfer = nlen;
3968 cp = dp->d_name;
3969 while (xfer > 0) {
3970 nfsm_clget;
3971 if ((bp + xfer) > be)
3972 tsiz = be - bp;
3973 else
3974 tsiz = xfer;
3975 bcopy(cp, bp, tsiz);
3976 bp += tsiz;
3977 xfer -= tsiz;
3978 if (xfer > 0)
3979 cp += tsiz;
3980 }
3981 /* And null pad to a long boundary */
3982 for (i = 0; i < rem; i++)
3983 *bp++ = '\0';
3984
3985 /*
3986 * Now copy the flrep structure out.
3987 */
0c530ab8 3988 xfer = sizeof(struct flrep) - sizeof(fl.fl_nfh) + fhsize;
1c79356b
A
3989 cp = (caddr_t)&fl;
3990 while (xfer > 0) {
3991 nfsm_clget;
3992 if ((bp + xfer) > be)
3993 tsiz = be - bp;
3994 else
3995 tsiz = xfer;
3996 bcopy(cp, bp, tsiz);
3997 bp += tsiz;
3998 xfer -= tsiz;
3999 if (xfer > 0)
4000 cp += tsiz;
4001 }
4002 }
4003invalid:
4004 cpos += dp->d_reclen;
91447636
A
4005 dp = (struct direntry *)cpos;
4006 nentries--;
1c79356b 4007 }
91447636
A
4008 vnode_put(vp);
4009 vp = NULL;
1c79356b
A
4010 nfsm_clget;
4011 *tl = nfs_false;
4012 bp += NFSX_UNSIGNED;
4013 nfsm_clget;
4014 if (eofflag)
4015 *tl = nfs_true;
4016 else
4017 *tl = nfs_false;
4018 bp += NFSX_UNSIGNED;
4019 if (mp != mb) {
4020 if (bp < be)
91447636 4021 mbuf_setlen(mp, bp - (char*)mbuf_data(mp));
1c79356b 4022 } else
91447636
A
4023 mbuf_setlen(mp, mbuf_len(mp) + (bp - bpos));
4024 FREE(rbuf, M_TEMP);
4025nfsmout:
4026 if (vp)
4027 vnode_put(vp);
4028 return (error);
1c79356b
A
4029}
4030
4031/*
4032 * nfs commit service
4033 */
4034int
4035nfsrv_commit(nfsd, slp, procp, mrq)
4036 struct nfsrv_descript *nfsd;
4037 struct nfssvc_sock *slp;
91447636
A
4038 proc_t procp;
4039 mbuf_t *mrq;
1c79356b 4040{
91447636
A
4041 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
4042 mbuf_t nam = nfsd->nd_nam;
1c79356b 4043 caddr_t dpos = nfsd->nd_dpos;
91447636
A
4044 struct vnode_attr bfor, aft;
4045 vnode_t vp;
4046 struct nfs_filehandle nfh;
4047 struct nfs_export *nx;
4048 struct nfs_export_options *nxo;
4049 u_long *tl;
4050 long t1;
1c79356b 4051 caddr_t bpos;
91447636 4052 int error = 0, for_ret = 1, aft_ret = 1, count;
1c79356b 4053 char *cp2;
91447636
A
4054 mbuf_t mb, mb2, mreq;
4055 u_quad_t off;
4056 struct vfs_context context;
1c79356b 4057
91447636 4058 nfsm_srvmtofh(&nfh);
1c79356b
A
4059 nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
4060
4061 /*
91447636 4062 * XXX At this time VNOP_FSYNC() does not accept offset and byte
1c79356b
A
4063 * count parameters, so these arguments are useless (someday maybe).
4064 */
4065 fxdr_hyper(tl, &off);
4066 tl += 2;
91447636
A
4067 count = fxdr_unsigned(int, *tl);
4068 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b
A
4069 nfsm_reply(2 * NFSX_UNSIGNED);
4070 nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
4071 return (0);
4072 }
91447636
A
4073 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
4074 vnode_put(vp);
4075 nfsm_reply(2 * NFSX_UNSIGNED);
4076 nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
4077 return (0);
4078 }
4079 context.vc_proc = procp;
4080 context.vc_ucred = nfsd->nd_cr;
4081
4082 nfsm_srv_pre_vattr_init(&bfor, 1);
4083 for_ret = vnode_getattr(vp, &bfor, &context);
4084 error = VNOP_FSYNC(vp, MNT_WAIT, &context);
4085 nfsm_srv_vattr_init(&aft, 1);
4086 aft_ret = vnode_getattr(vp, &aft, &context);
4087 vnode_put(vp);
1c79356b
A
4088 nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4089 nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
4090 if (!error) {
4091 nfsm_build(tl, u_long *, NFSX_V3WRITEVERF);
91447636
A
4092 *tl++ = txdr_unsigned(boottime_sec());
4093 *tl = txdr_unsigned(0);
1c79356b
A
4094 } else
4095 return (0);
91447636
A
4096nfsmout:
4097 return (error);
1c79356b
A
4098}
4099
4100/*
4101 * nfs statfs service
4102 */
4103int
4104nfsrv_statfs(nfsd, slp, procp, mrq)
4105 struct nfsrv_descript *nfsd;
4106 struct nfssvc_sock *slp;
91447636
A
4107 proc_t procp;
4108 mbuf_t *mrq;
1c79356b 4109{
91447636
A
4110 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
4111 mbuf_t nam = nfsd->nd_nam;
1c79356b 4112 caddr_t dpos = nfsd->nd_dpos;
91447636
A
4113 struct vfs_attr va;
4114 struct nfs_statfs *sfp;
4115 u_long *tl;
4116 long t1;
1c79356b 4117 caddr_t bpos;
91447636 4118 int error = 0, getret = 1;
1c79356b
A
4119 int v3 = (nfsd->nd_flag & ND_NFSV3);
4120 char *cp2;
91447636
A
4121 mbuf_t mb, mb2, mreq;
4122 vnode_t vp;
4123 struct vnode_attr at;
4124 struct nfs_filehandle nfh;
4125 struct nfs_export *nx;
4126 struct nfs_export_options *nxo;
4127 u_quad_t tval;
4128 off_t blksize;
4129 struct vfs_context context;
1c79356b 4130
91447636
A
4131 nfsm_srvmtofh(&nfh);
4132 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
4133 nfsm_reply(NFSX_UNSIGNED);
4134 nfsm_srvpostop_attr(getret, &at);
4135 return (0);
4136 }
4137 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
4138 vnode_put(vp);
1c79356b
A
4139 nfsm_reply(NFSX_UNSIGNED);
4140 nfsm_srvpostop_attr(getret, &at);
4141 return (0);
4142 }
91447636
A
4143 context.vc_proc = procp;
4144 context.vc_ucred = nfsd->nd_cr;
4145
4146 VFSATTR_INIT(&va);
4147 VFSATTR_WANTED(&va, f_blocks);
4148 VFSATTR_WANTED(&va, f_bavail);
4149 VFSATTR_WANTED(&va, f_files);
4150 VFSATTR_WANTED(&va, f_ffree);
4151 error = vfs_getattr(vnode_mount(vp), &va, &context);
4152 blksize = vnode_mount(vp)->mnt_vfsstat.f_bsize;
4153 nfsm_srv_vattr_init(&at, v3);
4154 getret = vnode_getattr(vp, &at, &context);
4155 vnode_put(vp);
1c79356b
A
4156 nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3));
4157 if (v3)
4158 nfsm_srvpostop_attr(getret, &at);
4159 if (error)
4160 return (0);
4161 nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
4162 if (v3) {
91447636 4163 tval = (u_quad_t)(va.f_blocks * blksize);
1c79356b 4164 txdr_hyper(&tval, &sfp->sf_tbytes);
91447636 4165 tval = (u_quad_t)(va.f_bfree * blksize);
1c79356b 4166 txdr_hyper(&tval, &sfp->sf_fbytes);
91447636 4167 tval = (u_quad_t)(va.f_bavail * blksize);
1c79356b 4168 txdr_hyper(&tval, &sfp->sf_abytes);
91447636
A
4169 txdr_hyper(&va.f_files, &sfp->sf_tfiles);
4170 txdr_hyper(&va.f_ffree, &sfp->sf_ffiles);
4171 txdr_hyper(&va.f_ffree, &sfp->sf_afiles);
1c79356b
A
4172 sfp->sf_invarsec = 0;
4173 } else {
55e303ae 4174 sfp->sf_tsize = txdr_unsigned(NFS_V2MAXDATA);
91447636
A
4175 sfp->sf_bsize = txdr_unsigned((unsigned)blksize);
4176 sfp->sf_blocks = txdr_unsigned((unsigned)va.f_blocks);
4177 sfp->sf_bfree = txdr_unsigned((unsigned)va.f_bfree);
4178 sfp->sf_bavail = txdr_unsigned((unsigned)va.f_bavail);
1c79356b 4179 }
91447636
A
4180nfsmout:
4181 return (error);
1c79356b
A
4182}
4183
4184/*
4185 * nfs fsinfo service
4186 */
4187int
4188nfsrv_fsinfo(nfsd, slp, procp, mrq)
4189 struct nfsrv_descript *nfsd;
4190 struct nfssvc_sock *slp;
91447636
A
4191 proc_t procp;
4192 mbuf_t *mrq;
1c79356b 4193{
91447636
A
4194 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
4195 mbuf_t nam = nfsd->nd_nam;
1c79356b 4196 caddr_t dpos = nfsd->nd_dpos;
91447636
A
4197 u_long *tl;
4198 struct nfsv3_fsinfo *sip;
4199 long t1;
1c79356b 4200 caddr_t bpos;
91447636 4201 int error = 0, getret = 1, prefsize, maxsize;
1c79356b 4202 char *cp2;
91447636
A
4203 mbuf_t mb, mb2, mreq;
4204 vnode_t vp;
4205 struct vnode_attr at;
4206 struct nfs_filehandle nfh;
4207 struct nfs_export *nx;
4208 struct nfs_export_options *nxo;
4209 struct vfs_context context;
1c79356b 4210
91447636
A
4211 nfsm_srvmtofh(&nfh);
4212 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
1c79356b
A
4213 nfsm_reply(NFSX_UNSIGNED);
4214 nfsm_srvpostop_attr(getret, &at);
4215 return (0);
4216 }
91447636
A
4217 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
4218 vnode_put(vp);
4219 nfsm_reply(NFSX_UNSIGNED);
4220 nfsm_srvpostop_attr(getret, &at);
4221 return (0);
4222 }
4223 context.vc_proc = procp;
4224 context.vc_ucred = nfsd->nd_cr;
4225
4226 nfsm_srv_vattr_init(&at, 1);
4227 getret = vnode_getattr(vp, &at, &context);
4228 vnode_put(vp);
1c79356b
A
4229 nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4230 nfsm_srvpostop_attr(getret, &at);
4231 nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
4232
4233 /*
4234 * XXX
4235 * There should be file system VFS OP(s) to get this information.
55e303ae 4236 * For now, assume our usual NFS defaults.
1c79356b 4237 */
91447636
A
4238 if (slp->ns_sotype == SOCK_DGRAM) {
4239 maxsize = NFS_MAXDGRAMDATA;
4240 prefsize = NFS_PREFDGRAMDATA;
4241 } else
4242 maxsize = prefsize = NFS_MAXDATA;
4243 sip->fs_rtmax = txdr_unsigned(maxsize);
4244 sip->fs_rtpref = txdr_unsigned(prefsize);
1c79356b 4245 sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE);
91447636
A
4246 sip->fs_wtmax = txdr_unsigned(maxsize);
4247 sip->fs_wtpref = txdr_unsigned(prefsize);
1c79356b 4248 sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE);
91447636 4249 sip->fs_dtpref = txdr_unsigned(prefsize);
1c79356b
A
4250 sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff;
4251 sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff;
4252 sip->fs_timedelta.nfsv3_sec = 0;
4253 sip->fs_timedelta.nfsv3_nsec = txdr_unsigned(1);
4254 sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK |
4255 NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS |
4256 NFSV3FSINFO_CANSETTIME);
91447636
A
4257nfsmout:
4258 return (error);
1c79356b
A
4259}
4260
4261/*
4262 * nfs pathconf service
4263 */
4264int
4265nfsrv_pathconf(nfsd, slp, procp, mrq)
4266 struct nfsrv_descript *nfsd;
4267 struct nfssvc_sock *slp;
91447636
A
4268 proc_t procp;
4269 mbuf_t *mrq;
1c79356b 4270{
91447636
A
4271 mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
4272 mbuf_t nam = nfsd->nd_nam;
1c79356b 4273 caddr_t dpos = nfsd->nd_dpos;
91447636
A
4274 u_long *tl;
4275 struct nfsv3_pathconf *pc;
4276 long t1;
1c79356b 4277 caddr_t bpos;
91447636 4278 int error = 0, getret = 1, linkmax, namemax;
55e303ae 4279 int chownres, notrunc, case_sensitive, case_preserving;
1c79356b 4280 char *cp2;
91447636
A
4281 mbuf_t mb, mb2, mreq;
4282 vnode_t vp;
4283 struct vnode_attr at;
4284 struct nfs_filehandle nfh;
4285 struct nfs_export *nx;
4286 struct nfs_export_options *nxo;
4287 struct vfs_context context;
1c79356b 4288
91447636
A
4289 nfsm_srvmtofh(&nfh);
4290 if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
4291 nfsm_reply(NFSX_UNSIGNED);
4292 nfsm_srvpostop_attr(getret, &at);
4293 return (0);
4294 }
4295 if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
4296 vnode_put(vp);
1c79356b
A
4297 nfsm_reply(NFSX_UNSIGNED);
4298 nfsm_srvpostop_attr(getret, &at);
4299 return (0);
4300 }
91447636
A
4301 context.vc_proc = procp;
4302 context.vc_ucred = nfsd->nd_cr;
4303
4304 error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, &context);
1c79356b 4305 if (!error)
91447636 4306 error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, &context);
1c79356b 4307 if (!error)
91447636 4308 error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, &context);
1c79356b 4309 if (!error)
91447636 4310 error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, &notrunc, &context);
55e303ae 4311 if (!error)
91447636 4312 error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, &context);
55e303ae 4313 if (!error)
91447636
A
4314 error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, &context);
4315 nfsm_srv_vattr_init(&at, 1);
4316 getret = vnode_getattr(vp, &at, &context);
4317 vnode_put(vp);
1c79356b
A
4318 nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
4319 nfsm_srvpostop_attr(getret, &at);
4320 if (error)
4321 return (0);
4322 nfsm_build(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
4323
4324 pc->pc_linkmax = txdr_unsigned(linkmax);
4325 pc->pc_namemax = txdr_unsigned(namemax);
4326 pc->pc_notrunc = txdr_unsigned(notrunc);
4327 pc->pc_chownrestricted = txdr_unsigned(chownres);
55e303ae
A
4328 pc->pc_caseinsensitive = txdr_unsigned(!case_sensitive);
4329 pc->pc_casepreserving = txdr_unsigned(case_preserving);
1c79356b 4330
91447636
A
4331nfsmout:
4332 return (error);
1c79356b
A
4333}
4334
4335/*
4336 * Null operation, used by clients to ping server
4337 */
4338/* ARGSUSED */
4339int
91447636
A
4340nfsrv_null(
4341 struct nfsrv_descript *nfsd,
4342 struct nfssvc_sock *slp,
4343 __unused proc_t procp,
4344 mbuf_t *mrq)
1c79356b 4345{
91447636 4346 mbuf_t mrep = nfsd->nd_mrep;
1c79356b 4347 caddr_t bpos;
91447636
A
4348 int error = NFSERR_RETVOID;
4349 mbuf_t mb, mreq;
1c79356b 4350
1c79356b 4351 nfsm_reply(0);
91447636 4352nfsmout:
1c79356b
A
4353 return (0);
4354}
4355
4356/*
4357 * No operation, used for obsolete procedures
4358 */
4359/* ARGSUSED */
4360int
91447636
A
4361nfsrv_noop(
4362 struct nfsrv_descript *nfsd,
4363 struct nfssvc_sock *slp,
4364 __unused proc_t procp,
4365 mbuf_t *mrq)
1c79356b 4366{
91447636 4367 mbuf_t mrep = nfsd->nd_mrep;
1c79356b 4368 caddr_t bpos;
91447636
A
4369 int error;
4370 mbuf_t mb, mreq;
1c79356b 4371
1c79356b
A
4372 if (nfsd->nd_repstat)
4373 error = nfsd->nd_repstat;
4374 else
4375 error = EPROCUNAVAIL;
4376 nfsm_reply(0);
91447636 4377nfsmout:
1c79356b
A
4378 return (0);
4379}
4380
4381/*
4382 * Perform access checking for vnodes obtained from file handles that would
4383 * refer to files already opened by a Unix client. You cannot just use
91447636 4384 * vnode_authorize() for two reasons.
1c79356b
A
4385 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
4386 * 2 - The owner is to be given access irrespective of mode bits so that
4387 * processes that chmod after opening a file don't break. I don't like
4388 * this because it opens a security hole, but since the nfs server opens
4389 * a security hole the size of a barn door anyhow, what the heck.
91447636
A
4390 *
4391 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
1c79356b
A
4392 * will return EPERM instead of EACCESS. EPERM is always an error.
4393 */
4394
4395static int
91447636
A
4396nfsrv_authorize(
4397 vnode_t vp,
4398 vnode_t dvp,
4399 kauth_action_t action,
4400 vfs_context_t context,
4401 struct nfs_export_options *nxo,
4402 int override)
1c79356b 4403{
91447636 4404 struct vnode_attr vattr;
1c79356b 4405 int error;
91447636
A
4406
4407 if (action & KAUTH_VNODE_WRITE_RIGHTS) {
1c79356b 4408 /*
91447636 4409 * Disallow write attempts on read-only exports;
1c79356b
A
4410 * unless the file is a socket or a block or character
4411 * device resident on the file system.
4412 */
91447636
A
4413 if (nxo->nxo_flags & NX_READONLY) {
4414 switch (vnode_vtype(vp)) {
1c79356b
A
4415 case VREG: case VDIR: case VLNK: case VCPLX:
4416 return (EROFS);
91447636
A
4417 default:
4418 break;
1c79356b
A
4419 }
4420 }
1c79356b 4421 }
91447636 4422 error = vnode_authorize(vp, dvp, action, context);
1c79356b
A
4423 /*
4424 * Allow certain operations for the owner (reads and writes
4425 * on files that are already open). Picking up from FreeBSD.
4426 */
91447636
A
4427 if (override && (error == EACCES)) {
4428 VATTR_INIT(&vattr);
4429 VATTR_WANTED(&vattr, va_uid);
4430 if ((vnode_getattr(vp, &vattr, context) == 0) &&
4431 (kauth_cred_getuid(vfs_context_ucred(context)) == vattr.va_uid))
4432 error = 0;
4433 }
1c79356b
A
4434 return error;
4435}
4436#endif /* NFS_NOSERVER */
4437