Extend lookup handling for WebNFS. This means that nfs_namei deals

with full pathname lookups if a public filehandle is used, and that
it translates the '%' escapes (URL-style) in the same case. Also,
make nfsrv_fhtovp convert the public filehandle to the vp of the
publicly exported filesystem, as stored in the nfs_pub structure.
This commit is contained in:
fvdl 1997-06-24 23:36:02 +00:00
parent 6611bf92a5
commit 28e824c9b7
1 changed files with 186 additions and 32 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: nfs_subs.c,v 1.42 1997/05/08 16:20:35 mycroft Exp $ */
/* $NetBSD: nfs_subs.c,v 1.43 1997/06/24 23:36:02 fvdl Exp $ */
/*
* Copyright (c) 1989, 1993
@ -56,6 +56,7 @@
#include <sys/stat.h>
#include <sys/malloc.h>
#include <sys/time.h>
#include <sys/dirent.h>
#include <vm/vm.h>
@ -98,6 +99,7 @@ nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
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;
extern struct nfs_public nfs_pub;
/* NFS client/server stats. */
struct nfsstats nfsstats;
@ -536,6 +538,8 @@ extern u_long nfsnodehash;
LIST_HEAD(nfsnodehashhead, nfsnode);
int nfs_webnamei __P((struct nameidata *, struct vnode *, struct proc *));
/*
* Create the header for an rpc request packet
* The hsiz is the size of the rest of the nfs request header.
@ -1418,10 +1422,16 @@ nfs_getattrcache(vp, vaper)
#endif /* NFS */
/*
* Set up nameidata for a lookup() call and do it
* Set up nameidata for a lookup() call and do it.
*
* If pubflag is set, this call is done for a lookup operation on the
* public filehandle. In that case we allow crossing mountpoints and
* absolute pathnames. However, the caller is expected to check that
* the lookup result is within the public fs, and deny access if
* it is not.
*/
int
nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag, pubflag)
register struct nameidata *ndp;
fhandle_t *fhp;
int len;
@ -1431,13 +1441,15 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
caddr_t *dposp;
struct vnode **retdirp;
struct proc *p;
int kerbflag;
int kerbflag, pubflag;
{
register int i, rem;
register struct mbuf *md;
register char *fromcp, *tocp;
register char *fromcp, *tocp, *cp;
struct iovec aiov;
struct uio auio;
struct vnode *dp;
int error, rdonly;
int error, rdonly, linklen;
struct componentname *cnp = &ndp->ni_cnd;
*retdirp = (struct vnode *)0;
@ -1460,7 +1472,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
fromcp = mtod(md, caddr_t);
rem = md->m_len;
}
if (*fromcp == '\0' || *fromcp == '/') {
if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
error = EACCES;
goto out;
}
@ -1477,13 +1489,12 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
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;
/*
* Extract and set starting directory.
*/
error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
nam, &rdonly, kerbflag);
nam, &rdonly, kerbflag, pubflag);
if (error)
goto out;
if (dp->v_type != VDIR) {
@ -1491,40 +1502,158 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag)
error = ENOTDIR;
goto out;
}
VREF(dp);
*retdirp = dp;
ndp->ni_startdir = dp;
if (rdonly)
cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
else
cnp->cn_flags |= RDONLY;
*retdirp = dp;
if (pubflag) {
/*
* Oh joy. For WebNFS, handle those pesky '%' escapes,
* and the 'native path' indicator.
*/
MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
fromcp = cnp->cn_pnbuf;
tocp = cp;
if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
switch ((unsigned char)*fromcp) {
case WEBNFS_NATIVE_CHAR:
/*
* 'Native' path for us is the same
* as a path according to the NFS spec,
* just skip the escape char.
*/
fromcp++;
break;
/*
* More may be added in the future, range 0x80-0xff
*/
default:
error = EIO;
FREE(cp, M_NAMEI);
goto out;
}
}
/*
* Translate the '%' escapes, URL-style.
*/
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;
FREE(cp, M_NAMEI);
goto out;
}
} else
*tocp++ = *fromcp++;
}
*tocp = '\0';
FREE(cnp->cn_pnbuf, M_NAMEI);
cnp->cn_pnbuf = cp;
}
ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
ndp->ni_segflg = UIO_SYSSPACE;
if (pubflag) {
ndp->ni_rootdir = rootvnode;
ndp->ni_loopcnt = 0;
if (cnp->cn_pnbuf[0] == '/')
dp = rootvnode;
} else {
cnp->cn_flags |= NOCROSSMOUNT;
}
cnp->cn_proc = p;
VREF(dp);
for (;;) {
cnp->cn_nameptr = cnp->cn_pnbuf;
ndp->ni_startdir = dp;
/*
* And call lookup() to do the real work
*/
cnp->cn_proc = p;
error = lookup(ndp);
if (error)
goto out;
break;
/*
* Check for encountering a symbolic link
*/
if (cnp->cn_flags & ISSYMLINK) {
if ((cnp->cn_flags & ISSYMLINK) == 0) {
if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
cnp->cn_flags |= HASBUF;
return (0);
}
break;
} else {
if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
vput(ndp->ni_dvp);
else
VOP_UNLOCK(ndp->ni_dvp);
if (!pubflag) {
vrele(ndp->ni_dvp);
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
error = EINVAL;
break;
}
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
error = ELOOP;
break;
}
if (ndp->ni_pathlen > 1)
MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
else
cp = cnp->cn_pnbuf;
aiov.iov_base = cp;
aiov.iov_len = MAXPATHLEN;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_procp = (struct proc *)0;
auio.uio_resid = MAXPATHLEN;
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
if (error) {
badlink:
if (ndp->ni_pathlen > 1)
FREE(cp, M_NAMEI);
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
if (linklen == 0) {
error = ENOENT;
goto badlink;
}
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
error = ENAMETOOLONG;
goto badlink;
}
if (ndp->ni_pathlen > 1) {
bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
FREE(cnp->cn_pnbuf, M_NAMEI);
cnp->cn_pnbuf = cp;
} else
cnp->cn_pnbuf[linklen] = '\0';
ndp->ni_pathlen += linklen;
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
error = EINVAL;
goto out;
}
/*
* Check for saved name request
*/
if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
cnp->cn_flags |= HASBUF;
return (0);
dp = ndp->ni_dvp;
/*
* Check if root directory should replace current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
vrele(dp);
dp = ndp->ni_rootdir;
VREF(dp);
}
}
}
out:
FREE(cnp->cn_pnbuf, M_NAMEI);
return (error);
@ -1703,7 +1832,7 @@ nfsm_srvfattr(nfsd, vap, fp)
* - if not lockflag unlock it with VOP_UNLOCK()
*/
int
nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag, pubflag)
fhandle_t *fhp;
int lockflag;
struct vnode **vpp;
@ -1723,6 +1852,13 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
struct sockaddr_in *saddr;
*vpp = (struct vnode *)0;
if (nfs_ispublicfh(fhp)) {
if (!pubflag || !nfs_pub.np_valid)
return (ESTALE);
fhp = &nfs_pub.np_handle;
}
#ifdef Lite2_integrated
mp = vfs_getvfs(&fhp->fh_fsid);
#else
@ -1734,7 +1870,7 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
if (error)
return (error);
if (!(exflags & MNT_EXNORESPORT)) {
if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
saddr = mtod(nam, struct sockaddr_in *);
if (saddr->sin_family == AF_INET &&
ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
@ -1773,6 +1909,24 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag)
return (0);
}
/*
* WebNFS: check if a filehandle is a public filehandle. For v3, this
* means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
* transformed this to all zeroes in both cases, so check for it.
*/
int
nfs_ispublicfh(fhp)
fhandle_t *fhp;
{
char *cp = (char *)fhp;
int i;
for (i = 0; i < NFSX_V3FH; i++)
if (*cp++ != 0)
return (FALSE);
return (TRUE);
}
/*
* This function compares two net addresses by family and returns TRUE
* if they are the same host.