#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/pool.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfs_var.h>
#include <nfs/nfsm_subs.h>
#include <netinet/in.h>
extern u_int32_t nfs_false, nfs_true;
extern const nfstype nfsv2_type[9];
extern const nfstype nfsv3_type[9];
int
nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp,
caddr_t *dposp, struct vnode **retdirp, struct proc *p)
{
int i, rem;
struct mbuf *md;
char *fromcp, *tocp;
struct vnode *dp;
int error, rdonly;
struct componentname *cnp = &ndp->ni_cnd;
*retdirp = NULL;
cnp->cn_pnbuf = pool_get(&namei_pool, PR_WAITOK);
fromcp = *dposp;
tocp = cnp->cn_pnbuf;
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' || *fromcp == '/') {
error = EACCES;
goto out;
}
*tocp++ = *fromcp++;
rem--;
}
*tocp = '\0';
*mdp = md;
*dposp = fromcp;
len = nfsm_padlen(len);
if (len > 0) {
if (rem >= len)
*dposp += len;
else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
goto out;
}
ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
cnp->cn_nameptr = cnp->cn_pnbuf;
error = nfsrv_fhtovp(fhp, 0, &dp, ndp->ni_cnd.cn_cred, slp,
nam, &rdonly);
if (error)
goto out;
if (dp->v_type != VDIR) {
vrele(dp);
error = ENOTDIR;
goto out;
}
vref(dp);
*retdirp = dp;
ndp->ni_startdir = dp;
if (rdonly)
cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
else
cnp->cn_flags |= NOCROSSMOUNT;
cnp->cn_proc = p;
error = vfs_lookup(ndp);
if (error)
goto out;
if (cnp->cn_flags & ISSYMLINK) {
if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
vput(ndp->ni_dvp);
else
vrele(ndp->ni_dvp);
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
error = EINVAL;
goto out;
}
if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
cnp->cn_flags |= HASBUF;
return (0);
}
out:
pool_put(&namei_pool, cnp->cn_pnbuf);
return (error);
}
void
nfsm_adj(struct mbuf *mp, int len, int nul)
{
struct mbuf *m;
int count, i;
char *cp;
count = 0;
m = mp;
for (;;) {
count += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
if (m->m_len > len) {
m->m_len -= len;
if (nul > 0) {
cp = mtod(m, caddr_t)+m->m_len-nul;
for (i = 0; i < nul; i++)
*cp++ = '\0';
}
return;
}
count -= len;
if (count < 0)
count = 0;
for (m = mp; m; m = m->m_next) {
if (m->m_len >= count) {
m->m_len = count;
if (nul > 0) {
cp = mtod(m, caddr_t)+m->m_len-nul;
for (i = 0; i < nul; i++)
*cp++ = '\0';
}
break;
}
count -= m->m_len;
}
for (m = m->m_next;m;m = m->m_next)
m->m_len = 0;
}
void
nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
struct vattr *before_vap, int after_ret, struct vattr *after_vap,
struct nfsm_info *info)
{
u_int32_t *tl;
if (before_ret) {
tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
tl = nfsm_build(&info->nmi_mb, 7 * NFSX_UNSIGNED);
*tl++ = nfs_true;
txdr_hyper(before_vap->va_size, tl);
tl += 2;
txdr_nfsv3time(&(before_vap->va_mtime), tl);
tl += 2;
txdr_nfsv3time(&(before_vap->va_ctime), tl);
}
nfsm_srvpostop_attr(nfsd, after_ret, after_vap, info);
}
void
nfsm_srvpostop_attr(struct nfsrv_descript *nfsd, int after_ret,
struct vattr *after_vap, struct nfsm_info *info)
{
u_int32_t *tl;
struct nfs_fattr *fp;
if (after_ret) {
tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
tl = nfsm_build(&info->nmi_mb, NFSX_UNSIGNED + NFSX_V3FATTR);
*tl++ = nfs_true;
fp = (struct nfs_fattr *)tl;
nfsm_srvfattr(nfsd, after_vap, fp);
}
}
void
nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
struct nfs_fattr *fp)
{
fp->fa_nlink = txdr_unsigned(vap->va_nlink);
fp->fa_uid = txdr_unsigned(vap->va_uid);
fp->fa_gid = txdr_unsigned(vap->va_gid);
if (nfsd->nd_flag & ND_NFSV3) {
fp->fa_type = vtonfsv3_type(vap->va_type);
fp->fa_mode = vtonfsv3_mode(vap->va_mode);
txdr_hyper(vap->va_size, &fp->fa3_size);
txdr_hyper(vap->va_bytes, &fp->fa3_used);
fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
fp->fa3_fsid.nfsuquad[0] = 0;
fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
txdr_hyper(vap->va_fileid, &fp->fa3_fileid);
txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
} else {
fp->fa_type = vtonfsv2_type(vap->va_type);
fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
fp->fa2_size = txdr_unsigned(vap->va_size);
fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
if (vap->va_type == VFIFO)
fp->fa2_rdev = 0xffffffff;
else
fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
fp->fa2_fileid = txdr_unsigned((u_int32_t)vap->va_fileid);
txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
}
}
int
nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
struct ucred *cred, struct nfssvc_sock *slp, struct mbuf *nam,
int *rdonlyp)
{
struct mount *mp;
int i;
struct ucred *credanon;
int error, exflags;
struct sockaddr_in *saddr;
*vpp = NULL;
mp = vfs_getvfs(&fhp->fh_fsid);
if (!mp)
return (ESTALE);
error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
if (error)
return (error);
error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
if (error)
return (error);
saddr = mtod(nam, struct sockaddr_in *);
if (saddr->sin_family == AF_INET &&
(ntohs(saddr->sin_port) >= IPPORT_RESERVED ||
(slp->ns_so->so_type == SOCK_STREAM && ntohs(saddr->sin_port) == 20))) {
vput(*vpp);
return (NFSERR_AUTHERR | AUTH_TOOWEAK);
}
if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
cred->cr_uid = credanon->cr_uid;
cred->cr_gid = credanon->cr_gid;
for (i = 0; i < credanon->cr_ngroups && i < NGROUPS_MAX; i++)
cred->cr_groups[i] = credanon->cr_groups[i];
cred->cr_ngroups = i;
}
if (exflags & MNT_EXRDONLY)
*rdonlyp = 1;
else
*rdonlyp = 0;
if (!lockflag)
VOP_UNLOCK(*vpp);
return (0);
}
int
netaddr_match(int family, union nethostaddr *haddr, struct mbuf *nam)
{
struct sockaddr_in *inetaddr;
switch (family) {
case AF_INET:
inetaddr = mtod(nam, struct sockaddr_in *);
if (inetaddr->sin_family == AF_INET &&
inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
return (1);
break;
default:
break;
}
return (0);
}
int
nfsm_srvsattr(struct mbuf **mp, struct vattr *va, struct mbuf *mrep,
caddr_t *dposp)
{
struct nfsm_info info;
int error = 0;
uint32_t *tl;
info.nmi_md = *mp;
info.nmi_dpos = *dposp;
info.nmi_mrep = mrep;
info.nmi_errorp = &error;
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
if (*tl == nfs_true) {
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
va->va_mode = nfstov_mode(*tl);
}
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
if (*tl == nfs_true) {
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
va->va_uid = fxdr_unsigned(uid_t, *tl);
}
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
if (*tl == nfs_true) {
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
va->va_gid = fxdr_unsigned(gid_t, *tl);
}
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
if (*tl == nfs_true) {
tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
if (tl == NULL)
return error;
va->va_size = fxdr_hyper(tl);
}
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
va->va_vaflags |= VA_UTIMES_CHANGE;
va->va_vaflags &= ~VA_UTIMES_NULL;
tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
if (tl == NULL)
return error;
fxdr_nfsv3time(tl, &va->va_atime);
break;
case NFSV3SATTRTIME_TOSERVER:
va->va_vaflags |= VA_UTIMES_CHANGE;
getnanotime(&va->va_atime);
break;
}
tl = (uint32_t *)nfsm_dissect(&info, NFSX_UNSIGNED);
if (tl == NULL)
return error;
switch (fxdr_unsigned(int, *tl)) {
case NFSV3SATTRTIME_TOCLIENT:
va->va_vaflags |= VA_UTIMES_CHANGE;
va->va_vaflags &= ~VA_UTIMES_NULL;
tl = (uint32_t *)nfsm_dissect(&info, 2 * NFSX_UNSIGNED);
if (tl == NULL)
return error;
fxdr_nfsv3time(tl, &va->va_mtime);
break;
case NFSV3SATTRTIME_TOSERVER:
va->va_vaflags |= VA_UTIMES_CHANGE;
getnanotime(&va->va_mtime);
break;
}
*dposp = info.nmi_dpos;
*mp = info.nmi_md;
return 0;
}