2 * Copyright (c) 2000-2009 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>
102 #include <nfs/xdr_subs.h>
103 #include <nfs/nfsm_subs.h>
104 #include <nfs/nfs_gss.h>
105 #include <nfs/nfsmount.h>
106 #include <nfs/nfs_lock.h>
108 #include <miscfs/specfs/specdev.h>
110 #include <netinet/in.h>
111 #include <net/kpi_interface.h>
116 struct nfsstats nfsstats
;
117 size_t nfs_mbuf_mhlen
= 0, nfs_mbuf_minclsize
= 0;
120 * functions to convert between NFS and VFS types
123 vtonfs_type(enum vtype vtype
, int nfsvers
)
139 if (nfsvers
> NFS_VER2
)
142 if (nfsvers
> NFS_VER2
)
153 nfstov_type(nfstype nvtype
, int nfsvers
)
169 if (nfsvers
> NFS_VER2
)
172 if (nfsvers
> NFS_VER2
)
175 if (nfsvers
> NFS_VER3
)
178 if (nfsvers
> NFS_VER3
)
186 vtonfsv2_mode(enum vtype vtype
, mode_t m
)
196 return vnode_makeimode(vtype
, m
);
198 return vnode_makeimode(VCHR
, m
);
203 return vnode_makeimode(VNON
, m
);
210 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
212 int nfsv3_procid
[NFS_NPROCS
] = {
238 #endif /* NFSSERVER */
241 * and the reverse mapping from generic to Version 2 procedure numbers
243 int nfsv2_procid
[NFS_NPROCS
] = {
271 * initialize NFS's cache of mbuf constants
279 nfs_mbuf_mhlen
= ms
.mhlen
;
280 nfs_mbuf_minclsize
= ms
.minclsize
;
286 * allocate a list of mbufs to hold the given amount of data
289 nfsm_mbuf_get_list(size_t size
, mbuf_t
*mp
, int *mbcnt
)
292 mbuf_t mhead
, mlast
, m
;
296 mhead
= mlast
= NULL
;
300 nfsm_mbuf_get(error
, &m
, (size
- len
));
305 if (mlast
&& ((error
= mbuf_setnext(mlast
, m
)))) {
309 mlen
= mbuf_maxlen(m
);
310 if ((len
+ mlen
) > size
)
312 mbuf_setlen(m
, mlen
);
325 #endif /* NFSSERVER */
328 * nfsm_chain_new_mbuf()
330 * Add a new mbuf to the given chain.
333 nfsm_chain_new_mbuf(struct nfsm_chain
*nmc
, size_t sizehint
)
338 if (nmc
->nmc_flags
& NFSM_CHAIN_FLAG_ADD_CLUSTERS
)
339 sizehint
= nfs_mbuf_minclsize
;
341 /* allocate a new mbuf */
342 nfsm_mbuf_get(error
, &mb
, sizehint
);
346 panic("got NULL mbuf?");
348 /* do we have a current mbuf? */
350 /* first cap off current mbuf */
351 mbuf_setlen(nmc
->nmc_mcur
, nmc
->nmc_ptr
- (caddr_t
)mbuf_data(nmc
->nmc_mcur
));
352 /* then append the new mbuf */
353 error
= mbuf_setnext(nmc
->nmc_mcur
, mb
);
360 /* set up for using the new mbuf */
362 nmc
->nmc_ptr
= mbuf_data(mb
);
363 nmc
->nmc_left
= mbuf_trailingspace(mb
);
369 * nfsm_chain_add_opaque_f()
371 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
374 nfsm_chain_add_opaque_f(struct nfsm_chain
*nmc
, const u_char
*buf
, uint32_t len
)
376 uint32_t paddedlen
, tlen
;
379 paddedlen
= nfsm_rndup(len
);
382 if (!nmc
->nmc_left
) {
383 error
= nfsm_chain_new_mbuf(nmc
, paddedlen
);
387 tlen
= MIN(nmc
->nmc_left
, paddedlen
);
392 bcopy(buf
, nmc
->nmc_ptr
, tlen
);
394 bzero(nmc
->nmc_ptr
, tlen
);
396 nmc
->nmc_ptr
+= tlen
;
397 nmc
->nmc_left
-= tlen
;
409 * nfsm_chain_add_opaque_nopad_f()
411 * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
415 nfsm_chain_add_opaque_nopad_f(struct nfsm_chain
*nmc
, const u_char
*buf
, uint32_t len
)
421 if (nmc
->nmc_left
<= 0) {
422 error
= nfsm_chain_new_mbuf(nmc
, len
);
426 tlen
= MIN(nmc
->nmc_left
, len
);
427 bcopy(buf
, nmc
->nmc_ptr
, tlen
);
428 nmc
->nmc_ptr
+= tlen
;
429 nmc
->nmc_left
-= tlen
;
437 * nfsm_chain_add_uio()
439 * Add "len" bytes of data from "uio" to the given chain.
442 nfsm_chain_add_uio(struct nfsm_chain
*nmc
, uio_t uio
, uint32_t len
)
444 uint32_t paddedlen
, tlen
;
447 paddedlen
= nfsm_rndup(len
);
450 if (!nmc
->nmc_left
) {
451 error
= nfsm_chain_new_mbuf(nmc
, paddedlen
);
455 tlen
= MIN(nmc
->nmc_left
, paddedlen
);
460 uiomove(nmc
->nmc_ptr
, tlen
, uio
);
462 bzero(nmc
->nmc_ptr
, tlen
);
464 nmc
->nmc_ptr
+= tlen
;
465 nmc
->nmc_left
-= tlen
;
475 * Find the length of the NFS mbuf chain
476 * up to the current encoding/decoding offset.
479 nfsm_chain_offset(struct nfsm_chain
*nmc
)
484 for (mb
= nmc
->nmc_mhead
; mb
; mb
= mbuf_next(mb
)) {
485 if (mb
== nmc
->nmc_mcur
)
486 return (len
+ (nmc
->nmc_ptr
- (caddr_t
) mbuf_data(mb
)));
494 * nfsm_chain_advance()
496 * Advance an nfsm_chain by "len" bytes.
499 nfsm_chain_advance(struct nfsm_chain
*nmc
, uint32_t len
)
504 if (nmc
->nmc_left
>= len
) {
505 nmc
->nmc_left
-= len
;
509 len
-= nmc
->nmc_left
;
510 nmc
->nmc_mcur
= mb
= mbuf_next(nmc
->nmc_mcur
);
513 nmc
->nmc_ptr
= mbuf_data(mb
);
514 nmc
->nmc_left
= mbuf_len(mb
);
521 * nfsm_chain_reverse()
523 * Reverse decode offset in an nfsm_chain by "len" bytes.
526 nfsm_chain_reverse(struct nfsm_chain
*nmc
, uint32_t len
)
528 uint32_t mlen
, new_offset
;
531 mlen
= nmc
->nmc_ptr
- (caddr_t
) mbuf_data(nmc
->nmc_mcur
);
534 nmc
->nmc_left
+= len
;
538 new_offset
= nfsm_chain_offset(nmc
) - len
;
539 nfsm_chain_dissect_init(error
, nmc
, nmc
->nmc_mhead
);
543 return (nfsm_chain_advance(nmc
, new_offset
));
547 * nfsm_chain_get_opaque_pointer_f()
549 * Return a pointer to the next "len" bytes of contiguous data in
550 * the mbuf chain. If the next "len" bytes are not contiguous, we
551 * try to manipulate the mbuf chain so that it is.
553 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
556 nfsm_chain_get_opaque_pointer_f(struct nfsm_chain
*nmc
, uint32_t len
, u_char
**pptr
)
559 uint32_t left
, need
, mblen
, cplen
, padlen
;
563 /* move to next mbuf with data */
564 while (nmc
->nmc_mcur
&& (nmc
->nmc_left
== 0)) {
565 mb
= mbuf_next(nmc
->nmc_mcur
);
569 nmc
->nmc_ptr
= mbuf_data(mb
);
570 nmc
->nmc_left
= mbuf_len(mb
);
572 /* check if we've run out of data */
576 /* do we already have a contiguous buffer? */
577 if (nmc
->nmc_left
>= len
) {
578 /* the returned pointer will be the current pointer */
579 *pptr
= (u_char
*)nmc
->nmc_ptr
;
580 error
= nfsm_chain_advance(nmc
, nfsm_rndup(len
));
584 padlen
= nfsm_rndup(len
) - len
;
586 /* we need (len - left) more bytes */
587 mbcur
= nmc
->nmc_mcur
;
588 left
= nmc
->nmc_left
;
591 if (need
> mbuf_trailingspace(mbcur
)) {
593 * The needed bytes won't fit in the current mbuf so we'll
594 * allocate a new mbuf to hold the contiguous range of data.
596 nfsm_mbuf_get(error
, &mb
, len
);
599 /* double check that this mbuf can hold all the data */
600 if (mbuf_maxlen(mb
) < len
) {
605 /* the returned pointer will be the new mbuf's data pointer */
606 *pptr
= ptr
= mbuf_data(mb
);
608 /* copy "left" bytes to the new mbuf */
609 bcopy(nmc
->nmc_ptr
, ptr
, left
);
611 mbuf_setlen(mb
, left
);
613 /* insert the new mbuf between the current and next mbufs */
614 error
= mbuf_setnext(mb
, mbuf_next(mbcur
));
616 error
= mbuf_setnext(mbcur
, mb
);
622 /* reduce current mbuf's length by "left" */
623 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - left
);
626 * update nmc's state to point at the end of the mbuf
627 * where the needed data will be copied to.
629 nmc
->nmc_mcur
= mbcur
= mb
;
631 nmc
->nmc_ptr
= (caddr_t
)ptr
;
633 /* The rest of the data will fit in this mbuf. */
635 /* the returned pointer will be the current pointer */
636 *pptr
= (u_char
*)nmc
->nmc_ptr
;
639 * update nmc's state to point at the end of the mbuf
640 * where the needed data will be copied to.
642 nmc
->nmc_ptr
+= left
;
647 * move the next "need" bytes into the current
648 * mbuf from the mbufs that follow
651 /* extend current mbuf length */
652 mbuf_setlen(mbcur
, mbuf_len(mbcur
) + need
);
654 /* mb follows mbufs we're copying/compacting data from */
655 mb
= mbuf_next(mbcur
);
658 /* copy as much as we need/can */
660 mblen
= mbuf_len(mb
);
661 cplen
= MIN(mblen
, need
);
663 bcopy(ptr
, nmc
->nmc_ptr
, cplen
);
665 * update the mbuf's pointer and length to reflect that
666 * the data was shifted to an earlier mbuf in the chain
668 error
= mbuf_setdata(mb
, ptr
+ cplen
, mblen
- cplen
);
670 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - need
);
673 /* update pointer/need */
674 nmc
->nmc_ptr
+= cplen
;
677 /* if more needed, go to next mbuf */
682 /* did we run out of data in the mbuf chain? */
684 mbuf_setlen(mbcur
, mbuf_len(mbcur
) - need
);
689 * update nmc's state to point after this contiguous data
691 * "mb" points to the last mbuf we copied data from so we
692 * just set nmc to point at whatever remains in that mbuf.
695 nmc
->nmc_ptr
= mbuf_data(mb
);
696 nmc
->nmc_left
= mbuf_len(mb
);
698 /* move past any padding */
700 error
= nfsm_chain_advance(nmc
, padlen
);
706 * nfsm_chain_get_opaque_f()
708 * Read the next "len" bytes in the chain into "buf".
709 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
712 nfsm_chain_get_opaque_f(struct nfsm_chain
*nmc
, uint32_t len
, u_char
*buf
)
714 uint32_t cplen
, padlen
;
717 padlen
= nfsm_rndup(len
) - len
;
719 /* loop through mbufs copying all the data we need */
720 while (len
&& nmc
->nmc_mcur
) {
721 /* copy as much as we need/can */
722 cplen
= MIN(nmc
->nmc_left
, len
);
724 bcopy(nmc
->nmc_ptr
, buf
, cplen
);
725 nmc
->nmc_ptr
+= cplen
;
726 nmc
->nmc_left
-= cplen
;
730 /* if more needed, go to next mbuf */
732 mbuf_t mb
= mbuf_next(nmc
->nmc_mcur
);
734 nmc
->nmc_ptr
= mb
? mbuf_data(mb
) : NULL
;
735 nmc
->nmc_left
= mb
? mbuf_len(mb
) : 0;
739 /* did we run out of data in the mbuf chain? */
744 nfsm_chain_adv(error
, nmc
, padlen
);
750 * nfsm_chain_get_uio()
752 * Read the next "len" bytes in the chain into the given uio.
753 * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
756 nfsm_chain_get_uio(struct nfsm_chain
*nmc
, uint32_t len
, uio_t uio
)
758 uint32_t cplen
, padlen
;
761 padlen
= nfsm_rndup(len
) - len
;
763 /* loop through mbufs copying all the data we need */
764 while (len
&& nmc
->nmc_mcur
) {
765 /* copy as much as we need/can */
766 cplen
= MIN(nmc
->nmc_left
, len
);
768 error
= uiomove(nmc
->nmc_ptr
, cplen
, uio
);
771 nmc
->nmc_ptr
+= cplen
;
772 nmc
->nmc_left
-= cplen
;
775 /* if more needed, go to next mbuf */
777 mbuf_t mb
= mbuf_next(nmc
->nmc_mcur
);
779 nmc
->nmc_ptr
= mb
? mbuf_data(mb
) : NULL
;
780 nmc
->nmc_left
= mb
? mbuf_len(mb
) : 0;
784 /* did we run out of data in the mbuf chain? */
789 nfsm_chain_adv(error
, nmc
, padlen
);
797 * Add an NFSv2 "sattr" structure to an mbuf chain
800 nfsm_chain_add_v2sattr_f(struct nfsm_chain
*nmc
, struct vnode_attr
*vap
, uint32_t szrdev
)
804 nfsm_chain_add_32(error
, nmc
, vtonfsv2_mode(vap
->va_type
,
805 (VATTR_IS_ACTIVE(vap
, va_mode
) ? vap
->va_mode
: 0600)));
806 nfsm_chain_add_32(error
, nmc
,
807 VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: (uint32_t)-1);
808 nfsm_chain_add_32(error
, nmc
,
809 VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: (uint32_t)-1);
810 nfsm_chain_add_32(error
, nmc
, szrdev
);
811 nfsm_chain_add_v2time(error
, nmc
,
812 VATTR_IS_ACTIVE(vap
, va_access_time
) ?
813 &vap
->va_access_time
: NULL
);
814 nfsm_chain_add_v2time(error
, nmc
,
815 VATTR_IS_ACTIVE(vap
, va_modify_time
) ?
816 &vap
->va_modify_time
: NULL
);
822 * Add an NFSv3 "sattr" structure to an mbuf chain
825 nfsm_chain_add_v3sattr_f(struct nfsm_chain
*nmc
, struct vnode_attr
*vap
)
829 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
830 nfsm_chain_add_32(error
, nmc
, TRUE
);
831 nfsm_chain_add_32(error
, nmc
, vap
->va_mode
);
833 nfsm_chain_add_32(error
, nmc
, FALSE
);
835 if (VATTR_IS_ACTIVE(vap
, va_uid
)) {
836 nfsm_chain_add_32(error
, nmc
, TRUE
);
837 nfsm_chain_add_32(error
, nmc
, vap
->va_uid
);
839 nfsm_chain_add_32(error
, nmc
, FALSE
);
841 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
842 nfsm_chain_add_32(error
, nmc
, TRUE
);
843 nfsm_chain_add_32(error
, nmc
, vap
->va_gid
);
845 nfsm_chain_add_32(error
, nmc
, FALSE
);
847 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
848 nfsm_chain_add_32(error
, nmc
, TRUE
);
849 nfsm_chain_add_64(error
, nmc
, vap
->va_data_size
);
851 nfsm_chain_add_32(error
, nmc
, FALSE
);
853 if (vap
->va_vaflags
& VA_UTIMES_NULL
) {
854 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_SERVER
);
855 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_SERVER
);
857 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
858 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_CLIENT
);
859 nfsm_chain_add_32(error
, nmc
, vap
->va_access_time
.tv_sec
);
860 nfsm_chain_add_32(error
, nmc
, vap
->va_access_time
.tv_nsec
);
862 nfsm_chain_add_32(error
, nmc
, NFS_TIME_DONT_CHANGE
);
864 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
865 nfsm_chain_add_32(error
, nmc
, NFS_TIME_SET_TO_CLIENT
);
866 nfsm_chain_add_32(error
, nmc
, vap
->va_modify_time
.tv_sec
);
867 nfsm_chain_add_32(error
, nmc
, vap
->va_modify_time
.tv_nsec
);
869 nfsm_chain_add_32(error
, nmc
, NFS_TIME_DONT_CHANGE
);
878 * nfsm_chain_get_fh_attr()
880 * Get the file handle and attributes from an mbuf chain. (NFSv2/v3)
883 nfsm_chain_get_fh_attr(
884 struct nfsm_chain
*nmc
,
890 struct nfs_vattr
*nvap
)
892 int error
= 0, gotfh
, gotattr
;
896 if (nfsvers
== NFS_VER3
) /* check for file handle */
897 nfsm_chain_get_32(error
, nmc
, gotfh
);
898 if (!error
&& gotfh
) /* get file handle */
899 nfsm_chain_get_fh(error
, nmc
, nfsvers
, fhp
);
902 if (nfsvers
== NFS_VER3
) /* check for file attributes */
903 nfsm_chain_get_32(error
, nmc
, gotattr
);
906 if (!gotfh
) /* skip attributes */
907 nfsm_chain_adv(error
, nmc
, NFSX_V3FATTR
);
908 else /* get attributes */
909 error
= nfs_parsefattr(nmc
, nfsvers
, nvap
);
911 /* we need valid attributes in order to call nfs_nget() */
912 if (nfs3_getattr_rpc(NULL
, NFSTOMP(dnp
), fhp
->fh_data
, fhp
->fh_len
, ctx
, nvap
, xidp
)) {
922 * Get and process NFSv3 WCC data from an mbuf chain
925 nfsm_chain_get_wcc_data_f(
926 struct nfsm_chain
*nmc
,
928 struct timespec
*premtime
,
935 nfsm_chain_get_32(error
, nmc
, flag
);
936 if (!error
&& flag
) {
937 nfsm_chain_adv(error
, nmc
, 2 * NFSX_UNSIGNED
);
938 nfsm_chain_get_32(error
, nmc
, premtime
->tv_sec
);
939 nfsm_chain_get_32(error
, nmc
, premtime
->tv_nsec
);
940 nfsm_chain_adv(error
, nmc
, 2 * NFSX_UNSIGNED
);
942 premtime
->tv_sec
= 0;
943 premtime
->tv_nsec
= 0;
945 nfsm_chain_postop_attr_update_flag(error
, nmc
, np
, *newpostattr
, xidp
);
951 * Get the next RPC transaction ID (XID)
954 nfs_get_xid(uint64_t *xidp
)
958 lck_mtx_lock(nfs_request_mutex
);
961 * Derive initial xid from system time.
963 * Note: it's OK if this code inits nfs_xid to 0 (for example,
964 * due to a broken clock) because we immediately increment it
965 * and we guarantee to never use xid 0. So, nfs_xid should only
966 * ever be 0 the first time this function is called.
969 nfs_xid
= tv
.tv_sec
<< 12;
971 if (++nfs_xid
== 0) {
972 /* Skip zero xid if it should ever happen. */
976 *xidp
= nfs_xid
+ ((uint64_t)nfs_xidwrap
<< 32);
977 lck_mtx_unlock(nfs_request_mutex
);
981 * Build the RPC header and fill in the authorization info.
982 * Returns the head of the mbuf list and the xid.
993 struct nfsmount
*nmp
= req
->r_nmp
;
994 int nfsvers
= nmp
->nm_vers
;
995 int proc
= ((nfsvers
== NFS_VER2
) ? nfsv2_procid
[req
->r_procnum
] : (int)req
->r_procnum
);
996 int auth_type
= (!auth_len
&& !req
->r_cred
) ? RPCAUTH_NULL
: nmp
->nm_auth
;
998 return nfsm_rpchead2(nmp
->nm_sotype
, NFS_PROG
, nfsvers
, proc
,
999 auth_type
, auth_len
, req
->r_cred
, req
, mrest
, xidp
, mreqp
);
1003 nfsm_rpchead2(int sotype
, int prog
, int vers
, int proc
, int auth_type
, int auth_len
,
1004 kauth_cred_t cred
, struct nfsreq
*req
, mbuf_t mrest
, u_int64_t
*xidp
, mbuf_t
*mreqp
)
1007 int error
, i
, grpsiz
, authsiz
, reqlen
;
1009 struct nfsm_chain nmreq
;
1011 /* allocate the packet */
1012 authsiz
= nfsm_rndup(auth_len
);
1013 headlen
= authsiz
+ 10 * NFSX_UNSIGNED
;
1014 if (sotype
== SOCK_STREAM
) /* also include room for any RPC Record Mark */
1015 headlen
+= NFSX_UNSIGNED
;
1016 if (headlen
>= nfs_mbuf_minclsize
) {
1017 error
= mbuf_getpacket(MBUF_WAITOK
, &mreq
);
1019 error
= mbuf_gethdr(MBUF_WAITOK
, MBUF_TYPE_DATA
, &mreq
);
1021 if (headlen
< nfs_mbuf_mhlen
)
1022 mbuf_align_32(mreq
, headlen
);
1024 mbuf_align_32(mreq
, 8 * NFSX_UNSIGNED
);
1028 /* unable to allocate packet */
1029 /* XXX should we keep statistics for these errors? */
1034 * If the caller gave us a non-zero XID then use it because
1035 * it may be a higher-level resend with a GSSAPI credential.
1036 * Otherwise, allocate a new one.
1041 /* build the header(s) */
1042 nfsm_chain_init(&nmreq
, mreq
);
1044 /* First, if it's a TCP stream insert space for an RPC record mark */
1045 if (sotype
== SOCK_STREAM
)
1046 nfsm_chain_add_32(error
, &nmreq
, 0);
1048 /* Then the RPC header. */
1049 nfsm_chain_add_32(error
, &nmreq
, (*xidp
& 0xffffffff));
1050 nfsm_chain_add_32(error
, &nmreq
, RPC_CALL
);
1051 nfsm_chain_add_32(error
, &nmreq
, RPC_VER2
);
1052 nfsm_chain_add_32(error
, &nmreq
, prog
);
1053 nfsm_chain_add_32(error
, &nmreq
, vers
);
1054 nfsm_chain_add_32(error
, &nmreq
, proc
);
1057 switch (auth_type
) {
1059 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NULL
); /* auth */
1060 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1061 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NULL
); /* verf */
1062 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1063 nfsm_chain_build_done(error
, &nmreq
);
1066 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_UNIX
);
1067 nfsm_chain_add_32(error
, &nmreq
, authsiz
);
1068 nfsm_chain_add_32(error
, &nmreq
, 0); /* stamp */
1069 nfsm_chain_add_32(error
, &nmreq
, 0); /* zero-length hostname */
1070 nfsm_chain_add_32(error
, &nmreq
, kauth_cred_getuid(cred
)); /* UID */
1071 nfsm_chain_add_32(error
, &nmreq
, cred
->cr_groups
[0]); /* GID */
1072 grpsiz
= (auth_len
>> 2) - 5;
1073 nfsm_chain_add_32(error
, &nmreq
, grpsiz
);/* additional GIDs */
1074 for (i
= 1; i
<= grpsiz
; i
++)
1075 nfsm_chain_add_32(error
, &nmreq
, cred
->cr_groups
[i
]);
1077 /* And the verifier... */
1078 nfsm_chain_add_32(error
, &nmreq
, RPCAUTH_NULL
); /* flavor */
1079 nfsm_chain_add_32(error
, &nmreq
, 0); /* length */
1080 nfsm_chain_build_done(error
, &nmreq
);
1082 /* Append the args mbufs */
1084 error
= mbuf_setnext(nmreq
.nmc_mcur
, mrest
);
1089 error
= nfs_gss_clnt_cred_put(req
, &nmreq
, mrest
);
1090 if (error
== ENEEDAUTH
) {
1092 * Use sec=sys for this user
1095 auth_type
= RPCAUTH_UNIX
;
1101 /* finish setting up the packet */
1103 error
= mbuf_pkthdr_setrcvif(mreq
, 0);
1110 /* Calculate the size of the request */
1112 for (mb
= nmreq
.nmc_mhead
; mb
; mb
= mbuf_next(mb
))
1113 reqlen
+= mbuf_len(mb
);
1115 mbuf_pkthdr_setlen(mreq
, reqlen
);
1118 * If the request goes on a TCP stream,
1119 * set its size in the RPC record mark.
1120 * The record mark count doesn't include itself
1121 * and the last fragment bit is set.
1123 if (sotype
== SOCK_STREAM
)
1124 nfsm_chain_set_recmark(error
, &nmreq
,
1125 (reqlen
- NFSX_UNSIGNED
) | 0x80000000);
1132 * Parse an NFS file attribute structure out of an mbuf chain.
1135 nfs_parsefattr(struct nfsm_chain
*nmc
, int nfsvers
, struct nfs_vattr
*nvap
)
1145 nfsm_chain_get_32(error
, nmc
, vtype
);
1146 nfsm_chain_get_32(error
, nmc
, vmode
);
1149 if (nfsvers
== NFS_VER3
) {
1150 nvap
->nva_type
= nfstov_type(vtype
, nfsvers
);
1153 * The duplicate information returned in fa_type and fa_mode
1154 * is an ambiguity in the NFS version 2 protocol.
1156 * VREG should be taken literally as a regular file. If a
1157 * server intends to return some type information differently
1158 * in the upper bits of the mode field (e.g. for sockets, or
1159 * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we
1160 * leave the examination of the mode bits even in the VREG
1161 * case to avoid breakage for bogus servers, but we make sure
1162 * that there are actually type bits set in the upper part of
1163 * fa_mode (and failing that, trust the va_type field).
1165 * NFSv3 cleared the issue, and requires fa_mode to not
1166 * contain any type information (while also introducing
1167 * sockets and FIFOs for fa_type).
1169 vtype
= nfstov_type(vtype
, nfsvers
);
1170 if ((vtype
== VNON
) || ((vtype
== VREG
) && ((vmode
& S_IFMT
) != 0)))
1171 vtype
= IFTOVT(vmode
);
1172 nvap
->nva_type
= vtype
;
1175 nvap
->nva_mode
= (vmode
& 07777);
1177 nfsm_chain_get_32(error
, nmc
, nvap
->nva_nlink
);
1178 nfsm_chain_get_32(error
, nmc
, nvap
->nva_uid
);
1179 nfsm_chain_get_32(error
, nmc
, nvap
->nva_gid
);
1181 if (nfsvers
== NFS_VER3
) {
1182 nfsm_chain_get_64(error
, nmc
, nvap
->nva_size
);
1183 nfsm_chain_get_64(error
, nmc
, nvap
->nva_bytes
);
1184 nfsm_chain_get_32(error
, nmc
, nvap
->nva_rawdev
.specdata1
);
1185 nfsm_chain_get_32(error
, nmc
, nvap
->nva_rawdev
.specdata2
);
1187 nfsm_chain_get_64(error
, nmc
, nvap
->nva_fsid
.major
);
1188 nvap
->nva_fsid
.minor
= 0;
1189 nfsm_chain_get_64(error
, nmc
, nvap
->nva_fileid
);
1191 nfsm_chain_get_32(error
, nmc
, nvap
->nva_size
);
1192 nfsm_chain_adv(error
, nmc
, NFSX_UNSIGNED
);
1193 nfsm_chain_get_32(error
, nmc
, rdev
);
1195 nvap
->nva_rawdev
.specdata1
= major(rdev
);
1196 nvap
->nva_rawdev
.specdata2
= minor(rdev
);
1197 nfsm_chain_get_32(error
, nmc
, val
); /* blocks */
1199 nvap
->nva_bytes
= val
* NFS_FABLKSIZE
;
1200 nfsm_chain_get_32(error
, nmc
, val
);
1202 nvap
->nva_fsid
.major
= (uint64_t)val
;
1203 nvap
->nva_fsid
.minor
= 0;
1204 nfsm_chain_get_32(error
, nmc
, val
);
1206 nvap
->nva_fileid
= (uint64_t)val
;
1207 /* Really ugly NFSv2 kludge. */
1208 if ((vtype
== VCHR
) && (rdev
== (dev_t
)0xffffffff))
1209 nvap
->nva_type
= VFIFO
;
1211 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1212 nvap
->nva_timesec
[NFSTIME_ACCESS
],
1213 nvap
->nva_timensec
[NFSTIME_ACCESS
]);
1214 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1215 nvap
->nva_timesec
[NFSTIME_MODIFY
],
1216 nvap
->nva_timensec
[NFSTIME_MODIFY
]);
1217 nfsm_chain_get_time(error
, nmc
, nfsvers
,
1218 nvap
->nva_timesec
[NFSTIME_CHANGE
],
1219 nvap
->nva_timensec
[NFSTIME_CHANGE
]);
1225 * Load the attribute cache (that lives in the nfsnode entry) with
1226 * the value pointed to by nvap, unless the file type in the attribute
1227 * cache doesn't match the file type in the nvap, in which case log a
1228 * warning and return ESTALE.
1230 * If the dontshrink flag is set, then it's not safe to call ubc_setsize()
1231 * to shrink the size of the file.
1236 struct nfs_vattr
*nvap
,
1243 struct nfs_vattr
*npnvap
;
1245 if (np
->n_hflag
& NHINIT
) {
1250 mp
= vnode_mount(vp
);
1253 FSDBG_TOP(527, np
, vp
, *xidp
>> 32, *xidp
);
1255 if (!VFSTONFS(mp
)) {
1256 FSDBG_BOT(527, ENXIO
, 1, 0, *xidp
);
1260 if (*xidp
< np
->n_xid
) {
1262 * We have already updated attributes with a response from
1263 * a later request. The attributes we have here are probably
1264 * stale so we drop them (just return). However, our
1265 * out-of-order receipt could be correct - if the requests were
1266 * processed out of order at the server. Given the uncertainty
1267 * we invalidate our cached attributes. *xidp is zeroed here
1268 * to indicate the attributes were dropped - only getattr
1269 * cares - it needs to retry the rpc.
1271 NATTRINVALIDATE(np
);
1272 FSDBG_BOT(527, 0, np
, np
->n_xid
, *xidp
);
1277 if (vp
&& (nvap
->nva_type
!= vnode_vtype(vp
))) {
1279 * The filehandle has changed type on us. This can be
1280 * caused by either the server not having unique filehandles
1281 * or because another client has removed the previous
1282 * filehandle and a new object (of a different type)
1283 * has been created with the same filehandle.
1285 * We can't simply switch the type on the vnode because
1286 * there may be type-specific fields that need to be
1287 * cleaned up or set up.
1289 * So, what should we do with this vnode?
1291 * About the best we can do is log a warning and return
1292 * an error. ESTALE is about the closest error, but it
1293 * is a little strange that we come up with this error
1294 * internally instead of simply passing it through from
1295 * the server. Hopefully, the vnode will be reclaimed
1296 * soon so the filehandle can be reincarnated as the new
1299 printf("nfs loadattrcache vnode changed type, was %d now %d\n",
1300 vnode_vtype(vp
), nvap
->nva_type
);
1301 FSDBG_BOT(527, ESTALE
, 3, 0, *xidp
);
1306 np
->n_attrstamp
= now
.tv_sec
;
1309 npnvap
= &np
->n_vattr
;
1310 bcopy((caddr_t
)nvap
, (caddr_t
)npnvap
, sizeof(*nvap
));
1312 if (!vp
|| (nvap
->nva_type
!= VREG
)) {
1313 np
->n_size
= nvap
->nva_size
;
1314 } else if (nvap
->nva_size
!= np
->n_size
) {
1315 FSDBG(527, np
, nvap
->nva_size
, np
->n_size
, (nvap
->nva_type
== VREG
) | (np
->n_flag
& NMODIFIED
? 6 : 4));
1316 if (!UBCINFOEXISTS(vp
) || (dontshrink
&& (nvap
->nva_size
< np
->n_size
))) {
1317 /* asked not to shrink, so stick with current size */
1318 FSDBG(527, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0001);
1319 nvap
->nva_size
= np
->n_size
;
1320 NATTRINVALIDATE(np
);
1321 } else if ((np
->n_flag
& NMODIFIED
) && (nvap
->nva_size
< np
->n_size
)) {
1322 /* if we've modified, stick with larger size */
1323 FSDBG(527, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d0002);
1324 nvap
->nva_size
= np
->n_size
;
1325 npnvap
->nva_size
= np
->n_size
;
1328 * n_size is protected by the data lock, so we need to
1329 * defer updating it until it's safe. We save the new size
1330 * and set a flag and it'll get updated the next time we get/drop
1331 * the data lock or the next time we do a getattr.
1333 np
->n_newsize
= nvap
->nva_size
;
1334 SET(np
->n_flag
, NUPDATESIZE
);
1338 if (np
->n_flag
& NCHG
) {
1339 if (np
->n_flag
& NACC
) {
1340 nvap
->nva_timesec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_sec
;
1341 nvap
->nva_timensec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_nsec
;
1343 if (np
->n_flag
& NUPD
) {
1344 nvap
->nva_timesec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_sec
;
1345 nvap
->nva_timensec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_nsec
;
1349 FSDBG_BOT(527, 0, np
, np
->n_size
, *xidp
);
1354 * Calculate the attribute timeout based on
1355 * how recently the file has been modified.
1358 nfs_attrcachetimeout(nfsnode_t np
)
1360 struct nfsmount
*nmp
;
1364 if (!(nmp
= NFSTONMP(np
)))
1367 isdir
= vnode_isdir(NFSTOV(np
));
1369 if ((np
)->n_flag
& NMODIFIED
)
1370 timeo
= isdir
? nmp
->nm_acdirmin
: nmp
->nm_acregmin
;
1372 /* Note that if the client and server clocks are way out of sync, */
1373 /* timeout will probably get clamped to a min or max value */
1375 timeo
= (now
.tv_sec
- (np
)->n_vattr
.nva_timesec
[NFSTIME_MODIFY
]) / 10;
1377 if (timeo
< nmp
->nm_acdirmin
)
1378 timeo
= nmp
->nm_acdirmin
;
1379 else if (timeo
> nmp
->nm_acdirmax
)
1380 timeo
= nmp
->nm_acdirmax
;
1382 if (timeo
< nmp
->nm_acregmin
)
1383 timeo
= nmp
->nm_acregmin
;
1384 else if (timeo
> nmp
->nm_acregmax
)
1385 timeo
= nmp
->nm_acregmax
;
1393 * Check the attribute cache time stamp.
1394 * If the cache is valid, copy contents to *nvaper and return 0
1395 * otherwise return an error.
1396 * Must be called with the node locked.
1399 nfs_getattrcache(nfsnode_t np
, struct nfs_vattr
*nvaper
)
1401 struct nfs_vattr
*nvap
;
1402 struct timeval nowup
;
1405 if (!NATTRVALID(np
)) {
1406 FSDBG(528, np
, 0, 0xffffff01, ENOENT
);
1407 OSAddAtomic(1, &nfsstats
.attrcache_misses
);
1411 timeo
= nfs_attrcachetimeout(np
);
1413 microuptime(&nowup
);
1414 if ((nowup
.tv_sec
- np
->n_attrstamp
) >= timeo
) {
1415 FSDBG(528, np
, 0, 0xffffff02, ENOENT
);
1416 OSAddAtomic(1, &nfsstats
.attrcache_misses
);
1420 nvap
= &np
->n_vattr
;
1421 FSDBG(528, np
, nvap
->nva_size
, np
->n_size
, 0xcace);
1422 OSAddAtomic(1, &nfsstats
.attrcache_hits
);
1424 if (nvap
->nva_type
!= VREG
) {
1425 np
->n_size
= nvap
->nva_size
;
1426 } else if (nvap
->nva_size
!= np
->n_size
) {
1427 FSDBG(528, np
, nvap
->nva_size
, np
->n_size
, (nvap
->nva_type
== VREG
) | (np
->n_flag
& NMODIFIED
? 6 : 4));
1428 if ((np
->n_flag
& NMODIFIED
) && (nvap
->nva_size
< np
->n_size
)) {
1429 /* if we've modified, stick with larger size */
1430 nvap
->nva_size
= np
->n_size
;
1433 * n_size is protected by the data lock, so we need to
1434 * defer updating it until it's safe. We save the new size
1435 * and set a flag and it'll get updated the next time we get/drop
1436 * the data lock or the next time we do a getattr.
1438 np
->n_newsize
= nvap
->nva_size
;
1439 SET(np
->n_flag
, NUPDATESIZE
);
1443 bcopy((caddr_t
)nvap
, (caddr_t
)nvaper
, sizeof(struct nfs_vattr
));
1444 if (np
->n_flag
& NCHG
) {
1445 if (np
->n_flag
& NACC
) {
1446 nvaper
->nva_timesec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_sec
;
1447 nvaper
->nva_timensec
[NFSTIME_ACCESS
] = np
->n_atim
.tv_nsec
;
1449 if (np
->n_flag
& NUPD
) {
1450 nvaper
->nva_timesec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_sec
;
1451 nvaper
->nva_timensec
[NFSTIME_MODIFY
] = np
->n_mtim
.tv_nsec
;
1457 #endif /* NFSCLIENT */
1460 * Schedule a callout thread to run an NFS timer function
1461 * interval milliseconds in the future.
1464 nfs_interval_timer_start(thread_call_t call
, int interval
)
1468 clock_interval_to_deadline(interval
, 1000 * 1000, &deadline
);
1469 thread_call_enter_delayed(call
, deadline
);
1475 int nfsrv_cmp_secflavs(struct nfs_sec
*, struct nfs_sec
*);
1476 int nfsrv_hang_addrlist(struct nfs_export
*, struct user_nfs_export_args
*);
1477 int nfsrv_free_netopt(struct radix_node
*, void *);
1478 int nfsrv_free_addrlist(struct nfs_export
*, struct user_nfs_export_args
*);
1479 struct nfs_export_options
*nfsrv_export_lookup(struct nfs_export
*, mbuf_t
);
1480 struct nfs_export
*nfsrv_fhtoexport(struct nfs_filehandle
*);
1481 int nfsrv_cmp_sockaddr(struct sockaddr_storage
*, struct sockaddr_storage
*);
1482 struct nfs_user_stat_node
*nfsrv_get_user_stat_node(struct nfs_active_user_list
*, struct sockaddr_storage
*, uid_t
);
1483 void nfsrv_init_user_list(struct nfs_active_user_list
*);
1484 void nfsrv_free_user_list(struct nfs_active_user_list
*);
1487 * add NFSv3 WCC data to an mbuf chain
1490 nfsm_chain_add_wcc_data_f(
1491 struct nfsrv_descript
*nd
,
1492 struct nfsm_chain
*nmc
,
1494 struct vnode_attr
*prevap
,
1496 struct vnode_attr
*postvap
)
1501 nfsm_chain_add_32(error
, nmc
, FALSE
);
1503 nfsm_chain_add_32(error
, nmc
, TRUE
);
1504 nfsm_chain_add_64(error
, nmc
, prevap
->va_data_size
);
1505 nfsm_chain_add_time(error
, nmc
, NFS_VER3
, &prevap
->va_modify_time
);
1506 nfsm_chain_add_time(error
, nmc
, NFS_VER3
, &prevap
->va_change_time
);
1508 nfsm_chain_add_postop_attr(error
, nd
, nmc
, postattrerr
, postvap
);
1514 * Extract a lookup path from the given mbufs and store it in
1515 * a newly allocated buffer saved in the given nameidata structure.
1518 nfsm_chain_get_path_namei(
1519 struct nfsm_chain
*nmc
,
1521 struct nameidata
*nip
)
1523 struct componentname
*cnp
= &nip
->ni_cnd
;
1527 if (len
> (MAXPATHLEN
- 1))
1528 return (ENAMETOOLONG
);
1531 * Get a buffer for the name to be translated, and copy the
1532 * name into the buffer.
1534 MALLOC_ZONE(cnp
->cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
1537 cnp
->cn_pnlen
= MAXPATHLEN
;
1538 cnp
->cn_flags
|= HASBUF
;
1540 /* Copy the name from the mbuf list to the string */
1542 nfsm_chain_get_opaque(error
, nmc
, len
, cp
);
1545 cnp
->cn_pnbuf
[len
] = '\0';
1547 /* sanity check the string */
1548 if ((strlen(cp
) != len
) || strchr(cp
, '/'))
1553 FREE_ZONE(cnp
->cn_pnbuf
, MAXPATHLEN
, M_NAMEI
);
1554 cnp
->cn_flags
&= ~HASBUF
;
1556 nip
->ni_pathlen
= len
;
1562 * Set up nameidata for a lookup() call and do it.
1566 struct nfsrv_descript
*nd
,
1568 struct nameidata
*nip
,
1569 struct nfs_filehandle
*nfhp
,
1571 struct nfs_export
**nxp
,
1572 struct nfs_export_options
**nxop
)
1576 struct componentname
*cnp
= &nip
->ni_cnd
;
1583 * Extract and set starting directory.
1585 error
= nfsrv_fhtovp(nfhp
, nd
, &dp
, nxp
, nxop
);
1588 error
= nfsrv_credcheck(nd
, ctx
, *nxp
, *nxop
);
1589 if (error
|| (vnode_vtype(dp
) != VDIR
)) {
1596 nip
->ni_cnd
.cn_context
= ctx
;
1598 if (*nxop
&& ((*nxop
)->nxo_flags
& NX_READONLY
))
1599 cnp
->cn_flags
|= RDONLY
;
1601 cnp
->cn_flags
|= NOCROSSMOUNT
;
1602 cnp
->cn_nameptr
= cnp
->cn_pnbuf
;
1603 nip
->ni_usedvp
= nip
->ni_startdir
= dp
;
1606 * And call lookup() to do the real work
1608 cnflags
= nip
->ni_cnd
.cn_flags
; /* store in case we have to restore */
1609 while ((error
= lookup(nip
)) == ERECYCLE
) {
1610 nip
->ni_cnd
.cn_flags
= cnflags
;
1611 cnp
->cn_nameptr
= cnp
->cn_pnbuf
;
1612 nip
->ni_usedvp
= nip
->ni_dvp
= nip
->ni_startdir
= dp
;
1617 /* Check for encountering a symbolic link */
1618 if (cnp
->cn_flags
& ISSYMLINK
) {
1620 if ((cnp
->cn_flags
& FSNODELOCKHELD
)) {
1621 cnp
->cn_flags
&= ~FSNODELOCKHELD
;
1622 unlock_fsnode(nip
->ni_dvp
, NULL
);
1624 #endif /* __LP64__ */
1625 if (cnp
->cn_flags
& (LOCKPARENT
| WANTPARENT
))
1626 vnode_put(nip
->ni_dvp
);
1628 vnode_put(nip
->ni_vp
);
1635 tmppn
= cnp
->cn_pnbuf
;
1636 cnp
->cn_pnbuf
= NULL
;
1637 cnp
->cn_flags
&= ~HASBUF
;
1638 FREE_ZONE(tmppn
, cnp
->cn_pnlen
, M_NAMEI
);
1644 * A fiddled version of m_adj() that ensures null fill to a 4-byte
1645 * boundary and only trims off the back end
1648 nfsm_adj(mbuf_t mp
, int len
, int nul
)
1655 * Trim from tail. Scan the mbuf chain,
1656 * calculating its length and finding the last mbuf.
1657 * If the adjustment only affects this mbuf, then just
1658 * adjust and return. Otherwise, rescan and truncate
1659 * after the remaining size.
1666 mnext
= mbuf_next(m
);
1673 mbuf_setlen(m
, mlen
);
1675 cp
= (caddr_t
)mbuf_data(m
) + mlen
- nul
;
1676 for (i
= 0; i
< nul
; i
++)
1685 * Correct length for chain is "count".
1686 * Find the mbuf with last data, adjust its length,
1687 * and toss data from remaining mbufs on chain.
1689 for (m
= mp
; m
; m
= mbuf_next(m
)) {
1691 if (mlen
>= count
) {
1693 mbuf_setlen(m
, count
);
1695 cp
= (caddr_t
)mbuf_data(m
) + mlen
- nul
;
1696 for (i
= 0; i
< nul
; i
++)
1703 for (m
= mbuf_next(m
); m
; m
= mbuf_next(m
))
1708 * Trim the header out of the mbuf list and trim off any trailing
1709 * junk so that the mbuf list has only the write data.
1712 nfsm_chain_trim_data(struct nfsm_chain
*nmc
, int len
, int *mlen
)
1714 int cnt
= 0, dlen
, adjust
;
1722 for (m
= nmc
->nmc_mhead
; m
&& (m
!= nmc
->nmc_mcur
); m
= mbuf_next(m
))
1727 /* trim current mbuf */
1728 data
= mbuf_data(m
);
1730 adjust
= nmc
->nmc_ptr
- data
;
1732 if ((dlen
> 0) && (adjust
> 0)) {
1733 if (mbuf_setdata(m
, nmc
->nmc_ptr
, dlen
))
1736 mbuf_setlen(m
, dlen
);
1738 /* skip next len bytes */
1739 for (; m
&& (cnt
< len
); m
= mbuf_next(m
)) {
1743 /* truncate to end of data */
1744 mbuf_setlen(m
, dlen
- (cnt
- len
));
1745 if (m
== nmc
->nmc_mcur
)
1746 nmc
->nmc_left
-= (cnt
- len
);
1753 /* trim any trailing data */
1754 if (m
== nmc
->nmc_mcur
)
1756 for (; m
; m
= mbuf_next(m
))
1763 nfsm_chain_add_fattr(
1764 struct nfsrv_descript
*nd
,
1765 struct nfsm_chain
*nmc
,
1766 struct vnode_attr
*vap
)
1770 // XXX Should we assert here that all fields are supported?
1772 nfsm_chain_add_32(error
, nmc
, vtonfs_type(vap
->va_type
, nd
->nd_vers
));
1773 if (nd
->nd_vers
== NFS_VER3
) {
1774 nfsm_chain_add_32(error
, nmc
, vap
->va_mode
& 07777);
1776 nfsm_chain_add_32(error
, nmc
, vtonfsv2_mode(vap
->va_type
, vap
->va_mode
));
1778 nfsm_chain_add_32(error
, nmc
, vap
->va_nlink
);
1779 nfsm_chain_add_32(error
, nmc
, vap
->va_uid
);
1780 nfsm_chain_add_32(error
, nmc
, vap
->va_gid
);
1781 if (nd
->nd_vers
== NFS_VER3
) {
1782 nfsm_chain_add_64(error
, nmc
, vap
->va_data_size
);
1783 nfsm_chain_add_64(error
, nmc
, vap
->va_data_alloc
);
1784 nfsm_chain_add_32(error
, nmc
, major(vap
->va_rdev
));
1785 nfsm_chain_add_32(error
, nmc
, minor(vap
->va_rdev
));
1786 nfsm_chain_add_64(error
, nmc
, vap
->va_fsid
);
1787 nfsm_chain_add_64(error
, nmc
, vap
->va_fileid
);
1789 nfsm_chain_add_32(error
, nmc
, vap
->va_data_size
);
1790 nfsm_chain_add_32(error
, nmc
, NFS_FABLKSIZE
);
1791 if (vap
->va_type
== VFIFO
)
1792 nfsm_chain_add_32(error
, nmc
, 0xffffffff);
1794 nfsm_chain_add_32(error
, nmc
, vap
->va_rdev
);
1795 nfsm_chain_add_32(error
, nmc
, vap
->va_data_alloc
/ NFS_FABLKSIZE
);
1796 nfsm_chain_add_32(error
, nmc
, vap
->va_fsid
);
1797 nfsm_chain_add_32(error
, nmc
, vap
->va_fileid
);
1799 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_access_time
);
1800 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_modify_time
);
1801 nfsm_chain_add_time(error
, nmc
, nd
->nd_vers
, &vap
->va_change_time
);
1807 nfsm_chain_get_sattr(
1808 struct nfsrv_descript
*nd
,
1809 struct nfsm_chain
*nmc
,
1810 struct vnode_attr
*vap
)
1815 struct timespec now
;
1817 if (nd
->nd_vers
== NFS_VER2
) {
1819 * There is/was a bug in the Sun client that puts 0xffff in the mode
1820 * field of sattr when it should put in 0xffffffff. The u_short
1821 * doesn't sign extend. So check the low order 2 bytes for 0xffff.
1823 nfsm_chain_get_32(error
, nmc
, val
);
1824 if ((val
& 0xffff) != 0xffff) {
1825 VATTR_SET(vap
, va_mode
, val
& 07777);
1826 /* save the "type" bits for NFSv2 create */
1827 VATTR_SET(vap
, va_type
, IFTOVT(val
));
1828 VATTR_CLEAR_ACTIVE(vap
, va_type
);
1830 nfsm_chain_get_32(error
, nmc
, val
);
1831 if (val
!= (uint32_t)-1)
1832 VATTR_SET(vap
, va_uid
, val
);
1833 nfsm_chain_get_32(error
, nmc
, val
);
1834 if (val
!= (uint32_t)-1)
1835 VATTR_SET(vap
, va_gid
, val
);
1836 /* save the "size" bits for NFSv2 create (even if they appear unset) */
1837 nfsm_chain_get_32(error
, nmc
, val
);
1838 VATTR_SET(vap
, va_data_size
, val
);
1839 if (val
== (uint32_t)-1)
1840 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
1841 nfsm_chain_get_time(error
, nmc
, NFS_VER2
,
1842 vap
->va_access_time
.tv_sec
,
1843 vap
->va_access_time
.tv_nsec
);
1844 if (vap
->va_access_time
.tv_sec
!= -1)
1845 VATTR_SET_ACTIVE(vap
, va_access_time
);
1846 nfsm_chain_get_time(error
, nmc
, NFS_VER2
,
1847 vap
->va_modify_time
.tv_sec
,
1848 vap
->va_modify_time
.tv_nsec
);
1849 if (vap
->va_modify_time
.tv_sec
!= -1)
1850 VATTR_SET_ACTIVE(vap
, va_modify_time
);
1855 nfsm_chain_get_32(error
, nmc
, val
);
1857 nfsm_chain_get_32(error
, nmc
, val
);
1858 VATTR_SET(vap
, va_mode
, val
& 07777);
1860 nfsm_chain_get_32(error
, nmc
, val
);
1862 nfsm_chain_get_32(error
, nmc
, val
);
1863 VATTR_SET(vap
, va_uid
, val
);
1865 nfsm_chain_get_32(error
, nmc
, val
);
1867 nfsm_chain_get_32(error
, nmc
, val
);
1868 VATTR_SET(vap
, va_gid
, val
);
1870 nfsm_chain_get_32(error
, nmc
, val
);
1872 nfsm_chain_get_64(error
, nmc
, val64
);
1873 VATTR_SET(vap
, va_data_size
, val64
);
1876 nfsm_chain_get_32(error
, nmc
, val
);
1878 case NFS_TIME_SET_TO_CLIENT
:
1879 nfsm_chain_get_time(error
, nmc
, nd
->nd_vers
,
1880 vap
->va_access_time
.tv_sec
,
1881 vap
->va_access_time
.tv_nsec
);
1882 VATTR_SET_ACTIVE(vap
, va_access_time
);
1883 vap
->va_vaflags
&= ~VA_UTIMES_NULL
;
1885 case NFS_TIME_SET_TO_SERVER
:
1886 VATTR_SET(vap
, va_access_time
, now
);
1887 vap
->va_vaflags
|= VA_UTIMES_NULL
;
1890 nfsm_chain_get_32(error
, nmc
, val
);
1892 case NFS_TIME_SET_TO_CLIENT
:
1893 nfsm_chain_get_time(error
, nmc
, nd
->nd_vers
,
1894 vap
->va_modify_time
.tv_sec
,
1895 vap
->va_modify_time
.tv_nsec
);
1896 VATTR_SET_ACTIVE(vap
, va_modify_time
);
1897 vap
->va_vaflags
&= ~VA_UTIMES_NULL
;
1899 case NFS_TIME_SET_TO_SERVER
:
1900 VATTR_SET(vap
, va_modify_time
, now
);
1901 if (!VATTR_IS_ACTIVE(vap
, va_access_time
))
1902 vap
->va_vaflags
|= VA_UTIMES_NULL
;
1910 * Compare two security flavor structs
1913 nfsrv_cmp_secflavs(struct nfs_sec
*sf1
, struct nfs_sec
*sf2
)
1917 if (sf1
->count
!= sf2
->count
)
1919 for (i
= 0; i
< sf1
->count
; i
++)
1920 if (sf1
->flavors
[i
] != sf2
->flavors
[i
])
1926 * Build hash lists of net addresses and hang them off the NFS export.
1927 * Called by nfsrv_export() to set up the lists of export addresses.
1930 nfsrv_hang_addrlist(struct nfs_export
*nx
, struct user_nfs_export_args
*unxa
)
1932 struct nfs_export_net_args nxna
;
1933 struct nfs_netopt
*no
, *rn_no
;
1934 struct radix_node_head
*rnh
;
1935 struct radix_node
*rn
;
1936 struct sockaddr
*saddr
, *smask
;
1942 struct ucred temp_cred
;
1944 uaddr
= unxa
->nxa_nets
;
1945 for (net
= 0; net
< unxa
->nxa_netcount
; net
++, uaddr
+= sizeof(nxna
)) {
1946 error
= copyin(uaddr
, &nxna
, sizeof(nxna
));
1950 if (nxna
.nxna_flags
& (NX_MAPROOT
|NX_MAPALL
)) {
1951 bzero(&temp_cred
, sizeof(temp_cred
));
1952 temp_cred
.cr_uid
= nxna
.nxna_cred
.cr_uid
;
1953 temp_cred
.cr_ngroups
= nxna
.nxna_cred
.cr_ngroups
;
1954 for (i
=0; i
< nxna
.nxna_cred
.cr_ngroups
&& i
< NGROUPS
; i
++)
1955 temp_cred
.cr_groups
[i
] = nxna
.nxna_cred
.cr_groups
[i
];
1956 cred
= kauth_cred_create(&temp_cred
);
1957 if (!IS_VALID_CRED(cred
))
1963 if (nxna
.nxna_addr
.ss_len
== 0) {
1964 /* No address means this is a default/world export */
1965 if (nx
->nx_flags
& NX_DEFAULTEXPORT
) {
1966 if (IS_VALID_CRED(cred
))
1967 kauth_cred_unref(&cred
);
1970 nx
->nx_flags
|= NX_DEFAULTEXPORT
;
1971 nx
->nx_defopt
.nxo_flags
= nxna
.nxna_flags
;
1972 nx
->nx_defopt
.nxo_cred
= cred
;
1973 bcopy(&nxna
.nxna_sec
, &nx
->nx_defopt
.nxo_sec
, sizeof(struct nfs_sec
));
1978 i
= sizeof(struct nfs_netopt
);
1979 i
+= nxna
.nxna_addr
.ss_len
+ nxna
.nxna_mask
.ss_len
;
1980 MALLOC(no
, struct nfs_netopt
*, i
, M_NETADDR
, M_WAITOK
);
1982 if (IS_VALID_CRED(cred
))
1983 kauth_cred_unref(&cred
);
1986 bzero(no
, sizeof(struct nfs_netopt
));
1987 no
->no_opt
.nxo_flags
= nxna
.nxna_flags
;
1988 no
->no_opt
.nxo_cred
= cred
;
1989 bcopy(&nxna
.nxna_sec
, &no
->no_opt
.nxo_sec
, sizeof(struct nfs_sec
));
1991 saddr
= (struct sockaddr
*)(no
+ 1);
1992 bcopy(&nxna
.nxna_addr
, saddr
, nxna
.nxna_addr
.ss_len
);
1993 if (nxna
.nxna_mask
.ss_len
) {
1994 smask
= (struct sockaddr
*)((caddr_t
)saddr
+ nxna
.nxna_addr
.ss_len
);
1995 bcopy(&nxna
.nxna_mask
, smask
, nxna
.nxna_mask
.ss_len
);
1999 i
= saddr
->sa_family
;
2000 if ((rnh
= nx
->nx_rtable
[i
]) == 0) {
2002 * Seems silly to initialize every AF when most are not
2003 * used, do so on demand here
2005 for (dom
= domains
; dom
; dom
= dom
->dom_next
)
2006 if (dom
->dom_family
== i
&& dom
->dom_rtattach
) {
2007 dom
->dom_rtattach((void **)&nx
->nx_rtable
[i
],
2011 if ((rnh
= nx
->nx_rtable
[i
]) == 0) {
2012 if (IS_VALID_CRED(cred
))
2013 kauth_cred_unref(&cred
);
2014 _FREE(no
, M_NETADDR
);
2018 rn
= (*rnh
->rnh_addaddr
)((caddr_t
)saddr
, (caddr_t
)smask
, rnh
, no
->no_rnodes
);
2021 * One of the reasons that rnh_addaddr may fail is that
2022 * the entry already exists. To check for this case, we
2023 * look up the entry to see if it is there. If so, we
2024 * do not need to make a new entry but do continue.
2026 * XXX should this be rnh_lookup() instead?
2029 rn
= (*rnh
->rnh_matchaddr
)((caddr_t
)saddr
, rnh
);
2030 rn_no
= (struct nfs_netopt
*)rn
;
2031 if (rn
!= 0 && (rn
->rn_flags
& RNF_ROOT
) == 0 &&
2032 (rn_no
->no_opt
.nxo_flags
== nxna
.nxna_flags
) &&
2033 (!nfsrv_cmp_secflavs(&rn_no
->no_opt
.nxo_sec
, &nxna
.nxna_sec
))) {
2034 kauth_cred_t cred2
= rn_no
->no_opt
.nxo_cred
;
2035 if (cred
== cred2
) {
2036 /* creds are same (or both NULL) */
2038 } else if (cred
&& cred2
&& (cred
->cr_uid
== cred2
->cr_uid
) &&
2039 (cred
->cr_ngroups
== cred2
->cr_ngroups
)) {
2040 for (i
=0; i
< cred2
->cr_ngroups
&& i
< NGROUPS
; i
++)
2041 if (cred
->cr_groups
[i
] != cred2
->cr_groups
[i
])
2043 if (i
>= cred2
->cr_ngroups
|| i
>= NGROUPS
)
2047 if (IS_VALID_CRED(cred
))
2048 kauth_cred_unref(&cred
);
2049 _FREE(no
, M_NETADDR
);
2061 * In order to properly track an export's netopt count, we need to pass
2062 * an additional argument to nfsrv_free_netopt() so that it can decrement
2063 * the export's netopt count.
2065 struct nfsrv_free_netopt_arg
{
2067 struct radix_node_head
*rnh
;
2071 nfsrv_free_netopt(struct radix_node
*rn
, void *w
)
2073 struct nfsrv_free_netopt_arg
*fna
= (struct nfsrv_free_netopt_arg
*)w
;
2074 struct radix_node_head
*rnh
= fna
->rnh
;
2075 uint32_t *cnt
= fna
->cnt
;
2076 struct nfs_netopt
*nno
= (struct nfs_netopt
*)rn
;
2078 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
2079 if (IS_VALID_CRED(nno
->no_opt
.nxo_cred
))
2080 kauth_cred_unref(&nno
->no_opt
.nxo_cred
);
2081 _FREE((caddr_t
)rn
, M_NETADDR
);
2087 * Free the net address hash lists that are hanging off the mount points.
2090 nfsrv_free_addrlist(struct nfs_export
*nx
, struct user_nfs_export_args
*unxa
)
2092 struct nfs_export_net_args nxna
;
2093 struct radix_node_head
*rnh
;
2094 struct radix_node
*rn
;
2095 struct nfsrv_free_netopt_arg fna
;
2096 struct nfs_netopt
*nno
;
2101 if (!unxa
|| !unxa
->nxa_netcount
) {
2102 /* delete everything */
2103 for (i
= 0; i
<= AF_MAX
; i
++)
2104 if ( (rnh
= nx
->nx_rtable
[i
]) ) {
2106 fna
.cnt
= &nx
->nx_expcnt
;
2107 (*rnh
->rnh_walktree
)(rnh
, nfsrv_free_netopt
, (caddr_t
)&fna
);
2108 _FREE((caddr_t
)rnh
, M_RTABLE
);
2109 nx
->nx_rtable
[i
] = 0;
2114 /* delete only the exports specified */
2115 uaddr
= unxa
->nxa_nets
;
2116 for (net
= 0; net
< unxa
->nxa_netcount
; net
++, uaddr
+= sizeof(nxna
)) {
2117 error
= copyin(uaddr
, &nxna
, sizeof(nxna
));
2121 if (nxna
.nxna_addr
.ss_len
== 0) {
2122 /* No address means this is a default/world export */
2123 if (nx
->nx_flags
& NX_DEFAULTEXPORT
) {
2124 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2125 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2126 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2133 if ((rnh
= nx
->nx_rtable
[nxna
.nxna_addr
.ss_family
]) == 0) {
2134 /* AF not initialized? */
2135 if (!(unxa
->nxa_flags
& NXA_ADD
))
2136 printf("nfsrv_free_addrlist: address not found (0)\n");
2140 rn
= (*rnh
->rnh_lookup
)(&nxna
.nxna_addr
,
2141 nxna
.nxna_mask
.ss_len
? &nxna
.nxna_mask
: NULL
, rnh
);
2142 if (!rn
|| (rn
->rn_flags
& RNF_ROOT
)) {
2143 if (!(unxa
->nxa_flags
& NXA_ADD
))
2144 printf("nfsrv_free_addrlist: address not found (1)\n");
2148 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
2149 nno
= (struct nfs_netopt
*)rn
;
2150 if (IS_VALID_CRED(nno
->no_opt
.nxo_cred
))
2151 kauth_cred_unref(&nno
->no_opt
.nxo_cred
);
2152 _FREE((caddr_t
)rn
, M_NETADDR
);
2155 if (nx
->nx_expcnt
== ((nx
->nx_flags
& NX_DEFAULTEXPORT
) ? 1 : 0)) {
2156 /* no more entries in rnh, so free it up */
2157 _FREE((caddr_t
)rnh
, M_RTABLE
);
2158 nx
->nx_rtable
[nxna
.nxna_addr
.ss_family
] = 0;
2165 void enablequotas(struct mount
*mp
, vfs_context_t ctx
); // XXX
2168 nfsrv_export(struct user_nfs_export_args
*unxa
, vfs_context_t ctx
)
2170 int error
= 0, pathlen
;
2171 struct nfs_exportfs
*nxfs
, *nxfs2
, *nxfs3
;
2172 struct nfs_export
*nx
, *nx2
, *nx3
;
2173 struct nfs_filehandle nfh
;
2174 struct nameidata mnd
, xnd
;
2175 vnode_t mvp
= NULL
, xvp
= NULL
;
2177 char path
[MAXPATHLEN
];
2180 if (unxa
->nxa_flags
== NXA_CHECK
) {
2181 /* just check if the path is an NFS-exportable file system */
2182 error
= copyinstr(unxa
->nxa_fspath
, path
, MAXPATHLEN
, (size_t *)&pathlen
);
2185 NDINIT(&mnd
, LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2186 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
2187 error
= namei(&mnd
);
2191 mp
= vnode_mount(mvp
);
2192 /* make sure it's the root of a file system */
2193 if (!vnode_isvroot(mvp
))
2195 /* make sure the file system is NFS-exportable */
2197 nfh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
2198 error
= VFS_VPTOFH(mvp
, (int*)&nfh
.nfh_len
, &nfh
.nfh_fid
[0], NULL
);
2200 if (!error
&& (nfh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
))
2202 if (!error
&& !(mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSREADDIR_EXTENDED
))
2209 /* all other operations: must be super user */
2210 if ((error
= vfs_context_suser(ctx
)))
2213 if (unxa
->nxa_flags
& NXA_DELETE_ALL
) {
2214 /* delete all exports on all file systems */
2215 lck_rw_lock_exclusive(&nfsrv_export_rwlock
);
2216 while ((nxfs
= LIST_FIRST(&nfsrv_exports
))) {
2217 mp
= vfs_getvfs_by_mntonname(nxfs
->nxfs_path
);
2219 vfs_clearflags(mp
, MNT_EXPORTED
);
2220 /* delete all exports on this file system */
2221 while ((nx
= LIST_FIRST(&nxfs
->nxfs_exports
))) {
2222 LIST_REMOVE(nx
, nx_next
);
2223 LIST_REMOVE(nx
, nx_hash
);
2224 /* delete all netopts for this export */
2225 nfsrv_free_addrlist(nx
, NULL
);
2226 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2227 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2228 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2230 /* free active user list for this export */
2231 nfsrv_free_user_list(&nx
->nx_user_list
);
2232 FREE(nx
->nx_path
, M_TEMP
);
2235 LIST_REMOVE(nxfs
, nxfs_next
);
2236 FREE(nxfs
->nxfs_path
, M_TEMP
);
2239 if (nfsrv_export_hashtbl
) {
2240 /* all exports deleted, clean up export hash table */
2241 FREE(nfsrv_export_hashtbl
, M_TEMP
);
2242 nfsrv_export_hashtbl
= NULL
;
2244 lck_rw_done(&nfsrv_export_rwlock
);
2248 error
= copyinstr(unxa
->nxa_fspath
, path
, MAXPATHLEN
, (size_t *)&pathlen
);
2252 lck_rw_lock_exclusive(&nfsrv_export_rwlock
);
2254 /* init export hash table if not already */
2255 if (!nfsrv_export_hashtbl
) {
2256 if (nfsrv_export_hash_size
<= 0)
2257 nfsrv_export_hash_size
= NFSRVEXPHASHSZ
;
2258 nfsrv_export_hashtbl
= hashinit(nfsrv_export_hash_size
, M_TEMP
, &nfsrv_export_hash
);
2261 // first check if we've already got an exportfs with the given ID
2262 LIST_FOREACH(nxfs
, &nfsrv_exports
, nxfs_next
) {
2263 if (nxfs
->nxfs_id
== unxa
->nxa_fsid
)
2267 /* verify exported FS path matches given path */
2268 if (strncmp(path
, nxfs
->nxfs_path
, MAXPATHLEN
)) {
2272 if ((unxa
->nxa_flags
& (NXA_ADD
|NXA_OFFLINE
)) == NXA_ADD
) {
2273 /* if adding, verify that the mount is still what we expect */
2274 mp
= vfs_getvfs_by_mntonname(nxfs
->nxfs_path
);
2275 /* find exported FS root vnode */
2276 NDINIT(&mnd
, LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2277 UIO_SYSSPACE
, CAST_USER_ADDR_T(nxfs
->nxfs_path
), ctx
);
2278 error
= namei(&mnd
);
2282 /* make sure it's (still) the root of a file system */
2283 if (!vnode_isvroot(mvp
)) {
2287 /* sanity check: this should be same mount */
2288 if (mp
!= vnode_mount(mvp
)) {
2294 /* no current exported file system with that ID */
2295 if (!(unxa
->nxa_flags
& NXA_ADD
)) {
2300 /* find exported FS root vnode */
2301 NDINIT(&mnd
, LOOKUP
, FOLLOW
| LOCKLEAF
| AUDITVNPATH1
,
2302 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
2303 error
= namei(&mnd
);
2305 if (!(unxa
->nxa_flags
& NXA_OFFLINE
))
2309 /* make sure it's the root of a file system */
2310 if (!vnode_isvroot(mvp
)) {
2311 /* bail if not marked offline */
2312 if (!(unxa
->nxa_flags
& NXA_OFFLINE
)) {
2320 mp
= vnode_mount(mvp
);
2322 /* make sure the file system is NFS-exportable */
2323 nfh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
2324 error
= VFS_VPTOFH(mvp
, (int*)&nfh
.nfh_len
, &nfh
.nfh_fid
[0], NULL
);
2325 if (!error
&& (nfh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
))
2327 if (!error
&& !(mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSREADDIR_EXTENDED
))
2334 /* add an exportfs for it */
2335 MALLOC(nxfs
, struct nfs_exportfs
*, sizeof(struct nfs_exportfs
), M_TEMP
, M_WAITOK
);
2340 bzero(nxfs
, sizeof(struct nfs_exportfs
));
2341 nxfs
->nxfs_id
= unxa
->nxa_fsid
;
2342 MALLOC(nxfs
->nxfs_path
, char*, pathlen
, M_TEMP
, M_WAITOK
);
2343 if (!nxfs
->nxfs_path
) {
2348 bcopy(path
, nxfs
->nxfs_path
, pathlen
);
2349 /* insert into list in reverse-sorted order */
2351 LIST_FOREACH(nxfs2
, &nfsrv_exports
, nxfs_next
) {
2352 if (strncmp(nxfs
->nxfs_path
, nxfs2
->nxfs_path
, MAXPATHLEN
) > 0)
2357 LIST_INSERT_BEFORE(nxfs2
, nxfs
, nxfs_next
);
2359 LIST_INSERT_AFTER(nxfs3
, nxfs
, nxfs_next
);
2361 LIST_INSERT_HEAD(&nfsrv_exports
, nxfs
, nxfs_next
);
2363 /* make sure any quotas are enabled before we export the file system */
2365 enablequotas(mp
, ctx
);
2368 if (unxa
->nxa_exppath
) {
2369 error
= copyinstr(unxa
->nxa_exppath
, path
, MAXPATHLEN
, (size_t *)&pathlen
);
2372 LIST_FOREACH(nx
, &nxfs
->nxfs_exports
, nx_next
) {
2373 if (nx
->nx_id
== unxa
->nxa_expid
)
2377 /* verify exported FS path matches given path */
2378 if (strncmp(path
, nx
->nx_path
, MAXPATHLEN
)) {
2383 /* no current export with that ID */
2384 if (!(unxa
->nxa_flags
& NXA_ADD
)) {
2388 /* add an export for it */
2389 MALLOC(nx
, struct nfs_export
*, sizeof(struct nfs_export
), M_TEMP
, M_WAITOK
);
2394 bzero(nx
, sizeof(struct nfs_export
));
2395 nx
->nx_id
= unxa
->nxa_expid
;
2397 microtime(&nx
->nx_exptime
);
2398 MALLOC(nx
->nx_path
, char*, pathlen
, M_TEMP
, M_WAITOK
);
2405 bcopy(path
, nx
->nx_path
, pathlen
);
2406 /* initialize the active user list */
2407 nfsrv_init_user_list(&nx
->nx_user_list
);
2408 /* insert into list in reverse-sorted order */
2410 LIST_FOREACH(nx2
, &nxfs
->nxfs_exports
, nx_next
) {
2411 if (strncmp(nx
->nx_path
, nx2
->nx_path
, MAXPATHLEN
) > 0)
2416 LIST_INSERT_BEFORE(nx2
, nx
, nx_next
);
2418 LIST_INSERT_AFTER(nx3
, nx
, nx_next
);
2420 LIST_INSERT_HEAD(&nxfs
->nxfs_exports
, nx
, nx_next
);
2421 /* insert into hash */
2422 LIST_INSERT_HEAD(NFSRVEXPHASH(nxfs
->nxfs_id
, nx
->nx_id
), nx
, nx_hash
);
2425 * We don't allow/support nested exports. Check if the new entry
2426 * nests with the entries before and after or if there's an
2427 * entry for the file system root and subdirs.
2430 if ((nx3
&& !strncmp(nx3
->nx_path
, nx
->nx_path
, pathlen
- 1) &&
2431 (nx3
->nx_path
[pathlen
-1] == '/')) ||
2432 (nx2
&& !strncmp(nx2
->nx_path
, nx
->nx_path
, strlen(nx2
->nx_path
)) &&
2433 (nx
->nx_path
[strlen(nx2
->nx_path
)] == '/')))
2436 /* check export conflict with fs root export and vice versa */
2437 expisroot
= !nx
->nx_path
[0] ||
2438 ((nx
->nx_path
[0] == '.') && !nx
->nx_path
[1]);
2439 LIST_FOREACH(nx2
, &nxfs
->nxfs_exports
, nx_next
) {
2443 } else if (!nx2
->nx_path
[0])
2445 else if ((nx2
->nx_path
[0] == '.') && !nx2
->nx_path
[1])
2453 * Don't actually return an error because mountd is
2454 * probably about to delete the conflicting export.
2455 * This can happen when a new export momentarily conflicts
2456 * with an old export while the transition is being made.
2457 * Theoretically, mountd could be written to avoid this
2458 * transient situation - but it would greatly increase the
2459 * complexity of mountd for very little overall benefit.
2461 printf("nfsrv_export: warning: nested exports: %s/%s\n",
2462 nxfs
->nxfs_path
, nx
->nx_path
);
2465 nx
->nx_fh
.nfh_xh
.nxh_flags
= NXHF_INVALIDFH
;
2467 /* make sure file handle is set up */
2468 if ((nx
->nx_fh
.nfh_xh
.nxh_version
!= htonl(NFS_FH_VERSION
)) ||
2469 (nx
->nx_fh
.nfh_xh
.nxh_flags
& NXHF_INVALIDFH
)) {
2470 /* try to set up export root file handle */
2471 nx
->nx_fh
.nfh_xh
.nxh_version
= htonl(NFS_FH_VERSION
);
2472 nx
->nx_fh
.nfh_xh
.nxh_fsid
= htonl(nx
->nx_fs
->nxfs_id
);
2473 nx
->nx_fh
.nfh_xh
.nxh_expid
= htonl(nx
->nx_id
);
2474 nx
->nx_fh
.nfh_xh
.nxh_flags
= 0;
2475 nx
->nx_fh
.nfh_xh
.nxh_reserved
= 0;
2476 nx
->nx_fh
.nfh_fhp
= (u_char
*)&nx
->nx_fh
.nfh_xh
;
2477 bzero(&nx
->nx_fh
.nfh_fid
[0], NFSV2_MAX_FID_SIZE
);
2479 /* find export root vnode */
2480 if (!nx
->nx_path
[0] || ((nx
->nx_path
[0] == '.') && !nx
->nx_path
[1])) {
2481 /* exporting file system's root directory */
2485 xnd
.ni_cnd
.cn_nameiop
= LOOKUP
;
2486 xnd
.ni_cnd
.cn_flags
= LOCKLEAF
;
2487 xnd
.ni_pathlen
= pathlen
- 1;
2488 xnd
.ni_cnd
.cn_nameptr
= xnd
.ni_cnd
.cn_pnbuf
= path
;
2489 xnd
.ni_startdir
= mvp
;
2490 xnd
.ni_usedvp
= mvp
;
2491 xnd
.ni_cnd
.cn_context
= ctx
;
2492 while ((error
= lookup(&xnd
)) == ERECYCLE
) {
2493 xnd
.ni_cnd
.cn_flags
= LOCKLEAF
;
2494 xnd
.ni_cnd
.cn_nameptr
= xnd
.ni_cnd
.cn_pnbuf
;
2495 xnd
.ni_usedvp
= xnd
.ni_dvp
= xnd
.ni_startdir
= mvp
;
2502 if (vnode_vtype(xvp
) != VDIR
) {
2508 /* grab file handle */
2509 nx
->nx_fh
.nfh_len
= NFSV3_MAX_FID_SIZE
;
2510 error
= VFS_VPTOFH(xvp
, (int*)&nx
->nx_fh
.nfh_len
, &nx
->nx_fh
.nfh_fid
[0], NULL
);
2511 if (!error
&& (nx
->nx_fh
.nfh_len
> (int)NFSV3_MAX_FID_SIZE
)) {
2514 nx
->nx_fh
.nfh_xh
.nxh_fidlen
= nx
->nx_fh
.nfh_len
;
2515 nx
->nx_fh
.nfh_len
+= sizeof(nx
->nx_fh
.nfh_xh
);
2522 nx
->nx_fh
.nfh_xh
.nxh_flags
= NXHF_INVALIDFH
;
2523 nx
->nx_fh
.nfh_xh
.nxh_fidlen
= 0;
2524 nx
->nx_fh
.nfh_len
= sizeof(nx
->nx_fh
.nfh_xh
);
2531 /* perform the export changes */
2532 if (unxa
->nxa_flags
& NXA_DELETE
) {
2534 /* delete all exports on this file system */
2535 while ((nx
= LIST_FIRST(&nxfs
->nxfs_exports
))) {
2536 LIST_REMOVE(nx
, nx_next
);
2537 LIST_REMOVE(nx
, nx_hash
);
2538 /* delete all netopts for this export */
2539 nfsrv_free_addrlist(nx
, NULL
);
2540 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2541 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2542 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2544 /* delete active user list for this export */
2545 nfsrv_free_user_list(&nx
->nx_user_list
);
2546 FREE(nx
->nx_path
, M_TEMP
);
2550 } else if (!unxa
->nxa_netcount
) {
2551 /* delete all netopts for this export */
2552 nfsrv_free_addrlist(nx
, NULL
);
2553 nx
->nx_flags
&= ~NX_DEFAULTEXPORT
;
2554 if (IS_VALID_CRED(nx
->nx_defopt
.nxo_cred
)) {
2555 kauth_cred_unref(&nx
->nx_defopt
.nxo_cred
);
2558 /* delete only the netopts for the given addresses */
2559 error
= nfsrv_free_addrlist(nx
, unxa
);
2564 if (unxa
->nxa_flags
& NXA_ADD
) {
2566 * If going offline set the export time so that when
2567 * coming back on line we will present a new write verifier
2570 if (unxa
->nxa_flags
& NXA_OFFLINE
)
2571 microtime(&nx
->nx_exptime
);
2573 error
= nfsrv_hang_addrlist(nx
, unxa
);
2575 vfs_setflags(mp
, MNT_EXPORTED
);
2579 if (nx
&& !nx
->nx_expcnt
) {
2580 /* export has no export options */
2581 LIST_REMOVE(nx
, nx_next
);
2582 LIST_REMOVE(nx
, nx_hash
);
2583 /* delete active user list for this export */
2584 nfsrv_free_user_list(&nx
->nx_user_list
);
2585 FREE(nx
->nx_path
, M_TEMP
);
2588 if (LIST_EMPTY(&nxfs
->nxfs_exports
)) {
2589 /* exported file system has no more exports */
2590 LIST_REMOVE(nxfs
, nxfs_next
);
2591 FREE(nxfs
->nxfs_path
, M_TEMP
);
2594 vfs_clearflags(mp
, MNT_EXPORTED
);
2603 lck_rw_done(&nfsrv_export_rwlock
);
2607 struct nfs_export_options
*
2608 nfsrv_export_lookup(struct nfs_export
*nx
, mbuf_t nam
)
2610 struct nfs_export_options
*nxo
= NULL
;
2611 struct nfs_netopt
*no
= NULL
;
2612 struct radix_node_head
*rnh
;
2613 struct sockaddr
*saddr
;
2615 /* Lookup in the export list first. */
2617 saddr
= mbuf_data(nam
);
2618 rnh
= nx
->nx_rtable
[saddr
->sa_family
];
2620 no
= (struct nfs_netopt
*)
2621 (*rnh
->rnh_matchaddr
)((caddr_t
)saddr
, rnh
);
2622 if (no
&& no
->no_rnodes
->rn_flags
& RNF_ROOT
)
2628 /* If no address match, use the default if it exists. */
2629 if ((nxo
== NULL
) && (nx
->nx_flags
& NX_DEFAULTEXPORT
))
2630 nxo
= &nx
->nx_defopt
;
2634 /* find an export for the given handle */
2636 nfsrv_fhtoexport(struct nfs_filehandle
*nfhp
)
2638 struct nfs_exphandle
*nxh
= (struct nfs_exphandle
*)nfhp
->nfh_fhp
;
2639 struct nfs_export
*nx
;
2640 uint32_t fsid
, expid
;
2642 if (!nfsrv_export_hashtbl
)
2644 fsid
= ntohl(nxh
->nxh_fsid
);
2645 expid
= ntohl(nxh
->nxh_expid
);
2646 nx
= NFSRVEXPHASH(fsid
, expid
)->lh_first
;
2647 for (; nx
; nx
= LIST_NEXT(nx
, nx_hash
)) {
2648 if (nx
->nx_fs
->nxfs_id
!= fsid
)
2650 if (nx
->nx_id
!= expid
)
2658 * nfsrv_fhtovp() - convert FH to vnode and export info
2662 struct nfs_filehandle
*nfhp
,
2663 struct nfsrv_descript
*nd
,
2665 struct nfs_export
**nxp
,
2666 struct nfs_export_options
**nxop
)
2668 struct nfs_exphandle
*nxh
= (struct nfs_exphandle
*)nfhp
->nfh_fhp
;
2669 struct nfs_export_options
*nxo
;
2684 v
= ntohl(nxh
->nxh_version
);
2685 if (v
!= NFS_FH_VERSION
) {
2686 /* file handle format not supported */
2689 if (nfhp
->nfh_len
> NFSV3_MAX_FH_SIZE
)
2691 if (nfhp
->nfh_len
< (int)sizeof(struct nfs_exphandle
))
2693 v
= ntohs(nxh
->nxh_flags
);
2694 if (v
& NXHF_INVALIDFH
)
2697 *nxp
= nfsrv_fhtoexport(nfhp
);
2701 /* Get the export option structure for this <export, client> tuple. */
2702 *nxop
= nxo
= nfsrv_export_lookup(*nxp
, nam
);
2703 if (nam
&& (*nxop
== NULL
))
2707 /* Validate the security flavor of the request */
2708 for (i
= 0, valid
= 0; i
< nxo
->nxo_sec
.count
; i
++) {
2709 if (nd
->nd_sec
== nxo
->nxo_sec
.flavors
[i
]) {
2716 * RFC 2623 section 2.3.2 recommends no authentication
2717 * requirement for certain NFS procedures used for mounting.
2718 * This allows an unauthenticated superuser on the client
2719 * to do mounts for the benefit of authenticated users.
2721 if (nd
->nd_vers
== NFS_VER2
)
2722 if (nd
->nd_procnum
== NFSV2PROC_GETATTR
||
2723 nd
->nd_procnum
== NFSV2PROC_STATFS
)
2725 if (nd
->nd_vers
== NFS_VER3
)
2726 if (nd
->nd_procnum
== NFSPROC_FSINFO
)
2730 return (NFSERR_AUTHERR
| AUTH_REJECTCRED
);
2734 if (nxo
&& (nxo
->nxo_flags
& NX_OFFLINE
))
2735 return ((nd
== NULL
|| nd
->nd_vers
== NFS_VER2
) ? ESTALE
: NFSERR_TRYLATER
);
2737 /* find mount structure */
2738 mp
= vfs_getvfs_by_mntonname((*nxp
)->nx_fs
->nxfs_path
);
2741 * We have an export, but no mount?
2742 * Perhaps the export just hasn't been marked offline yet.
2744 return ((nd
== NULL
|| nd
->nd_vers
== NFS_VER2
) ? ESTALE
: NFSERR_TRYLATER
);
2747 fidp
= nfhp
->nfh_fhp
+ sizeof(*nxh
);
2748 error
= VFS_FHTOVP(mp
, nxh
->nxh_fidlen
, fidp
, vpp
, NULL
);
2751 /* vnode pointer should be good at this point or ... */
2758 * nfsrv_credcheck() - check/map credentials according
2759 * to given export options.
2763 struct nfsrv_descript
*nd
,
2765 __unused
struct nfs_export
*nx
,
2766 struct nfs_export_options
*nxo
)
2768 if (nxo
&& nxo
->nxo_cred
) {
2769 if ((nxo
->nxo_flags
& NX_MAPALL
) ||
2770 ((nxo
->nxo_flags
& NX_MAPROOT
) && !suser(nd
->nd_cr
, NULL
))) {
2771 kauth_cred_ref(nxo
->nxo_cred
);
2772 kauth_cred_unref(&nd
->nd_cr
);
2773 nd
->nd_cr
= nxo
->nxo_cred
;
2776 ctx
->vc_ucred
= nd
->nd_cr
;
2781 * nfsrv_vptofh() - convert vnode to file handle for given export
2783 * If the caller is passing in a vnode for a ".." directory entry,
2784 * they can pass a directory NFS file handle (dnfhp) which will be
2785 * checked against the root export file handle. If it matches, we
2786 * refuse to provide the file handle for the out-of-export directory.
2790 struct nfs_export
*nx
,
2792 struct nfs_filehandle
*dnfhp
,
2795 struct nfs_filehandle
*nfhp
)
2798 uint32_t maxfidsize
;
2800 nfhp
->nfh_fhp
= (u_char
*)&nfhp
->nfh_xh
;
2801 nfhp
->nfh_xh
.nxh_version
= htonl(NFS_FH_VERSION
);
2802 nfhp
->nfh_xh
.nxh_fsid
= htonl(nx
->nx_fs
->nxfs_id
);
2803 nfhp
->nfh_xh
.nxh_expid
= htonl(nx
->nx_id
);
2804 nfhp
->nfh_xh
.nxh_flags
= 0;
2805 nfhp
->nfh_xh
.nxh_reserved
= 0;
2807 if (nfsvers
== NFS_VER2
)
2808 bzero(&nfhp
->nfh_fid
[0], NFSV2_MAX_FID_SIZE
);
2810 /* if directory FH matches export root, return invalid FH */
2811 if (dnfhp
&& nfsrv_fhmatch(dnfhp
, &nx
->nx_fh
)) {
2812 if (nfsvers
== NFS_VER2
)
2813 nfhp
->nfh_len
= NFSX_V2FH
;
2815 nfhp
->nfh_len
= sizeof(nfhp
->nfh_xh
);
2816 nfhp
->nfh_xh
.nxh_fidlen
= 0;
2817 nfhp
->nfh_xh
.nxh_flags
= htons(NXHF_INVALIDFH
);
2821 if (nfsvers
== NFS_VER2
)
2822 maxfidsize
= NFSV2_MAX_FID_SIZE
;
2824 maxfidsize
= NFSV3_MAX_FID_SIZE
;
2825 nfhp
->nfh_len
= maxfidsize
;
2827 error
= VFS_VPTOFH(vp
, (int*)&nfhp
->nfh_len
, &nfhp
->nfh_fid
[0], ctx
);
2830 if (nfhp
->nfh_len
> maxfidsize
)
2832 nfhp
->nfh_xh
.nxh_fidlen
= nfhp
->nfh_len
;
2833 nfhp
->nfh_len
+= sizeof(nfhp
->nfh_xh
);
2834 if ((nfsvers
== NFS_VER2
) && (nfhp
->nfh_len
< NFSX_V2FH
))
2835 nfhp
->nfh_len
= NFSX_V2FH
;
2841 * Compare two file handles to see it they're the same.
2842 * Note that we don't use nfh_len because that may include
2843 * padding in an NFSv2 file handle.
2846 nfsrv_fhmatch(struct nfs_filehandle
*fh1
, struct nfs_filehandle
*fh2
)
2848 struct nfs_exphandle
*nxh1
, *nxh2
;
2851 nxh1
= (struct nfs_exphandle
*)fh1
->nfh_fhp
;
2852 nxh2
= (struct nfs_exphandle
*)fh2
->nfh_fhp
;
2853 len1
= sizeof(fh1
->nfh_xh
) + nxh1
->nxh_fidlen
;
2854 len2
= sizeof(fh2
->nfh_xh
) + nxh2
->nxh_fidlen
;
2857 if (bcmp(nxh1
, nxh2
, len1
))
2863 * Functions for dealing with active user lists
2867 * Compare address fields of two sockaddr_storage structures.
2868 * Returns zero if they match.
2871 nfsrv_cmp_sockaddr(struct sockaddr_storage
*sock1
, struct sockaddr_storage
*sock2
)
2873 struct sockaddr_in
*ipv4_sock1
, *ipv4_sock2
;
2874 struct sockaddr_in6
*ipv6_sock1
, *ipv6_sock2
;
2876 /* check for valid parameters */
2877 if (sock1
== NULL
|| sock2
== NULL
)
2880 /* check address length */
2881 if (sock1
->ss_len
!= sock2
->ss_len
)
2884 /* Check address family */
2885 if (sock1
->ss_family
!= sock2
->ss_family
)
2888 if (sock1
->ss_family
== AF_INET
) {
2890 ipv4_sock1
= (struct sockaddr_in
*)sock1
;
2891 ipv4_sock2
= (struct sockaddr_in
*)sock2
;
2893 if (!bcmp(&ipv4_sock1
->sin_addr
, &ipv4_sock2
->sin_addr
, sizeof(struct in_addr
)))
2897 ipv6_sock1
= (struct sockaddr_in6
*)sock1
;
2898 ipv6_sock2
= (struct sockaddr_in6
*)sock2
;
2900 if (!bcmp(&ipv6_sock1
->sin6_addr
, &ipv6_sock2
->sin6_addr
, sizeof(struct in6_addr
)))
2907 * Search the hash table for a user node with a matching IP address and uid field.
2908 * If found, the node's tm_last timestamp is updated and the node is returned.
2910 * If not found, a new node is allocated (or reclaimed via LRU), initialized, and returned.
2911 * Returns NULL if a new node could not be allcoated.
2913 * The list's user_mutex lock MUST be held.
2915 struct nfs_user_stat_node
*
2916 nfsrv_get_user_stat_node(struct nfs_active_user_list
*list
, struct sockaddr_storage
*sock
, uid_t uid
)
2918 struct nfs_user_stat_node
*unode
;
2920 struct nfs_user_stat_hashtbl_head
*head
;
2922 /* seach the hash table */
2923 head
= NFS_USER_STAT_HASH(list
->user_hashtbl
, uid
);
2924 LIST_FOREACH(unode
, head
, hash_link
) {
2925 if (uid
== unode
->uid
&& nfsrv_cmp_sockaddr(sock
, &unode
->sock
) == 0) {
2926 /* found matching node */
2932 /* found node in the hash table, now update lru position */
2933 TAILQ_REMOVE(&list
->user_lru
, unode
, lru_link
);
2934 TAILQ_INSERT_TAIL(&list
->user_lru
, unode
, lru_link
);
2936 /* update time stamp */
2938 unode
->tm_last
= (uint32_t)now
.tv_sec
;
2942 if (list
->node_count
< nfsrv_user_stat_max_nodes
) {
2943 /* Allocate a new node */
2944 MALLOC(unode
, struct nfs_user_stat_node
*, sizeof(struct nfs_user_stat_node
),
2945 M_TEMP
, M_WAITOK
| M_ZERO
);
2950 /* increment node count */
2951 OSAddAtomic(1, &nfsrv_user_stat_node_count
);
2954 /* reuse the oldest node in the lru list */
2955 unode
= TAILQ_FIRST(&list
->user_lru
);
2960 /* Remove the node */
2961 TAILQ_REMOVE(&list
->user_lru
, unode
, lru_link
);
2962 LIST_REMOVE(unode
, hash_link
);
2965 /* Initialize the node */
2967 bcopy(sock
, &unode
->sock
, sock
->ss_len
);
2970 unode
->bytes_read
= 0;
2971 unode
->bytes_written
= 0;
2972 unode
->tm_start
= (uint32_t)now
.tv_sec
;
2973 unode
->tm_last
= (uint32_t)now
.tv_sec
;
2975 /* insert the node */
2976 TAILQ_INSERT_TAIL(&list
->user_lru
, unode
, lru_link
);
2977 LIST_INSERT_HEAD(head
, unode
, hash_link
);
2983 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
)
2985 struct nfs_user_stat_node
*unode
;
2986 struct nfs_active_user_list
*ulist
;
2987 struct sockaddr_storage
*sock_stor
;
2989 if ((!nfsrv_user_stat_enabled
) || (!nx
) || (!nd
) || (!nd
->nd_nam
))
2992 sock_stor
= (struct sockaddr_storage
*)mbuf_data(nd
->nd_nam
);
2994 /* check address family before going any further */
2995 if ((sock_stor
->ss_family
!= AF_INET
) && (sock_stor
->ss_family
!= AF_INET6
))
2998 ulist
= &nx
->nx_user_list
;
3000 /* lock the active user list */
3001 lck_mtx_lock(&ulist
->user_mutex
);
3003 /* get the user node */
3004 unode
= nfsrv_get_user_stat_node(ulist
, sock_stor
, uid
);
3007 lck_mtx_unlock(&ulist
->user_mutex
);
3011 /* update counters */
3013 unode
->bytes_read
+= rd_bytes
;
3014 unode
->bytes_written
+= wr_bytes
;
3017 lck_mtx_unlock(&ulist
->user_mutex
);
3020 /* initialize an active user list */
3022 nfsrv_init_user_list(struct nfs_active_user_list
*ulist
)
3026 /* initialize the lru */
3027 TAILQ_INIT(&ulist
->user_lru
);
3029 /* initialize the hash table */
3030 for(i
= 0; i
< NFS_USER_STAT_HASH_SIZE
; i
++)
3031 LIST_INIT(&ulist
->user_hashtbl
[i
]);
3032 ulist
->node_count
= 0;
3034 lck_mtx_init(&ulist
->user_mutex
, nfsrv_active_user_mutex_group
, LCK_ATTR_NULL
);
3037 /* Free all nodes in an active user list */
3039 nfsrv_free_user_list(struct nfs_active_user_list
*ulist
)
3041 struct nfs_user_stat_node
*unode
;
3046 while ((unode
= TAILQ_FIRST(&ulist
->user_lru
))) {
3047 /* Remove node and free */
3048 TAILQ_REMOVE(&ulist
->user_lru
, unode
, lru_link
);
3049 LIST_REMOVE(unode
, hash_link
);
3050 FREE(unode
, M_TEMP
);
3052 /* decrement node count */
3053 OSAddAtomic(-1, &nfsrv_user_stat_node_count
);
3055 ulist
->node_count
= 0;
3057 lck_mtx_destroy(&ulist
->user_mutex
, nfsrv_active_user_mutex_group
);
3060 /* Reclaim old expired user nodes from active user lists. */
3062 nfsrv_active_user_list_reclaim(void)
3064 struct nfs_exportfs
*nxfs
;
3065 struct nfs_export
*nx
;
3066 struct nfs_active_user_list
*ulist
;
3067 struct nfs_user_stat_hashtbl_head oldlist
;
3068 struct nfs_user_stat_node
*unode
, *unode_next
;
3072 LIST_INIT(&oldlist
);
3074 lck_rw_lock_shared(&nfsrv_export_rwlock
);
3076 tstale
= now
.tv_sec
- nfsrv_user_stat_max_idle_sec
;
3077 LIST_FOREACH(nxfs
, &nfsrv_exports
, nxfs_next
) {
3078 LIST_FOREACH(nx
, &nxfs
->nxfs_exports
, nx_next
) {
3079 /* Scan through all user nodes of this export */
3080 ulist
= &nx
->nx_user_list
;
3081 lck_mtx_lock(&ulist
->user_mutex
);
3082 for (unode
= TAILQ_FIRST(&ulist
->user_lru
); unode
; unode
= unode_next
) {
3083 unode_next
= TAILQ_NEXT(unode
, lru_link
);
3085 /* check if this node has expired */
3086 if (unode
->tm_last
>= tstale
)
3089 /* Remove node from the active user list */
3090 TAILQ_REMOVE(&ulist
->user_lru
, unode
, lru_link
);
3091 LIST_REMOVE(unode
, hash_link
);
3093 /* Add node to temp list */
3094 LIST_INSERT_HEAD(&oldlist
, unode
, hash_link
);
3096 /* decrement node count */
3097 OSAddAtomic(-1, &nfsrv_user_stat_node_count
);
3098 ulist
->node_count
--;
3100 /* can unlock this export's list now */
3101 lck_mtx_unlock(&ulist
->user_mutex
);
3104 lck_rw_done(&nfsrv_export_rwlock
);
3106 /* Free expired nodes */
3107 while ((unode
= LIST_FIRST(&oldlist
))) {
3108 LIST_REMOVE(unode
, hash_link
);
3109 FREE(unode
, M_TEMP
);
3114 * Maps errno values to nfs error numbers.
3115 * Use NFSERR_IO as the catch all for ones not specifically defined in
3118 static u_char nfsrv_v2errmap
[] = {
3119 NFSERR_PERM
, NFSERR_NOENT
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3120 NFSERR_NXIO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3121 NFSERR_IO
, NFSERR_IO
, NFSERR_ACCES
, NFSERR_IO
, NFSERR_IO
,
3122 NFSERR_IO
, NFSERR_EXIST
, NFSERR_IO
, NFSERR_NODEV
, NFSERR_NOTDIR
,
3123 NFSERR_ISDIR
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3124 NFSERR_IO
, NFSERR_FBIG
, NFSERR_NOSPC
, NFSERR_IO
, NFSERR_ROFS
,
3125 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3126 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3127 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3128 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3129 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3130 NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
, NFSERR_IO
,
3131 NFSERR_IO
, NFSERR_IO
, NFSERR_NAMETOL
, NFSERR_IO
, NFSERR_IO
,
3132 NFSERR_NOTEMPTY
, NFSERR_IO
, NFSERR_IO
, NFSERR_DQUOT
, NFSERR_STALE
,
3136 * Maps errno values to nfs error numbers.
3137 * Although it is not obvious whether or not NFS clients really care if
3138 * a returned error value is in the specified list for the procedure, the
3139 * safest thing to do is filter them appropriately. For Version 2, the
3140 * X/Open XNFS document is the only specification that defines error values
3141 * for each RPC (The RFC simply lists all possible error values for all RPCs),
3142 * so I have decided to not do this for Version 2.
3143 * The first entry is the default error return and the rest are the valid
3144 * errors for that RPC in increasing numeric order.
3146 static short nfsv3err_null
[] = {
3151 static short nfsv3err_getattr
[] = {
3161 static short nfsv3err_setattr
[] = {
3178 static short nfsv3err_lookup
[] = {
3192 static short nfsv3err_access
[] = {
3202 static short nfsv3err_readlink
[] = {
3215 static short nfsv3err_read
[] = {
3228 static short nfsv3err_write
[] = {
3244 static short nfsv3err_create
[] = {
3262 static short nfsv3err_mkdir
[] = {
3280 static short nfsv3err_symlink
[] = {
3298 static short nfsv3err_mknod
[] = {
3317 static short nfsv3err_remove
[] = {
3332 static short nfsv3err_rmdir
[] = {
3351 static short nfsv3err_rename
[] = {
3375 static short nfsv3err_link
[] = {
3396 static short nfsv3err_readdir
[] = {
3410 static short nfsv3err_readdirplus
[] = {
3425 static short nfsv3err_fsstat
[] = {
3435 static short nfsv3err_fsinfo
[] = {
3444 static short nfsv3err_pathconf
[] = {
3453 static short nfsv3err_commit
[] = {
3463 static short *nfsrv_v3errmap
[] = {
3481 nfsv3err_readdirplus
,
3489 * Map errnos to NFS error numbers. For Version 3 also filter out error
3490 * numbers not specified for the associated procedure.
3493 nfsrv_errmap(struct nfsrv_descript
*nd
, int err
)
3495 short *defaulterrp
, *errp
;
3497 if (nd
->nd_vers
== NFS_VER2
) {
3498 if (err
<= (int)sizeof(nfsrv_v2errmap
))
3499 return ((int)nfsrv_v2errmap
[err
- 1]);
3503 if (nd
->nd_procnum
> NFSPROC_COMMIT
)
3504 return (err
& 0xffff);
3505 errp
= defaulterrp
= nfsrv_v3errmap
[nd
->nd_procnum
];
3509 else if (*errp
> err
)
3512 return ((int)*defaulterrp
);
3515 #endif /* NFSSERVER */