freebsd-dev/sys/fs/nfsserver/nfs_nfsdsocket.c
Rick Macklem ff2f1f691c nfsd: Add support for the SP4_MACH_CRED case in ExchangeID
Commit f4179ad46f added support for operation bitmaps for
NFSv4.1/4.2.  This commit uses those to implement the SP4_MACH_CRED
case for the NFSv4.1/4.2 ExchangeID operation since the Linux
NFSv4.1/4.2 client is now using this for Kerberized mounts.
The Linux Kerberized NFSv4.1/4.2 mounts currently work without
support for this because Linux will fall back to SP4_NONE,
but there is no guarantee this fallback will work forever.

This commit only affects Kerberized NFSv4.1/4.2 mounts from
Linux at this time.

MFC after:	3 months
2023-04-07 12:49:23 -07:00

1441 lines
53 KiB
C

/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Socket operations for use by the nfs server.
*/
#include <fs/nfs/nfsport.h>
#include <security/mac/mac_framework.h>
extern struct nfsrvfh nfs_pubfh;
extern int nfs_pubfhset;
extern struct nfsv4lock nfsv4rootfs_lock;
extern int nfsrv_clienthashsize;
extern int nfsd_debuglevel;
extern int nfsrv_layouthighwater;
extern volatile int nfsrv_layoutcnt;
NFSV4ROOTLOCKMUTEX;
NFSSTATESPINLOCK;
NFSD_VNET_DECLARE(struct nfsrv_stablefirst, nfsrv_stablefirst);
NFSD_VNET_DECLARE(struct nfsclienthashhead *, nfsclienthash);
NFSD_VNET_DECLARE(int, nfsrc_floodlevel);
NFSD_VNET_DECLARE(int, nfsrc_tcpsavedreplies);
NFSD_VNET_DECLARE(struct nfsrvfh, nfs_rootfh);
NFSD_VNET_DECLARE(int, nfs_rootfhset);
NFSD_VNET_DECLARE(struct nfsstatsv1 *, nfsstatsv1_p);
int (*nfsrv3_procs0[NFS_V3NPROCS])(struct nfsrv_descript *,
int, vnode_t , struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_getattr,
nfsrvd_setattr,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_access,
nfsrvd_readlink,
nfsrvd_read,
nfsrvd_write,
nfsrvd_create,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_remove,
nfsrvd_remove,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_readdir,
nfsrvd_readdirplus,
nfsrvd_statfs,
nfsrvd_fsinfo,
nfsrvd_pathconf,
nfsrvd_commit,
};
int (*nfsrv3_procs1[NFS_V3NPROCS])(struct nfsrv_descript *,
int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
nfsrvd_lookup,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
nfsrvd_mkdir,
nfsrvd_symlink,
nfsrvd_mknod,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
};
int (*nfsrv3_procs2[NFS_V3NPROCS])(struct nfsrv_descript *,
int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
nfsrvd_rename,
nfsrvd_link,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
};
int (*nfsrv4_ops0[NFSV42_NOPS])(struct nfsrv_descript *,
int, vnode_t , struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_access,
nfsrvd_close,
nfsrvd_commit,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_delegpurge,
nfsrvd_delegreturn,
nfsrvd_getattr,
nfsrvd_getfh,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_lock,
nfsrvd_lockt,
nfsrvd_locku,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_verify,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_openconfirm,
nfsrvd_opendowngrade,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_read,
nfsrvd_readdirplus,
nfsrvd_readlink,
nfsrvd_remove,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_renew,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_secinfo,
nfsrvd_setattr,
nfsrvd_setclientid,
nfsrvd_setclientidcfrm,
nfsrvd_verify,
nfsrvd_write,
nfsrvd_releaselckown,
nfsrvd_notsupp,
nfsrvd_bindconnsess,
nfsrvd_exchangeid,
nfsrvd_createsession,
nfsrvd_destroysession,
nfsrvd_freestateid,
nfsrvd_notsupp,
nfsrvd_getdevinfo,
nfsrvd_notsupp,
nfsrvd_layoutcommit,
nfsrvd_layoutget,
nfsrvd_layoutreturn,
nfsrvd_secinfononame,
nfsrvd_sequence,
nfsrvd_notsupp,
nfsrvd_teststateid,
nfsrvd_notsupp,
nfsrvd_destroyclientid,
nfsrvd_reclaimcomplete,
nfsrvd_allocate,
(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
nfsrvd_notsupp,
nfsrvd_deallocate,
nfsrvd_ioadvise,
nfsrvd_layouterror,
nfsrvd_layoutstats,
nfsrvd_notsupp,
nfsrvd_notsupp,
nfsrvd_notsupp,
nfsrvd_seek,
nfsrvd_notsupp,
nfsrvd_notsupp,
nfsrvd_getxattr,
nfsrvd_setxattr,
nfsrvd_listxattr,
nfsrvd_rmxattr,
};
int (*nfsrv4_ops1[NFSV42_NOPS])(struct nfsrv_descript *,
int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
nfsrvd_mknod,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
nfsrvd_lookup,
nfsrvd_lookup,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
nfsrvd_open,
nfsrvd_openattr,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, struct nfsexstuff *))0,
};
int (*nfsrv4_ops2[NFSV42_NOPS])(struct nfsrv_descript *,
int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *) = {
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
nfsrvd_link,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
nfsrvd_rename,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
nfsrvd_copy_file_range,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
};
/*
* Static array that defines which nfs rpc's are nonidempotent
*/
static int nfsrv_nonidempotent[NFS_V3NPROCS] = {
FALSE,
FALSE,
TRUE,
FALSE,
FALSE,
FALSE,
FALSE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
FALSE,
FALSE,
FALSE,
FALSE,
FALSE,
FALSE,
};
/*
* This static array indicates whether or not the RPC modifies the
* file system.
*/
int nfsrv_writerpc[NFS_NPROCS] = { 0, 0, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
SYSCTL_DECL(_vfs_nfsd);
static int nfs_minminorv4 = NFSV4_MINORVERSION;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_min_minorversion4, CTLFLAG_RWTUN,
&nfs_minminorv4, 0,
"The lowest minor version of NFSv4 handled by the server");
static int nfs_maxminorv4 = NFSV42_MINORVERSION;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_minorversion4, CTLFLAG_RWTUN,
&nfs_maxminorv4, 0,
"The highest minor version of NFSv4 handled by the server");
/* local functions */
static void nfsrvd_compound(struct nfsrv_descript *nd, int isdgram,
u_char *tag, int taglen, u_int32_t minorvers);
/*
* This static array indicates which server procedures require the extra
* arguments to return the current file handle for V2, 3.
*/
static int nfs_retfh[NFS_V3NPROCS] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,
1, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0 };
extern struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS];
static int nfsv3to4op[NFS_V3NPROCS] = {
NFSPROC_NULL,
NFSV4OP_GETATTR,
NFSV4OP_SETATTR,
NFSV4OP_LOOKUP,
NFSV4OP_ACCESS,
NFSV4OP_READLINK,
NFSV4OP_READ,
NFSV4OP_WRITE,
NFSV4OP_V3CREATE,
NFSV4OP_MKDIR,
NFSV4OP_SYMLINK,
NFSV4OP_MKNOD,
NFSV4OP_REMOVE,
NFSV4OP_RMDIR,
NFSV4OP_RENAME,
NFSV4OP_LINK,
NFSV4OP_READDIR,
NFSV4OP_READDIRPLUS,
NFSV4OP_FSSTAT,
NFSV4OP_FSINFO,
NFSV4OP_PATHCONF,
NFSV4OP_COMMIT,
};
static struct mtx nfsrvd_statmtx;
MTX_SYSINIT(nfsst, &nfsrvd_statmtx, "NFSstat", MTX_DEF);
static struct ucred *nfsrv_createrootcred(void);
static void
nfsrvd_statstart(int op, struct bintime *now)
{
if (op > (NFSV42_NOPS + NFSV4OP_FAKENOPS)) {
printf("%s: op %d invalid\n", __func__, op);
return;
}
mtx_lock(&nfsrvd_statmtx);
if (NFSD_VNET(nfsstatsv1_p)->srvstartcnt ==
NFSD_VNET(nfsstatsv1_p)->srvdonecnt) {
if (now != NULL)
NFSD_VNET(nfsstatsv1_p)->busyfrom = *now;
else
binuptime(&NFSD_VNET(nfsstatsv1_p)->busyfrom);
}
NFSD_VNET(nfsstatsv1_p)->srvrpccnt[op]++;
NFSD_VNET(nfsstatsv1_p)->srvstartcnt++;
mtx_unlock(&nfsrvd_statmtx);
}
static void
nfsrvd_statend(int op, uint64_t bytes, struct bintime *now,
struct bintime *then)
{
struct bintime dt, lnow;
if (op > (NFSV42_NOPS + NFSV4OP_FAKENOPS)) {
printf("%s: op %d invalid\n", __func__, op);
return;
}
if (now == NULL) {
now = &lnow;
binuptime(now);
}
mtx_lock(&nfsrvd_statmtx);
NFSD_VNET(nfsstatsv1_p)->srvbytes[op] += bytes;
NFSD_VNET(nfsstatsv1_p)->srvops[op]++;
if (then != NULL) {
dt = *now;
bintime_sub(&dt, then);
bintime_add(&NFSD_VNET(nfsstatsv1_p)->srvduration[op], &dt);
}
dt = *now;
bintime_sub(&dt, &NFSD_VNET(nfsstatsv1_p)->busyfrom);
bintime_add(&NFSD_VNET(nfsstatsv1_p)->busytime, &dt);
NFSD_VNET(nfsstatsv1_p)->busyfrom = *now;
NFSD_VNET(nfsstatsv1_p)->srvdonecnt++;
mtx_unlock(&nfsrvd_statmtx);
}
/*
* Do an RPC. Basically, get the file handles translated to vnode pointers
* and then call the appropriate server routine. The server routines are
* split into groups, based on whether they use a file handle or file
* handle plus name or ...
* The NFS V4 Compound RPC is performed separately by nfsrvd_compound().
*/
void
nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, u_char *tag, int taglen,
u_int32_t minorvers)
{
int error = 0, lktype;
vnode_t vp;
mount_t mp;
struct nfsrvfh fh;
struct nfsexstuff nes;
struct mbuf *md;
char *dpos;
/*
* Save the current position in the request mbuf list so
* that a rollback to this location can be done upon an
* ERELOOKUP error return from an RPC function.
*/
md = nd->nd_md;
dpos = nd->nd_dpos;
tryagain:
mp = NULL;
/*
* Get a locked vnode for the first file handle
*/
if (!(nd->nd_flag & ND_NFSV4)) {
KASSERT(nd->nd_repstat == 0, ("nfsrvd_dorpc"));
/*
* For NFSv3, if the malloc/mget allocation is near limits,
* return NFSERR_DELAY.
*/
if ((nd->nd_flag & ND_NFSV3) && nfsrv_mallocmget_limit()) {
nd->nd_repstat = NFSERR_DELAY;
vp = NULL;
} else {
error = nfsrv_mtofh(nd, &fh);
if (error) {
if (error != EBADRPC)
printf("nfs dorpc err1=%d\n", error);
nd->nd_repstat = NFSERR_GARBAGE;
goto out;
}
if (nd->nd_procnum == NFSPROC_READ ||
nd->nd_procnum == NFSPROC_WRITE ||
nd->nd_procnum == NFSPROC_READDIR ||
nd->nd_procnum == NFSPROC_READDIRPLUS ||
nd->nd_procnum == NFSPROC_READLINK ||
nd->nd_procnum == NFSPROC_GETATTR ||
nd->nd_procnum == NFSPROC_ACCESS ||
nd->nd_procnum == NFSPROC_FSSTAT ||
nd->nd_procnum == NFSPROC_FSINFO)
lktype = LK_SHARED;
else
lktype = LK_EXCLUSIVE;
if (nd->nd_flag & ND_PUBLOOKUP)
nfsd_fhtovp(nd, &nfs_pubfh, lktype, &vp, &nes,
&mp, nfsrv_writerpc[nd->nd_procnum], -1);
else
nfsd_fhtovp(nd, &fh, lktype, &vp, &nes,
&mp, nfsrv_writerpc[nd->nd_procnum], -1);
if (nd->nd_repstat == NFSERR_PROGNOTV4)
goto out;
}
}
/*
* For V2 and 3, set the ND_SAVEREPLY flag for the recent request
* cache, as required.
* For V4, nfsrvd_compound() does this.
*/
if (!(nd->nd_flag & ND_NFSV4) && nfsrv_nonidempotent[nd->nd_procnum])
nd->nd_flag |= ND_SAVEREPLY;
nfsrvd_rephead(nd);
/*
* If nd_repstat is non-zero, just fill in the reply status
* to complete the RPC reply for V2. Otherwise, you must do
* the RPC.
*/
if (nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
*nd->nd_errp = nfsd_errmap(nd);
nfsrvd_statstart(nfsv3to4op[nd->nd_procnum], /*now*/ NULL);
nfsrvd_statend(nfsv3to4op[nd->nd_procnum], /*bytes*/ 0,
/*now*/ NULL, /*then*/ NULL);
vn_finished_write(mp);
goto out;
}
/*
* Now the procedure can be performed. For V4, nfsrvd_compound()
* works through the sub-rpcs, otherwise just call the procedure.
* The procedures are in three groups with different arguments.
* The group is indicated by the value in nfs_retfh[].
*/
if (nd->nd_flag & ND_NFSV4) {
nfsrvd_compound(nd, isdgram, tag, taglen, minorvers);
} else {
struct bintime start_time;
binuptime(&start_time);
nfsrvd_statstart(nfsv3to4op[nd->nd_procnum], &start_time);
if (nfs_retfh[nd->nd_procnum] == 1) {
if (vp)
NFSVOPUNLOCK(vp);
error = (*(nfsrv3_procs1[nd->nd_procnum]))(nd, isdgram,
vp, NULL, (fhandle_t *)fh.nfsrvfh_data, &nes);
} else if (nfs_retfh[nd->nd_procnum] == 2) {
error = (*(nfsrv3_procs2[nd->nd_procnum]))(nd, isdgram,
vp, NULL, &nes, NULL);
} else {
error = (*(nfsrv3_procs0[nd->nd_procnum]))(nd, isdgram,
vp, &nes);
}
vn_finished_write(mp);
if (error == 0 && nd->nd_repstat == ERELOOKUP) {
/*
* Roll back to the beginning of the RPC request
* arguments.
*/
nd->nd_md = md;
nd->nd_dpos = dpos;
/* Free the junk RPC reply and redo the RPC. */
m_freem(nd->nd_mreq);
nd->nd_mreq = nd->nd_mb = NULL;
nd->nd_repstat = 0;
goto tryagain;
}
nfsrvd_statend(nfsv3to4op[nd->nd_procnum], /*bytes*/ 0,
/*now*/ NULL, /*then*/ &start_time);
}
if (error) {
if (error != EBADRPC)
printf("nfs dorpc err2=%d\n", error);
nd->nd_repstat = NFSERR_GARBAGE;
}
*nd->nd_errp = nfsd_errmap(nd);
/*
* Don't cache certain reply status values.
*/
if (nd->nd_repstat && (nd->nd_flag & ND_SAVEREPLY) &&
(nd->nd_repstat == NFSERR_GARBAGE ||
nd->nd_repstat == NFSERR_BADXDR ||
nd->nd_repstat == NFSERR_MOVED ||
nd->nd_repstat == NFSERR_DELAY ||
nd->nd_repstat == NFSERR_BADSEQID ||
nd->nd_repstat == NFSERR_RESOURCE ||
nd->nd_repstat == NFSERR_SERVERFAULT ||
nd->nd_repstat == NFSERR_STALECLIENTID ||
nd->nd_repstat == NFSERR_STALESTATEID ||
nd->nd_repstat == NFSERR_OLDSTATEID ||
nd->nd_repstat == NFSERR_BADSTATEID ||
nd->nd_repstat == NFSERR_GRACE ||
nd->nd_repstat == NFSERR_NOGRACE))
nd->nd_flag &= ~ND_SAVEREPLY;
out:
NFSEXITCODE2(0, nd);
}
/*
* Breaks down a compound RPC request and calls the server routines for
* the subprocedures.
* Some suboperations are performed directly here to simplify file handle<-->
* vnode pointer handling.
*/
static void
nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag,
int taglen, u_int32_t minorvers)
{
int i, lktype, op, op0 = 0, rstat, statsinprog = 0;
u_int32_t *tl;
struct nfsclient *clp, *nclp;
int error = 0, igotlock, nextop, numops, savefhcnt;
u_int32_t retops = 0, *retopsp = NULL, *repp;
vnode_t vp, nvp, savevp;
struct nfsrvfh fh;
mount_t new_mp, temp_mp = NULL;
struct ucred *credanon, *rootcred, *savecred;
struct nfsexstuff nes, vpnes, savevpnes;
fsid_t cur_fsid, save_fsid;
static u_int64_t compref = 0;
struct bintime start_time;
struct thread *p;
struct mbuf *mb, *md;
char *bpos, *dpos;
int bextpg, bextpgsiz;
p = curthread;
rootcred = savecred = NULL;
/* Check for and optionally clear the no space flags for DSs. */
nfsrv_checknospc();
NFSVNO_EXINIT(&vpnes);
NFSVNO_EXINIT(&savevpnes);
/*
* Put the seq# of the current compound RPC in nfsrv_descript.
* (This is used by nfsrv_checkgetattr(), to see if the write
* delegation was created by the same compound RPC as the one
* with that Getattr in it.)
* Don't worry about the 64bit number wrapping around. It ain't
* gonna happen before this server gets shut down/rebooted.
*/
nd->nd_compref = compref++;
/*
* Check for and optionally get a lock on the root. This lock means that
* no nfsd will be fiddling with the V4 file system and state stuff. It
* is required when the V4 root is being changed, the stable storage
* restart file is being updated, or callbacks are being done.
* When any of the nfsd are processing an NFSv4 compound RPC, they must
* either hold a reference count (nfs_usecnt) or the lock. When
* nfsrv_unlock() is called to release the lock, it can optionally
* also get a reference count, which saves the need for a call to
* nfsrv_getref() after nfsrv_unlock().
*/
/*
* First, check to see if we need to wait for an update lock.
*/
igotlock = 0;
NFSLOCKV4ROOTMUTEX();
if (NFSD_VNET(nfsrv_stablefirst).nsf_flags & NFSNSF_NEEDLOCK)
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
NFSV4ROOTLOCKMUTEXPTR, NULL);
else
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 0, NULL,
NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
if (igotlock) {
/*
* If I got the lock, I can update the stable storage file.
* Done when the grace period is over or a client has long
* since expired.
*/
NFSD_VNET(nfsrv_stablefirst).nsf_flags &= ~NFSNSF_NEEDLOCK;
if ((NFSD_VNET(nfsrv_stablefirst).nsf_flags &
(NFSNSF_GRACEOVER | NFSNSF_UPDATEDONE)) == NFSNSF_GRACEOVER)
nfsrv_updatestable(p);
/*
* If at least one client has long since expired, search
* the client list for them, write a REVOKE record on the
* stable storage file and then remove them from the client
* list.
*/
if (NFSD_VNET(nfsrv_stablefirst).nsf_flags &
NFSNSF_EXPIREDCLIENT) {
NFSD_VNET(nfsrv_stablefirst).nsf_flags &=
~NFSNSF_EXPIREDCLIENT;
for (i = 0; i < nfsrv_clienthashsize; i++) {
LIST_FOREACH_SAFE(clp, &NFSD_VNET(nfsclienthash)[i],
lc_hash, nclp) {
if (clp->lc_flags & LCL_EXPIREIT) {
if (!LIST_EMPTY(&clp->lc_open) ||
!LIST_EMPTY(&clp->lc_deleg))
nfsrv_writestable(clp->lc_id,
clp->lc_idlen, NFSNST_REVOKE, p);
nfsrv_cleanclient(clp, p);
nfsrv_freedeleglist(&clp->lc_deleg);
nfsrv_freedeleglist(&clp->lc_olddeleg);
LIST_REMOVE(clp, lc_hash);
nfsrv_zapclient(clp, p);
}
}
}
}
NFSLOCKV4ROOTMUTEX();
nfsv4_unlock(&nfsv4rootfs_lock, 1);
NFSUNLOCKV4ROOTMUTEX();
} else {
/*
* If we didn't get the lock, we need to get a refcnt,
* which also checks for and waits for the lock.
*/
NFSLOCKV4ROOTMUTEX();
nfsv4_getref(&nfsv4rootfs_lock, NULL,
NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
}
/*
* If flagged, search for open owners that haven't had any opens
* for a long time.
*/
if (NFSD_VNET(nfsrv_stablefirst).nsf_flags & NFSNSF_NOOPENS) {
nfsrv_throwawayopens(p);
}
/* Do a CBLAYOUTRECALL callback if over the high water mark. */
if (nfsrv_layoutcnt > nfsrv_layouthighwater)
nfsrv_recalloldlayout(p);
savevp = vp = NULL;
save_fsid.val[0] = save_fsid.val[1] = 0;
cur_fsid.val[0] = cur_fsid.val[1] = 0;
nextop = -1;
savefhcnt = 0;
/* If taglen < 0, there was a parsing error in nfsd_getminorvers(). */
if (taglen < 0) {
error = EBADRPC;
goto nfsmout;
}
(void) nfsm_strtom(nd, tag, taglen);
NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if ((minorvers != NFSV4_MINORVERSION &&
minorvers != NFSV41_MINORVERSION &&
minorvers != NFSV42_MINORVERSION) ||
minorvers < nfs_minminorv4 || minorvers > nfs_maxminorv4)
nd->nd_repstat = NFSERR_MINORVERMISMATCH;
if (nd->nd_repstat)
numops = 0;
else
numops = fxdr_unsigned(int, *tl);
/*
* Loop around doing the sub ops.
* vp - is an unlocked vnode pointer for the CFH
* savevp - is an unlocked vnode pointer for the SAVEDFH
* (at some future date, it might turn out to be more appropriate
* to keep the file handles instead of vnode pointers?)
* savevpnes and vpnes - are the export flags for the above.
*/
for (i = 0; i < numops; i++) {
NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
if (savefhcnt > 0) {
op = NFSV4OP_SAVEFH;
*repp = txdr_unsigned(op);
savefhcnt--;
} else if (nextop == -1) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
*repp = *tl;
op = fxdr_unsigned(int, *tl);
} else {
op = nextop;
*repp = txdr_unsigned(op);
nextop = -1;
}
NFSD_DEBUG(4, "op=%d\n", op);
if (op < NFSV4OP_ACCESS || op >= NFSV42_NOPS ||
(op >= NFSV4OP_NOPS && (nd->nd_flag & ND_NFSV41) == 0) ||
(op >= NFSV41_NOPS && (nd->nd_flag & ND_NFSV42) == 0)) {
nd->nd_repstat = NFSERR_OPILLEGAL;
*repp++ = txdr_unsigned(NFSV4OP_OPILLEGAL);
*repp = nfsd_errmap(nd);
retops++;
break;
} else {
repp++;
}
binuptime(&start_time);
nfsrvd_statstart(op, &start_time);
statsinprog = 1;
if (i == 0)
op0 = op;
if (i == numops - 1)
nd->nd_flag |= ND_LASTOP;
/*
* Check for a referral on the current FH and, if so, return
* NFSERR_MOVED for all ops that allow it, except Getattr.
*/
if (vp != NULL && op != NFSV4OP_GETATTR &&
nfsv4root_getreferral(vp, NULL, 0) != NULL &&
nfsrv_errmoved(op)) {
nd->nd_repstat = NFSERR_MOVED;
*repp = nfsd_errmap(nd);
retops++;
break;
}
/*
* For NFSv4.1, check for a Sequence Operation being first
* or one of the other allowed operations by itself.
*/
if ((nd->nd_flag & ND_NFSV41) != 0) {
if (i != 0 && op == NFSV4OP_SEQUENCE)
nd->nd_repstat = NFSERR_SEQUENCEPOS;
else if (i == 0 && op != NFSV4OP_SEQUENCE &&
op != NFSV4OP_EXCHANGEID &&
op != NFSV4OP_CREATESESSION &&
op != NFSV4OP_BINDCONNTOSESS &&
op != NFSV4OP_DESTROYCLIENTID &&
op != NFSV4OP_DESTROYSESSION)
nd->nd_repstat = NFSERR_OPNOTINSESS;
else if (i != 0 && op0 != NFSV4OP_SEQUENCE)
nd->nd_repstat = NFSERR_NOTONLYOP;
if (nd->nd_repstat != 0) {
*repp = nfsd_errmap(nd);
retops++;
break;
}
}
nd->nd_procnum = op;
/*
* If over flood level, reply NFSERR_RESOURCE, if at the first
* Op. (Since a client recovery from NFSERR_RESOURCE can get
* really nasty for certain Op sequences, I'll play it safe
* and only return the error at the beginning.) The cache
* will still function over flood level, but uses lots of
* mbufs.)
* If nfsrv_mallocmget_limit() returns True, the system is near
* to its limit for memory that malloc()/mget() can allocate.
*/
if (i == 0 && (nd->nd_rp == NULL ||
nd->nd_rp->rc_refcnt == 0) &&
(nfsrv_mallocmget_limit() ||
NFSD_VNET(nfsrc_tcpsavedreplies) >
NFSD_VNET(nfsrc_floodlevel))) {
if (NFSD_VNET(nfsrc_tcpsavedreplies) >
NFSD_VNET(nfsrc_floodlevel))
printf("nfsd server cache flooded, try "
"increasing vfs.nfsd.tcphighwater\n");
nd->nd_repstat = NFSERR_RESOURCE;
*repp = nfsd_errmap(nd);
if (op == NFSV4OP_SETATTR) {
/*
* Setattr replies require a bitmap.
* even for errors like these.
*/
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = 0;
}
retops++;
break;
}
/*
* Check for the case of SP4_MACH_CRED and an operation in
* the allow set. For these operations, replace nd_cred with
* root credentials so that the operation will not fail due
* to credentials.
* NB: ND_MACHCRED is set by Sequence when the ClientID
* specifies LCL_MACHCRED and the RPC is being performed
* via krb5i or krb5p using the machine principal.
*/
if ((nd->nd_flag & ND_MACHCRED) != 0) {
if (NFSISSET_OPBIT(&nd->nd_allowops, op)) {
/* Replace nd_cred with root creds. */
if (rootcred == NULL)
rootcred = nfsrv_createrootcred();
if (savecred == NULL)
savecred = nd->nd_cred;
nd->nd_cred = rootcred;
} else if (savecred != NULL) {
nd->nd_cred = savecred;
savecred = NULL;
}
}
if (nfsv4_opflag[op].savereply)
nd->nd_flag |= ND_SAVEREPLY;
switch (op) {
case NFSV4OP_PUTFH:
error = nfsrv_mtofh(nd, &fh);
if (error)
goto nfsmout;
if ((nd->nd_flag & ND_LASTOP) == 0) {
/*
* Pre-parse the next op#. If it is
* SaveFH, count it and skip to the
* next op#, if not the last op#.
* nextop is used to determine if
* NFSERR_WRONGSEC can be returned,
* per RFC5661 Sec. 2.6.
*/
do {
NFSM_DISSECT(tl, uint32_t *,
NFSX_UNSIGNED);
nextop = fxdr_unsigned(int, *tl);
if (nextop == NFSV4OP_SAVEFH &&
i < numops - 1)
savefhcnt++;
} while (nextop == NFSV4OP_SAVEFH &&
i < numops - 1);
}
if (!nd->nd_repstat)
nfsd_fhtovp(nd, &fh, LK_SHARED, &nvp, &nes,
NULL, 0, nextop);
/* For now, allow this for non-export FHs */
if (!nd->nd_repstat) {
if (vp)
vrele(vp);
vp = nvp;
cur_fsid = vp->v_mount->mnt_stat.f_fsid;
NFSVOPUNLOCK(vp);
vpnes = nes;
}
break;
case NFSV4OP_PUTPUBFH:
if (nfs_pubfhset) {
if ((nd->nd_flag & ND_LASTOP) == 0) {
/*
* Pre-parse the next op#. If it is
* SaveFH, count it and skip to the
* next op#, if not the last op#.
* nextop is used to determine if
* NFSERR_WRONGSEC can be returned,
* per RFC5661 Sec. 2.6.
*/
do {
NFSM_DISSECT(tl, uint32_t *,
NFSX_UNSIGNED);
nextop = fxdr_unsigned(int,
*tl);
if (nextop == NFSV4OP_SAVEFH &&
i < numops - 1)
savefhcnt++;
} while (nextop == NFSV4OP_SAVEFH &&
i < numops - 1);
}
nfsd_fhtovp(nd, &nfs_pubfh, LK_SHARED, &nvp,
&nes, NULL, 0, nextop);
} else
nd->nd_repstat = NFSERR_NOFILEHANDLE;
if (!nd->nd_repstat) {
if (vp)
vrele(vp);
vp = nvp;
cur_fsid = vp->v_mount->mnt_stat.f_fsid;
NFSVOPUNLOCK(vp);
vpnes = nes;
}
break;
case NFSV4OP_PUTROOTFH:
if (NFSD_VNET(nfs_rootfhset)) {
if ((nd->nd_flag & ND_LASTOP) == 0) {
/*
* Pre-parse the next op#. If it is
* SaveFH, count it and skip to the
* next op#, if not the last op#.
* nextop is used to determine if
* NFSERR_WRONGSEC can be returned,
* per RFC5661 Sec. 2.6.
*/
do {
NFSM_DISSECT(tl, uint32_t *,
NFSX_UNSIGNED);
nextop = fxdr_unsigned(int,
*tl);
if (nextop == NFSV4OP_SAVEFH &&
i < numops - 1)
savefhcnt++;
} while (nextop == NFSV4OP_SAVEFH &&
i < numops - 1);
}
nfsd_fhtovp(nd, &NFSD_VNET(nfs_rootfh),
LK_SHARED, &nvp, &nes, NULL, 0, nextop);
if (!nd->nd_repstat) {
if (vp)
vrele(vp);
vp = nvp;
cur_fsid = vp->v_mount->mnt_stat.f_fsid;
NFSVOPUNLOCK(vp);
vpnes = nes;
}
} else
nd->nd_repstat = NFSERR_NOFILEHANDLE;
break;
case NFSV4OP_SAVEFH:
if (vp && NFSVNO_EXPORTED(&vpnes)) {
nd->nd_repstat = 0;
/* If vp == savevp, a no-op */
if (vp != savevp) {
if (savevp)
vrele(savevp);
VREF(vp);
savevp = vp;
savevpnes = vpnes;
save_fsid = cur_fsid;
}
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
nd->nd_savedcurstateid =
nd->nd_curstateid;
nd->nd_flag |= ND_SAVEDCURSTATEID;
}
} else {
nd->nd_repstat = NFSERR_NOFILEHANDLE;
}
break;
case NFSV4OP_RESTOREFH:
if (savevp) {
if ((nd->nd_flag & ND_LASTOP) == 0) {
/*
* Pre-parse the next op#. If it is
* SaveFH, count it and skip to the
* next op#, if not the last op#.
* nextop is used to determine if
* NFSERR_WRONGSEC can be returned,
* per RFC5661 Sec. 2.6.
*/
do {
NFSM_DISSECT(tl, uint32_t *,
NFSX_UNSIGNED);
nextop = fxdr_unsigned(int,
*tl);
if (nextop == NFSV4OP_SAVEFH &&
i < numops - 1)
savefhcnt++;
} while (nextop == NFSV4OP_SAVEFH &&
i < numops - 1);
}
nd->nd_repstat = 0;
/* If vp == savevp, a no-op */
if (vp != savevp) {
if (nfsrv_checkwrongsec(nd, nextop,
savevp->v_type))
nd->nd_repstat =
nfsvno_testexp(nd,
&savevpnes);
if (nd->nd_repstat == 0) {
VREF(savevp);
vrele(vp);
vp = savevp;
vpnes = savevpnes;
cur_fsid = save_fsid;
}
}
if (nd->nd_repstat == 0 &&
(nd->nd_flag & ND_SAVEDCURSTATEID) != 0) {
nd->nd_curstateid =
nd->nd_savedcurstateid;
nd->nd_flag |= ND_CURSTATEID;
}
} else {
nd->nd_repstat = NFSERR_RESTOREFH;
}
break;
default:
/*
* Allow a Lookup, Getattr, GetFH, Secinfo on an
* non-exported directory if
* nfs_rootfhset. Do I need to allow any other Ops?
* (You can only have a non-exported vpnes if
* nfs_rootfhset is true. See nfsd_fhtovp())
* Allow AUTH_SYS to be used for file systems
* exported GSS only for certain Ops, to allow
* clients to do mounts more easily.
*/
if (nfsv4_opflag[op].needscfh && vp) {
if (!NFSVNO_EXPORTED(&vpnes) &&
op != NFSV4OP_LOOKUP &&
op != NFSV4OP_GETATTR &&
op != NFSV4OP_GETFH &&
op != NFSV4OP_ACCESS &&
op != NFSV4OP_READLINK &&
op != NFSV4OP_SECINFO &&
op != NFSV4OP_SECINFONONAME)
nd->nd_repstat = NFSERR_NOFILEHANDLE;
if (nd->nd_repstat) {
if (op == NFSV4OP_SETATTR) {
/*
* Setattr reply requires a bitmap
* even for errors like these.
*/
NFSM_BUILD(tl, u_int32_t *,
NFSX_UNSIGNED);
*tl = 0;
}
break;
}
}
/*
* Save the current positions in the mbuf lists so
* that a rollback to this location can be done upon a
* redo due to a ERELOOKUP return for a operation.
*/
mb = nd->nd_mb;
bpos = nd->nd_bpos;
bextpg = nd->nd_bextpg;
bextpgsiz = nd->nd_bextpgsiz;
md = nd->nd_md;
dpos = nd->nd_dpos;
tryagain:
if (nfsv4_opflag[op].retfh == 1) {
if (!vp) {
nd->nd_repstat = NFSERR_NOFILEHANDLE;
break;
}
if (NFSVNO_EXPORTED(&vpnes) && (op == NFSV4OP_LOOKUP ||
op == NFSV4OP_LOOKUPP || (op == NFSV4OP_OPEN &&
vp->v_type == VDIR))) {
/* Check for wrong security. */
rstat = nfsvno_testexp(nd, &vpnes);
if (rstat != 0) {
nd->nd_repstat = rstat;
break;
}
}
VREF(vp);
if (nfsv4_opflag[op].modifyfs)
vn_start_write(vp, &temp_mp, V_WAIT);
error = (*(nfsrv4_ops1[op]))(nd, isdgram, vp,
&nvp, (fhandle_t *)fh.nfsrvfh_data, &vpnes);
if (!error && !nd->nd_repstat) {
if (op == NFSV4OP_LOOKUP || op == NFSV4OP_LOOKUPP) {
new_mp = nvp->v_mount;
if (fsidcmp(&cur_fsid, &new_mp->mnt_stat.f_fsid) != 0) {
/* crossed a server mount point */
nd->nd_repstat = nfsvno_checkexp(new_mp,
nd->nd_nam, &nes, &credanon);
if (!nd->nd_repstat)
nd->nd_repstat = nfsd_excred(nd,
&nes, credanon, true);
if (credanon != NULL)
crfree(credanon);
if (!nd->nd_repstat) {
vpnes = nes;
cur_fsid = new_mp->mnt_stat.f_fsid;
}
}
/* Lookup ops return a locked vnode */
NFSVOPUNLOCK(nvp);
}
if (!nd->nd_repstat) {
vrele(vp);
vp = nvp;
} else
vrele(nvp);
}
if (nfsv4_opflag[op].modifyfs)
vn_finished_write(temp_mp);
} else if (nfsv4_opflag[op].retfh == 2) {
if (vp == NULL || savevp == NULL) {
nd->nd_repstat = NFSERR_NOFILEHANDLE;
break;
} else if (fsidcmp(&cur_fsid, &save_fsid) != 0) {
nd->nd_repstat = NFSERR_XDEV;
break;
}
if (nfsv4_opflag[op].modifyfs)
vn_start_write(savevp, &temp_mp, V_WAIT);
if (NFSVOPLOCK(savevp, LK_EXCLUSIVE) == 0) {
VREF(vp);
VREF(savevp);
error = (*(nfsrv4_ops2[op]))(nd, isdgram,
savevp, vp, &savevpnes, &vpnes);
} else
nd->nd_repstat = NFSERR_PERM;
if (nfsv4_opflag[op].modifyfs)
vn_finished_write(temp_mp);
} else {
if (nfsv4_opflag[op].retfh != 0)
panic("nfsrvd_compound");
if (nfsv4_opflag[op].needscfh) {
if (vp != NULL) {
lktype = nfsv4_opflag[op].lktype;
if (nfsv4_opflag[op].modifyfs) {
vn_start_write(vp, &temp_mp,
V_WAIT);
if (op == NFSV4OP_WRITE &&
MNT_SHARED_WRITES(temp_mp))
lktype = LK_SHARED;
}
if (NFSVOPLOCK(vp, lktype) == 0)
VREF(vp);
else
nd->nd_repstat = NFSERR_PERM;
} else {
nd->nd_repstat = NFSERR_NOFILEHANDLE;
if (op == NFSV4OP_SETATTR) {
/*
* Setattr reply requires a
* bitmap even for errors like
* these.
*/
NFSM_BUILD(tl, u_int32_t *,
NFSX_UNSIGNED);
*tl = 0;
}
break;
}
if (nd->nd_repstat == 0) {
error = (*(nfsrv4_ops0[op]))(nd,
isdgram, vp, &vpnes);
if ((op == NFSV4OP_SECINFO ||
op == NFSV4OP_SECINFONONAME) &&
error == 0 && nd->nd_repstat == 0) {
/*
* Secinfo and Secinfo_no_name
* consume the current FH.
*/
vrele(vp);
vp = NULL;
}
}
if (nfsv4_opflag[op].modifyfs)
vn_finished_write(temp_mp);
} else {
error = (*(nfsrv4_ops0[op]))(nd, isdgram,
NULL, &vpnes);
}
}
}
if (error) {
if (error == EBADRPC || error == NFSERR_BADXDR) {
nd->nd_repstat = NFSERR_BADXDR;
} else {
nd->nd_repstat = error;
printf("nfsv4 comperr0=%d\n", error);
}
error = 0;
}
if (nd->nd_repstat == ERELOOKUP) {
/*
* Roll back to the beginning of the operation
* arguments.
*/
nd->nd_md = md;
nd->nd_dpos = dpos;
/*
* Trim off the bogus reply for this operation
* and redo the operation.
*/
nfsm_trimtrailing(nd, mb, bpos, bextpg, bextpgsiz);
nd->nd_repstat = 0;
nd->nd_flag |= ND_ERELOOKUP;
goto tryagain;
}
nd->nd_flag &= ~ND_ERELOOKUP;
if (statsinprog != 0) {
nfsrvd_statend(op, /*bytes*/ 0, /*now*/ NULL,
/*then*/ &start_time);
statsinprog = 0;
}
retops++;
if (nd->nd_repstat) {
*repp = nfsd_errmap(nd);
break;
} else {
*repp = 0; /* NFS4_OK */
}
}
nfsmout:
if (statsinprog != 0) {
nfsrvd_statend(op, /*bytes*/ 0, /*now*/ NULL,
/*then*/ &start_time);
statsinprog = 0;
}
if (error) {
if (error == EBADRPC || error == NFSERR_BADXDR)
nd->nd_repstat = NFSERR_BADXDR;
else
printf("nfsv4 comperr1=%d\n", error);
}
if (taglen == -1) {
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = 0;
*tl = 0;
} else {
*retopsp = txdr_unsigned(retops);
}
if (vp)
vrele(vp);
if (savevp)
vrele(savevp);
if (savecred != NULL)
nd->nd_cred = savecred;
if (rootcred != NULL)
crfree(rootcred);
NFSLOCKV4ROOTMUTEX();
nfsv4_relref(&nfsv4rootfs_lock);
NFSUNLOCKV4ROOTMUTEX();
NFSEXITCODE2(0, nd);
}
/* Create a credential for "root". */
static struct ucred *
nfsrv_createrootcred(void)
{
struct ucred *cr;
gid_t grp;
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = UID_ROOT;
grp = GID_WHEEL;
crsetgroups(cr, 1, &grp);
cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
#ifdef MAC
mac_cred_associate_nfsd(cr);
#endif
return (cr);
}