#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/nlookup.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/malloc.h>
#include <sys/sysent.h>
#include <sys/syscall.h>
#include <sys/sysmsg.h>
#include <sys/conf.h>
#include <sys/objcache.h>
#include <sys/jail.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <sys/buf2.h>
#include "rpcv2.h"
#include "nfsproto.h"
#include "nfs.h"
#include "nfsmount.h"
#include "nfsnode.h"
#include "xdr_subs.h"
#include "nfsm_subs.h"
#include "nfsrtt.h"
#include <netinet/in.h>
u_int32_t nfs_xdrneg1;
u_int32_t rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers;
u_int32_t rpc_auth_unix, rpc_msgaccepted, rpc_call, rpc_autherr;
u_int32_t rpc_auth_kerb;
u_int32_t nfs_prog, nfs_true, nfs_false;
static enum vtype nv2tov_type[8]= {
VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON
};
enum vtype nv3tov_type[8]= {
VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
};
int nfs_ticks;
struct lwkt_token nfs_token = LWKT_TOKEN_INITIALIZER(unp_token);
static long nfs_pbuf_freecnt = -1;
struct nfsmount_head nfs_mountq = TAILQ_HEAD_INITIALIZER(nfs_mountq);
struct nfssvc_sockhead nfssvc_sockhead;
int nfssvc_sockhead_flag;
struct nfsd_head nfsd_head;
int nfsd_head_flag;
struct nfs_bufq nfs_bufq;
struct nqfhhashhead *nqfhhashtbl;
u_long nqfhhash;
static int nfs_prev_nfssvc_sy_narg;
static sy_call_t *nfs_prev_nfssvc_sy_call;
#ifndef NFS_NOSERVER
int nfsv3_procid[NFS_NPROCS] = {
NFSPROC_NULL,
NFSPROC_GETATTR,
NFSPROC_SETATTR,
NFSPROC_NOOP,
NFSPROC_LOOKUP,
NFSPROC_READLINK,
NFSPROC_READ,
NFSPROC_NOOP,
NFSPROC_WRITE,
NFSPROC_CREATE,
NFSPROC_REMOVE,
NFSPROC_RENAME,
NFSPROC_LINK,
NFSPROC_SYMLINK,
NFSPROC_MKDIR,
NFSPROC_RMDIR,
NFSPROC_READDIR,
NFSPROC_FSSTAT,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP,
NFSPROC_NOOP
};
#endif
int nfsv2_procid[NFS_NPROCS] = {
NFSV2PROC_NULL,
NFSV2PROC_GETATTR,
NFSV2PROC_SETATTR,
NFSV2PROC_LOOKUP,
NFSV2PROC_NOOP,
NFSV2PROC_READLINK,
NFSV2PROC_READ,
NFSV2PROC_WRITE,
NFSV2PROC_CREATE,
NFSV2PROC_MKDIR,
NFSV2PROC_SYMLINK,
NFSV2PROC_CREATE,
NFSV2PROC_REMOVE,
NFSV2PROC_RMDIR,
NFSV2PROC_RENAME,
NFSV2PROC_LINK,
NFSV2PROC_READDIR,
NFSV2PROC_NOOP,
NFSV2PROC_STATFS,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
NFSV2PROC_NOOP,
};
#ifndef NFS_NOSERVER
static u_char nfsrv_v2errmap[ELAST] = {
NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
NFSERR_IO
};
static short nfsv3err_null[] = {
0,
0,
};
static short nfsv3err_getattr[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_setattr[] = {
NFSERR_IO,
NFSERR_PERM,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_INVAL,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOT_SYNC,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_lookup[] = {
NFSERR_IO,
NFSERR_NOENT,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_NOTDIR,
NFSERR_NAMETOL,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_access[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_readlink[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_INVAL,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_read[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_NXIO,
NFSERR_ACCES,
NFSERR_INVAL,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_write[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_INVAL,
NFSERR_FBIG,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_create[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_NOTDIR,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_mkdir[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_NOTDIR,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_symlink[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_NOTDIR,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_mknod[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_NOTDIR,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
NFSERR_BADTYPE,
0,
};
static short nfsv3err_remove[] = {
NFSERR_IO,
NFSERR_NOENT,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_NOTDIR,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_rmdir[] = {
NFSERR_IO,
NFSERR_NOENT,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_NOTDIR,
NFSERR_INVAL,
NFSERR_ROFS,
NFSERR_NAMETOL,
NFSERR_NOTEMPTY,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_rename[] = {
NFSERR_IO,
NFSERR_NOENT,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_XDEV,
NFSERR_NOTDIR,
NFSERR_ISDIR,
NFSERR_INVAL,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_MLINK,
NFSERR_NAMETOL,
NFSERR_NOTEMPTY,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_link[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_EXIST,
NFSERR_XDEV,
NFSERR_NOTDIR,
NFSERR_INVAL,
NFSERR_NOSPC,
NFSERR_ROFS,
NFSERR_MLINK,
NFSERR_NAMETOL,
NFSERR_DQUOT,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_NOTSUPP,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_readdir[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_NOTDIR,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_BAD_COOKIE,
NFSERR_TOOSMALL,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_readdirplus[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_ACCES,
NFSERR_NOTDIR,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_BAD_COOKIE,
NFSERR_NOTSUPP,
NFSERR_TOOSMALL,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_fsstat[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_fsinfo[] = {
NFSERR_STALE,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_pathconf[] = {
NFSERR_STALE,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short nfsv3err_commit[] = {
NFSERR_IO,
NFSERR_IO,
NFSERR_STALE,
NFSERR_BADHANDLE,
NFSERR_SERVERFAULT,
0,
};
static short *nfsrv_v3errmap[] = {
nfsv3err_null,
nfsv3err_getattr,
nfsv3err_setattr,
nfsv3err_lookup,
nfsv3err_access,
nfsv3err_readlink,
nfsv3err_read,
nfsv3err_write,
nfsv3err_create,
nfsv3err_mkdir,
nfsv3err_symlink,
nfsv3err_mknod,
nfsv3err_remove,
nfsv3err_rmdir,
nfsv3err_rename,
nfsv3err_link,
nfsv3err_readdir,
nfsv3err_readdirplus,
nfsv3err_fsstat,
nfsv3err_fsinfo,
nfsv3err_pathconf,
nfsv3err_commit,
};
#endif
u_quad_t
nfs_curusec(void)
{
struct timeval tv;
getmicrouptime(&tv);
return ((u_quad_t)tv.tv_sec * 1000000 + (u_quad_t)tv.tv_usec);
}
int
nfs_init(struct vfsconf *vfsp)
{
callout_init_mp(&nfs_timer_handle);
nfs_mount_type = vfsp->vfc_typenum;
nfsrtt.pos = 0;
rpc_vers = txdr_unsigned(RPC_VER2);
rpc_call = txdr_unsigned(RPC_CALL);
rpc_reply = txdr_unsigned(RPC_REPLY);
rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
rpc_autherr = txdr_unsigned(RPC_AUTHERR);
rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
nfs_prog = txdr_unsigned(NFS_PROG);
nfs_true = txdr_unsigned(TRUE);
nfs_false = txdr_unsigned(FALSE);
nfs_xdrneg1 = txdr_unsigned(-1);
nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
if (nfs_ticks < 1)
nfs_ticks = 1;
nfs_nhinit();
#ifndef NFS_NOSERVER
nfsrv_init(0);
nfsrv_initcache();
#endif
if (nfs_maxasyncbio > nmbclusters * MCLBYTES / NFS_MAXDATA / 3)
nfs_maxasyncbio = nmbclusters * MCLBYTES / NFS_MAXDATA / 3;
if (nfs_maxasyncbio < 4)
nfs_maxasyncbio = 4;
nfs_timer_callout(0);
#if 1
nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
sysent[SYS_nfssvc].sy_narg = 2;
#endif
nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
sysent[SYS_nfssvc].sy_call = (sy_call_t *)sys_nfssvc;
nfs_pbuf_freecnt = nswbuf_kva / 2 + 1;
return (0);
}
int
nfs_uninit(struct vfsconf *vfsp)
{
callout_stop(&nfs_timer_handle);
nfs_mount_type = -1;
sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
nfs_nhdestroy();
#ifndef NFS_NOSERVER
nfsrv_destroycache();
#endif
return (0);
}
int
nfs_loadattrcache(struct vnode *vp, struct mbuf **mdp, caddr_t *dposp,
struct vattr *vaper, int lattr_flags)
{
struct vattr *vap;
struct nfs_fattr *fp;
struct nfsnode *np;
int32_t t1;
caddr_t cp2;
int error = 0;
int rmajor, rminor;
dev_t rdev;
struct mbuf *md;
enum vtype vtyp;
u_short vmode;
struct timespec mtime;
int v3 = NFS_ISV3(vp);
md = *mdp;
t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) != 0)
return (error);
fp = (struct nfs_fattr *)cp2;
if (v3) {
vtyp = nfsv3tov_type(fp->fa_type);
vmode = fxdr_unsigned(u_short, fp->fa_mode);
rmajor = (int)fxdr_unsigned(int, fp->fa3_rdev.specdata1);
rminor = (int)fxdr_unsigned(int, fp->fa3_rdev.specdata2);
fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
} else {
vtyp = nfsv2tov_type(fp->fa_type);
vmode = fxdr_unsigned(u_short, fp->fa_mode);
if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0))
vtyp = IFTOVT(vmode);
rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
rmajor = umajor(rdev);
rminor = uminor(rdev);
fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
if (vtyp == VCHR && rdev == (dev_t)0xffffffff)
vtyp = VFIFO;
}
np = VTONFS(vp);
if (vp->v_type != vtyp) {
nfs_setvtype(vp, vtyp);
if (vp->v_type == VFIFO) {
vp->v_ops = &vp->v_mount->mnt_vn_fifo_ops;
} else if (vp->v_type == VCHR || vp->v_type == VBLK) {
vp->v_ops = &vp->v_mount->mnt_vn_spec_ops;
addaliasu(vp, rmajor, rminor);
} else {
vp->v_ops = &vp->v_mount->mnt_vn_use_ops;
}
np->n_mtime = mtime.tv_sec;
} else if (np->n_mtime != mtime.tv_sec) {
np->n_mtime = mtime.tv_sec;
if ((lattr_flags & NFS_LATTR_NOMTIMECHECK) == 0)
np->n_flag |= NRMODIFIED;
}
vap = &np->n_vattr;
vap->va_type = vtyp;
vap->va_mode = (vmode & 07777);
vap->va_rmajor = rmajor;
vap->va_rminor = rminor;
vap->va_mtime = mtime;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
if (v3) {
vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
vap->va_size = fxdr_hyper(&fp->fa3_size);
vap->va_blocksize = NFS_FABLKSIZE;
vap->va_bytes = fxdr_hyper(&fp->fa3_used);
vap->va_fileid = fxdr_hyper(&fp->fa3_fileid);
fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
vap->va_flags = 0;
vap->va_filerev = 0;
} else {
vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks)
* NFS_FABLKSIZE;
vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
vap->va_flags = 0;
vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
fp->fa2_ctime.nfsv2_sec);
vap->va_ctime.tv_nsec = 0;
vap->va_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
vap->va_filerev = 0;
}
np->n_attrstamp = time_uptime;
if (vap->va_size != np->n_size) {
if (vap->va_type == VREG) {
if ((np->n_flag & NLMODIFIED) == 0)
np->n_flag |= NRMODIFIED;
#if 0
if ((lattr_flags & NFS_LATTR_NOSHRINK) &&
vap->va_size < np->n_size) {
vap->va_size = np->n_size;
np->n_attrstamp = 0;
if ((np->n_flag & NLMODIFIED) == 0)
np->n_flag |= NRMODIFIED;
} else if (np->n_flag & NLMODIFIED) {
if (vap->va_size < np->n_size) {
vap->va_size = np->n_size;
} else {
np->n_size = vap->va_size;
np->n_flag |= NRMODIFIED;
}
} else {
np->n_size = vap->va_size;
np->n_flag |= NRMODIFIED;
}
nvnode_pager_setsize(vp, np->n_size, XXX);
#endif
} else {
np->n_size = vap->va_size;
}
}
if (vaper != NULL) {
bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
if (np->n_flag & NCHG) {
if (np->n_flag & NACC)
vaper->va_atime = np->n_atim;
if (np->n_flag & NUPD)
vaper->va_mtime = np->n_mtim;
}
}
return (0);
}
#ifdef NFS_ACDEBUG
#include <sys/sysctl.h>
SYSCTL_DECL(_vfs_nfs);
static int nfs_acdebug;
SYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
#endif
int
nfs_getattrcache(struct vnode *vp, struct vattr *vaper)
{
struct nfsnode *np;
struct vattr *vap;
struct nfsmount *nmp;
int timeo;
np = VTONFS(vp);
vap = &np->n_vattr;
nmp = VFSTONFS(vp->v_mount);
timeo = (get_approximate_time_t() - np->n_mtime) / 60;
#ifdef NFS_ACDEBUG
if (nfs_acdebug>1)
kprintf("nfs_getattrcache: initial timeo = %d\n", timeo);
#endif
if (vap->va_type == VDIR) {
if ((np->n_flag & NLMODIFIED) || timeo < nmp->nm_acdirmin)
timeo = nmp->nm_acdirmin;
else if (timeo > nmp->nm_acdirmax)
timeo = nmp->nm_acdirmax;
} else {
if ((np->n_flag & NLMODIFIED) || timeo < nmp->nm_acregmin)
timeo = nmp->nm_acregmin;
else if (timeo > nmp->nm_acregmax)
timeo = nmp->nm_acregmax;
}
#ifdef NFS_ACDEBUG
if (nfs_acdebug > 2)
kprintf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
nmp->nm_acregmin, nmp->nm_acregmax,
nmp->nm_acdirmin, nmp->nm_acdirmax);
if (nfs_acdebug)
kprintf("nfs_getattrcache: age = %d; final timeo = %d\n",
(int)(time_uptime - np->n_attrstamp), timeo);
#endif
if (np->n_attrstamp == 0 || (time_uptime - np->n_attrstamp) >= timeo) {
nfsstats.attrcache_misses++;
return (ENOENT);
}
nfsstats.attrcache_hits++;
if (vap->va_size != np->n_size) {
if (vap->va_type == VREG) {
if (np->n_flag & NLMODIFIED)
vap->va_size = np->n_size;
nfs_meta_setsize(vp, curthread, vap->va_size, 0);
} else {
np->n_size = vap->va_size;
}
}
bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
if (np->n_flag & NCHG) {
if (np->n_flag & NACC)
vaper->va_atime = np->n_atim;
if (np->n_flag & NUPD)
vaper->va_mtime = np->n_mtim;
}
return (0);
}
#ifndef NFS_NOSERVER
int
nfs_namei(struct nlookupdata *nd, struct ucred *cred, int nflags,
struct vnode **dvpp, struct vnode **vpp,
fhandle_t *fhp, int len,
struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
caddr_t *dposp, struct vnode **dirpp, struct thread *td,
int kerbflag, int pubflag)
{
int i, rem;
struct mbuf *md;
char *fromcp, *tocp, *cp;
char *namebuf;
struct nchandle nch;
struct vnode *dp;
struct mount *mp;
int error, rdonly;
int isretry;
if (nd->nl_flags & NLC_HASBUF) {
namebuf = nd->nl_path;
nd->nl_flags &= ~NLC_HASBUF;
nd->nl_path = NULL;
isretry = 1;
} else {
namebuf = objcache_get(namei_oc, M_WAITOK);
isretry = 0;
}
*dirpp = NULL;
if (isretry == 0) {
fromcp = *dposp;
tocp = namebuf;
md = *mdp;
rem = mtod(md, caddr_t) + md->m_len - fromcp;
for (i = 0; i < len; i++) {
while (rem == 0) {
md = md->m_next;
if (md == NULL) {
error = EBADRPC;
goto out;
}
fromcp = mtod(md, caddr_t);
rem = md->m_len;
}
if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
error = EACCES;
goto out;
}
*tocp++ = *fromcp++;
rem--;
}
*tocp = '\0';
*mdp = md;
*dposp = fromcp;
len = nfsm_rndup(len)-len;
if (len > 0) {
if (rem >= len)
*dposp += len;
else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
goto out;
}
}
error = nfsrv_fhtovp(fhp, FALSE, &mp, &dp, cred, slp,
nam, &rdonly, kerbflag, pubflag);
if (error)
goto out;
if (dp->v_type != VDIR) {
vrele(dp);
error = ENOTDIR;
goto out;
}
*dirpp = dp;
if (rdonly) {
nflags |= NLC_NFS_RDONLY;
if (nflags & (NLC_DELETE | NLC_RENAME_DST)) {
error = EROFS;
goto out;
}
}
if (pubflag) {
cp = objcache_get(namei_oc, M_WAITOK);
fromcp = namebuf;
tocp = cp;
if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
switch ((unsigned char)*fromcp) {
case WEBNFS_NATIVE_CHAR:
fromcp++;
break;
default:
error = EIO;
objcache_put(namei_oc, cp);
goto out;
}
}
while (*fromcp != '\0') {
if (*fromcp == WEBNFS_ESC_CHAR) {
if (fromcp[1] != '\0' && fromcp[2] != '\0') {
fromcp++;
*tocp++ = HEXSTRTOI(fromcp);
fromcp += 2;
continue;
} else {
error = ENOENT;
objcache_put(namei_oc, cp);
goto out;
}
} else
*tocp++ = *fromcp++;
}
*tocp = '\0';
objcache_put(namei_oc, namebuf);
namebuf = cp;
}
if (pubflag == 0) {
nflags |= NLC_NFS_NOSOFTLINKTRAV;
nflags |= NLC_NOCROSSMOUNT;
}
if ((error = cache_fromdvp(dp, cred, 1, &nch)) != 0)
goto out;
nlookup_init_raw(nd, namebuf, UIO_SYSSPACE, nflags, cred, &nch);
cache_drop(&nch);
error = nlookup(nd);
if (error == 0) {
if (dvpp) {
if (nd->nl_nch.ncp->nc_parent) {
nch = nd->nl_nch;
nch.ncp = nch.ncp->nc_parent;
cache_hold(&nch);
cache_lock(&nch);
error = cache_vget(&nch, nd->nl_cred,
LK_EXCLUSIVE, dvpp);
cache_put(&nch);
} else {
error = ENXIO;
}
}
if (vpp && nd->nl_nch.ncp->nc_vp) {
error = cache_vget(&nd->nl_nch, nd->nl_cred, LK_EXCLUSIVE, vpp);
}
if (error) {
if (dvpp && *dvpp) {
vput(*dvpp);
*dvpp = NULL;
}
if (vpp && *vpp) {
vput(*vpp);
*vpp = NULL;
}
}
}
out:
objcache_put(namei_oc, namebuf);
return (error);
}
int
nfsrv_fhtovp(fhandle_t *fhp, int lockflag,
struct mount **mpp, struct vnode **vpp,
struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
int *rdonlyp, int kerbflag, int pubflag)
{
struct mount *mp;
int i;
struct ucred *credanon;
int error, exflags;
#ifdef MNT_EXNORESPORT
struct sockaddr_int *saddr;
#endif
*vpp = NULL;
*mpp = NULL;
if (nfs_ispublicfh(fhp)) {
if (!pubflag || !nfs_pub.np_valid)
return (ESTALE);
fhp = &nfs_pub.np_handle;
}
mp = *mpp = vfs_getvfs(&fhp->fh_fsid);
if (mp == NULL)
return (ESTALE);
error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
if (error) {
mount_drop(mp);
return (error);
}
error = VFS_FHTOVP(mp, NULL, &fhp->fh_fid, vpp);
mount_drop(mp);
if (error)
return (ESTALE);
#ifdef MNT_EXNORESPORT
if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
saddr = (struct sockaddr_in *)nam;
if (saddr->sin_family == AF_INET &&
ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
vput(*vpp);
*vpp = NULL;
return (NFSERR_AUTHERR | AUTH_TOOWEAK);
}
}
#endif
if (exflags & MNT_EXKERB) {
if (!kerbflag) {
vput(*vpp);
*vpp = NULL;
return (NFSERR_AUTHERR | AUTH_TOOWEAK);
}
} else if (kerbflag) {
vput(*vpp);
*vpp = NULL;
return (NFSERR_AUTHERR | AUTH_TOOWEAK);
} else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
cred->cr_uid = credanon->cr_uid;
for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
cred->cr_groups[i] = credanon->cr_groups[i];
cred->cr_ngroups = i;
}
if (exflags & MNT_EXRDONLY)
*rdonlyp = 1;
else
*rdonlyp = 0;
if (!lockflag)
vn_unlock(*vpp);
return (0);
}
int
nfs_ispublicfh(fhandle_t *fhp)
{
char *cp = (char *)fhp;
int i;
for (i = 0; i < NFSX_V3FH; i++)
if (*cp++ != 0)
return (FALSE);
return (TRUE);
}
#endif
int
netaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
{
struct sockaddr_in *inetaddr;
switch (family) {
case AF_INET:
inetaddr = (struct sockaddr_in *)nam;
if (inetaddr->sin_family == AF_INET &&
inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
return (1);
break;
default:
break;
}
return (0);
}
static nfsuint64 nfs_nullcookie = { { 0, 0 } };
nfsuint64 *
nfs_getcookie(struct nfsnode *np, off_t off, int add)
{
struct nfsdmap *dp, *dp2;
int pos;
pos = (uoff_t)off / NFS_DIRBLKSIZ;
if (pos == 0 || off < 0) {
#ifdef DIAGNOSTIC
if (add)
panic("nfs getcookie add at <= 0");
#endif
return (&nfs_nullcookie);
}
pos--;
dp = np->n_cookies.lh_first;
if (!dp) {
if (add) {
dp = kmalloc(sizeof(struct nfsdmap), M_NFSDIROFF,
M_WAITOK);
dp->ndm_eocookie = 0;
LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
} else
return (NULL);
}
while (pos >= NFSNUMCOOKIES) {
pos -= NFSNUMCOOKIES;
if (dp->ndm_list.le_next) {
if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
pos >= dp->ndm_eocookie)
return (NULL);
dp = dp->ndm_list.le_next;
} else if (add) {
dp2 = kmalloc(sizeof(struct nfsdmap), M_NFSDIROFF,
M_WAITOK);
dp2->ndm_eocookie = 0;
LIST_INSERT_AFTER(dp, dp2, ndm_list);
dp = dp2;
} else
return (NULL);
}
if (pos >= dp->ndm_eocookie) {
if (add)
dp->ndm_eocookie = pos + 1;
else
return (NULL);
}
return (&dp->ndm_cookies[pos]);
}
void
nfs_invaldir(struct vnode *vp)
{
struct nfsnode *np = VTONFS(vp);
#ifdef DIAGNOSTIC
if (vp->v_type != VDIR)
panic("nfs: invaldir not dir");
#endif
np->n_direofoffset = 0;
np->n_cookieverf.nfsuquad[0] = 0;
np->n_cookieverf.nfsuquad[1] = 0;
if (np->n_cookies.lh_first)
np->n_cookies.lh_first->ndm_eocookie = 0;
}
void
nfs_setvtype(struct vnode *vp, enum vtype vtyp)
{
vp->v_type = vtyp;
switch(vtyp) {
case VREG:
case VDIR:
case VLNK:
vinitvmio(vp, 0, PAGE_SIZE, -1);
break;
default:
break;
}
}
static int nfs_clearcommit_bp(struct buf *bp, void *data __unused);
static int nfs_clearcommit_callback(struct mount *mp, struct vnode *vp,
void *data __unused);
void
nfs_clearcommit(struct mount *mp)
{
vsyncscan(mp, VMSC_NOWAIT, nfs_clearcommit_callback, NULL);
}
static int
nfs_clearcommit_callback(struct mount *mp, struct vnode *vp,
void *data __unused)
{
lwkt_gettoken(&vp->v_token);
RB_SCAN(buf_rb_tree, &vp->v_rbdirty_tree, NULL,
nfs_clearcommit_bp, NULL);
lwkt_reltoken(&vp->v_token);
return(0);
}
static int
nfs_clearcommit_bp(struct buf *bp, void *data __unused)
{
if (BUF_LOCKINUSE(bp) == 0 &&
(bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
== (B_DELWRI | B_NEEDCOMMIT)) {
bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
}
return(0);
}
#ifndef NFS_NOSERVER
int
nfsrv_errmap(struct nfsrv_descript *nd, int err)
{
short *defaulterrp, *errp;
if (nd->nd_flag & ND_NFSV3) {
if (nd->nd_procnum <= NFSPROC_COMMIT) {
errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
while (*++errp) {
if (*errp == err)
return (err);
else if (*errp > err)
break;
}
return ((int)*defaulterrp);
} else
return (err & 0xffff);
}
if (err <= ELAST)
return ((int)nfsrv_v2errmap[err - 1]);
return (NFSERR_IO);
}
void
nfsrvw_sort(gid_t *list, int num)
{
int i, j;
gid_t v;
for (i = 1; i < num; i++) {
v = list[i];
for (j = i; --j >= 0 && v < list[j];)
list[j + 1] = list[j];
list[j + 1] = v;
}
}
void
nfsrv_setcred(struct ucred *incred, struct ucred *outcred)
{
int i;
bzero((caddr_t)outcred, sizeof (struct ucred));
outcred->cr_ref = 1;
outcred->cr_uid = incred->cr_uid;
outcred->cr_ngroups = incred->cr_ngroups;
for (i = 0; i < incred->cr_ngroups; i++)
outcred->cr_groups[i] = incred->cr_groups[i];
nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
}
#endif
struct ucred *
nfs_crhold(struct ucred *cred)
{
if (cred) {
if (cred->cr_prison) {
cred = crdup_nocaps(cred);
} else {
cred = crhold(cred);
}
}
return cred;
}
int
nfs_crsame(struct ucred *cr1, struct ucred *cr2)
{
if (cr1 != cr2) {
if (cr1 == NULL || cr2 == NULL)
return (cr1 == cr2);
if (cr1->cr_uid != cr2->cr_uid ||
cr1->cr_ruid != cr2->cr_ruid ||
cr1->cr_rgid != cr2->cr_rgid ||
cr1->cr_ngroups != cr2->cr_ngroups) {
return 0;
}
if (bcmp(cr1->cr_groups, cr2->cr_groups,
cr1->cr_ngroups * sizeof(cr1->cr_groups[0])) != 0) {
return 0;
}
}
return 1;
}