2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95
65 * FreeBSD-Id: nfs_subs.c,v 1.47 1997/11/07 08:53:24 phk Exp $
69 * These functions support the macros and help fiddle mbuf chains for
70 * the nfs op functions. They do things like create the rpc header and
71 * copy data between mbuf chains and uio lists.
73 #include <sys/param.h>
75 #include <sys/kauth.h>
76 #include <sys/systm.h>
77 #include <sys/kernel.h>
78 #include <sys/mount_internal.h>
79 #include <sys/vnode_internal.h>
80 #include <sys/kpi_mbuf.h>
81 #include <sys/socket.h>
83 #include <sys/malloc.h>
84 #include <sys/syscall.h>
85 #include <sys/ubc_internal.h>
86 #include <sys/fcntl.h>
88 #include <sys/domain.h>
89 #include <libkern/OSAtomic.h>
90 #include <kern/thread_call.h>
93 #include <sys/vmparam.h>
96 #include <kern/clock.h>
98 #include <nfs/rpcv2.h>
99 #include <nfs/nfsproto.h>
101 #include <nfs/nfsnode.h>
103 #define _NFS_XDR_SUBS_FUNCS_ /* define this to get xdrbuf function definitions */
105 #include <nfs/xdr_subs.h>
106 #include <nfs/nfsm_subs.h>
107 #include <nfs/nfs_gss.h>
108 #include <nfs/nfsmount.h>
109 #include <nfs/nfs_lock.h>
111 #include <miscfs/specfs/specdev.h>
113 #include <netinet/in.h>
114 #include <net/kpi_interface.h>
116 #include <sys/utfconv.h>
121 struct nfsstats
__attribute__((aligned(8))) nfsstats
;
122 size_t nfs_mbuf_mhlen
= 0, nfs_mbuf_minclsize
= 0;
125 * functions to convert between NFS and VFS types
128 vtonfs_type(enum vtype vtype
, int nfsvers
)
144 if (nfsvers
> NFS_VER2
)
147 if (nfsvers
> NFS_VER2
)
158 nfstov_type(nfstype nvtype
, int nfsvers
)
174 if (nfsvers
> NFS_VER2
)
177 if (nfsvers
> NFS_VER2
)
180 if (nfsvers
> NFS_VER3
)
183 if (nfsvers
> NFS_VER3
)
191 vtonfsv2_mode(enum vtype vtype
, mode_t m
)
201 return vnode_makeimode(vtype
, m
);
203 return vnode_makeimode(VCHR
, m
);
208 return vnode_makeimode(VNON
, m
);
215 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
217 int nfsv3_procid
[NFS_NPROCS
] = {
243 #endif /* NFSSERVER */
246 * and the reverse mapping from generic to Version 2 procedure numbers
248 int nfsv2_procid
[NFS_NPROCS
] = {
276 * initialize NFS's cache of mbuf constants
284 nfs_mbuf_mhlen
= ms
.mhlen
;
285 nfs_mbuf_minclsize
= ms
.minclsize
;
291 * allocate a list of mbufs to hold the given amount of data
294 nfsm_mbuf_get_list(size_t size
, mbuf_t
*mp
, int *mbcnt
)
297 mbuf_t mhead
, mlast
, m
;
301 mhead
= mlast
= NULL
;
305 nfsm_mbuf_get(error
, &m
, (size
- len
));
310 if (mlast
&& ((error
= mbuf_setnext(mlast
, m
)))) {
314 mlen
= mbuf_maxlen(m
);
315 if ((len
+ mlen
) > size
)
317 mbuf_setlen(m
, mlen
);
330 #endif /* NFSSERVER */
333 * nfsm_chain_new_mbuf()
335 * Add a new mbuf to the given chain.
338 nfsm_chain_new_mbuf(struct nfsm_chain
*nmc
, size_t sizehint
)
343 if (nmc
->nmc_flags
& NFSM_CHAIN_FLAG_ADD_CLUSTERS
)
344 sizehint
= nfs_mbuf_minclsize
;
346 /* allocate a new mbuf */
347 nfsm_mbuf_get(error
, &mb
, sizehint
);
351 panic("got NULL mbuf?");
353 /* do we have a current mbuf? */
355 /* first cap off current mbuf */
356 mbuf_setlen(nmc
->nmc_mcur
, nmc
->nmc_ptr
- (caddr_t
)mbuf_data(nmc
->nmc_mcur
));
357 /* then append the new mbuf */
358 error
= mbuf_setnext(nmc
->nmc_mcur
, mb
);
365 /* set up for using the new mbuf */
367 nmc
->nmc_ptr
= mbuf_data(mb
);
368 nmc
->nmc_left
= mbuf_trailingspace(mb
);
374 * nfsm_chain_add_opaque_f()
376 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
379 nfsm_chain_add_opaque_f(struct nfsm_chain
*nmc
, const u_char
*buf
, uint32_t len
)
381 uint32_t paddedlen
, tlen
;
384 paddedlen
= nfsm_rndup(len
);
387 if (!nmc
->nmc_left
) {
388 error
= nfsm_chain_new_mbuf(nmc
, paddedlen
);
392 tlen
= MIN(nmc
->nmc_left
, paddedlen
);
397 bcopy(buf
, nmc
->nmc_ptr
, tlen
);
399 bzero(nmc
->nmc_ptr
, tlen
);
401 nmc
->nmc_ptr
+= tlen
;
402 nmc
->nmc_left
-= tlen
;
414 * nfsm_chain_add_opaque_nopad_f()
416 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
420 nfsm_chain_add_opaque_nopad_f(struct nfsm_chain
*nmc
, const u_char
*buf
, uint32_t len
)
426 if (nmc
->nmc_left
<= 0) {
427 error
= nfsm_chain_new_mbuf(nmc
, len
);
431 tlen
= MIN(nmc
->nmc_left
, len
);
432 bcopy(buf
, nmc
->nmc_ptr
, tlen
);
433 nmc
->nmc_ptr
+= tlen
;
434 nmc
->nmc_left
-= tlen
;
442 * nfsm_chain_add_uio()
444 * Add "len" bytes of data from "uio" to the given chain.
447 nfsm_chain_add_uio(struct nfsm_chain
*nmc
, uio_t uio
, uint32_t len
)
449 uint32_t paddedlen
, tlen
;
452 paddedlen
= nfsm_rndup(len
);
455 if (!nmc
->nmc_left
) {
456 error
= nfsm_chain_new_mbuf(nmc
, paddedlen
);
460 tlen
= MIN(nmc
->nmc_left
, paddedlen
);
465 uiomove(nmc
->nmc_ptr
, tlen
, uio
);
467 bzero(nmc
->nmc_ptr
, tlen
);
469 nmc
->nmc_ptr
+= tlen
;
470 nmc
->nmc_left
-= tlen
;
480 * Find the length of the NFS mbuf chain
481 * up to the current encoding/decoding offset.
484 nfsm_chain_offset(struct nfsm_chain
*nmc
)
489 for (mb
= nmc
->nmc_mhead
; mb
; mb
= mbuf_next(mb
)) {
490 if (mb
== nmc
->nmc_mcur
)
491 return (len
+ (nmc
->nmc_ptr
- (caddr_t
) mbuf_data(mb
)));
499 * nfsm_chain_advance()
501 * Advance an nfsm_chain by "len" bytes.
504 nfsm_chain_advance(struct nfsm_chain
*nmc
, uint32_t len
)
509 if (nmc
->nmc_left
>= len
) {
510 nmc
->nmc_left
-= len
;
514 len
-= nmc
->nmc_left
;
515 nmc
->nmc_mcur
= mb
= mbuf_next(nmc
->nmc_mcur
);
518 nmc
->nmc_ptr
= mbuf_data(mb
);
519 nmc
->nmc_left
= mbuf_len(mb
);
526 * nfsm_chain_reverse()
528 * Reverse decode offset in an nfsm_chain by "len" bytes.
531 nfsm_chain_reverse(struct nfsm_chain
*nmc
, uint32_t len
)
533 uint32_t mlen
, new_offset
;
536 mlen
= nmc
->nmc_ptr
- (caddr_t
) mbuf_data(nmc
->nmc_mcur
);
539 nmc
->nmc_left
+= len
;
543 new_offset
= nfsm_chain_offset(nmc
) - len
;
544 nfsm_chain_dissect_init(error
, nmc
, nmc
->nmc_mhead
);
548 return (nfsm_chain_advance(nmc
, new_offset
));
552 * nfsm_chain_get_opaque_pointer_f()
554 * Return a pointer to the next "len" bytes of contiguous data in
555 * the mbuf chain. If the next "len" bytes are not contiguous, we
556 * try to manipulate the mbuf chain so that it is.
558 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
561 nfsm_chain_get_opaque_pointer_f(struct nfsm_chain
*nmc
, uint32_t len
, u_char
**pptr
)
564 uint32_t left
, need
, mblen
, cplen
, padlen
;
568 /* move to next mbuf with data */
569 while (nmc
->nmc_mcur
&& (nmc
->nmc_left
== 0)) {
570 mb
= mbuf_next(nmc
->nmc_mcur
);
574 nmc
->nmc_ptr
= mbuf_data(mb
);
575 nmc
->nmc_left
= mbuf_len(mb
);
577 /* check if we've run out of data */
581 /* do we already have a contiguous buffer? */
582 if (nmc
->nmc_left
>= len
) {
583 /* the returned pointer will be the current pointer */
584 *pptr
= (u_char
*)nmc
->nmc_ptr
;
585 error
= nfsm_chain_advance(nmc
, nfsm_rndup(len
));
589 padlen
= nfsm_rndup(len
) - len
;
591 /* we need (len - left) more bytes */
592 mbcur
= nmc
->nmc_mcur
;
593 left
= nmc
->nmc_left
;
596 if (need
> mbuf_trailingspace(mbcur
)) {
598 * The needed bytes won't fit in the current mbuf so we'll
599 * allocate a new mbuf to hold the contiguous range of data.
601 nfsm_mbuf_get(error
, &mb
, len
);
604 /* double check that this mbuf can hold all the data */
605 if (mbuf_maxlen(mb
) < len
) {
610 /* the returned pointer will be the new mbuf's data pointer */
611 *pptr
= ptr
= mbuf_data(mb
);
613 /* copy "left" bytes to the new mbuf */
614 bcopy(nmc
->nmc_ptr
, ptr
, left
);
616 mbuf_setlen(mb
, left
);
618 /* insert the new mbuf between the current and next mbufs */
619 error
= mbuf_setnext(mb
, mbuf_next(mbcur
));
621 error
= mbuf_setnext(mbcur
, mb
);
627 /* reduce current mbuf's length by "left" */
628 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - left
);
631 * update nmc's state to point at the end of the mbuf
632 * where the needed data will be copied to.
634 nmc
->nmc_mcur
= mbcur
= mb
;
636 nmc
->nmc_ptr
= (caddr_t
)ptr
;
638 /* The rest of the data will fit in this mbuf. */
640 /* the returned pointer will be the current pointer */
641 *pptr
= (u_char
*)nmc
->nmc_ptr
;
644 * update nmc's state to point at the end of the mbuf
645 * where the needed data will be copied to.
647 nmc
->nmc_ptr
+= left
;
652 * move the next "need" bytes into the current
653 * mbuf from the mbufs that follow
656 /* extend current mbuf length */
657 mbuf_setlen(mbcur
, mbuf_len(mbcur
) + need
);
659 /* mb follows mbufs we're copying/compacting data from */
660 mb
= mbuf_next(mbcur
);
663 /* copy as much as we need/can */
665 mblen
= mbuf_len(mb
);
666 cplen
= MIN(mblen
, need
);
668 bcopy(ptr
, nmc
->nmc_ptr
, cplen
);
670 * update the mbuf's pointer and length to reflect that
671 * the data was shifted to an earlier mbuf in the chain
673 error
= mbuf_setdata(mb
, ptr
+ cplen
, mblen
- cplen
);
675 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - need
);
678 /* update pointer/need */
679 nmc
->nmc_ptr
+= cplen
;
682 /* if more needed, go to next mbuf */
687 /* did we run out of data in the mbuf chain? */
689 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - need
);
694 * update nmc's state to point after this contiguous data
696 * "mb" points to the last mbuf we copied data from so we
697 * just set nmc to point at whatever remains in that mbuf.
700 nmc
->nmc_ptr
= mbuf_data(mb
);
701 nmc
->nmc_left
= mbuf_len(mb
);
703 /* move past any padding */
705 error
= nfsm_chain_advance(nmc
, padlen
);
711 * nfsm_chain_get_opaque_f()
713 * Read the next "len" bytes in the chain into "buf".
714 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
717 nfsm_chain_get_opaque_f(struct nfsm_chain
*nmc
, uint32_t len
, u_char
*buf
)
719 uint32_t cplen
, padlen
;
722 padlen
= nfsm_rndup(len
) - len
;
724 /* loop through mbufs copying all the data we need */
725 while (len
&& nmc
->nmc_mcur
) {
726 /* copy as much as we need/can */
727 cplen
= MIN(nmc
->nmc_left
, len
);
729 bcopy(nmc
->nmc_ptr
, buf
, cplen
);
730 nmc
->nmc_ptr
+= cplen
;
731 nmc
->nmc_left
-= cplen
;
735 /* if more needed, go to next mbuf */
737 mbuf_t mb
= mbuf_next(nmc
->nmc_mcur
);
739 nmc
->nmc_ptr
= mb
? mbuf_data(mb
) : NULL
;
740 nmc
->nmc_left
= mb
? mbuf_len(mb
) : 0;
744 /* did we run out of data in the mbuf chain? */
749 nfsm_chain_adv(error
, nmc
, padlen
);
755 * nfsm_chain_get_uio()
757 * Read the next "len" bytes in the chain into the given uio.
758 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
761 nfsm_chain_get_uio(struct nfsm_chain
*nmc
, uint32_t len
, uio_t uio
)
763 uint32_t cplen
, padlen
;
766 padlen
= nfsm_rndup(len
) - len
;
768 /* loop through mbufs copying all the data we need */
769 while (len
&& nmc
->nmc_mcur
) {
770 /* copy as much as we need/can */
771 cplen
= MIN(nmc
->nmc_left
, len
);
773 error
= uiomove(nmc
->nmc_ptr
, cplen
, uio
);
776 nmc
->nmc_ptr
+= cplen
;
777 nmc
->nmc_left
-= cplen
;
780 /* if more needed, go to next mbuf */
782 mbuf_t mb
= mbuf_next(nmc
->nmc_mcur
);
784 nmc
->nmc_ptr
= mb
? mbuf_data(mb
) : NULL
;
785 nmc
->nmc_left
= mb
? mbuf_len(mb
) : 0;
789 /* did we run out of data in the mbuf chain? */
794 nfsm_chain_adv(error
, nmc
, padlen
);
802 nfsm_chain_add_string_nfc(struct nfsm_chain
*nmc
, const uint8_t *s
, uint32_t slen
)
804 uint8_t smallbuf
[64];
805 uint8_t *nfcname
= smallbuf
;
806 size_t buflen
= sizeof(smallbuf
), nfclen
;
809 error
= utf8_normalizestr(s
, slen
, nfcname
, &nfclen
, buflen
, UTF_PRECOMPOSED
|UTF_NO_NULL_TERM
);
810 if (error
== ENAMETOOLONG
) {
812 MALLOC_ZONE(nfcname
, uint8_t *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
814 error
= utf8_normalizestr(s
, slen
, nfcname
, &nfclen
, buflen
, UTF_PRECOMPOSED
|UTF_NO_NULL_TERM
);
817 /* if we got an error, just use the original string */
819 nfsm_chain_add_string(error
, nmc
, s
, slen
);
821 nfsm_chain_add_string(error
, nmc
, nfcname
, nfclen
);
823 if (nfcname
&& (nfcname
!= smallbuf
))
824 FREE_ZONE(nfcname
, MAXPATHLEN
, M_NAMEI
);
829 * Add an NFSv2 "sattr" structure to an mbuf chain
832 nfsm_chain_add_v2sattr_f(struct nfsm_chain
*nmc
, struct vnode_attr
*vap
, uint32_t szrdev
)
836 nfsm_chain_add_32(error
, nmc
, vtonfsv2_mode(vap
->va_type
,
837 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600)));
838 nfsm_chain_add_32(error
, nmc
,
839 VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: (uint32_t)-1);
840 nfsm_chain_add_32(error
, nmc
,
841 VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: (uint32_t)-1);
842 nfsm_chain_add_32(error
, nmc
, szrdev
);
843 nfsm_chain_add_v2time(error
, nmc
,
844 VATTR_IS_ACTIVE(vap
, va_access_time
) ?
845 &vap
->va_access_time
: NULL
);
846 nfsm_chain_add_v2time(error
, nmc
,
847 VATTR_IS_ACTIVE(vap
, va_modify_time
) ?
848 &vap
->va_modify_time
: NULL
);
854 * Add an NFSv3 "sattr" structure to an mbuf chain
857 nfsm_chain_add_v3sattr_f(struct nfsm_chain
*nmc
, struct vnode_attr
*vap
)
861 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
862 nfsm_chain_add_32(error
, nmc
, TRUE
);
863 nfsm_chain_add_32(error
, nmc
, vap
->va_mode
);
865 nfsm_chain_add_32(error
, nmc
, FALSE
);
867 if (VATTR_IS_ACTIVE(vap
, va_uid
)) {
868 nfsm_chain_add_32(error
, nmc
, TRUE
);
869 nfsm_chain_add_32(error
, nmc
, vap
->va_uid
);
871 nfsm_chain_add_32(error
, nmc
, FALSE
);
873 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
874 nfsm_chain_add_32(error
, nmc
, TRUE
);
875 nfsm_chain_add_32(error
, nmc
, vap
->va_gid
);
877 nfsm_chain_add_32(error
, nmc
, FALSE
);
879 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
880 nfsm_chain_add_32(error
, nmc
, TRUE
);
881 nfsm_chain_add_64(error
, nmc
, vap
->va_data_size
);
883 nfsm_chain_add_32(error
, nmc
, FALSE
);
885 if (vap
->va_vaflags
& VA_UTIMES_NULL
) {
886 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_SERVER
);
887 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_SERVER
);
889 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
890 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_CLIENT
);
891 nfsm_chain_add_32(error
, nmc
, vap
->va_access_time
.tv_sec
);
892 nfsm_chain_add_32(error
, nmc
, vap
->va_access_time
.tv_nsec
);
894 nfsm_chain_add_32(error
, nmc
, NFS_TIME_DONT_CHANGE
);
896 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
897 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_CLIENT
);
898 nfsm_chain_add_32(error
, nmc
, vap
->va_modify_time
.tv_sec
);
899 nfsm_chain_add_32(error
, nmc
, vap
->va_modify_time
.tv_nsec
);
901 nfsm_chain_add_32(error
, nmc
, NFS_TIME_DONT_CHANGE
);
910 * nfsm_chain_get_fh_attr()
912 * Get the file handle and attributes from an mbuf chain. (NFSv2/v3)
915 nfsm_chain_get_fh_attr(
916 struct nfsm_chain
*nmc
,
922 struct nfs_vattr
*nvap
)
924 int error
= 0, gotfh
, gotattr
;
928 if (nfsvers
== NFS_VER3
) /* check for file handle */
929 nfsm_chain_get_32(error
, nmc
, gotfh
);
930 if (!error
&& gotfh
) /* get file handle */
931 nfsm_chain_get_fh(error
, nmc
, nfsvers
, fhp
);
934 if (nfsvers
== NFS_VER3
) /* check for file attributes */
935 nfsm_chain_get_32(error
, nmc
, gotattr
);
938 if (!gotfh
) /* skip attributes */
939 nfsm_chain_adv(error
, nmc
, NFSX_V3FATTR
);
940 else /* get attributes */
941 error
= nfs_parsefattr(nmc
, nfsvers
, nvap
);
943 /* we need valid attributes in order to call nfs_nget() */
944 if (nfs3_getattr_rpc(NULL
, NFSTOMP(dnp
), fhp
->fh_data
, fhp
->fh_len
, 0, ctx
, nvap
, xidp
)) {
954 * Get and process NFSv3 WCC data from an mbuf chain
957 nfsm_chain_get_wcc_data_f(
958 struct nfsm_chain
*nmc
,
960 struct timespec
*premtime
,
967 nfsm_chain_get_32(error
, nmc
, flag
);
968 if (!error
&& flag
) {
969 nfsm_chain_adv(error
, nmc
, 2 * NFSX_UNSIGNED
);
970 nfsm_chain_get_32(error
, nmc
, premtime
->tv_sec
);
971 nfsm_chain_get_32(error
, nmc
, premtime
->tv_nsec
);
972 nfsm_chain_adv(error
, nmc
, 2 * NFSX_UNSIGNED
);
974 premtime
->tv_sec
= 0;
975 premtime
->tv_nsec
= 0;
977 nfsm_chain_postop_attr_update_flag(error
, nmc
, np
, *newpostattr
, xidp
);
983 * Get the next RPC transaction ID (XID)
986 nfs_get_xid(uint64_t *xidp
)
990 lck_mtx_lock(nfs_request_mutex
);
993 * Derive initial xid from system time.
995 * Note: it's OK if this code inits nfs_xid to 0 (for example,
996 * due to a broken clock) because we immediately increment it
997 * and we guarantee to never use xid 0. So, nfs_xid should only
998 * ever be 0 the first time this function is called.
1001 nfs_xid
= tv
.tv_sec
<< 12;
1003 if (++nfs_xid
== 0) {
1004 /* Skip zero xid if it should ever happen. */
1008 *xidp
= nfs_xid
+ ((uint64_t)nfs_xidwrap
<< 32);
1009 lck_mtx_unlock(nfs_request_mutex
);
1013 * Build the RPC header and fill in the authorization info.
1014 * Returns the head of the mbuf list and the xid.
1024 struct nfsmount
*nmp
= req
->r_nmp
;
1025 int nfsvers
= nmp
->nm_vers
;
1026 int proc
= ((nfsvers
== NFS_VER2
) ? nfsv2_procid
[req
->r_procnum
] : (int)req
->r_procnum
);
1028 return nfsm_rpchead2(nmp
, nmp
->nm_sotype
, NFS_PROG
, nfsvers
, proc
,
1029 req
->r_auth
, req
->r_cred
, req
, mrest
, xidp
, mreqp
);
1033 nfsm_rpchead2(struct nfsmount
*nmp
, int sotype
, int prog
, int vers
, int proc
, int auth_type
,
1034 kauth_cred_t cred
, struct nfsreq
*req
, mbuf_t mrest
, u_int64_t
*xidp
, mbuf_t
*mreqp
)
1037 int error
, i
, grpsiz
, auth_len
= 0, authsiz
, reqlen
;
1039 struct nfsm_chain nmreq
;
1041 /* calculate expected auth length */
1042 switch (auth_type
) {
1048 gid_t grouplist
[NGROUPS
];
1049 int groupcount
= NGROUPS
;
1054 (void)kauth_cred_getgroups(cred
, grouplist
, &groupcount
);
1058 auth_len
= (((((uint32_t)groupcount
- 1) > nmp
->nm_numgrps
) ?
1059 nmp
->nm_numgrps
: (groupcount
- 1)) << 2) +
1068 auth_len
= 5 * NFSX_UNSIGNED
+ 0; // zero context handle for now
1073 authsiz
= nfsm_rndup(auth_len
);
1075 /* allocate the packet */
1076 headlen
= authsiz
+ 10 * NFSX_UNSIGNED
;
1077 if (sotype
== SOCK_STREAM
) /* also include room for any RPC Record Mark */
1078 headlen
+= NFSX_UNSIGNED
;
1079 if (headlen
>= nfs_mbuf_minclsize
) {
1080 error
= mbuf_getpacket(MBUF_WAITOK
, &mreq
);
1082 error
= mbuf_gethdr(MBUF_WAITOK
, MBUF_TYPE_DATA
, &mreq
);
1084 if (headlen
< nfs_mbuf_mhlen
)
1085 mbuf_align_32(mreq
, headlen
);
1087 mbuf_align_32(mreq
, 8 * NFSX_UNSIGNED
);
1091 /* unable to allocate packet */
1092 /* XXX should we keep statistics for these errors? */
1097 * If the caller gave us a non-zero XID then use it because
1098 * it may be a higher-level resend with a GSSAPI credential.
1099 * Otherwise, allocate a new one.
1104 /* build the header(s) */
1105 nfsm_chain_init(&nmreq
, mreq
);
1107 /* First, if it's a TCP stream insert space for an RPC record mark */
1108 if (sotype
== SOCK_STREAM
)
1109 nfsm_chain_add_32(error
, &nmreq
, 0);
1111 /* Then the RPC header. */
1112 nfsm_chain_add_32(error
, &nmreq
, (*xidp
& 0xffffffff));
1113 nfsm_chain_add_32(error
, &nmreq
, RPC_CALL
);
1114 nfsm_chain_add_32(error
, &nmreq
, RPC_VER2
);
1115 nfsm_chain_add_32(error
, &nmreq
, prog
);
1116 nfsm_chain_add_32(error
, &nmreq
, vers
);
1117 nfsm_chain_add_32(error
, &nmreq
, proc
);
1120 switch (auth_type
) {
1122 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NONE
); /* auth */
1123 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1124 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NONE
); /* verf */
1125 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1126 nfsm_chain_build_done(error
, &nmreq
);
1127 /* Append the args mbufs */
1129 error
= mbuf_setnext(nmreq
.nmc_mcur
, mrest
);
1132 gid_t grouplist
[NGROUPS
];
1135 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_SYS
);
1136 nfsm_chain_add_32(error
, &nmreq
, authsiz
);
1137 nfsm_chain_add_32(error
, &nmreq
, 0); /* stamp */
1138 nfsm_chain_add_32(error
, &nmreq
, 0); /* zero-length hostname */
1139 nfsm_chain_add_32(error
, &nmreq
, kauth_cred_getuid(cred
)); /* UID */
1140 nfsm_chain_add_32(error
, &nmreq
, kauth_cred_getgid(cred
)); /* GID */
1141 grpsiz
= (auth_len
>> 2) - 5;
1142 nfsm_chain_add_32(error
, &nmreq
, grpsiz
);/* additional GIDs */
1143 memset(grouplist
, 0, sizeof(grouplist
));
1144 groupcount
= grpsiz
;
1145 (void)kauth_cred_getgroups(cred
, grouplist
, &groupcount
);
1146 for (i
= 1; i
<= grpsiz
; i
++)
1147 nfsm_chain_add_32(error
, &nmreq
, grouplist
[i
]);
1149 /* And the verifier... */
1150 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NONE
); /* flavor */
1151 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1152 nfsm_chain_build_done(error
, &nmreq
);
1154 /* Append the args mbufs */
1156 error
= mbuf_setnext(nmreq
.nmc_mcur
, mrest
);
1162 error
= nfs_gss_clnt_cred_put(req
, &nmreq
, mrest
);
1163 if (error
== ENEEDAUTH
) {
1164 gid_t grouplist
[NGROUPS
];
1165 int groupcount
= NGROUPS
;
1167 * Use sec=sys for this user
1170 req
->r_auth
= auth_type
= RPCAUTH_SYS
;
1171 (void)kauth_cred_getgroups(cred
, grouplist
, &groupcount
);
1172 auth_len
= (((((uint32_t)groupcount
- 1) > nmp
->nm_numgrps
) ?
1173 nmp
->nm_numgrps
: (groupcount
- 1)) << 2) +
1175 authsiz
= nfsm_rndup(auth_len
);
1181 /* finish setting up the packet */
1183 error
= mbuf_pkthdr_setrcvif(mreq
, 0);
1190 /* Calculate the size of the request */
1192 for (mb
= nmreq
.nmc_mhead
; mb
; mb
= mbuf_next(mb
))
1193 reqlen
+= mbuf_len(mb
);
1195 mbuf_pkthdr_setlen(mreq
, reqlen
);
1198 * If the request goes on a TCP stream,
1199 * set its size in the RPC record mark.
1200 * The record mark count doesn't include itself
1201 * and the last fragment bit is set.
1203 if (sotype
== SOCK_STREAM
)
1204 nfsm_chain_set_recmark(error
, &nmreq
,
1205 (reqlen
- NFSX_UNSIGNED
) | 0x80000000);
1212 * Parse an NFS file attribute structure out of an mbuf chain.
1215 nfs_parsefattr(struct nfsm_chain
*nmc
, int nfsvers
, struct nfs_vattr
*nvap
)
1227 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_TYPE
);
1228 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_MODE
);
1229 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_NUMLINKS
);
1230 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_OWNER
);
1231 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_OWNER_GROUP
);
1232 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_SIZE
);
1233 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_SPACE_USED
);
1234 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_RAWDEV
);
1235 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_FSID
);
1236 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_FILEID
);
1237 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_TIME_ACCESS
);
1238 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_TIME_MODIFY
);
1239 NFS_BITMAP_SET(nvap
->nva_bitmap
, NFS_FATTR_TIME_METADATA
);
1241 nfsm_chain_get_32(error
, nmc
, nvtype
);
1242 nfsm_chain_get_32(error
, nmc
, vmode
);
1245 if (nfsvers
== NFS_VER3
) {
1246 nvap
->nva_type
= vtype
= nfstov_type(nvtype
, nfsvers
);
1249 * The duplicate information returned in fa_type and fa_mode
1250 * is an ambiguity in the NFS version 2 protocol.
1252 * VREG should be taken literally as a regular file. If a
1253 * server intends to return some type information differently
1254 * in the upper bits of the mode field (e.g. for sockets, or
1255 * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we
1256 * leave the examination of the mode bits even in the VREG
1257 * case to avoid breakage for bogus servers, but we make sure
1258 * that there are actually type bits set in the upper part of
1259 * fa_mode (and failing that, trust the va_type field).
1261 * NFSv3 cleared the issue, and requires fa_mode to not
1262 * contain any type information (while also introducing
1263 * sockets and FIFOs for fa_type).
1265 vtype
= nfstov_type(nvtype
, nfsvers
);
1266 if ((vtype
== VNON
) || ((vtype
== VREG
) && ((vmode
& S_IFMT
) != 0)))
1267 vtype
= IFTOVT(vmode
);
1268 nvap
->nva_type
= vtype
;
1271 nvap
->nva_mode
= (vmode
& 07777);
1273 nfsm_chain_get_32(error
, nmc
, nvap
->nva_nlink
);
1274 nfsm_chain_get_32(error
, nmc
, nvap
->nva_uid
);
1275 nfsm_chain_get_32(error
, nmc
, nvap
->nva_gid
);
1277 if (nfsvers
== NFS_VER3
) {
1278 nfsm_chain_get_64(error
, nmc
, nvap
->nva_size
);
1279 nfsm_chain_get_64(error
, nmc
, nvap
->nva_bytes
);
1280 nfsm_chain_get_32(error
, nmc
, nvap
->nva_rawdev
.specdata1
);
1281 nfsm_chain_get_32(error
, nmc
, nvap
->nva_rawdev
.specdata2
);
1283 nfsm_chain_get_64(error
, nmc
, nvap
->nva_fsid
.major
);
1284 nvap
->nva_fsid
.minor
= 0;
1285 nfsm_chain_get_64(error
, nmc
, nvap
->nva_fileid
);
1287 nfsm_chain_get_32(error
, nmc
, nvap
->nva_size
);
1288 nfsm_chain_adv(error
, nmc
, NFSX_UNSIGNED
);
1289 nfsm_chain_get_32(error
, nmc
, rdev
);
1291 nvap
->nva_rawdev
.specdata1
= major(rdev
);
1292 nvap
->nva_rawdev
.specdata2
= minor(rdev
);
1293 nfsm_chain_get_32(error
, nmc
, val
); /* blocks */
1295 nvap
->nva_bytes
= val
* NFS_FABLKSIZE
;
1296 nfsm_chain_get_32(error
, nmc
, val
);
1298 nvap
->nva_fsid
.major
= (uint64_t)val
;
1299 nvap
->nva_fsid
.minor
= 0;
1300 nfsm_chain_get_32(error
, nmc
, val
);
1302 nvap
->nva_fileid
= (uint64_t)val
;
1303 /* Really ugly NFSv2 kludge. */
1304 if ((vtype
== VCHR
) && (rdev
== (dev_t
)0xffffffff))
1305 nvap
->nva_type
= VFIFO
;
1307 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1308 nvap
->nva_timesec
[NFSTIME_ACCESS
],
1309 nvap
->nva_timensec
[NFSTIME_ACCESS
]);
1310 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1311 nvap
->nva_timesec
[NFSTIME_MODIFY
],
1312 nvap
->nva_timensec
[NFSTIME_MODIFY
]);
1313 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1314 nvap
->nva_timesec
[NFSTIME_CHANGE
],
1315 nvap
->nva_timensec
[NFSTIME_CHANGE
]);
1321 * Load the attribute cache (that lives in the nfsnode entry) with
1322 * the value pointed to by nvap, unless the file type in the attribute
1323 * cache doesn't match the file type in the nvap, in which case log a
1324 * warning and return ESTALE.
1326 * If the dontshrink flag is set, then it's not safe to call ubc_setsize()
1327 * to shrink the size of the file.
1332 struct nfs_vattr
*nvap
,
1339 struct nfs_vattr
*npnvap
;
1340 int xattr
= np
->n_vattr
.nva_flags
& NFS_FFLAG_IS_ATTR
;
1341 int referral
= np
->n_vattr
.nva_flags
& NFS_FFLAG_TRIGGER_REFERRAL
;
1342 int aclbit
, monitored
, error
= 0;
1344 struct nfsmount
*nmp
;
1345 uint32_t events
= np
->n_events
;
1347 if (np
->n_hflag
& NHINIT
) {
1352 mp
= vnode_mount(vp
);
1354 monitored
= vp
? vnode_ismonitored(vp
) : 0;
1356 FSDBG_TOP(527, np
, vp
, *xidp
>> 32, *xidp
);
1358 if (!((nmp
= VFSTONFS(mp
)))) {
1359 FSDBG_BOT(527, ENXIO
, 1, 0, *xidp
);
1363 if (*xidp
< np
->n_xid
) {
1365 * We have already updated attributes with a response from
1366 * a later request. The attributes we have here are probably
1367 * stale so we drop them (just return). However, our
1368 * out-of-order receipt could be correct - if the requests were
1369 * processed out of order at the server. Given the uncertainty
1370 * we invalidate our cached attributes. *xidp is zeroed here
1371 * to indicate the attributes were dropped - only getattr
1372 * cares - it needs to retry the rpc.
1374 NATTRINVALIDATE(np
);
1375 FSDBG_BOT(527, 0, np
, np
->n_xid
, *xidp
);
1380 if (vp
&& (nvap
->nva_type
!= vnode_vtype(vp
))) {
1382 * The filehandle has changed type on us. This can be
1383 * caused by either the server not having unique filehandles
1384 * or because another client has removed the previous
1385 * filehandle and a new object (of a different type)
1386 * has been created with the same filehandle.
1388 * We can't simply switch the type on the vnode because
1389 * there may be type-specific fields that need to be
1390 * cleaned up or set up.
1392 * So, what should we do with this vnode?
1394 * About the best we can do is log a warning and return
1395 * an error. ESTALE is about the closest error, but it
1396 * is a little strange that we come up with this error
1397 * internally instead of simply passing it through from
1398 * the server. Hopefully, the vnode will be reclaimed
1399 * soon so the filehandle can be reincarnated as the new
1402 printf("nfs loadattrcache vnode changed type, was %d now %d\n",
1403 vnode_vtype(vp
), nvap
->nva_type
);
1406 events
|= VNODE_EVENT_DELETE
;
1410 npnvap
= &np
->n_vattr
;
1413 * The ACL cache needs special handling because it is not
1414 * always updated. Save current ACL cache state so it can
1415 * be restored after copying the new attributes into place.
1417 aclbit
= NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_ACL
);
1418 acl
= npnvap
->nva_acl
;
1422 * For monitored nodes, check for attribute changes that should generate events.
1424 if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_NUMLINKS
) &&
1425 (nvap
->nva_nlink
!= npnvap
->nva_nlink
))
1426 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_LINK
;
1427 if (events
& VNODE_EVENT_PERMS
)
1428 /* no need to do all the checking if it's already set */;
1429 else if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_MODE
) &&
1430 (nvap
->nva_mode
!= npnvap
->nva_mode
))
1431 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1432 else if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_OWNER
) &&
1433 (nvap
->nva_uid
!= npnvap
->nva_uid
))
1434 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1435 else if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_OWNER_GROUP
) &&
1436 (nvap
->nva_gid
!= npnvap
->nva_gid
))
1437 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1438 else if (nmp
->nm_vers
>= NFS_VER4
) {
1439 if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_OWNER
) &&
1440 !kauth_guid_equal(&nvap
->nva_uuuid
, &npnvap
->nva_uuuid
))
1441 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1442 else if (NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_OWNER_GROUP
) &&
1443 !kauth_guid_equal(&nvap
->nva_guuid
, &npnvap
->nva_guuid
))
1444 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1445 else if ((NFS_BITMAP_ISSET(nvap
->nva_bitmap
, NFS_FATTR_ACL
) &&
1446 nvap
->nva_acl
&& npnvap
->nva_acl
&&
1447 ((nvap
->nva_acl
->acl_entrycount
!= npnvap
->nva_acl
->acl_entrycount
) ||
1448 bcmp(nvap
->nva_acl
, npnvap
->nva_acl
, KAUTH_ACL_COPYSIZE(nvap
->nva_acl
)))))
1449 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_PERMS
;
1451 if (((nmp
->nm_vers
>= NFS_VER4
) && (nvap
->nva_change
!= npnvap
->nva_change
)) ||
1452 (NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_TIME_MODIFY
) &&
1453 ((nvap
->nva_timesec
[NFSTIME_MODIFY
] != npnvap
->nva_timesec
[NFSTIME_MODIFY
]) ||
1454 (nvap
->nva_timensec
[NFSTIME_MODIFY
] != npnvap
->nva_timensec
[NFSTIME_MODIFY
]))))
1455 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_WRITE
;
1456 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_RAWDEV
) &&
1457 ((nvap
->nva_rawdev
.specdata1
!= npnvap
->nva_rawdev
.specdata1
) ||
1458 (nvap
->nva_rawdev
.specdata2
!= npnvap
->nva_rawdev
.specdata2
)))
1459 events
|= VNODE_EVENT_ATTRIB
;
1460 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_FILEID
) &&
1461 (nvap
->nva_fileid
!= npnvap
->nva_fileid
))
1462 events
|= VNODE_EVENT_ATTRIB
;
1463 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_ARCHIVE
) &&
1464 ((nvap
->nva_flags
& NFS_FFLAG_ARCHIVED
) != (npnvap
->nva_flags
& NFS_FFLAG_ARCHIVED
)))
1465 events
|= VNODE_EVENT_ATTRIB
;
1466 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_HIDDEN
) &&
1467 ((nvap
->nva_flags
& NFS_FFLAG_HIDDEN
) != (npnvap
->nva_flags
& NFS_FFLAG_HIDDEN
)))
1468 events
|= VNODE_EVENT_ATTRIB
;
1469 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_TIME_CREATE
) &&
1470 ((nvap
->nva_timesec
[NFSTIME_CREATE
] != npnvap
->nva_timesec
[NFSTIME_CREATE
]) ||
1471 (nvap
->nva_timensec
[NFSTIME_CREATE
] != npnvap
->nva_timensec
[NFSTIME_CREATE
])))
1472 events
|= VNODE_EVENT_ATTRIB
;
1473 if (!events
&& NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_TIME_BACKUP
) &&
1474 ((nvap
->nva_timesec
[NFSTIME_BACKUP
] != npnvap
->nva_timesec
[NFSTIME_BACKUP
]) ||
1475 (nvap
->nva_timensec
[NFSTIME_BACKUP
] != npnvap
->nva_timensec
[NFSTIME_BACKUP
])))
1476 events
|= VNODE_EVENT_ATTRIB
;
1479 /* Copy the attributes to the attribute cache */
1480 bcopy((caddr_t
)nvap
, (caddr_t
)npnvap
, sizeof(*nvap
));
1483 np
->n_attrstamp
= now
.tv_sec
;
1485 /* NFS_FFLAG_IS_ATTR and NFS_FFLAG_TRIGGER_REFERRAL need to be sticky... */
1487 nvap
->nva_flags
|= xattr
;
1489 nvap
->nva_flags
|= referral
;
1491 if (NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_ACL
)) {
1492 /* we're updating the ACL */
1493 if (nvap
->nva_acl
) {
1494 /* make a copy of the acl for the cache */
1495 npnvap
->nva_acl
= kauth_acl_alloc(nvap
->nva_acl
->acl_entrycount
);
1496 if (npnvap
->nva_acl
) {
1497 bcopy(nvap
->nva_acl
, npnvap
->nva_acl
, KAUTH_ACL_COPYSIZE(nvap
->nva_acl
));
1499 /* can't make a copy to cache, invalidate ACL cache */
1500 NFS_BITMAP_CLR(npnvap
->nva_bitmap
, NFS_FATTR_ACL
);
1506 kauth_acl_free(acl
);
1510 if (NFS_BITMAP_ISSET(npnvap
->nva_bitmap
, NFS_FATTR_ACL
)) {
1511 /* update the ACL timestamp */
1512 np
->n_aclstamp
= now
.tv_sec
;
1514 /* we aren't updating the ACL, so restore original values */
1516 NFS_BITMAP_SET(npnvap
->nva_bitmap
, NFS_FATTR_ACL
);
1517 npnvap
->nva_acl
= acl
;
1522 * For NFSv4, if the fsid doesn't match the fsid for the mount, then
1523 * this node is for a different file system on the server. So we mark
1524 * this node as a trigger node that will trigger the mirror mount.
1526 if ((nmp
->nm_vers
>= NFS_VER4
) && (nvap
->nva_type
== VDIR
) &&
1527 ((np
->n_vattr
.nva_fsid
.major
!= nmp
->nm_fsid
.major
) ||
1528 (np
->n_vattr
.nva_fsid
.minor
!= nmp
->nm_fsid
.minor
)))
1529 np
->n_vattr
.nva_flags
|= NFS_FFLAG_TRIGGER
;
1532 if (!vp
|| (nvap
->nva_type
!= VREG
)) {
1533 np
->n_size
= nvap
->nva_size
;
1534 } else if (nvap
->nva_size
!= np
->n_size
) {
1535 FSDBG(527, np
, nvap
->nva_size
, np
->n_size
, (nvap
->nva_type
== VREG
) | (np
->n_flag
& NMODIFIED
? 6 : 4));
1536 if (!UBCINFOEXISTS(vp
) || (dontshrink
&& (nvap
->nva_size
< np
->n_size
))) {
1537 /* asked not to shrink, so stick with current size */
1538 FSDBG(527, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0001);
1539 nvap
->nva_size
= np
->n_size
;
1540 NATTRINVALIDATE(np
);
1541 } else if ((np
->n_flag
& NMODIFIED
) && (nvap
->nva_size
< np
->n_size
)) {
1542 /* if we've modified, stick with larger size */
1543 FSDBG(527, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0002);
1544 nvap
->nva_size
= np
->n_size
;
1545 npnvap
->nva_size
= np
->n_size
;
1548 * n_size is protected by the data lock, so we need to
1549 * defer updating it until it's safe. We save the new size
1550 * and set a flag and it'll get updated the next time we get/drop
1551 * the data lock or the next time we do a getattr.
1553 np
->n_newsize
= nvap
->nva_size
;
1554 SET(np
->n_flag
, NUPDATESIZE
);
1556 events
|= VNODE_EVENT_ATTRIB
| VNODE_EVENT_EXTEND
;
1560 if (np
->n_flag
& NCHG
) {
1561 if (np
->n_flag
& NACC
) {
1562 nvap
->nva_timesec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_sec
;
1563 nvap
->nva_timensec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_nsec
;
1565 if (np
->n_flag
& NUPD
) {
1566 nvap
->nva_timesec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_sec
;
1567 nvap
->nva_timensec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_nsec
;
1572 if (monitored
&& events
)
1573 nfs_vnode_notify(np
, events
);
1574 FSDBG_BOT(527, error
, np
, np
->n_size
, *xidp
);
1579 * Calculate the attribute timeout based on
1580 * how recently the file has been modified.
1583 nfs_attrcachetimeout(nfsnode_t np
)
1585 struct nfsmount
*nmp
;
1590 if (!(nmp
= NFSTONMP(np
)))
1593 isdir
= vnode_isdir(NFSTOV(np
));
1595 if ((nmp
->nm_vers
>= NFS_VER4
) && (np
->n_openflags
& N_DELEG_MASK
)) {
1596 /* If we have a delegation, we always use the max timeout. */
1597 timeo
= isdir
? nmp
->nm_acdirmax
: nmp
->nm_acregmax
;
1598 } else if ((np
)->n_flag
& NMODIFIED
) {
1599 /* If we have modifications, we always use the min timeout. */
1600 timeo
= isdir
? nmp
->nm_acdirmin
: nmp
->nm_acregmin
;
1602 /* Otherwise, we base the timeout on how old the file seems. */
1603 /* Note that if the client and server clocks are way out of sync, */
1604 /* timeout will probably get clamped to a min or max value */
1606 timeo
= (now
.tv_sec
- (np
)->n_vattr
.nva_timesec
[NFSTIME_MODIFY
]) / 10;
1608 if (timeo
< nmp
->nm_acdirmin
)
1609 timeo
= nmp
->nm_acdirmin
;
1610 else if (timeo
> nmp
->nm_acdirmax
)
1611 timeo
= nmp
->nm_acdirmax
;
1613 if (timeo
< nmp
->nm_acregmin
)
1614 timeo
= nmp
->nm_acregmin
;
1615 else if (timeo
> nmp
->nm_acregmax
)
1616 timeo
= nmp
->nm_acregmax
;
1624 * Check the attribute cache time stamp.
1625 * If the cache is valid, copy contents to *nvaper and return 0
1626 * otherwise return an error.
1627 * Must be called with the node locked.
1630 nfs_getattrcache(nfsnode_t np
, struct nfs_vattr
*nvaper
, int flags
)
1632 struct nfs_vattr
*nvap
;
1633 struct timeval nowup
;
1636 /* Check if the attributes are valid. */
1637 if (!NATTRVALID(np
) || ((flags
& NGA_ACL
) && !NACLVALID(np
))) {
1638 FSDBG(528, np
, 0, 0xffffff01, ENOENT
);
1639 OSAddAtomic64(1, &nfsstats
.attrcache_misses
);
1643 /* Verify the cached attributes haven't timed out. */
1644 timeo
= nfs_attrcachetimeout(np
);
1645 microuptime(&nowup
);
1646 if ((nowup
.tv_sec
- np
->n_attrstamp
) >= timeo
) {
1647 FSDBG(528, np
, 0, 0xffffff02, ENOENT
);
1648 OSAddAtomic64(1, &nfsstats
.attrcache_misses
);
1651 if ((flags
& NGA_ACL
) && ((nowup
.tv_sec
- np
->n_aclstamp
) >= timeo
)) {
1652 FSDBG(528, np
, 0, 0xffffff02, ENOENT
);
1653 OSAddAtomic64(1, &nfsstats
.attrcache_misses
);
1657 nvap
= &np
->n_vattr
;
1658 FSDBG(528, np
, nvap
->nva_size
, np
->n_size
, 0xcace);
1659 OSAddAtomic64(1, &nfsstats
.attrcache_hits
);
1661 if (nvap
->nva_type
!= VREG
) {
1662 np
->n_size
= nvap
->nva_size
;
1663 } else if (nvap
->nva_size
!= np
->n_size
) {
1664 FSDBG(528, np
, nvap
->nva_size
, np
->n_size
, (nvap
->nva_type
== VREG
) | (np
->n_flag
& NMODIFIED
? 6 : 4));
1665 if ((np
->n_flag
& NMODIFIED
) && (nvap
->nva_size
< np
->n_size
)) {
1666 /* if we've modified, stick with larger size */
1667 nvap
->nva_size
= np
->n_size
;
1670 * n_size is protected by the data lock, so we need to
1671 * defer updating it until it's safe. We save the new size
1672 * and set a flag and it'll get updated the next time we get/drop
1673 * the data lock or the next time we do a getattr.
1675 np
->n_newsize
= nvap
->nva_size
;
1676 SET(np
->n_flag
, NUPDATESIZE
);
1680 bcopy((caddr_t
)nvap
, (caddr_t
)nvaper
, sizeof(struct nfs_vattr
));
1681 if (np
->n_flag
& NCHG
) {
1682 if (np
->n_flag
& NACC
) {
1683 nvaper
->nva_timesec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_sec
;
1684 nvaper
->nva_timensec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_nsec
;
1686 if (np
->n_flag
& NUPD
) {
1687 nvaper
->nva_timesec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_sec
;
1688 nvaper
->nva_timensec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_nsec
;
1691 if (nvap
->nva_acl
) {
1692 if (flags
& NGA_ACL
) {
1693 nvaper
->nva_acl
= kauth_acl_alloc(nvap
->nva_acl
->acl_entrycount
);
1694 if (!nvaper
->nva_acl
)
1696 bcopy(nvap
->nva_acl
, nvaper
->nva_acl
, KAUTH_ACL_COPYSIZE(nvap
->nva_acl
));
1698 nvaper
->nva_acl
= NULL
;
1705 * When creating file system objects:
1706 * Don't bother setting UID if it's the same as the credential performing the create.
1707 * Don't bother setting GID if it's the same as the directory or credential.
1710 nfs_avoid_needless_id_setting_on_create(nfsnode_t dnp
, struct vnode_attr
*vap
, vfs_context_t ctx
)
1712 if (VATTR_IS_ACTIVE(vap
, va_uid
)) {
1713 if (kauth_cred_getuid(vfs_context_ucred(ctx
)) == vap
->va_uid
) {
1714 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
1715 VATTR_CLEAR_ACTIVE(vap
, va_uuuid
);
1718 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
1719 if ((vap
->va_gid
== dnp
->n_vattr
.nva_gid
) ||
1720 (kauth_cred_getgid(vfs_context_ucred(ctx
)) == vap
->va_gid
)) {
1721 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
1722 VATTR_CLEAR_ACTIVE(vap
, va_guuid
);
1728 * Convert a universal address string to a sockaddr structure.
1730 * Universal addresses can be in the following formats:
1732 * d = decimal (IPv4)
1733 * x = hexadecimal (IPv6)
1734 * p = port (decimal)
1739 * x:x:x:x:x:x:x:x.p.p
1740 * x:x:x:x:x:x:d.d.d.d
1741 * x:x:x:x:x:x:d.d.d.d.p.p
1743 * IPv6 strings can also have a series of zeroes elided
1744 * IPv6 strings can also have a %scope suffix at the end (after any port)
1746 * rules & exceptions:
1747 * - value before : is hex
1748 * - value before . is dec
1749 * - once . hit, all values are dec
1750 * - hex+port case means value before first dot is actually hex
1751 * - . is always preceded by digits except if last hex was double-colon
1753 * scan, converting #s to bytes
1754 * first time a . is encountered, scan the rest to count them.
1755 * 2 dots = just port
1756 * 3 dots = just IPv4 no port
1757 * 5 dots = IPv4 and port
1760 #define IS_DIGIT(C) \
1761 (((C) >= '0') && ((C) <= '9'))
1763 #define IS_XDIGIT(C) \
1765 (((C) >= 'A') && ((C) <= 'F')) || \
1766 (((C) >= 'a') && ((C) <= 'f')))
1769 nfs_uaddr2sockaddr(const char *uaddr
, struct sockaddr
*addr
)
1771 const char *p
, *pd
; /* pointers to current character in scan */
1772 const char *pnum
; /* pointer to current number to decode */
1773 const char *pscope
; /* pointer to IPv6 scope ID */
1774 uint8_t a
[18]; /* octet array to store address bytes */
1775 int i
; /* index of next octet to decode */
1776 int dci
; /* index of octet to insert double-colon zeroes */
1777 int dcount
, xdcount
; /* count of digits in current number */
1778 int needmore
; /* set when we know we need more input (e.g. after colon, period) */
1779 int dots
; /* # of dots */
1780 int hex
; /* contains hex values */
1781 unsigned long val
; /* decoded value */
1782 int s
; /* index used for sliding array to insert elided zeroes */
1785 #define DECIMALVALUE 1
1788 if ((dcount <= 0) || (dcount > (((TYPE) == DECIMALVALUE) ? 3 : 4))) \
1790 if (((TYPE) == DECIMALVALUE) && xdcount) \
1792 val = strtoul(pnum, NULL, ((TYPE) == DECIMALVALUE) ? 10 : 16); \
1793 if (((TYPE) == DECIMALVALUE) && (val >= 256)) \
1795 /* check if there is room left in the array */ \
1796 if (i > (int)(sizeof(a) - (((TYPE) == HEXVALUE) ? 2 : 1) - ((dci != -1) ? 2 : 0))) \
1798 if ((TYPE) == HEXVALUE) \
1799 a[i++] = ((val >> 8) & 0xff); \
1800 a[i++] = (val & 0xff); \
1806 i
= dcount
= xdcount
= 0;
1810 if ((*p
== ':') && (*++p
!= ':')) /* if it starts with colon, gotta be a double */
1814 if (IS_XDIGIT(*p
)) {
1820 } else if (*p
== '.') {
1821 /* rest is decimal IPv4 dotted quad and/or port */
1823 /* this is the first, so count them */
1824 for (pd
= p
; *pd
; pd
++) {
1828 } else if (hex
&& (*pd
== '%')) {
1830 } else if ((*pd
< '0') || (*pd
> '9')) {
1834 if ((dots
!= 2) && (dots
!= 3) && (dots
!= 5))
1836 if (hex
&& (dots
== 2)) { /* hex+port */
1837 if (!dcount
&& needmore
)
1839 if (dcount
) /* last hex may be elided zero */
1847 dcount
= xdcount
= 0;
1850 } else if (*p
== ':') {
1854 if (!dcount
) { /* missing number, probably double colon */
1855 if (dci
>= 0) /* can only have one double colon */
1861 dcount
= xdcount
= 0;
1865 } else if (*p
== '%') { /* scope ID delimiter */
1871 } else { /* unexpected character */
1875 if (needmore
&& !dcount
)
1877 if (dcount
) /* decode trailing number */
1878 GET(dots
? DECIMALVALUE
: HEXVALUE
);
1879 if (dci
>= 0) { /* got a double-colon at i, need to insert a range of zeroes */
1880 /* if we got a port, slide to end of array */
1881 /* otherwise, slide to end of address (non-port) values */
1882 int end
= ((dots
== 2) || (dots
== 5)) ? sizeof(a
) : (sizeof(a
) - 2);
1883 if (i
% 2) /* length of zero range must be multiple of 2 */
1885 if (i
>= end
) /* no room? */
1887 /* slide (i-dci) numbers up from index dci */
1888 for (s
=0; s
< (i
- dci
); s
++)
1889 a
[end
-1-s
] = a
[i
-1-s
];
1890 /* zero (end-i) numbers at index dci */
1891 for (s
=0; s
< (end
- i
); s
++)
1896 /* copy out resulting socket address */
1898 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)addr
;
1899 if ((((dots
== 0) || (dots
== 3)) && (i
!= (sizeof(a
)-2))))
1901 if ((((dots
== 2) || (dots
== 5)) && (i
!= sizeof(a
))))
1903 bzero(sin6
, sizeof(struct sockaddr_in6
));
1904 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1905 sin6
->sin6_family
= AF_INET6
;
1906 bcopy(a
, &sin6
->sin6_addr
.s6_addr
, sizeof(struct in6_addr
));
1907 if ((dots
== 5) || (dots
== 2))
1908 sin6
->sin6_port
= htons((a
[16] << 8) | a
[17]);
1910 for (p
=pscope
; IS_DIGIT(*p
); p
++)
1912 if (*p
&& !IS_DIGIT(*p
)) { /* name */
1913 ifnet_t interface
= NULL
;
1914 if (ifnet_find_by_name(pscope
, &interface
) == 0)
1915 sin6
->sin6_scope_id
= ifnet_index(interface
);
1917 ifnet_release(interface
);
1918 } else { /* decimal number */
1919 sin6
->sin6_scope_id
= strtoul(pscope
, NULL
, 10);
1921 /* XXX should we also embed scope id for linklocal? */
1924 struct sockaddr_in
*sin
= (struct sockaddr_in
*)addr
;
1925 if ((dots
!= 3) && (dots
!= 5))
1927 if ((dots
== 3) && (i
!= 4))
1929 if ((dots
== 5) && (i
!= 6))
1931 bzero(sin
, sizeof(struct sockaddr_in
));
1932 sin
->sin_len
= sizeof(struct sockaddr_in
);
1933 sin
->sin_family
= AF_INET
;
1934 bcopy(a
, &sin
->sin_addr
.s_addr
, sizeof(struct in_addr
));
1936 sin
->sin_port
= htons((a
[4] << 8) | a
[5]);
1942 #endif /* NFSCLIENT */
1945 * Schedule a callout thread to run an NFS timer function
1946 * interval milliseconds in the future.
1949 nfs_interval_timer_start(thread_call_t call
, int interval
)
1953 clock_interval_to_deadline(interval
, 1000 * 1000, &deadline
);
1954 thread_call_enter_delayed(call
, deadline
);
1960 int nfsrv_cmp_secflavs(struct nfs_sec
*, struct nfs_sec
*);
1961 int nfsrv_hang_addrlist(struct nfs_export
*, struct user_nfs_export_args
*);
1962 int nfsrv_free_netopt(struct radix_node
*, void *);
1963 int nfsrv_free_addrlist(struct nfs_export
*, struct user_nfs_export_args
*);
1964 struct nfs_export_options
*nfsrv_export_lookup(struct nfs_export
*, mbuf_t
);
1965 struct nfs_export
*nfsrv_fhtoexport(struct nfs_filehandle
*);
1966 struct nfs_user_stat_node
*nfsrv_get_user_stat_node(struct nfs_active_user_list
*, struct sockaddr
*, uid_t
);
1967 void nfsrv_init_user_list(struct nfs_active_user_list
*);
1968 void nfsrv_free_user_list(struct nfs_active_user_list
*);
1971 * add NFSv3 WCC data to an mbuf chain
1974 nfsm_chain_add_wcc_data_f(
1975 struct nfsrv_descript
*nd
,
1976 struct nfsm_chain
*nmc
,
1978 struct vnode_attr
*prevap
,
1980 struct vnode_attr
*postvap
)
1985 nfsm_chain_add_32(error
, nmc
, FALSE
);
1987 nfsm_chain_add_32(error
, nmc
, TRUE
);
1988 nfsm_chain_add_64(error
, nmc
, prevap
->va_data_size
);
1989 nfsm_chain_add_time(error
, nmc
, NFS_VER3
, &prevap
->va_modify_time
);
1990 nfsm_chain_add_time(error
, nmc
, NFS_VER3
, &prevap
->va_change_time
);
1992 nfsm_chain_add_postop_attr(error
, nd
, nmc
, postattrerr
, postvap
);
1998 * Extract a lookup path from the given mbufs and store it in
1999 * a newly allocated buffer saved in the given nameidata structure.
2002 nfsm_chain_get_path_namei(
2003 struct nfsm_chain
*nmc
,
2005 struct nameidata
*nip
)
2007 struct componentname
*cnp
= &nip
->ni_cnd
;
2011 if (len
> (MAXPATHLEN
- 1))
2012 return (ENAMETOOLONG
);
2015 * Get a buffer for the name to be translated, and copy the
2016 * name into the buffer.
2018 MALLOC_ZONE(cnp
->cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2021 cnp
->cn_pnlen
= MAXPATHLEN
;
2022 cnp
->cn_flags
|= HASBUF
;
2024 /* Copy the name from the mbuf list to the string */
2026 nfsm_chain_get_opaque(error
, nmc
, len
, cp
);
2029 cnp
->cn_pnbuf
[len
] = '\0';
2031 /* sanity check the string */
2032 if ((strlen(cp
) != len
) || strchr(cp
, '/'))
2037 FREE_ZONE(cnp
->cn_pnbuf
, MAXPATHLEN
, M_NAMEI
);
2038 cnp
->cn_flags
&= ~HASBUF
;
2040 nip
->ni_pathlen
= len
;
2046 * Set up nameidata for a lookup() call and do it.
2050 struct nfsrv_descript
*nd
,
2052 struct nameidata
*nip
,
2053 struct nfs_filehandle
*nfhp
,
2055 struct nfs_export
**nxp
,
2056 struct nfs_export_options
**nxop
)
2060 struct componentname
*cnp
= &nip
->ni_cnd
;
2067 * Extract and set starting directory.
2069 error
= nfsrv_fhtovp(nfhp
, nd
, &dp
, nxp
, nxop
);
2072 error
= nfsrv_credcheck(nd
, ctx
, *nxp
, *nxop
);
2073 if (error
|| (vnode_vtype(dp
) != VDIR
)) {
2080 nip
->ni_cnd
.cn_context
= ctx
;
2082 if (*nxop
&& ((*nxop
)->nxo_flags
& NX_READONLY
))
2083 cnp
->cn_flags
|= RDONLY
;
2085 cnp
->cn_flags
|= NOCROSSMOUNT
;
2086 cnp
->cn_nameptr
= cnp
->cn_pnbuf
;
2087 nip
->ni_usedvp
= nip
->ni_startdir
= dp
;
2090 * And call lookup() to do the real work
2092 cnflags
= nip
->ni_cnd
.cn_flags
; /* store in case we have to restore */
2093 while ((error
= lookup(nip
)) == ERECYCLE
) {
2094 nip
->ni_cnd
.cn_flags
= cnflags
;
2095 cnp
->cn_nameptr
= cnp
->cn_pnbuf
;
2096 nip
->ni_usedvp
= nip
->ni_dvp
= nip
->ni_startdir
= dp
;
2101 /* Check for encountering a symbolic link */
2102 if (cnp
->cn_flags
& ISSYMLINK
) {
2103 #if CONFIG_VFS_FUNNEL
2104 if ((cnp
->cn_flags
& FSNODELOCKHELD
)) {
2105 cnp
->cn_flags
&= ~FSNODELOCKHELD
;
2106 unlock_fsnode(nip
->ni_dvp
, NULL
);
2108 #endif /* CONFIG_VFS_FUNNEL */
2109 if (cnp
->cn_flags
& (LOCKPARENT
| WANTPARENT
))
2110 vnode_put(nip
->ni_dvp
);
2112 vnode_put(nip
->ni_vp
);
2119 tmppn
= cnp
->cn_pnbuf
;
2120 cnp
->cn_pnbuf
= NULL
;
2121 cnp
->cn_flags
&= ~HASBUF
;
2122 FREE_ZONE(tmppn
, cnp
->cn_pnlen
, M_NAMEI
);
2128 * A fiddled version of m_adj() that ensures null fill to a 4-byte
2129 * boundary and only trims off the back end
2132 nfsm_adj(mbuf_t mp
, int len
, int nul
)
2139 * Trim from tail. Scan the mbuf chain,
2140 * calculating its length and finding the last mbuf.
2141 * If the adjustment only affects this mbuf, then just
2142 * adjust and return. Otherwise, rescan and truncate
2143 * after the remaining size.
2150 mnext
= mbuf_next(m
);
2157 mbuf_setlen(m
, mlen
);
2159 cp
= (caddr_t
)mbuf_data(m
) + mlen
- nul
;
2160 for (i
= 0; i
< nul
; i
++)
2169 * Correct length for chain is "count".
2170 * Find the mbuf with last data, adjust its length,
2171 * and toss data from remaining mbufs on chain.
2173 for (m
= mp
; m
; m
= mbuf_next(m
)) {
2175 if (mlen
>= count
) {
2177 mbuf_setlen(m
, count
);
2179 cp
= (caddr_t
)mbuf_data(m
) + mlen
- nul
;
2180 for (i
= 0; i
< nul
; i
++)
2187 for (m
= mbuf_next(m
); m
; m
= mbuf_next(m
))
2192 * Trim the header out of the mbuf list and trim off any trailing
2193 * junk so that the mbuf list has only the write data.
2196 nfsm_chain_trim_data(struct nfsm_chain
*nmc
, int len
, int *mlen
)
2198 int cnt
= 0, dlen
, adjust
;
2206 for (m
= nmc
->nmc_mhead
; m
&& (m
!= nmc
->nmc_mcur
); m
= mbuf_next(m
))
2211 /* trim current mbuf */
2212 data
= mbuf_data(m
);
2214 adjust
= nmc
->nmc_ptr
- data
;
2216 if ((dlen
> 0) && (adjust
> 0)) {
2217 if (mbuf_setdata(m
, nmc
->nmc_ptr
, dlen
))
2220 mbuf_setlen(m
, dlen
);
2222 /* skip next len bytes */
2223 for (; m
&& (cnt
< len
); m
= mbuf_next(m
)) {
2227 /* truncate to end of data */
2228 mbuf_setlen(m
, dlen
- (cnt
- len
));
2229 if (m
== nmc
->nmc_mcur
)
2230 nmc
->nmc_left
-= (cnt
- len
);
2237 /* trim any trailing data */
2238 if (m
== nmc
->nmc_mcur
)
2240 for (; m
; m
= mbuf_next(m
))
2247 nfsm_chain_add_fattr(
2248 struct nfsrv_descript
*nd
,
2249 struct nfsm_chain
*nmc
,
2250 struct vnode_attr
*vap
)
2254 // XXX Should we assert here that all fields are supported?
2256 nfsm_chain_add_32(error
, nmc
, vtonfs_type(vap
->va_type
, nd
->nd_vers
));
2257 if (nd
->nd_vers
== NFS_VER3
) {
2258 nfsm_chain_add_32(error
, nmc
, vap
->va_mode
& 07777);
2260 nfsm_chain_add_32(error
, nmc
, vtonfsv2_mode(vap
->va_type
, vap
->va_mode
));
2262 nfsm_chain_add_32(error
, nmc
, vap
->va_nlink
);
2263 nfsm_chain_add_32(error
, nmc
, vap
->va_uid
);
2264 nfsm_chain_add_32(error
, nmc
, vap
->va_gid
);
2265 if (nd
->nd_vers
== NFS_VER3
) {
2266 nfsm_chain_add_64(error
, nmc
, vap
->va_data_size
);
2267 nfsm_chain_add_64(error
, nmc
, vap
->va_data_alloc
);
2268 nfsm_chain_add_32(error
, nmc
, major(vap
->va_rdev
));
2269 nfsm_chain_add_32(error
, nmc
, minor(vap
->va_rdev
));
2270 nfsm_chain_add_64(error
, nmc
, vap
->va_fsid
);
2271 nfsm_chain_add_64(error
, nmc
, vap
->va_fileid
);
2273 nfsm_chain_add_32(error
, nmc
, vap
->va_data_size
);
2274 nfsm_chain_add_32(error
, nmc
, NFS_FABLKSIZE
);
2275 if (vap
->va_type
== VFIFO
)
2276 nfsm_chain_add_32(error
, nmc
, 0xffffffff);
2278 nfsm_chain_add_32(error
, nmc
, vap
->va_rdev
);
2279 nfsm_chain_add_32(error
, nmc
, vap
->va_data_alloc
/ NFS_FABLKSIZE
);
2280 nfsm_chain_add_32(error
, nmc
, vap
->va_fsid
);
2281 nfsm_chain_add_32(error
, nmc
, vap
->va_fileid
);
2283 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_access_time
);
2284 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_modify_time
);
2285 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_change_time
);
2291 nfsm_chain_get_sattr(
2292 struct nfsrv_descript
*nd
,
2293 struct nfsm_chain
*nmc
,
2294 struct vnode_attr
*vap
)
2299 struct timespec now
;
2301 if (nd
->nd_vers
== NFS_VER2
) {
2303 * There is/was a bug in the Sun client that puts 0xffff in the mode
2304 * field of sattr when it should put in 0xffffffff. The u_short
2305 * doesn't sign extend. So check the low order 2 bytes for 0xffff.
2307 nfsm_chain_get_32(error
, nmc
, val
);
2308 if ((val
& 0xffff) != 0xffff) {
2309 VATTR_SET(vap
, va_mode
, val
& 07777);
2310 /* save the "type" bits for NFSv2 create */
2311 VATTR_SET(vap
, va_type
, IFTOVT(val
));
2312 VATTR_CLEAR_ACTIVE(vap
, va_type
);
2314 nfsm_chain_get_32(error
, nmc
, val
);
2315 if (val
!= (uint32_t)-1)
2316 VATTR_SET(vap
, va_uid
, val
);
2317 nfsm_chain_get_32(error
, nmc
, val
);
2318 if (val
!= (uint32_t)-1)
2319 VATTR_SET(vap
, va_gid
, val
);
2320 /* save the "size" bits for NFSv2 create (even if they appear unset) */
2321 nfsm_chain_get_32(error
, nmc
, val
);
2322 VATTR_SET(vap
, va_data_size
, val
);
2323 if (val
== (uint32_t)-1)
2324 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2325 nfsm_chain_get_time(error
, nmc
, NFS_VER2
,
2326 vap
->va_access_time
.tv_sec
,
2327 vap
->va_access_time
.tv_nsec
);
2328 if (vap
->va_access_time
.tv_sec
!= -1)
2329 VATTR_SET_ACTIVE(vap
, va_access_time
);
2330 nfsm_chain_get_time(error
, nmc
, NFS_VER2
,
2331 vap
->va_modify_time
.tv_sec
,
2332 vap
->va_modify_time
.tv_nsec
);
2333 if (vap
->va_modify_time
.tv_sec
!= -1)
2334 VATTR_SET_ACTIVE(vap
, va_modify_time
);
2339 nfsm_chain_get_32(error
, nmc
, val
);
2341 nfsm_chain_get_32(error
, nmc
, val
);
2342 VATTR_SET(vap
, va_mode
, val
& 07777);
2344 nfsm_chain_get_32(error
, nmc
, val
);
2346 nfsm_chain_get_32(error
, nmc
, val
);
2347 VATTR_SET(vap
, va_uid
, val
);
2349 nfsm_chain_get_32(error
, nmc
, val
);
2351 nfsm_chain_get_32(error
, nmc
, val
);
2352 VATTR_SET(vap
, va_gid
, val
);
2354 nfsm_chain_get_32(error
, nmc
, val
);
2356 nfsm_chain_get_64(error
, nmc
, val64
);
2357 VATTR_SET(vap
, va_data_size
, val64
);
2360 nfsm_chain_get_32(error
, nmc
, val
);
2362 case NFS_TIME_SET_TO_CLIENT
:
2363 nfsm_chain_get_time(error
, nmc
, nd
->nd_vers
,
2364 vap
->va_access_time
.tv_sec
,
2365 vap
->va_access_time
.tv_nsec
);
2366 VATTR_SET_ACTIVE(vap
, va_access_time
);
2367 vap
->va_vaflags
&= ~VA_UTIMES_NULL
;
2369 case NFS_TIME_SET_TO_SERVER
:
2370 VATTR_SET(vap
, va_access_time
, now
);
2371 vap
->va_vaflags
|= VA_UTIMES_NULL
;
2374 nfsm_chain_get_32(error
, nmc
, val
);
2376 case NFS_TIME_SET_TO_CLIENT
:
2377 nfsm_chain_get_time(error
, nmc
, nd
->nd_vers
,
2378 vap
->va_modify_time
.tv_sec
,
2379 vap
->va_modify_time
.tv_nsec
);
2380 VATTR_SET_ACTIVE(vap
, va_modify_time
);
2381 vap
->va_vaflags
&= ~VA_UTIMES_NULL
;
2383 case NFS_TIME_SET_TO_SERVER
:
2384 VATTR_SET(vap
, va_modify_time
, now
);
2385 if (!VATTR_IS_ACTIVE(vap
, va_access_time
))
2386 vap
->va_vaflags
|= VA_UTIMES_NULL
;
2394 * Compare two security flavor structs
2397 nfsrv_cmp_secflavs(struct nfs_sec
*sf1
, struct nfs_sec
*sf2
)
2401 if (sf1
->count
!= sf2
->count
)
2403 for (i
= 0; i
< sf1
->count
; i
++)
2404 if (sf1
->flavors
[i
] != sf2
->flavors
[i
])
2410 * Build hash lists of net addresses and hang them off the NFS export.
2411 * Called by nfsrv_export() to set up the lists of export addresses.
2414 nfsrv_hang_addrlist(struct nfs_export
*nx
, struct user_nfs_export_args
*unxa
)
2416 struct nfs_export_net_args nxna
;
2417 struct nfs_netopt
*no
, *rn_no
;
2418 struct radix_node_head
*rnh
;
2419 struct radix_node
*rn
;
2420 struct sockaddr
*saddr
, *smask
;
2427 uaddr
= unxa
->nxa_nets
;
2428 for (net
= 0; net
< unxa
->nxa_netcount
; net
++, uaddr
+= sizeof(nxna
)) {
2429 error
= copyin(uaddr
, &nxna
, sizeof(nxna
));
2433 if (nxna
.nxna_flags
& (NX_MAPROOT
|NX_MAPALL
)) {
2434 struct posix_cred temp_pcred
;
2435 bzero(&temp_pcred
, sizeof(temp_pcred
));
2436 temp_pcred
.cr_uid
= nxna
.nxna_cred
.cr_uid
;
2437 temp_pcred
.cr_ngroups
= nxna
.nxna_cred
.cr_ngroups
;
2438 for (i
=0; i
< nxna
.nxna_cred
.cr_ngroups
&& i
< NGROUPS
; i
++)
2439 temp_pcred
.cr_groups
[i
] = nxna
.nxna_cred
.cr_groups
[i
];
2440 cred
= posix_cred_create(&temp_pcred
);
2441 if (!IS_VALID_CRED(cred
))
2447 if (nxna
.nxna_addr
.ss_len
== 0) {
2448 /* No address means this is a default/world export */
2449 if (nx
->nx_flags
& NX_DEFAULTEXPORT
) {
2450 if (IS_VALID_CRED(cred
))
2451 kauth_cred_unref(&cred
);
2454 nx
->nx_flags
|= NX_DEFAULTEXPORT
;
2455 nx
->nx_defopt
.nxo_flags
= nxna
.nxna_flags
;
2456 nx
->nx_defopt
.nxo_cred
= cred
;
2457 bcopy(&nxna
.nxna_sec
, &nx
->nx_defopt
.nxo_sec
, sizeof(struct nfs_sec
));
2462 i
= sizeof(struct nfs_netopt
);
2463 i
+= nxna
.nxna_addr
.ss_len
+ nxna
.nxna_mask
.ss_len
;
2464 MALLOC(no
, struct nfs_netopt
*, i
, M_NETADDR
, M_WAITOK
);
2466 if (IS_VALID_CRED(cred
))
2467 kauth_cred_unref(&cred
);
2470 bzero(no
, sizeof(struct nfs_netopt
));
2471 no
->no_opt
.nxo_flags
= nxna
.nxna_flags
;
2472 no
->no_opt
.nxo_cred
= cred
;
2473 bcopy(&nxna
.nxna_sec
, &no
->no_opt
.nxo_sec
, sizeof(struct nfs_sec
));
2475 saddr
= (struct sockaddr
*)(no
+ 1);
2476 bcopy(&nxna
.nxna_addr
, saddr
, nxna
.nxna_addr
.ss_len
);
2477 if (nxna
.nxna_mask
.ss_len
) {
2478 smask
= (struct sockaddr
*)((caddr_t
)saddr
+ nxna
.nxna_addr
.ss_len
);
2479 bcopy(&nxna
.nxna_mask
, smask
, nxna
.nxna_mask
.ss_len
);
2483 i
= saddr
->sa_family
;
2484 if ((rnh
= nx
->nx_rtable
[i
]) == 0) {
2486 * Seems silly to initialize every AF when most are not
2487 * used, do so on demand here
2489 for (dom
= domains
; dom
; dom
= dom
->dom_next
)
2490 if (dom
->dom_family
== i
&& dom
->dom_rtattach
) {
2491 dom
->dom_rtattach((void **)&nx
->nx_rtable
[i
],
2495 if ((rnh
= nx
->nx_rtable
[i
]) == 0) {
2496 if (IS_VALID_CRED(cred
))
2497 kauth_cred_unref(&cred
);
2498 _FREE(no
, M_NETADDR
);
2502 rn
= (*rnh
->rnh_addaddr
)((caddr_t
)saddr
, (caddr_t
)smask
, rnh
, no
->no_rnodes
);
2505 * One of the reasons that rnh_addaddr may fail is that
2506 * the entry already exists. To check for this case, we
2507 * look up the entry to see if it is there. If so, we
2508 * do not need to make a new entry but do continue.
2510 * XXX should this be rnh_lookup() instead?
2513 rn
= (*rnh
->rnh_matchaddr
)((caddr_t
)saddr
, rnh
);
2514 rn_no
= (struct nfs_netopt
*)rn
;
2515 if (rn
!= 0 && (rn
->rn_flags
& RNF_ROOT
) == 0 &&
2516 (rn_no
->no_opt
.nxo_flags
== nxna
.nxna_flags
) &&
2517 (!nfsrv_cmp_secflavs(&rn_no
->no_opt
.nxo_sec
, &nxna
.nxna_sec
))) {
2518 kauth_cred_t cred2
= rn_no
->no_opt
.nxo_cred
;
2519 if (cred
== cred2
) {
2520 /* creds are same (or both NULL) */
2522 } else if (cred
&& cred2
&& (kauth_cred_getuid(cred
) == kauth_cred_getuid(cred2
))) {
2524 * Now compare the effective and
2525 * supplementary groups...
2527 * Note: This comparison, as written,
2528 * does not correctly indicate that
2529 * the groups are equivalent, since
2530 * other than the first supplementary
2531 * group, which is also the effective
2532 * group, order on the remaining groups
2533 * doesn't matter, and this is an
2536 gid_t groups
[NGROUPS
];
2537 gid_t groups2
[NGROUPS
];
2538 int groupcount
= NGROUPS
;
2539 int group2count
= NGROUPS
;
2541 if (!kauth_cred_getgroups(cred
, groups
, &groupcount
) &&
2542 !kauth_cred_getgroups(cred2
, groups2
, &group2count
) &&
2543 groupcount
== group2count
) {
2544 for (i
=0; i
< group2count
; i
++)
2545 if (groups
[i
] != groups2
[i
])
2547 if (i
>= group2count
|| i
>= NGROUPS
)
2552 if (IS_VALID_CRED(cred
))
2553 kauth_cred_unref(&cred
);
2554 _FREE(no
, M_NETADDR
);
2566 * In order to properly track an export's netopt count, we need to pass
2567 * an additional argument to nfsrv_free_netopt() so that it can decrement
2568 * the export's netopt count.
2570 struct nfsrv_free_netopt_arg
{
2572 struct radix_node_head
*rnh
;
2576 nfsrv_free_netopt(struct radix_node
*rn
, void *w
)
2578 struct nfsrv_free_netopt_arg
*fna
= (struct nfsrv_free_netopt_arg
*)w
;
2579 struct radix_node_head
*rnh
= fna
->rnh
;
2580 uint32_t *cnt
= fna
->cnt
;
2581 struct nfs_netopt
*nno
= (struct nfs_netopt
*)rn
;
2583 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
2584 if (IS_VALID_CRED(nno
->no_opt
.nxo_cred
))
2585 kauth_cred_unref(&nno
->no_opt
.nxo_cred
);
2586 _FREE((caddr_t
)rn
, M_NETADDR
);
2592 * Free the net address hash lists that are hanging off the mount points.
2595 nfsrv_free_addrlist(struct nfs_export
*nx
, struct user_nfs_export_args
*unxa
)
2597 struct nfs_export_net_args nxna
;
2598 struct radix_node_head
*rnh
;
2599 struct radix_node
*rn
;
2600 struct nfsrv_free_netopt_arg fna
;
2601 struct nfs_netopt
*nno
;
2606 if (!unxa
|| !unxa
->nxa_netcount
) {
2607 /* delete everything */
2608 for (i
= 0; i
<= AF_MAX
; i
++)
2609 if ( (rnh
= nx
->nx_rtable
[i
]) ) {
2611 fna
.cnt
= &nx
->nx_expcnt
;
2612 (*rnh
->rnh_walktree
)(rnh
, nfsrv_free_netopt
, (caddr_t
)&fna
);
2613 _FREE((caddr_t
)rnh
, M_RTABLE
);
2614 nx
->nx_rtable
[i
] = 0;
2619 /* delete only the exports specified */
2620 uaddr
= unxa
->nxa_nets
;
2621 for (net
= 0; net
< unxa
->nxa_netcount
; net
++, uaddr
+= sizeof(nxna
)) {
2622 error
= copyin(uaddr
, &nxna
, sizeof(nxna
));
2626 if (nxna
.nxna_addr
.ss_len
== 0) {
2627 /* No address means this is a default/world export */
2628 if (nx
->nx_flags
& NX_DEFAULTEXPORT
) {
2629 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2630 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2631 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2638 if ((rnh
= nx
->nx_rtable
[nxna
.nxna_addr
.ss_family
]) == 0) {
2639 /* AF not initialized? */
2640 if (!(unxa
->nxa_flags
& NXA_ADD
))
2641 printf("nfsrv_free_addrlist: address not found (0)\n");
2645 rn
= (*rnh
->rnh_lookup
)(&nxna
.nxna_addr
,
2646 nxna
.nxna_mask
.ss_len
? &nxna
.nxna_mask
: NULL
, rnh
);
2647 if (!rn
|| (rn
->rn_flags
& RNF_ROOT
)) {
2648 if (!(unxa
->nxa_flags
& NXA_ADD
))
2649 printf("nfsrv_free_addrlist: address not found (1)\n");
2653 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
2654 nno
= (struct nfs_netopt
*)rn
;
2655 if (IS_VALID_CRED(nno
->no_opt
.nxo_cred
))
2656 kauth_cred_unref(&nno
->no_opt
.nxo_cred
);
2657 _FREE((caddr_t
)rn
, M_NETADDR
);
2660 if (nx
->nx_expcnt
== ((nx
->nx_flags
& NX_DEFAULTEXPORT
) ? 1 : 0)) {
2661 /* no more entries in rnh, so free it up */
2662 _FREE((caddr_t
)rnh
, M_RTABLE
);
2663 nx
->nx_rtable
[nxna
.nxna_addr
.ss_family
] = 0;
2670 void enablequotas(struct mount
*mp
, vfs_context_t ctx
); // XXX
2673 nfsrv_export(struct user_nfs_export_args
*unxa
, vfs_context_t ctx
)
2677 struct nfs_exportfs
*nxfs
, *nxfs2
, *nxfs3
;
2678 struct nfs_export
*nx
, *nx2
, *nx3
;
2679 struct nfs_filehandle nfh
;
2680 struct nameidata mnd
, xnd
;
2681 vnode_t mvp
= NULL
, xvp
= NULL
;
2683 char path
[MAXPATHLEN
];
2686 if (unxa
->nxa_flags
== NXA_CHECK
) {
2687 /* just check if the path is an NFS-exportable file system */
2688 error
= copyinstr(unxa
->nxa_fspath
, path
, MAXPATHLEN
, &pathlen
);
2691 NDINIT(&mnd
, LOOKUP
, OP_LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2692 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
2693 error
= namei(&mnd
);
2697 mp
= vnode_mount(mvp
);
2698 /* make sure it's the root of a file system */
2699 if (!vnode_isvroot(mvp
))
2701 /* make sure the file system is NFS-exportable */
2703 nfh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
2704 error
= VFS_VPTOFH(mvp
, (int*)&nfh
.nfh_len
, &nfh
.nfh_fid
[0], NULL
);
2706 if (!error
&& (nfh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
))
2708 if (!error
&& !(mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSREADDIR_EXTENDED
))
2715 /* all other operations: must be super user */
2716 if ((error
= vfs_context_suser(ctx
)))
2719 if (unxa
->nxa_flags
& NXA_DELETE_ALL
) {
2720 /* delete all exports on all file systems */
2721 lck_rw_lock_exclusive(&nfsrv_export_rwlock
);
2722 while ((nxfs
= LIST_FIRST(&nfsrv_exports
))) {
2723 mp
= vfs_getvfs_by_mntonname(nxfs
->nxfs_path
);
2725 vfs_clearflags(mp
, MNT_EXPORTED
);
2729 /* delete all exports on this file system */
2730 while ((nx
= LIST_FIRST(&nxfs
->nxfs_exports
))) {
2731 LIST_REMOVE(nx
, nx_next
);
2732 LIST_REMOVE(nx
, nx_hash
);
2733 /* delete all netopts for this export */
2734 nfsrv_free_addrlist(nx
, NULL
);
2735 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2736 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2737 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2739 /* free active user list for this export */
2740 nfsrv_free_user_list(&nx
->nx_user_list
);
2741 FREE(nx
->nx_path
, M_TEMP
);
2744 LIST_REMOVE(nxfs
, nxfs_next
);
2745 FREE(nxfs
->nxfs_path
, M_TEMP
);
2748 if (nfsrv_export_hashtbl
) {
2749 /* all exports deleted, clean up export hash table */
2750 FREE(nfsrv_export_hashtbl
, M_TEMP
);
2751 nfsrv_export_hashtbl
= NULL
;
2753 lck_rw_done(&nfsrv_export_rwlock
);
2757 error
= copyinstr(unxa
->nxa_fspath
, path
, MAXPATHLEN
, &pathlen
);
2761 lck_rw_lock_exclusive(&nfsrv_export_rwlock
);
2763 /* init export hash table if not already */
2764 if (!nfsrv_export_hashtbl
) {
2765 if (nfsrv_export_hash_size
<= 0)
2766 nfsrv_export_hash_size
= NFSRVEXPHASHSZ
;
2767 nfsrv_export_hashtbl
= hashinit(nfsrv_export_hash_size
, M_TEMP
, &nfsrv_export_hash
);
2770 // first check if we've already got an exportfs with the given ID
2771 LIST_FOREACH(nxfs
, &nfsrv_exports
, nxfs_next
) {
2772 if (nxfs
->nxfs_id
== unxa
->nxa_fsid
)
2776 /* verify exported FS path matches given path */
2777 if (strncmp(path
, nxfs
->nxfs_path
, MAXPATHLEN
)) {
2781 if ((unxa
->nxa_flags
& (NXA_ADD
|NXA_OFFLINE
)) == NXA_ADD
) {
2782 /* if adding, verify that the mount is still what we expect */
2783 mp
= vfs_getvfs_by_mntonname(nxfs
->nxfs_path
);
2788 /* find exported FS root vnode */
2789 NDINIT(&mnd
, LOOKUP
, OP_LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2790 UIO_SYSSPACE
, CAST_USER_ADDR_T(nxfs
->nxfs_path
), ctx
);
2791 error
= namei(&mnd
);
2795 /* make sure it's (still) the root of a file system */
2796 if (!vnode_isvroot(mvp
)) {
2800 /* sanity check: this should be same mount */
2801 if (mp
!= vnode_mount(mvp
)) {
2807 /* no current exported file system with that ID */
2808 if (!(unxa
->nxa_flags
& NXA_ADD
)) {
2813 /* find exported FS root vnode */
2814 NDINIT(&mnd
, LOOKUP
, OP_LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2815 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
2816 error
= namei(&mnd
);
2818 if (!(unxa
->nxa_flags
& NXA_OFFLINE
))
2822 /* make sure it's the root of a file system */
2823 if (!vnode_isvroot(mvp
)) {
2824 /* bail if not marked offline */
2825 if (!(unxa
->nxa_flags
& NXA_OFFLINE
)) {
2833 mp
= vnode_mount(mvp
);
2836 /* make sure the file system is NFS-exportable */
2837 nfh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
2838 error
= VFS_VPTOFH(mvp
, (int*)&nfh
.nfh_len
, &nfh
.nfh_fid
[0], NULL
);
2839 if (!error
&& (nfh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
))
2841 if (!error
&& !(mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSREADDIR_EXTENDED
))
2848 /* add an exportfs for it */
2849 MALLOC(nxfs
, struct nfs_exportfs
*, sizeof(struct nfs_exportfs
), M_TEMP
, M_WAITOK
);
2854 bzero(nxfs
, sizeof(struct nfs_exportfs
));
2855 nxfs
->nxfs_id
= unxa
->nxa_fsid
;
2856 MALLOC(nxfs
->nxfs_path
, char*, pathlen
, M_TEMP
, M_WAITOK
);
2857 if (!nxfs
->nxfs_path
) {
2862 bcopy(path
, nxfs
->nxfs_path
, pathlen
);
2863 /* insert into list in reverse-sorted order */
2865 LIST_FOREACH(nxfs2
, &nfsrv_exports
, nxfs_next
) {
2866 if (strncmp(nxfs
->nxfs_path
, nxfs2
->nxfs_path
, MAXPATHLEN
) > 0)
2871 LIST_INSERT_BEFORE(nxfs2
, nxfs
, nxfs_next
);
2873 LIST_INSERT_AFTER(nxfs3
, nxfs
, nxfs_next
);
2875 LIST_INSERT_HEAD(&nfsrv_exports
, nxfs
, nxfs_next
);
2877 /* make sure any quotas are enabled before we export the file system */
2879 enablequotas(mp
, ctx
);
2882 if (unxa
->nxa_exppath
) {
2883 error
= copyinstr(unxa
->nxa_exppath
, path
, MAXPATHLEN
, &pathlen
);
2886 LIST_FOREACH(nx
, &nxfs
->nxfs_exports
, nx_next
) {
2887 if (nx
->nx_id
== unxa
->nxa_expid
)
2891 /* verify exported FS path matches given path */
2892 if (strncmp(path
, nx
->nx_path
, MAXPATHLEN
)) {
2897 /* no current export with that ID */
2898 if (!(unxa
->nxa_flags
& NXA_ADD
)) {
2902 /* add an export for it */
2903 MALLOC(nx
, struct nfs_export
*, sizeof(struct nfs_export
), M_TEMP
, M_WAITOK
);
2908 bzero(nx
, sizeof(struct nfs_export
));
2909 nx
->nx_id
= unxa
->nxa_expid
;
2911 microtime(&nx
->nx_exptime
);
2912 MALLOC(nx
->nx_path
, char*, pathlen
, M_TEMP
, M_WAITOK
);
2919 bcopy(path
, nx
->nx_path
, pathlen
);
2920 /* initialize the active user list */
2921 nfsrv_init_user_list(&nx
->nx_user_list
);
2922 /* insert into list in reverse-sorted order */
2924 LIST_FOREACH(nx2
, &nxfs
->nxfs_exports
, nx_next
) {
2925 if (strncmp(nx
->nx_path
, nx2
->nx_path
, MAXPATHLEN
) > 0)
2930 LIST_INSERT_BEFORE(nx2
, nx
, nx_next
);
2932 LIST_INSERT_AFTER(nx3
, nx
, nx_next
);
2934 LIST_INSERT_HEAD(&nxfs
->nxfs_exports
, nx
, nx_next
);
2935 /* insert into hash */
2936 LIST_INSERT_HEAD(NFSRVEXPHASH(nxfs
->nxfs_id
, nx
->nx_id
), nx
, nx_hash
);
2939 * We don't allow/support nested exports. Check if the new entry
2940 * nests with the entries before and after or if there's an
2941 * entry for the file system root and subdirs.
2944 if ((nx3
&& !strncmp(nx3
->nx_path
, nx
->nx_path
, pathlen
- 1) &&
2945 (nx3
->nx_path
[pathlen
-1] == '/')) ||
2946 (nx2
&& !strncmp(nx2
->nx_path
, nx
->nx_path
, strlen(nx2
->nx_path
)) &&
2947 (nx
->nx_path
[strlen(nx2
->nx_path
)] == '/')))
2950 /* check export conflict with fs root export and vice versa */
2951 expisroot
= !nx
->nx_path
[0] ||
2952 ((nx
->nx_path
[0] == '.') && !nx
->nx_path
[1]);
2953 LIST_FOREACH(nx2
, &nxfs
->nxfs_exports
, nx_next
) {
2957 } else if (!nx2
->nx_path
[0])
2959 else if ((nx2
->nx_path
[0] == '.') && !nx2
->nx_path
[1])
2967 * Don't actually return an error because mountd is
2968 * probably about to delete the conflicting export.
2969 * This can happen when a new export momentarily conflicts
2970 * with an old export while the transition is being made.
2971 * Theoretically, mountd could be written to avoid this
2972 * transient situation - but it would greatly increase the
2973 * complexity of mountd for very little overall benefit.
2975 printf("nfsrv_export: warning: nested exports: %s/%s\n",
2976 nxfs
->nxfs_path
, nx
->nx_path
);
2979 nx
->nx_fh
.nfh_xh
.nxh_flags
= NXHF_INVALIDFH
;
2981 /* make sure file handle is set up */
2982 if ((nx
->nx_fh
.nfh_xh
.nxh_version
!= htonl(NFS_FH_VERSION
)) ||
2983 (nx
->nx_fh
.nfh_xh
.nxh_flags
& NXHF_INVALIDFH
)) {
2984 /* try to set up export root file handle */
2985 nx
->nx_fh
.nfh_xh
.nxh_version
= htonl(NFS_FH_VERSION
);
2986 nx
->nx_fh
.nfh_xh
.nxh_fsid
= htonl(nx
->nx_fs
->nxfs_id
);
2987 nx
->nx_fh
.nfh_xh
.nxh_expid
= htonl(nx
->nx_id
);
2988 nx
->nx_fh
.nfh_xh
.nxh_flags
= 0;
2989 nx
->nx_fh
.nfh_xh
.nxh_reserved
= 0;
2990 nx
->nx_fh
.nfh_fhp
= (u_char
*)&nx
->nx_fh
.nfh_xh
;
2991 bzero(&nx
->nx_fh
.nfh_fid
[0], NFSV2_MAX_FID_SIZE
);
2993 /* find export root vnode */
2994 if (!nx
->nx_path
[0] || ((nx
->nx_path
[0] == '.') && !nx
->nx_path
[1])) {
2995 /* exporting file system's root directory */
2999 xnd
.ni_cnd
.cn_nameiop
= LOOKUP
;
3001 xnd
.ni_op
= OP_LOOKUP
;
3003 xnd
.ni_cnd
.cn_flags
= LOCKLEAF
;
3004 xnd
.ni_pathlen
= pathlen
- 1;
3005 xnd
.ni_cnd
.cn_nameptr
= xnd
.ni_cnd
.cn_pnbuf
= path
;
3006 xnd
.ni_startdir
= mvp
;
3007 xnd
.ni_usedvp
= mvp
;
3008 xnd
.ni_cnd
.cn_context
= ctx
;
3009 while ((error
= lookup(&xnd
)) == ERECYCLE
) {
3010 xnd
.ni_cnd
.cn_flags
= LOCKLEAF
;
3011 xnd
.ni_cnd
.cn_nameptr
= xnd
.ni_cnd
.cn_pnbuf
;
3012 xnd
.ni_usedvp
= xnd
.ni_dvp
= xnd
.ni_startdir
= mvp
;
3019 if (vnode_vtype(xvp
) != VDIR
) {
3025 /* grab file handle */
3026 nx
->nx_fh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
3027 error
= VFS_VPTOFH(xvp
, (int*)&nx
->nx_fh
.nfh_len
, &nx
->nx_fh
.nfh_fid
[0], NULL
);
3028 if (!error
&& (nx
->nx_fh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
)) {
3031 nx
->nx_fh
.nfh_xh
.nxh_fidlen
= nx
->nx_fh
.nfh_len
;
3032 nx
->nx_fh
.nfh_len
+= sizeof(nx
->nx_fh
.nfh_xh
);
3039 nx
->nx_fh
.nfh_xh
.nxh_flags
= NXHF_INVALIDFH
;
3040 nx
->nx_fh
.nfh_xh
.nxh_fidlen
= 0;
3041 nx
->nx_fh
.nfh_len
= sizeof(nx
->nx_fh
.nfh_xh
);
3048 /* perform the export changes */
3049 if (unxa
->nxa_flags
& NXA_DELETE
) {
3051 /* delete all exports on this file system */
3052 while ((nx
= LIST_FIRST(&nxfs
->nxfs_exports
))) {
3053 LIST_REMOVE(nx
, nx_next
);
3054 LIST_REMOVE(nx
, nx_hash
);
3055 /* delete all netopts for this export */
3056 nfsrv_free_addrlist(nx
, NULL
);
3057 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
3058 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
3059 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
3061 /* delete active user list for this export */
3062 nfsrv_free_user_list(&nx
->nx_user_list
);
3063 FREE(nx
->nx_path
, M_TEMP
);
3067 } else if (!unxa
->nxa_netcount
) {
3068 /* delete all netopts for this export */
3069 nfsrv_free_addrlist(nx
, NULL
);
3070 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
3071 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
3072 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
3075 /* delete only the netopts for the given addresses */
3076 error
= nfsrv_free_addrlist(nx
, unxa
);
3081 if (unxa
->nxa_flags
& NXA_ADD
) {
3083 * If going offline set the export time so that when
3084 * coming back on line we will present a new write verifier
3087 if (unxa
->nxa_flags
& NXA_OFFLINE
)
3088 microtime(&nx
->nx_exptime
);
3090 error
= nfsrv_hang_addrlist(nx
, unxa
);
3092 vfs_setflags(mp
, MNT_EXPORTED
);
3096 if (nx
&& !nx
->nx_expcnt
) {
3097 /* export has no export options */
3098 LIST_REMOVE(nx
, nx_next
);
3099 LIST_REMOVE(nx
, nx_hash
);
3100 /* delete active user list for this export */
3101 nfsrv_free_user_list(&nx
->nx_user_list
);
3102 FREE(nx
->nx_path
, M_TEMP
);
3105 if (LIST_EMPTY(&nxfs
->nxfs_exports
)) {
3106 /* exported file system has no more exports */
3107 LIST_REMOVE(nxfs
, nxfs_next
);
3108 FREE(nxfs
->nxfs_path
, M_TEMP
);
3111 vfs_clearflags(mp
, MNT_EXPORTED
);
3122 lck_rw_done(&nfsrv_export_rwlock
);
3126 struct nfs_export_options
*
3127 nfsrv_export_lookup(struct nfs_export
*nx
, mbuf_t nam
)
3129 struct nfs_export_options
*nxo
= NULL
;
3130 struct nfs_netopt
*no
= NULL
;
3131 struct radix_node_head
*rnh
;
3132 struct sockaddr
*saddr
;
3134 /* Lookup in the export list first. */
3136 saddr
= mbuf_data(nam
);
3137 rnh
= nx
->nx_rtable
[saddr
->sa_family
];
3139 no
= (struct nfs_netopt
*)
3140 (*rnh
->rnh_matchaddr
)((caddr_t
)saddr
, rnh
);
3141 if (no
&& no
->no_rnodes
->rn_flags
& RNF_ROOT
)
3147 /* If no address match, use the default if it exists. */
3148 if ((nxo
== NULL
) && (nx
->nx_flags
& NX_DEFAULTEXPORT
))
3149 nxo
= &nx
->nx_defopt
;
3153 /* find an export for the given handle */
3155 nfsrv_fhtoexport(struct nfs_filehandle
*nfhp
)
3157 struct nfs_exphandle
*nxh
= (struct nfs_exphandle
*)nfhp
->nfh_fhp
;
3158 struct nfs_export
*nx
;
3159 uint32_t fsid
, expid
;
3161 if (!nfsrv_export_hashtbl
)
3163 fsid
= ntohl(nxh
->nxh_fsid
);
3164 expid
= ntohl(nxh
->nxh_expid
);
3165 nx
= NFSRVEXPHASH(fsid
, expid
)->lh_first
;
3166 for (; nx
; nx
= LIST_NEXT(nx
, nx_hash
)) {
3167 if (nx
->nx_fs
->nxfs_id
!= fsid
)
3169 if (nx
->nx_id
!= expid
)
3177 * nfsrv_fhtovp() - convert FH to vnode and export info
3181 struct nfs_filehandle
*nfhp
,
3182 struct nfsrv_descript
*nd
,
3184 struct nfs_export
**nxp
,
3185 struct nfs_export_options
**nxop
)
3187 struct nfs_exphandle
*nxh
= (struct nfs_exphandle
*)nfhp
->nfh_fhp
;
3188 struct nfs_export_options
*nxo
;
3203 v
= ntohl(nxh
->nxh_version
);
3204 if (v
!= NFS_FH_VERSION
) {
3205 /* file handle format not supported */
3208 if (nfhp
->nfh_len
> NFSV3_MAX_FH_SIZE
)
3210 if (nfhp
->nfh_len
< (int)sizeof(struct nfs_exphandle
))
3212 v
= ntohs(nxh
->nxh_flags
);
3213 if (v
& NXHF_INVALIDFH
)
3216 *nxp
= nfsrv_fhtoexport(nfhp
);
3220 /* Get the export option structure for this <export, client> tuple. */
3221 *nxop
= nxo
= nfsrv_export_lookup(*nxp
, nam
);
3222 if (nam
&& (*nxop
== NULL
))
3226 /* Validate the security flavor of the request */
3227 for (i
= 0, valid
= 0; i
< nxo
->nxo_sec
.count
; i
++) {
3228 if (nd
->nd_sec
== nxo
->nxo_sec
.flavors
[i
]) {
3235 * RFC 2623 section 2.3.2 recommends no authentication
3236 * requirement for certain NFS procedures used for mounting.
3237 * This allows an unauthenticated superuser on the client
3238 * to do mounts for the benefit of authenticated users.
3240 if (nd
->nd_vers
== NFS_VER2
)
3241 if (nd
->nd_procnum
== NFSV2PROC_GETATTR
||
3242 nd
->nd_procnum
== NFSV2PROC_STATFS
)
3244 if (nd
->nd_vers
== NFS_VER3
)
3245 if (nd
->nd_procnum
== NFSPROC_FSINFO
)
3249 return (NFSERR_AUTHERR
| AUTH_REJECTCRED
);
3253 if (nxo
&& (nxo
->nxo_flags
& NX_OFFLINE
))
3254 return ((nd
== NULL
|| nd
->nd_vers
== NFS_VER2
) ? ESTALE
: NFSERR_TRYLATER
);
3256 /* find mount structure */
3257 mp
= vfs_getvfs_by_mntonname((*nxp
)->nx_fs
->nxfs_path
);
3259 error
= vfs_busy(mp
, LK_NOWAIT
);
3266 * We have an export, but no mount?
3267 * Perhaps the export just hasn't been marked offline yet.
3269 return ((nd
== NULL
|| nd
->nd_vers
== NFS_VER2
) ? ESTALE
: NFSERR_TRYLATER
);
3272 fidp
= nfhp
->nfh_fhp
+ sizeof(*nxh
);
3273 error
= VFS_FHTOVP(mp
, nxh
->nxh_fidlen
, fidp
, vpp
, NULL
);
3277 /* vnode pointer should be good at this point or ... */
3284 * nfsrv_credcheck() - check/map credentials according
3285 * to given export options.
3289 struct nfsrv_descript
*nd
,
3291 __unused
struct nfs_export
*nx
,
3292 struct nfs_export_options
*nxo
)
3294 if (nxo
&& nxo
->nxo_cred
) {
3295 if ((nxo
->nxo_flags
& NX_MAPALL
) ||
3296 ((nxo
->nxo_flags
& NX_MAPROOT
) && !suser(nd
->nd_cr
, NULL
))) {
3297 kauth_cred_ref(nxo
->nxo_cred
);
3298 kauth_cred_unref(&nd
->nd_cr
);
3299 nd
->nd_cr
= nxo
->nxo_cred
;
3302 ctx
->vc_ucred
= nd
->nd_cr
;
3307 * nfsrv_vptofh() - convert vnode to file handle for given export
3309 * If the caller is passing in a vnode for a ".." directory entry,
3310 * they can pass a directory NFS file handle (dnfhp) which will be
3311 * checked against the root export file handle. If it matches, we
3312 * refuse to provide the file handle for the out-of-export directory.
3316 struct nfs_export
*nx
,
3318 struct nfs_filehandle
*dnfhp
,
3321 struct nfs_filehandle
*nfhp
)
3324 uint32_t maxfidsize
;
3326 nfhp
->nfh_fhp
= (u_char
*)&nfhp
->nfh_xh
;
3327 nfhp
->nfh_xh
.nxh_version
= htonl(NFS_FH_VERSION
);
3328 nfhp
->nfh_xh
.nxh_fsid
= htonl(nx
->nx_fs
->nxfs_id
);
3329 nfhp
->nfh_xh
.nxh_expid
= htonl(nx
->nx_id
);
3330 nfhp
->nfh_xh
.nxh_flags
= 0;
3331 nfhp
->nfh_xh
.nxh_reserved
= 0;
3333 if (nfsvers
== NFS_VER2
)
3334 bzero(&nfhp
->nfh_fid
[0], NFSV2_MAX_FID_SIZE
);
3336 /* if directory FH matches export root, return invalid FH */
3337 if (dnfhp
&& nfsrv_fhmatch(dnfhp
, &nx
->nx_fh
)) {
3338 if (nfsvers
== NFS_VER2
)
3339 nfhp
->nfh_len
= NFSX_V2FH
;
3341 nfhp
->nfh_len
= sizeof(nfhp
->nfh_xh
);
3342 nfhp
->nfh_xh
.nxh_fidlen
= 0;
3343 nfhp
->nfh_xh
.nxh_flags
= htons(NXHF_INVALIDFH
);
3347 if (nfsvers
== NFS_VER2
)
3348 maxfidsize
= NFSV2_MAX_FID_SIZE
;
3350 maxfidsize
= NFSV3_MAX_FID_SIZE
;
3351 nfhp
->nfh_len
= maxfidsize
;
3353 error
= VFS_VPTOFH(vp
, (int*)&nfhp
->nfh_len
, &nfhp
->nfh_fid
[0], ctx
);
3356 if (nfhp
->nfh_len
> maxfidsize
)
3358 nfhp
->nfh_xh
.nxh_fidlen
= nfhp
->nfh_len
;
3359 nfhp
->nfh_len
+= sizeof(nfhp
->nfh_xh
);
3360 if ((nfsvers
== NFS_VER2
) && (nfhp
->nfh_len
< NFSX_V2FH
))
3361 nfhp
->nfh_len
= NFSX_V2FH
;
3367 * Compare two file handles to see it they're the same.
3368 * Note that we don't use nfh_len because that may include
3369 * padding in an NFSv2 file handle.
3372 nfsrv_fhmatch(struct nfs_filehandle
*fh1
, struct nfs_filehandle
*fh2
)
3374 struct nfs_exphandle
*nxh1
, *nxh2
;
3377 nxh1
= (struct nfs_exphandle
*)fh1
->nfh_fhp
;
3378 nxh2
= (struct nfs_exphandle
*)fh2
->nfh_fhp
;
3379 len1
= sizeof(fh1
->nfh_xh
) + nxh1
->nxh_fidlen
;
3380 len2
= sizeof(fh2
->nfh_xh
) + nxh2
->nxh_fidlen
;
3383 if (bcmp(nxh1
, nxh2
, len1
))
3389 * Functions for dealing with active user lists
3393 * Search the hash table for a user node with a matching IP address and uid field.
3394 * If found, the node's tm_last timestamp is updated and the node is returned.
3396 * If not found, a new node is allocated (or reclaimed via LRU), initialized, and returned.
3397 * Returns NULL if a new node could not be allcoated.
3399 * The list's user_mutex lock MUST be held.
3401 struct nfs_user_stat_node
*
3402 nfsrv_get_user_stat_node(struct nfs_active_user_list
*list
, struct sockaddr
*saddr
, uid_t uid
)
3404 struct nfs_user_stat_node
*unode
;
3406 struct nfs_user_stat_hashtbl_head
*head
;
3408 /* seach the hash table */
3409 head
= NFS_USER_STAT_HASH(list
->user_hashtbl
, uid
);
3410 LIST_FOREACH(unode
, head
, hash_link
) {
3411 if ((uid
== unode
->uid
) && (nfs_sockaddr_cmp(saddr
, (struct sockaddr
*)&unode
->sock
) == 0)) {
3412 /* found matching node */
3418 /* found node in the hash table, now update lru position */
3419 TAILQ_REMOVE(&list
->user_lru
, unode
, lru_link
);
3420 TAILQ_INSERT_TAIL(&list
->user_lru
, unode
, lru_link
);
3422 /* update time stamp */
3424 unode
->tm_last
= (uint32_t)now
.tv_sec
;
3428 if (list
->node_count
< nfsrv_user_stat_max_nodes
) {
3429 /* Allocate a new node */
3430 MALLOC(unode
, struct nfs_user_stat_node
*, sizeof(struct nfs_user_stat_node
),
3431 M_TEMP
, M_WAITOK
| M_ZERO
);
3436 /* increment node count */
3437 OSAddAtomic(1, &nfsrv_user_stat_node_count
);
3440 /* reuse the oldest node in the lru list */
3441 unode
= TAILQ_FIRST(&list
->user_lru
);
3446 /* Remove the node */
3447 TAILQ_REMOVE(&list
->user_lru
, unode
, lru_link
);
3448 LIST_REMOVE(unode
, hash_link
);
3451 /* Initialize the node */
3453 bcopy(saddr
, &unode
->sock
, saddr
->sa_len
);
3456 unode
->bytes_read
= 0;
3457 unode
->bytes_written
= 0;
3458 unode
->tm_start
= (uint32_t)now
.tv_sec
;
3459 unode
->tm_last
= (uint32_t)now
.tv_sec
;
3461 /* insert the node */
3462 TAILQ_INSERT_TAIL(&list
->user_lru
, unode
, lru_link
);
3463 LIST_INSERT_HEAD(head
, unode
, hash_link
);
3469 nfsrv_update_user_stat(struct nfs_export
*nx
, struct nfsrv_descript
*nd
, uid_t uid
, u_int ops
, u_int rd_bytes
, u_int wr_bytes
)
3471 struct nfs_user_stat_node
*unode
;
3472 struct nfs_active_user_list
*ulist
;
3473 struct sockaddr
*saddr
;
3475 if ((!nfsrv_user_stat_enabled
) || (!nx
) || (!nd
) || (!nd
->nd_nam
))
3478 saddr
= (struct sockaddr
*)mbuf_data(nd
->nd_nam
);
3480 /* check address family before going any further */
3481 if ((saddr
->sa_family
!= AF_INET
) && (saddr
->sa_family
!= AF_INET6
))
3484 ulist
= &nx
->nx_user_list
;
3486 /* lock the active user list */
3487 lck_mtx_lock(&ulist
->user_mutex
);
3489 /* get the user node */
3490 unode
= nfsrv_get_user_stat_node(ulist
, saddr
, uid
);
3493 lck_mtx_unlock(&ulist
->user_mutex
);
3497 /* update counters */
3499 unode
->bytes_read
+= rd_bytes
;
3500 unode
->bytes_written
+= wr_bytes
;
3503 lck_mtx_unlock(&ulist
->user_mutex
);
3506 /* initialize an active user list */
3508 nfsrv_init_user_list(struct nfs_active_user_list
*ulist
)
3512 /* initialize the lru */
3513 TAILQ_INIT(&ulist
->user_lru
);
3515 /* initialize the hash table */
3516 for(i
= 0; i
< NFS_USER_STAT_HASH_SIZE
; i
++)
3517 LIST_INIT(&ulist
->user_hashtbl
[i
]);
3518 ulist
->node_count
= 0;
3520 lck_mtx_init(&ulist
->user_mutex
, nfsrv_active_user_mutex_group
, LCK_ATTR_NULL
);
3523 /* Free all nodes in an active user list */
3525 nfsrv_free_user_list(struct nfs_active_user_list
*ulist
)
3527 struct nfs_user_stat_node
*unode
;
3532 while ((unode
= TAILQ_FIRST(&ulist
->user_lru
))) {
3533 /* Remove node and free */
3534 TAILQ_REMOVE(&ulist
->user_lru
, unode
, lru_link
);
3535 LIST_REMOVE(unode
, hash_link
);
3536 FREE(unode
, M_TEMP
);
3538 /* decrement node count */
3539 OSAddAtomic(-1, &nfsrv_user_stat_node_count
);
3541 ulist
->node_count
= 0;
3543 lck_mtx_destroy(&ulist
->user_mutex
, nfsrv_active_user_mutex_group
);
3546 /* Reclaim old expired user nodes from active user lists. */
3548 nfsrv_active_user_list_reclaim(void)
3550 struct nfs_exportfs
*nxfs
;
3551 struct nfs_export
*nx
;
3552 struct nfs_active_user_list
*ulist
;
3553 struct nfs_user_stat_hashtbl_head oldlist
;
3554 struct nfs_user_stat_node
*unode
, *unode_next
;
3558 LIST_INIT(&oldlist
);
3560 lck_rw_lock_shared(&nfsrv_export_rwlock
);
3562 tstale
= now
.tv_sec
- nfsrv_user_stat_max_idle_sec
;
3563 LIST_FOREACH(nxfs
, &nfsrv_exports
, nxfs_next
) {
3564 LIST_FOREACH(nx
, &nxfs
->nxfs_exports
, nx_next
) {
3565 /* Scan through all user nodes of this export */
3566 ulist
= &nx
->nx_user_list
;
3567 lck_mtx_lock(&ulist
->user_mutex
);
3568 for (unode
= TAILQ_FIRST(&ulist
->user_lru
); unode
; unode
= unode_next
) {
3569 unode_next
= TAILQ_NEXT(unode
, lru_link
);
3571 /* check if this node has expired */
3572 if (unode
->tm_last
>= tstale
)
3575 /* Remove node from the active user list */
3576 TAILQ_REMOVE(&ulist
->user_lru
, unode
, lru_link
);
3577 LIST_REMOVE(unode
, hash_link
);
3579 /* Add node to temp list */
3580 LIST_INSERT_HEAD(&oldlist
, unode
, hash_link
);
3582 /* decrement node count */
3583 OSAddAtomic(-1, &nfsrv_user_stat_node_count
);
3584 ulist
->node_count
--;
3586 /* can unlock this export's list now */
3587 lck_mtx_unlock(&ulist
->user_mutex
);
3590 lck_rw_done(&nfsrv_export_rwlock
);
3592 /* Free expired nodes */
3593 while ((unode
= LIST_FIRST(&oldlist
))) {
3594 LIST_REMOVE(unode
, hash_link
);
3595 FREE(unode
, M_TEMP
);
3600 * Maps errno values to nfs error numbers.
3601 * Use NFSERR_IO as the catch all for ones not specifically defined in
3604 static u_char nfsrv_v2errmap
[] = {
3605 NFSERR_PERM
, NFSERR_NOENT
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3606 NFSERR_NXIO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3607 NFSERR_IO
, NFSERR_IO
, NFSERR_ACCES
, NFSERR_IO
, NFSERR_IO
,
3608 NFSERR_IO
, NFSERR_EXIST
, NFSERR_IO
, NFSERR_NODEV
, NFSERR_NOTDIR
,
3609 NFSERR_ISDIR
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3610 NFSERR_IO
, NFSERR_FBIG
, NFSERR_NOSPC
, NFSERR_IO
, NFSERR_ROFS
,
3611 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3612 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3613 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3614 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3615 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3616 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3617 NFSERR_IO
, NFSERR_IO
, NFSERR_NAMETOL
, NFSERR_IO
, NFSERR_IO
,
3618 NFSERR_NOTEMPTY
, NFSERR_IO
, NFSERR_IO
, NFSERR_DQUOT
, NFSERR_STALE
,
3622 * Maps errno values to nfs error numbers.
3623 * Although it is not obvious whether or not NFS clients really care if
3624 * a returned error value is in the specified list for the procedure, the
3625 * safest thing to do is filter them appropriately. For Version 2, the
3626 * X/Open XNFS document is the only specification that defines error values
3627 * for each RPC (The RFC simply lists all possible error values for all RPCs),
3628 * so I have decided to not do this for Version 2.
3629 * The first entry is the default error return and the rest are the valid
3630 * errors for that RPC in increasing numeric order.
3632 static short nfsv3err_null
[] = {
3637 static short nfsv3err_getattr
[] = {
3647 static short nfsv3err_setattr
[] = {
3664 static short nfsv3err_lookup
[] = {
3678 static short nfsv3err_access
[] = {
3688 static short nfsv3err_readlink
[] = {
3701 static short nfsv3err_read
[] = {
3714 static short nfsv3err_write
[] = {
3730 static short nfsv3err_create
[] = {
3748 static short nfsv3err_mkdir
[] = {
3766 static short nfsv3err_symlink
[] = {
3784 static short nfsv3err_mknod
[] = {
3803 static short nfsv3err_remove
[] = {
3818 static short nfsv3err_rmdir
[] = {
3837 static short nfsv3err_rename
[] = {
3861 static short nfsv3err_link
[] = {
3882 static short nfsv3err_readdir
[] = {
3896 static short nfsv3err_readdirplus
[] = {
3911 static short nfsv3err_fsstat
[] = {
3921 static short nfsv3err_fsinfo
[] = {
3930 static short nfsv3err_pathconf
[] = {
3939 static short nfsv3err_commit
[] = {
3949 static short *nfsrv_v3errmap
[] = {
3967 nfsv3err_readdirplus
,
3975 * Map errnos to NFS error numbers. For Version 3 also filter out error
3976 * numbers not specified for the associated procedure.
3979 nfsrv_errmap(struct nfsrv_descript
*nd
, int err
)
3981 short *defaulterrp
, *errp
;
3983 if (nd
->nd_vers
== NFS_VER2
) {
3984 if (err
<= (int)sizeof(nfsrv_v2errmap
))
3985 return ((int)nfsrv_v2errmap
[err
- 1]);
3989 if (nd
->nd_procnum
> NFSPROC_COMMIT
)
3990 return (err
& 0xffff);
3991 errp
= defaulterrp
= nfsrv_v3errmap
[nd
->nd_procnum
];
3995 else if (*errp
> err
)
3998 return ((int)*defaulterrp
);
4001 #endif /* NFSSERVER */