#ifndef lint static char rcs_id[] = {"$Header: nfs_server.c,v 3.1 86/10/22 14:54:51 tadl Exp $"}; #endif not lint /* * RCS Info * $Locker: $ */ /* NFSSRC porting nfssrc2 @(#)nfs_server.c 2.1 86/04/14 */ /* @(#)nfs_server.c 1.1 86/02/03 SMI */ /* * Copyright (c) 1986 by Sun Microsystems, Inc. */ #include "../h/param.h" #include "../h/systm.h" #include "../h/user.h" #include "../h/buf.h" #include "../h/vfs.h" #include "../h/vnode.h" #include "../h/pathname.h" #include "../h/uio.h" #include "../h/file.h" #include "../h/socketvar.h" #include "../h/errno.h" #include "../ufs/fsdir.h" #include "../netinet/in.h" #include "../rpc/types.h" #include "../rpc/auth.h" #include "../rpc/auth_unix.h" #include "../rpc/svc.h" #include "../rpc/xdr.h" #include "../nfs/nfs.h" #include "../h/mbuf.h" /* * rpc service program version range supported */ #define VERSIONMIN 2 #define VERSIONMAX 2 /* * Returns true iff filesystem for a given fsid is exported read-only */ #define rdonly(vp) (((vp)->v_vfsp->vfs_flag & VFS_EXPORTED) && \ ((vp)->v_vfsp->vfs_exflags & EX_RDONLY)) struct vnode *fhtovp(); struct file *getsock(); void svcerr_progvers(); void rfs_dispatch(); #ifdef NFSDEBUG extern int nfsdebug; #endif struct { int ncalls; /* number of calls received */ int nbadcalls; /* calls that failed */ int reqs[32]; /* count for each request */ } svstat; /* * NFS Server system call. * Does all of the work of running a NFS server. * sock is the fd of an open UDP socket. */ nfs_svc() { struct a { int sock; } *uap = (struct a *) u.u_ap; struct vnode *rdir; struct vnode *cdir; struct socket *so; struct file *fp; SVCXPRT *xprt; u_long vers; fp = getsock(uap->sock); if (fp == 0) { u.u_error = EBADF; return; } so = (struct socket *)fp->f_data; /* * Be sure that rdir (the server's root vnode) is set. * Save the current directory and restore it again when * the call terminates. rfs_lookup uses u.u_cdir for lookupname. */ rdir = u.u_rdir; cdir = u.u_cdir; if (u.u_rdir == (struct vnode *)0) { u.u_rdir = u.u_cdir; } xprt = svckudp_create(so, NFS_PORT); for (vers = VERSIONMIN; vers <= VERSIONMAX; vers++) { (void) svc_register(xprt, NFS_PROGRAM, vers, rfs_dispatch, FALSE); } if (setjmp(&u.u_qsave)) { for (vers = VERSIONMIN; vers <= VERSIONMAX; vers++) { svc_unregister(NFS_PROGRAM, vers); } SVC_DESTROY(xprt); u.u_error = EINTR; } else { svc_run(xprt); /* never returns */ } u.u_rdir = rdir; u.u_cdir = cdir; } /* * Get file handle system call. * Takes open file descriptor and returns a file handle for it. */ nfs_getfh() { register struct a { int fdes; fhandle_t *fhp; } *uap = (struct a *) u.u_ap; register struct file *fp; fhandle_t fh; struct vnode *vp; if (!suser()) { return; } fp = getf(uap->fdes); if (fp == NULL) { return; } vp = (struct vnode *)fp->f_data; u.u_error = makefh(&fh, vp); if (!u.u_error) { u.u_error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof(fh)); } return; } /* * These are the interface routines for the server side of the * Networked File System. See the NFS protocol specification * for a description of this interface. */ /* * Get file attributes. * Returns the current attributes of the file with the given fhandle. */ int rfs_getattr(fhp, ns) fhandle_t *fhp; register struct nfsattrstat *ns; { int error; register struct vnode *vp; struct vattr va; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_getattr fh %o %d\n", fhp->fh_fsid, fhp->fh_fno); #endif vp = fhtovp(fhp); if (vp == NULL) { ns->ns_status = NFSERR_STALE; return; } error = VOP_GETATTR(vp, &va, u.u_cred); if (!error) { vattr_to_nattr(&va, &ns->ns_attr); } ns->ns_status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_getattr: returning %d\n", error); #endif } /* * Set file attributes. * Sets the attributes of the file with the given fhandle. Returns * the new attributes. */ int rfs_setattr(args, ns) struct nfssaargs *args; register struct nfsattrstat *ns; { int error; register struct vnode *vp; struct vattr va; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_setattr fh %o %d\n", args->saa_fh.fh_fsid, args->saa_fh.fh_fno); #endif vp = fhtovp(&args->saa_fh); if (vp == NULL) { ns->ns_status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { sattr_to_vattr(&args->saa_sa, &va); error = VOP_SETATTR(vp, &va, u.u_cred); if (!error) { error = VOP_GETATTR(vp, &va, u.u_cred); if (!error) { vattr_to_nattr(&va, &ns->ns_attr); } } } ns->ns_status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_setattr: returning %d\n", error); #endif } /* * Directory lookup. * Returns an fhandle and file attributes for file name in a directory. */ int rfs_lookup(da, dr) struct nfsdiropargs *da; register struct nfsdiropres *dr; { int error; register struct vnode *dvp; struct vnode *vp; struct vattr va; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_lookup %s fh %o %d\n", da->da_name, da->da_fhandle.fh_fsid, da->da_fhandle.fh_fno); #endif dvp = fhtovp(&da->da_fhandle); if (dvp == NULL) { dr->dr_status = NFSERR_STALE; return; } /* * do lookup. */ error = VOP_LOOKUP(dvp, da->da_name, &vp, u.u_cred); if (error) { vp = (struct vnode *)0; } else { error = VOP_GETATTR(vp, &va, u.u_cred); if (!error) { vattr_to_nattr(&va, &dr->dr_attr); error = makefh(&dr->dr_fhandle, vp); } } dr->dr_status = puterrno(error); if (vp) { VN_RELE(vp); } VN_RELE(dvp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_lookup: returning %d\n", error); #endif } /* * Read symbolic link. * Returns the string in the symbolic link at the given fhandle. */ int rfs_readlink(fhp, rl) fhandle_t *fhp; register struct nfsrdlnres *rl; { int error; struct iovec iov; struct uio uio; struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_readlink fh %o %d\n", fhp->fh_fsid, fhp->fh_fno); #endif vp = fhtovp(fhp); if (vp == NULL) { rl->rl_status = NFSERR_STALE; return; } /* * Allocate data for pathname. This will be freed by rfs_rlfree. */ rl->rl_data = (char *)kmem_alloc((u_int)MAXPATHLEN); /* * Set up io vector to read sym link data */ iov.iov_base = rl->rl_data; iov.iov_len = NFS_MAXPATHLEN; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_offset = 0; uio.uio_resid = NFS_MAXPATHLEN; /* * read link */ error = VOP_READLINK(vp, &uio, u.u_cred); /* * Clean up */ if (error) { kmem_free((caddr_t)rl->rl_data, (u_int)NFS_MAXPATHLEN); rl->rl_count = 0; rl->rl_data = NULL; } else { rl->rl_count = NFS_MAXPATHLEN - uio.uio_resid; } rl->rl_status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_readlink: returning '%s' %d\n", rl->rl_data, error); #endif } /* * Free data allocated by rfs_readlink */ rfs_rlfree(rl) struct nfsrdlnres *rl; { if (rl->rl_data) { kmem_free((caddr_t)rl->rl_data, (u_int)NFS_MAXPATHLEN); } } /* * Read data. * Returns some data read from the file at the given fhandle. */ int rfs_read(ra, rr) struct nfsreadargs *ra; register struct nfsrdresult *rr; { int error; struct vnode *vp; struct vattr va; struct iovec iov; struct uio uio; int offset, fsbsize; struct buf *bp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_read %d from fh %o %d\n", ra->ra_count, ra->ra_fhandle.fh_fsid, ra->ra_fhandle.fh_fno); #endif rr->rr_data = NULL; rr->rr_count = 0; vp = fhtovp(&ra->ra_fhandle); if (vp == NULL) { rr->rr_status = NFSERR_STALE; return; } error = VOP_GETATTR(vp, &va, u.u_cred); if (error) { goto bad; } /* * This is a kludge to allow reading of files created * with no read permission. The owner of the file * is always allowed to read it. */ if (u.u_uid != va.va_uid) { error = VOP_ACCESS(vp, VREAD, u.u_cred); if (error) { /* * Exec is the same as read over the net because * of demand loading. */ error = VOP_ACCESS(vp, VEXEC, u.u_cred); } if (error) { goto bad; } } /* * Check whether we can do this with bread, which would * save the copy through the uio. */ fsbsize = vp->v_vfsp->vfs_bsize; offset = ra->ra_offset % fsbsize; if (offset + ra->ra_count <= fsbsize) { if (ra->ra_offset >= va.va_size) { rr->rr_count = 0; vattr_to_nattr(&va, &rr->rr_attr); goto done; } error = VOP_BREAD(vp, ra->ra_offset / fsbsize, &bp); if (error == 0) { rr->rr_data = bp->b_un.b_addr + offset; rr->rr_count = min( (u_int)(va.va_size - ra->ra_offset), (u_int)ra->ra_count); rr->rr_bp = bp; rr->rr_vp = vp; VN_HOLD(vp); vattr_to_nattr(&va, &rr->rr_attr); goto done; } else { printf("nfs read: failed, errno %d\n", error); } } rr->rr_bp = (struct buf *) 0; /* * Allocate space for data. This will be freed by xdr_rdresult * when it is called with x_op = XDR_FREE. */ rr->rr_data = (char *)kmem_alloc((u_int)ra->ra_count); /* * Set up io vector */ iov.iov_base = rr->rr_data; iov.iov_len = ra->ra_count; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_offset = ra->ra_offset; uio.uio_resid = ra->ra_count; /* * for now we assume no append mode and ignore * totcount (read ahead) */ error = VOP_RDWR(vp, &uio, UIO_READ, IO_SYNC, u.u_cred); if (error) { goto bad; } vattr_to_nattr(&va, &rr->rr_attr); rr->rr_count = ra->ra_count - uio.uio_resid; /* * free the unused part of the data allocated */ if (uio.uio_resid) { kmem_free((caddr_t)(rr->rr_data + rr->rr_count), (u_int)uio.uio_resid); } bad: if (error && rr->rr_data != NULL) { kmem_free((caddr_t)rr->rr_data, (u_int)ra->ra_count); rr->rr_data = NULL; rr->rr_count = 0; } done: rr->rr_status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_read returning %d, count = %d\n", error, rr->rr_count); #endif } /* * Free data allocated by rfs_read. */ rfs_rdfree(rr) struct nfsrdresult *rr; { if (rr->rr_bp == 0 && rr->rr_data) { kmem_free((caddr_t)rr->rr_data, (u_int)rr->rr_count); } } /* * Write data to file. * Returns attributes of a file after writing some data to it. */ int rfs_write(wa, ns) struct nfswriteargs *wa; struct nfsattrstat *ns; { register int error; register struct vnode *vp; struct vattr va; struct iovec iov; struct uio uio; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_write: %d bytes fh %o %d\n", wa->wa_count, wa->wa_fhandle.fh_fsid, wa->wa_fhandle.fh_fno); #endif vp = fhtovp(&wa->wa_fhandle); if (vp == NULL) { ns->ns_status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { error = VOP_GETATTR(vp, &va, u.u_cred); } if (!error) { if (u.u_uid != va.va_uid) { /* * This is a kludge to allow writes of files created * with read only permission. The owner of the file * is always allowed to write it. */ error = VOP_ACCESS(vp, VWRITE, u.u_cred); } if (!error) { iov.iov_base = wa->wa_data; iov.iov_len = wa->wa_count; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_offset = wa->wa_offset; uio.uio_resid = wa->wa_count; /* * for now we assume no append mode */ error = VOP_RDWR(vp, &uio, UIO_WRITE, IO_SYNC, u.u_cred); } } if (!error) { /* * Get attributes again so we send the latest mod * time to the client side for his cache. */ error = VOP_GETATTR(vp, &va, u.u_cred); } ns->ns_status = puterrno(error); if (!error) { vattr_to_nattr(&va, &ns->ns_attr); } VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_write: returning %d\n", error); #endif } /* * Create a file. * Creates a file with given attributes and returns those attributes * and an fhandle for the new file. */ int rfs_create(args, dr, req) struct nfscreatargs *args; struct nfsdiropres *dr; struct svc_req *req; { register int error; struct vattr va; struct vnode *vp; register struct vnode *dvp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_create: %s dfh %o %d\n", args->ca_da.da_name, args->ca_da.da_fhandle.fh_fsid, args->ca_da.da_fhandle.fh_fno); #endif sattr_to_vattr(&args->ca_sa, &va); va.va_type = VREG; /* * XXX Should get exclusive flag and use it. */ dvp = fhtovp(&args->ca_da.da_fhandle); if (dvp == NULL) { dr->dr_status = NFSERR_STALE; return; } if (rdonly(dvp)) { error = EROFS; } else { error = VOP_CREATE(dvp, args->ca_da.da_name, &va, NONEXCL, VWRITE, &vp, u.u_cred); if (error == EEXIST) { /* * check for dup request */ if (svckudp_dup(req)) { error = VOP_LOOKUP(dvp, args->ca_da.da_name, &vp, u.u_cred); } } } if (!error) { error = VOP_GETATTR(vp, &va, u.u_cred); if (!error) { vattr_to_nattr(&va, &dr->dr_attr); error = makefh(&dr->dr_fhandle, vp); } VN_RELE(vp); } dr->dr_status = puterrno(error); VN_RELE(dvp); if (!error) { svckudp_dupsave(req); } #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_create: returning %d\n", error); #endif } /* * Remove a file. * Remove named file from parent directory. */ int rfs_remove(da, status, req) struct nfsdiropargs *da; enum nfsstat *status; struct svc_req *req; { int error; register struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_remove %s dfh %o %d\n", da->da_name, da->da_fhandle.fh_fsid, da->da_fhandle.fh_fno); #endif vp = fhtovp(&da->da_fhandle); if (vp == NULL) { *status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { error = VOP_REMOVE(vp, da->da_name, u.u_cred); if (error == ENOENT) { /* * check for dup request */ if (svckudp_dup(req)) { error = 0; } } } *status = puterrno(error); VN_RELE(vp); if (!error) { svckudp_dupsave(req); } #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_remove: %s returning %d\n", da->da_name, error); #endif } /* * rename a file * Give a file (from) a new name (to). */ int rfs_rename(args, status) struct nfsrnmargs *args; enum nfsstat *status; { int error; register struct vnode *fromvp; register struct vnode *tovp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_rename %s ffh %o %d -> %s tfh %o %d\n", args->rna_from.da_name, args->rna_from.da_fhandle.fh_fsid, args->rna_from.da_fhandle.fh_fno, args->rna_to.da_name, args->rna_to.da_fhandle.fh_fsid, args->rna_to.da_fhandle.fh_fno); #endif fromvp = fhtovp(&args->rna_from.da_fhandle); if (fromvp == NULL) { *status = NFSERR_STALE; return; } if (rdonly(fromvp)) { error = EROFS; goto fromerr; } tovp = fhtovp(&args->rna_to.da_fhandle); if (tovp == NULL) { *status = NFSERR_STALE; VN_RELE(fromvp); return; } if (rdonly(tovp)) { error = EROFS; } else { error = VOP_RENAME(fromvp, args->rna_from.da_name, tovp, args->rna_to.da_name, u.u_cred); } VN_RELE(tovp); fromerr: VN_RELE(fromvp); *status = puterrno(error); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_rename: returning %d\n", error); #endif } /* * Link to a file. * Create a file (to) which is a hard link to the given file (from). */ int rfs_link(args, status, req) struct nfslinkargs *args; enum nfsstat *status; struct svc_req *req; { int error; register struct vnode *fromvp; register struct vnode *tovp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_link ffh %o %d -> %s tfh %o %d\n", args->la_from.fh_fsid, args->la_from.fh_fno, args->la_to.da_name, args->la_to.da_fhandle.fh_fsid, args->la_to.da_fhandle.fh_fno); #endif fromvp = fhtovp(&args->la_from); if (fromvp == NULL) { *status = NFSERR_STALE; return; } tovp = fhtovp(&args->la_to.da_fhandle); if (tovp == NULL) { *status = NFSERR_STALE; VN_RELE(fromvp); return; } if (rdonly(tovp)) { error = EROFS; } else { error = VOP_LINK(fromvp, tovp, args->la_to.da_name, u.u_cred); if (error == EEXIST) { /* * check for dup request */ if (svckudp_dup(req)) { error = 0; } } } *status = puterrno(error); VN_RELE(fromvp); VN_RELE(tovp); if (!error) { svckudp_dupsave(req); } #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_link: returning %d\n", error); #endif } /* * Symbolicly link to a file. * Create a file (to) with the given attributes which is a symbolic link * to the given path name (to). */ int rfs_symlink(args, status) struct nfsslargs *args; enum nfsstat *status; { int error; struct vattr va; register struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_symlink %s ffh %o %d -> %s\n", args->sla_from.da_name, args->sla_from.da_fhandle.fh_fsid, args->sla_from.da_fhandle.fh_fno, args->sla_tnm); #endif sattr_to_vattr(&args->sla_sa, &va); va.va_type = VLNK; vp = fhtovp(&args->sla_from.da_fhandle); if (vp == NULL) { *status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { error = VOP_SYMLINK(vp, args->sla_from.da_name, &va, args->sla_tnm, u.u_cred); } *status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_symlink: returning %d\n", error); #endif } /* * Make a directory. * Create a directory with the given name, parent directory, and attributes. * Returns a file handle and attributes for the new directory. */ int rfs_mkdir(args, dr, req) struct nfscreatargs *args; struct nfsdiropres *dr; struct svc_req *req; { int error; struct vattr va; struct vnode *dvp; register struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_mkdir %s fh %o %d\n", args->ca_da.da_name, args->ca_da.da_fhandle.fh_fsid, args->ca_da.da_fhandle.fh_fno); #endif sattr_to_vattr(&args->ca_sa, &va); va.va_type = VDIR; /* * Should get exclusive flag and pass it on here */ vp = fhtovp(&args->ca_da.da_fhandle); if (vp == NULL) { dr->dr_status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { error = VOP_MKDIR(vp, args->ca_da.da_name, &va, &dvp, u.u_cred); if (error == EEXIST) { /* * check for dup request */ if (svckudp_dup(req)) { error = VOP_LOOKUP(vp, args->ca_da.da_name, &dvp, u.u_cred); if (!error) { error = VOP_GETATTR(vp, &va, u.u_cred); } } } } if (!error) { vattr_to_nattr(&va, &dr->dr_attr); error = makefh(&dr->dr_fhandle, dvp); VN_RELE(dvp); } dr->dr_status = puterrno(error); VN_RELE(vp); if (!error) { svckudp_dupsave(req); } #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_mkdir: returning %d\n", error); #endif } /* * Remove a directory. * Remove the given directory name from the given parent directory. */ int rfs_rmdir(da, status, req) struct nfsdiropargs *da; enum nfsstat *status; struct svc_req *req; { int error; register struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_rmdir %s fh %o %d\n", da->da_name, da->da_fhandle.fh_fsid, da->da_fhandle.fh_fno); #endif vp = fhtovp(&da->da_fhandle); if (vp == NULL) { *status = NFSERR_STALE; return; } if (rdonly(vp)) { error = EROFS; } else { error = VOP_RMDIR(vp, da->da_name, u.u_cred); if (error == ENOENT) { /* * check for dup request */ if (svckudp_dup(req)) { error = 0; } } } *status = puterrno(error); VN_RELE(vp); if (!error) { svckudp_dupsave(req); } #ifdef NFSDEBUG dprint(nfsdebug, 5, "nfs_rmdir returning %d\n", error); #endif } int rfs_readdir(rda, rd) struct nfsrddirargs *rda; register struct nfsrddirres *rd; { int error; u_long offset; u_long skipped; u_long bufsize; struct iovec iov; struct uio uio; register struct vnode *vp; struct direct *dp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_readdir fh %o %d count %d\n", rda->rda_fh.fh_fsid, rda->rda_fh.fh_fno, rda->rda_count); #endif vp = fhtovp(&rda->rda_fh); if (vp == NULL) { rd->rd_status = NFSERR_STALE; return; } /* * check cd access to dir. we have to do this here because * the opendir doesn't go over the wire. */ error = VOP_ACCESS(vp, VEXEC, u.u_cred); if (error) { goto bad; } nxtblk: bufsize = max(DIRBLKSIZ,rda->rda_count); /* * Allocate data for entries. This will be freed by rfs_rdfree. */ rd->rd_entries = (struct direct *)kmem_alloc((u_int)bufsize); rd->rd_bufsize = bufsize; rd->rd_offset = offset = rda->rda_offset & ~(DIRBLKSIZ -1); /* * Set up io vector to read directory data */ iov.iov_base = (caddr_t)rd->rd_entries; iov.iov_len = bufsize; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_offset = offset; uio.uio_resid = bufsize; /* * read directory */ error = VOP_READDIR(vp, &uio, u.u_cred); /* * Clean up */ if (error) { kmem_free((caddr_t)rd->rd_entries, (u_int)rd->rd_bufsize); rd->rd_bufsize = 0; rd->rd_size = 0; goto bad; } /* * set size and eof */ if (uio.uio_resid) { rd->rd_size = bufsize - uio.uio_resid; rd->rd_eof = TRUE; } else { rd->rd_size = bufsize; rd->rd_eof = FALSE; } /* * if client request was in the middle of a block * or block begins with null entries skip entries * til we are on a valid entry >= client's requested * offset. */ dp = rd->rd_entries; skipped = 0; while ((skipped < rd->rd_size) && ((offset + dp->d_reclen <= rda->rda_offset) || (dp->d_ino == 0))) { skipped += dp->d_reclen; offset += dp->d_reclen; dp = (struct direct *)((int)dp + dp->d_reclen); } /* * Reset entries pointer and free space we are skipping */ if (skipped) { rd->rd_size -= skipped; rd->rd_bufsize -= skipped; rd->rd_offset = offset; kmem_free((caddr_t)rd->rd_entries, (u_int)skipped); rd->rd_entries = (struct direct *) ((int)rd->rd_entries + skipped); if (rd->rd_size == 0 && !rd->rd_eof) { /* * we have skipped a whole block, reset offset * and read another block (unless eof) */ rda->rda_offset = rd->rd_offset; goto nxtblk; } } bad: rd->rd_status = puterrno(error); VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_readdir: returning %d\n", error); #endif } rfs_rddirfree(rd) struct nfsrddirres *rd; { kmem_free((caddr_t)rd->rd_entries, (u_int)rd->rd_bufsize); } rfs_statfs(fh, fs) fhandle_t *fh; register struct nfsstatfs *fs; { int error; struct statfs sb; register struct vnode *vp; #ifdef NFSDEBUG dprint(nfsdebug, 4, "rfs_statfs fh %o %d\n", fh->fh_fsid, fh->fh_fno); #endif vp = fhtovp(fh); if (vp == NULL) { fs->fs_status = NFSERR_STALE; return; } error = VFS_STATFS(vp->v_vfsp, &sb); fs->fs_status = puterrno(error); if (!error) { fs->fs_tsize = nfstsize(); fs->fs_bsize = sb.f_bsize; fs->fs_blocks = sb.f_blocks; fs->fs_bfree = sb.f_bfree; fs->fs_bavail = sb.f_bavail; } VN_RELE(vp); #ifdef NFSDEBUG dprint(nfsdebug, 5, "rfs_statfs returning %d\n", error); #endif } /*ARGSUSED*/ rfs_null(argp, resp) caddr_t *argp; caddr_t *resp; { /* do nothing */ return (0); } /*ARGSUSED*/ rfs_error(argp, resp) caddr_t *argp; caddr_t *resp; { return (EOPNOTSUPP); } int nullfree() { } /* * rfs dispatch table * Indexed by version,proc */ struct rfsdisp { int (*dis_proc)(); /* proc to call */ xdrproc_t dis_xdrargs; /* xdr routine to get args */ int dis_argsz; /* sizeof args */ xdrproc_t dis_xdrres; /* xdr routine to put results */ int dis_ressz; /* size of results */ int (*dis_resfree)(); /* frees space allocated by proc */ } rfsdisptab[][RFS_NPROC] = { { /* * VERSION 2 * Changed rddirres to have eof at end instead of beginning */ /* RFS_NULL = 0 */ {rfs_null, xdr_void, 0, xdr_void, 0, nullfree}, /* RFS_GETATTR = 1 */ {rfs_getattr, xdr_fhandle, sizeof(fhandle_t), xdr_attrstat, sizeof(struct nfsattrstat), nullfree}, /* RFS_SETATTR = 2 */ {rfs_setattr, xdr_saargs, sizeof(struct nfssaargs), xdr_attrstat, sizeof(struct nfsattrstat), nullfree}, /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ {rfs_error, xdr_void, 0, xdr_void, 0, nullfree}, /* RFS_LOOKUP = 4 */ {rfs_lookup, xdr_diropargs, sizeof(struct nfsdiropargs), xdr_diropres, sizeof(struct nfsdiropres), nullfree}, /* RFS_READLINK = 5 */ {rfs_readlink, xdr_fhandle, sizeof(fhandle_t), xdr_rdlnres, sizeof(struct nfsrdlnres), rfs_rlfree}, /* RFS_READ = 6 */ {rfs_read, xdr_readargs, sizeof(struct nfsreadargs), xdr_rdresult, sizeof(struct nfsrdresult), rfs_rdfree}, /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ {rfs_error, xdr_void, 0, xdr_void, 0, nullfree}, /* RFS_WRITE = 8 */ {rfs_write, xdr_writeargs, sizeof(struct nfswriteargs), xdr_attrstat, sizeof(struct nfsattrstat), nullfree}, /* RFS_CREATE = 9 */ {rfs_create, xdr_creatargs, sizeof(struct nfscreatargs), xdr_diropres, sizeof(struct nfsdiropres), nullfree}, /* RFS_REMOVE = 10 */ {rfs_remove, xdr_diropargs, sizeof(struct nfsdiropargs), xdr_enum, sizeof(enum nfsstat), nullfree}, /* RFS_RENAME = 11 */ {rfs_rename, xdr_rnmargs, sizeof(struct nfsrnmargs), xdr_enum, sizeof(enum nfsstat), nullfree}, /* RFS_LINK = 12 */ {rfs_link, xdr_linkargs, sizeof(struct nfslinkargs), xdr_enum, sizeof(enum nfsstat), nullfree}, /* RFS_SYMLINK = 13 */ {rfs_symlink, xdr_slargs, sizeof(struct nfsslargs), xdr_enum, sizeof(enum nfsstat), nullfree}, /* RFS_MKDIR = 14 */ {rfs_mkdir, xdr_creatargs, sizeof(struct nfscreatargs), xdr_diropres, sizeof(struct nfsdiropres), nullfree}, /* RFS_RMDIR = 15 */ {rfs_rmdir, xdr_diropargs, sizeof(struct nfsdiropargs), xdr_enum, sizeof(enum nfsstat), nullfree}, /* RFS_READDIR = 16 */ {rfs_readdir, xdr_rddirargs, sizeof(struct nfsrddirargs), xdr_putrddirres, sizeof(struct nfsrddirres), rfs_rddirfree}, /* RFS_STATFS = 17 */ {rfs_statfs, xdr_fhandle, sizeof(fhandle_t), xdr_statfs, sizeof(struct nfsstatfs), nullfree}, } }; struct rfsspace { struct rfsspace *rs_next; caddr_t rs_dummy; }; struct rfsspace *rfsfreesp = NULL; int rfssize = 0; caddr_t rfsget() { int i; struct rfsdisp *dis; caddr_t ret; if (rfssize == 0) { for (i = 0; i < 1 + VERSIONMAX - VERSIONMIN; i++) { for (dis = &rfsdisptab[i][0]; dis < &rfsdisptab[i][RFS_NPROC]; dis++) { rfssize = MAX(rfssize, dis->dis_argsz); rfssize = MAX(rfssize, dis->dis_ressz); } } } if (rfsfreesp) { ret = (caddr_t)rfsfreesp; rfsfreesp = rfsfreesp->rs_next; } else { ret = (caddr_t)kmem_alloc((u_int)rfssize); } return (ret); } rfsput(rs) struct rfsspace *rs; { rs->rs_next = rfsfreesp; rfsfreesp = rs; } int nobody = 32767; /* * If nfs_portmon is set, then clients are required to use * privileged ports (ports < IPPORT_RESERVED) in order to get NFS services. */ int nfs_portmon = 0; void rfs_dispatch(req, xprt) struct svc_req *req; register SVCXPRT *xprt; { int which; int vers; caddr_t *args = NULL; caddr_t *res = NULL; register struct rfsdisp *disp; struct authunix_parms *aup; gid_t *gp; struct ucred *tmpcr; struct ucred *newcr = NULL; int error; svstat.ncalls++; error = 0; which = req->rq_proc; if (which < 0 || which >= RFS_NPROC) { #ifdef NFSDEBUG dprint(nfsdebug, 2, "rfs_dispatch: bad proc %d\n", which); #endif svcerr_noproc(req->rq_xprt); error++; goto done; } vers = req->rq_vers; if (vers < VERSIONMIN || vers > VERSIONMAX) { #ifdef NFSDEBUG dprint(nfsdebug, 2, "rfs_dispatch: bad vers %d low %d high %d\n", vers, VERSIONMIN, VERSIONMAX); #endif svcerr_progvers(req->rq_xprt, (u_long)VERSIONMIN, (u_long)VERSIONMAX); error++; goto done; } vers -= VERSIONMIN; disp = &rfsdisptab[vers][which]; /* * Clean up as if a system call just started */ u.u_error = 0; /* * Allocate args struct and deserialize into it. */ args = (caddr_t *)rfsget(); bzero((caddr_t)args, rfssize); if ( ! SVC_GETARGS(xprt, disp->dis_xdrargs, args)) { svcerr_decode(xprt); error++; goto done; } /* * Check for unix style credentials */ if (req->rq_cred.oa_flavor != AUTH_UNIX && which != RFS_NULL) { svcerr_weakauth(xprt); error++; goto done; } if (nfs_portmon) { /* * Check for privileged port number */ static count = 0; if (ntohs(xprt->xp_raddr.sin_port) >= IPPORT_RESERVED) { svcerr_weakauth(xprt); if (count == 0) { char *p = (char *)(&xprt->xp_raddr.sin_addr); printf("NFS request from unprivileged port, "); printf("source IP address = %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]); } count++; count %= 256; error++; goto done; } } /* * Set uid, gid, and gids to auth params */ if (which != RFS_NULL) { aup = (struct authunix_parms *)req->rq_clntcred; newcr = crget(); if (aup->aup_uid == 0) { /* * root over the net becomes other on the server (uid 32767) */ newcr->cr_uid = nobody; } else { newcr->cr_uid = aup->aup_uid; } newcr->cr_gid = aup->aup_gid; bcopy((caddr_t)aup->aup_gids, (caddr_t)newcr->cr_groups, aup->aup_len * sizeof(newcr->cr_groups[0])); for (gp = &newcr->cr_groups[aup->aup_len]; gp < &newcr->cr_groups[NGROUPS]; gp++) { *gp = NOGROUP; } tmpcr = u.u_cred; u.u_cred = newcr; } /* * Allocate results struct. */ res = (caddr_t *)rfsget(); bzero((caddr_t)res, rfssize); svstat.reqs[which]++; /* * Call service routine with arg struct and results struct */ (*disp->dis_proc)(args, res, req); done: /* * Free arguments struct */ if (!SVC_FREEARGS(xprt, disp->dis_xdrargs, args) ) { error++; } if (args != NULL) { rfsput((struct rfsspace *)args); } /* * Serialize and send results struct */ if (!error) { if (!svc_sendreply(xprt, disp->dis_xdrres, (caddr_t)res)) { error++; } } /* * Free results struct */ if (res != NULL) { if ( disp->dis_resfree != nullfree ) { (*disp->dis_resfree)(res); } rfsput((struct rfsspace *)res); } /* * restore original credentials */ if (newcr) { u.u_cred = tmpcr; crfree(newcr); } svstat.nbadcalls += error; }