#include "opt_inet.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/resourcevar.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/namei.h>
#include <sys/nlookup.h>
#include <sys/socket.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/conf.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <sys/buf2.h>
#include <vfs/fifofs/fifo.h>
#include <vfs/ufs/dir.h>
#undef DIRBLKSIZ
#include "rpcv2.h"
#include "nfsproto.h"
#include "nfs.h"
#include "nfsmount.h"
#include "nfsnode.h"
#include "xdr_subs.h"
#include "nfsm_subs.h"
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#define TRUE 1
#define FALSE 0
static int nfsfifo_read (struct vop_read_args *);
static int nfsfifo_write (struct vop_write_args *);
static int nfsfifo_close (struct vop_close_args *);
static int nfs_setattrrpc (struct vnode *,struct vattr *,struct ucred *,struct thread *);
static int nfs_lookup (struct vop_old_lookup_args *);
static int nfs_create (struct vop_old_create_args *);
static int nfs_mknod (struct vop_old_mknod_args *);
static int nfs_open (struct vop_open_args *);
static int nfs_close (struct vop_close_args *);
static int nfs_access (struct vop_access_args *);
static int nfs_getattr (struct vop_getattr_args *);
static int nfs_setattr (struct vop_setattr_args *);
static int nfs_read (struct vop_read_args *);
static int nfs_fsync (struct vop_fsync_args *);
static int nfs_remove (struct vop_old_remove_args *);
static int nfs_link (struct vop_old_link_args *);
static int nfs_rename (struct vop_old_rename_args *);
static int nfs_mkdir (struct vop_old_mkdir_args *);
static int nfs_rmdir (struct vop_old_rmdir_args *);
static int nfs_symlink (struct vop_old_symlink_args *);
static int nfs_readdir (struct vop_readdir_args *);
static int nfs_bmap (struct vop_bmap_args *);
static int nfs_strategy (struct vop_strategy_args *);
static int nfs_lookitup (struct vnode *, const char *, int,
struct ucred *, struct thread *, struct nfsnode **);
static int nfs_sillyrename (struct vnode *,struct vnode *,struct componentname *);
static int nfs_laccess (struct vop_access_args *);
static int nfs_readlink (struct vop_readlink_args *);
static int nfs_print (struct vop_print_args *);
static int nfs_advlock (struct vop_advlock_args *);
static int nfs_kqfilter (struct vop_kqfilter_args *ap);
static int nfs_nresolve (struct vop_nresolve_args *);
struct vop_ops nfsv2_vnode_vops = {
.vop_default = vop_defaultop,
.vop_access = nfs_access,
.vop_advlock = nfs_advlock,
.vop_bmap = nfs_bmap,
.vop_close = nfs_close,
.vop_old_create = nfs_create,
.vop_fsync = nfs_fsync,
.vop_getattr = nfs_getattr,
.vop_getpages = vop_stdgetpages,
.vop_putpages = vop_stdputpages,
.vop_inactive = nfs_inactive,
.vop_old_link = nfs_link,
.vop_old_lookup = nfs_lookup,
.vop_old_mkdir = nfs_mkdir,
.vop_old_mknod = nfs_mknod,
.vop_open = nfs_open,
.vop_print = nfs_print,
.vop_read = nfs_read,
.vop_readdir = nfs_readdir,
.vop_readlink = nfs_readlink,
.vop_reclaim = nfs_reclaim,
.vop_old_remove = nfs_remove,
.vop_old_rename = nfs_rename,
.vop_old_rmdir = nfs_rmdir,
.vop_setattr = nfs_setattr,
.vop_strategy = nfs_strategy,
.vop_old_symlink = nfs_symlink,
.vop_write = nfs_write,
.vop_nresolve = nfs_nresolve,
.vop_kqfilter = nfs_kqfilter
};
struct vop_ops nfsv2_spec_vops = {
.vop_default = vop_defaultop,
.vop_access = nfs_laccess,
.vop_close = nfs_close,
.vop_fsync = nfs_fsync,
.vop_getattr = nfs_getattr,
.vop_inactive = nfs_inactive,
.vop_print = nfs_print,
.vop_read = vop_stdnoread,
.vop_reclaim = nfs_reclaim,
.vop_setattr = nfs_setattr,
.vop_write = vop_stdnowrite
};
struct vop_ops nfsv2_fifo_vops = {
.vop_default = fifo_vnoperate,
.vop_access = nfs_laccess,
.vop_close = nfsfifo_close,
.vop_fsync = nfs_fsync,
.vop_getattr = nfs_getattr,
.vop_inactive = nfs_inactive,
.vop_print = nfs_print,
.vop_read = nfsfifo_read,
.vop_reclaim = nfs_reclaim,
.vop_setattr = nfs_setattr,
.vop_write = nfsfifo_write
};
static int nfs_mknodrpc (struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp,
struct vattr *vap);
static int nfs_removerpc (struct vnode *dvp, const char *name,
int namelen,
struct ucred *cred, struct thread *td);
static int nfs_renamerpc (struct vnode *fdvp, const char *fnameptr,
int fnamelen, struct vnode *tdvp,
const char *tnameptr, int tnamelen,
struct ucred *cred, struct thread *td);
static int nfs_renameit (struct vnode *sdvp,
struct componentname *scnp,
struct sillyrename *sp);
SYSCTL_DECL(_vfs_nfs);
static int nfs_flush_on_rename = 1;
SYSCTL_INT(_vfs_nfs, OID_AUTO, flush_on_rename, CTLFLAG_RW,
&nfs_flush_on_rename, 0, "flush fvp prior to rename");
static int nfs_flush_on_hlink = 0;
SYSCTL_INT(_vfs_nfs, OID_AUTO, flush_on_hlink, CTLFLAG_RW,
&nfs_flush_on_hlink, 0, "flush fvp prior to hard link");
static int nfsaccess_cache_timeout = NFS_DEFATTRTIMO;
SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW,
&nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
static int nfsneg_cache_timeout = NFS_MINATTRTIMO;
SYSCTL_INT(_vfs_nfs, OID_AUTO, neg_cache_timeout, CTLFLAG_RW,
&nfsneg_cache_timeout, 0, "NFS NEGATIVE NAMECACHE timeout");
static int nfspos_cache_timeout = NFS_MINATTRTIMO;
SYSCTL_INT(_vfs_nfs, OID_AUTO, pos_cache_timeout, CTLFLAG_RW,
&nfspos_cache_timeout, 0, "NFS POSITIVE NAMECACHE timeout");
static int nfsv3_commit_on_close = 0;
SYSCTL_INT(_vfs_nfs, OID_AUTO, nfsv3_commit_on_close, CTLFLAG_RW,
&nfsv3_commit_on_close, 0, "write+commit on close, else only write");
#if 0
SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_hits, CTLFLAG_RD,
&nfsstats.accesscache_hits, 0, "NFS ACCESS cache hit count");
SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_misses, CTLFLAG_RD,
&nfsstats.accesscache_misses, 0, "NFS ACCESS cache miss count");
#endif
#define NFSV3ACCESS_ALL (NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY \
| NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE \
| NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
static __inline
void
nfs_knote(struct vnode *vp, int flags)
{
if (flags)
KNOTE(&vp->v_pollinfo.vpi_kqinfo.ki_note, flags);
}
static __inline
int
nlcdegenerate(struct nlcomponent *nlc)
{
if (nlc->nlc_namelen == 1 && nlc->nlc_nameptr[0] == '.')
return(1);
if (nlc->nlc_namelen == 2 &&
nlc->nlc_nameptr[0] == '.' && nlc->nlc_nameptr[1] == '.')
return(1);
return(0);
}
static int
nfs3_access_otw(struct vnode *vp, int wmode,
struct thread *td, struct ucred *cred)
{
struct nfsnode *np = VTONFS(vp);
int attrflag;
int error = 0;
u_int32_t *tl;
u_int32_t rmode;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = 1;
nfsstats.rpccnt[NFSPROC_ACCESS]++;
nfsm_reqhead(&info, vp, NFSPROC_ACCESS,
NFSX_FH(info.v3) + NFSX_UNSIGNED);
ERROROUT(nfsm_fhtom(&info, vp));
tl = nfsm_build(&info, NFSX_UNSIGNED);
*tl = txdr_unsigned(wmode);
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_ACCESS, td, cred, &error));
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag, NFS_LATTR_NOSHRINK));
if (error == 0) {
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
rmode = fxdr_unsigned(u_int32_t, *tl);
np->n_mode = rmode;
np->n_modeuid = cred->cr_uid;
np->n_modestamp = mycpu->gd_time_seconds;
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
return error;
}
static int
nfs_access(struct vop_access_args *ap)
{
struct ucred *cred;
struct ucred *ncred;
struct ucred *ocred;
struct vnode *vp = ap->a_vp;
thread_t td = curthread;
int error = 0;
u_int32_t mode, wmode;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int v3 = NFS_ISV3(vp);
lwkt_gettoken(&nmp->nm_token);
if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
lwkt_reltoken(&nmp->nm_token);
return (EROFS);
default:
break;
}
}
if (ap->a_flags & AT_EACCESS) {
cred = crhold(ap->a_cred);
} else {
cred = ap->a_cred;
if (cred->cr_uid == cred->cr_ruid &&
cred->cr_gid == cred->cr_rgid) {
cred = crhold(ap->a_cred);
} else {
cred = crdup(ap->a_cred);
cred->cr_uid = cred->cr_ruid;
cred->cr_gid = cred->cr_rgid;
}
}
if (v3) {
if (ap->a_mode & VREAD)
mode = NFSV3ACCESS_READ;
else
mode = 0;
if (vp->v_type != VDIR) {
if (ap->a_mode & VWRITE)
mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
if (ap->a_mode & VEXEC)
mode |= NFSV3ACCESS_EXECUTE;
} else {
if (ap->a_mode & VWRITE)
mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND |
NFSV3ACCESS_DELETE);
if (ap->a_mode & VEXEC)
mode |= NFSV3ACCESS_LOOKUP;
}
if (nfsaccess_cache_timeout > 0) {
wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
} else {
wmode = mode;
}
if (np->n_modestamp &&
(mycpu->gd_time_seconds < (np->n_modestamp + nfsaccess_cache_timeout)) &&
(cred->cr_uid == np->n_modeuid) &&
((np->n_mode & mode) == mode)) {
nfsstats.accesscache_hits++;
} else {
nfsstats.accesscache_misses++;
error = nfs3_access_otw(vp, wmode, td, cred);
if (!error) {
if ((np->n_mode & mode) != mode) {
error = EACCES;
}
}
}
} else {
if ((error = nfs_laccess(ap)) != 0) {
crfree(cred);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
if (cred->cr_uid == 0 && (ap->a_mode & VREAD)
&& VTONFS(vp)->n_size > 0) {
struct iovec aiov;
struct uio auio;
char buf[1];
aiov.iov_base = buf;
aiov.iov_len = 1;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_resid = 1;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = td;
if (vp->v_type == VREG) {
error = nfs_readrpc_uio(vp, &auio);
} else if (vp->v_type == VDIR) {
char* bp;
bp = kmalloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK);
aiov.iov_base = bp;
aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ;
error = nfs_readdirrpc_uio(vp, &auio);
kfree(bp, M_TEMP);
} else if (vp->v_type == VLNK) {
error = nfs_readlinkrpc_uio(vp, &auio);
} else {
error = EACCES;
}
}
}
if (error == 0) {
if ((ap->a_mode & (VREAD|VEXEC)) &&
!nfs_crsame(cred, np->n_rucred)) {
ncred = nfs_crhold(cred);
ocred = np->n_rucred;
np->n_rucred = ncred;
if (ocred)
crfree(ocred);
}
if ((ap->a_mode & VWRITE) &&
!nfs_crsame(cred, np->n_wucred)) {
ncred = nfs_crhold(cred);
ocred = np->n_wucred;
np->n_wucred = ncred;
if (ocred)
crfree(ocred);
}
}
lwkt_reltoken(&nmp->nm_token);
crfree(cred);
return(error);
}
static int
nfs_open(struct vop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct vattr vattr;
struct ucred *ncred;
struct ucred *ocred;
int error;
lwkt_gettoken(&nmp->nm_token);
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) {
#ifdef DIAGNOSTIC
kprintf("open eacces vtyp=%d\n",vp->v_type);
#endif
lwkt_reltoken(&nmp->nm_token);
return (EOPNOTSUPP);
}
if ((ap->a_mode & FREAD) && !nfs_crsame(ap->a_cred, np->n_rucred)) {
ncred = nfs_crhold(ap->a_cred);
ocred = np->n_rucred;
np->n_rucred = ncred;
if (ocred)
crfree(ocred);
}
if ((ap->a_mode & FWRITE) && !nfs_crsame(ap->a_cred, np->n_wucred)) {
ncred = nfs_crhold(ap->a_cred);
ocred = np->n_wucred;
np->n_wucred = ncred;
if (ocred)
crfree(ocred);
}
if (ap->a_mode & FWRITE)
np->n_attrstamp = 0;
if (np->n_flag & NLMODIFIED) {
np->n_attrstamp = 0;
if (vp->v_type == VDIR) {
error = nfs_vinvalbuf(vp, V_SAVE, 1);
if (error == EINTR) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
nfs_invaldir(vp);
}
}
error = VOP_GETATTR(vp, &vattr);
if (error) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
if (np->n_flag & NRMODIFIED) {
if (vp->v_type == VDIR)
nfs_invaldir(vp);
error = nfs_vinvalbuf(vp, V_SAVE, 1);
if (error == EINTR) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
np->n_flag &= ~NRMODIFIED;
}
error = vop_stdopen(ap);
lwkt_reltoken(&nmp->nm_token);
return error;
}
static int
nfs_close(struct vop_close_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0;
thread_t td = curthread;
vn_lock(vp, LK_UPGRADE | LK_RETRY);
lwkt_gettoken(&nmp->nm_token);
if (vp->v_type == VREG) {
if (np->n_flag & NLMODIFIED) {
if (NFS_ISV3(vp)) {
int cm = nfsv3_commit_on_close ? 1 : 0;
error = nfs_flush(vp, MNT_WAIT, td, cm);
} else {
error = nfs_vinvalbuf(vp, V_SAVE, 1);
}
np->n_attrstamp = 0;
}
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
error = np->n_error;
}
}
vop_stdclose(ap);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_getattr(struct vop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp;
int error = 0;
thread_t td = curthread;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
nmp = VFSTONFS(vp->v_mount);
lwkt_gettoken(&nmp->nm_token);
if (np->n_flag & (NACC | NUPD))
np->n_flag |= NCHG;
if (nfs_getattrcache(vp, ap->a_vap) == 0)
goto done;
if (info.v3 && nfsaccess_cache_timeout > 0) {
nfsstats.accesscache_misses++;
nfs3_access_otw(vp, NFSV3ACCESS_ALL, td, nfs_vpcred(vp, ND_CHECK));
if (nfs_getattrcache(vp, ap->a_vap) == 0)
goto done;
}
nfsstats.rpccnt[NFSPROC_GETATTR]++;
nfsm_reqhead(&info, vp, NFSPROC_GETATTR, NFSX_FH(info.v3));
ERROROUT(nfsm_fhtom(&info, vp));
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_GETATTR, td,
nfs_vpcred(vp, ND_CHECK), &error));
if (error == 0) {
ERROROUT(nfsm_loadattr(&info, vp, ap->a_vap));
}
m_freem(info.mrep);
info.mrep = NULL;
done:
if ((nmp->nm_flag & NFSMNT_CACHE) && (vp->v_flag & VROOT))
ap->a_vap->va_flags |= UF_CACHE;
nfsmout:
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_setattr(struct vop_setattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct vattr *vap = ap->a_vap;
int error = 0;
int kflags = 0;
off_t tsize;
thread_t td = curthread;
#ifndef nolint
tsize = (off_t)0;
#endif
if (vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
(vp->v_mount->mnt_flag & MNT_RDONLY))
return (EROFS);
lwkt_gettoken(&nmp->nm_token);
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
lwkt_reltoken(&nmp->nm_token);
return (EISDIR);
case VCHR:
case VBLK:
case VSOCK:
case VFIFO:
if (vap->va_mtime.tv_sec == VNOVAL &&
vap->va_atime.tv_sec == VNOVAL &&
vap->va_mode == (mode_t)VNOVAL &&
vap->va_uid == (uid_t)VNOVAL &&
vap->va_gid == (gid_t)VNOVAL) {
lwkt_reltoken(&nmp->nm_token);
return (0);
}
vap->va_size = VNOVAL;
break;
default:
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
lwkt_reltoken(&nmp->nm_token);
return (EROFS);
}
tsize = np->n_size;
again:
error = nfs_meta_setsize(vp, td, vap->va_size, 0);
#if 0
if (np->n_flag & NLMODIFIED) {
if (vap->va_size == 0)
error = nfs_vinvalbuf(vp, 0, 1);
else
error = nfs_vinvalbuf(vp, V_SAVE, 1);
}
#endif
if (error == 0 && np->n_size != vap->va_size)
goto again;
np->n_vattr.va_size = vap->va_size;
kflags |= NOTE_WRITE;
if (tsize < vap->va_size)
kflags |= NOTE_EXTEND;
break;
}
}
if ((np->n_flag & NLMODIFIED) && vp->v_type == VREG) {
if ((np->n_flag & NRMODIFIED) ||
(vap->va_mtime.tv_sec != VNOVAL)) {
error = nfs_vinvalbuf(vp, V_SAVE, 1);
if (error == EINTR) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
}
}
if (vap->va_mtime.tv_sec != VNOVAL) {
np->n_mtime = vap->va_mtime.tv_sec;
np->n_flag &= ~NUPD;
np->n_vattr.va_mtime = vap->va_mtime;
}
error = nfs_setattrrpc(vp, vap, ap->a_cred, td);
if (error == 0)
kflags |= NOTE_EXTEND;
if (error == 0 && vap->va_size != VNOVAL &&
np->n_size != vap->va_size) {
kprintf("NFS ftruncate: server disagrees on the file size: "
"%jd/%jd/%jd\n",
(intmax_t)tsize,
(intmax_t)vap->va_size,
(intmax_t)np->n_size);
goto again;
}
if (error && vap->va_size != VNOVAL) {
np->n_size = np->n_vattr.va_size = tsize;
nfs_meta_setsize(vp, td, np->n_size, 0);
}
lwkt_reltoken(&nmp->nm_token);
nfs_knote(vp, kflags);
return (error);
}
static int
nfs_setattrrpc(struct vnode *vp, struct vattr *vap,
struct ucred *cred, struct thread *td)
{
struct nfsv2_sattr *sp;
struct nfsnode *np = VTONFS(vp);
u_int32_t *tl;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
nfsstats.rpccnt[NFSPROC_SETATTR]++;
nfsm_reqhead(&info, vp, NFSPROC_SETATTR,
NFSX_FH(info.v3) + NFSX_SATTR(info.v3));
ERROROUT(nfsm_fhtom(&info, vp));
if (info.v3) {
nfsm_v3attrbuild(&info, vap, TRUE);
tl = nfsm_build(&info, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
sp = nfsm_build(&info, NFSX_V2SATTR);
if (vap->va_mode == (mode_t)VNOVAL)
sp->sa_mode = nfs_xdrneg1;
else
sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode);
if (vap->va_uid == (uid_t)VNOVAL)
sp->sa_uid = nfs_xdrneg1;
else
sp->sa_uid = txdr_unsigned(vap->va_uid);
if (vap->va_gid == (gid_t)VNOVAL)
sp->sa_gid = nfs_xdrneg1;
else
sp->sa_gid = txdr_unsigned(vap->va_gid);
sp->sa_size = txdr_unsigned(vap->va_size);
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_SETATTR, td, cred, &error));
if (info.v3) {
np->n_modestamp = 0;
ERROROUT(nfsm_wcc_data(&info, vp, &wccflag));
} else {
ERROROUT(nfsm_loadattr(&info, vp, NULL));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
return (error);
}
static
void
nfs_cache_setvp(struct nchandle *nch, struct vnode *vp, int nctimeout)
{
if (nctimeout == 0)
nctimeout = 1;
else
nctimeout *= hz;
cache_setvp(nch, vp);
cache_settimeout(nch, nctimeout);
}
static int
nfs_nresolve(struct vop_nresolve_args *ap)
{
struct thread *td = curthread;
struct namecache *ncp;
struct nfsmount *nmp;
struct nfsnode *np;
struct vnode *dvp;
struct vnode *nvp;
nfsfh_t *fhp;
int attrflag;
int fhsize;
int error;
int tmp_error;
int len;
struct nfsm_info info;
dvp = ap->a_dvp;
nmp = VFSTONFS(dvp->v_mount);
lwkt_gettoken(&nmp->nm_token);
if ((error = vget(dvp, LK_SHARED)) != 0) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
nvp = NULL;
nfsstats.lookupcache_misses++;
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
ncp = ap->a_nch->ncp;
len = ncp->nc_nlen;
nfsm_reqhead(&info, dvp, NFSPROC_LOOKUP,
NFSX_FH(info.v3) + NFSX_UNSIGNED + nfsm_rndup(len));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, ncp->nc_name, len, NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_LOOKUP, td,
ap->a_cred, &error));
if (error) {
if (error == ENOENT)
nfs_cache_setvp(ap->a_nch, NULL, nfsneg_cache_timeout);
tmp_error = nfsm_postop_attr(&info, dvp, &attrflag,
NFS_LATTR_NOSHRINK);
if (tmp_error) {
error = tmp_error;
goto nfsmout;
}
m_freem(info.mrep);
info.mrep = NULL;
goto nfsmout;
}
NEGATIVEOUT(fhsize = nfsm_getfh(&info, &fhp));
np = VTONFS(dvp);
if (NFS_CMPFH(np, fhp, fhsize)) {
vref(dvp);
nvp = dvp;
} else {
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np, NULL);
if (error) {
m_freem(info.mrep);
info.mrep = NULL;
vput(dvp);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
nvp = NFSTOV(np);
}
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, nvp, &attrflag,
NFS_LATTR_NOSHRINK));
ERROROUT(nfsm_postop_attr(&info, dvp, &attrflag,
NFS_LATTR_NOSHRINK));
} else {
ERROROUT(nfsm_loadattr(&info, nvp, NULL));
}
nfs_cache_setvp(ap->a_nch, nvp, nfspos_cache_timeout);
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
lwkt_reltoken(&nmp->nm_token);
vput(dvp);
if (nvp) {
if (nvp == dvp)
vrele(nvp);
else
vput(nvp);
}
return (error);
}
static int
nfs_lookup(struct vop_old_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
int flags = cnp->cn_flags;
struct vnode *newvp;
struct vnode *notvp;
struct nfsmount *nmp;
long len;
nfsfh_t *fhp;
struct nfsnode *np;
int lockparent, wantparent, attrflag, fhsize;
int error;
int tmp_error;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
error = 0;
notvp = (cnp->cn_flags & CNP_NOTVP) ? cnp->cn_notvp : NULL;
*vpp = NULLVP;
if ((dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == NAMEI_DELETE || cnp->cn_nameiop == NAMEI_RENAME))
return (EROFS);
if (dvp->v_type != VDIR)
return (ENOTDIR);
lockparent = flags & CNP_LOCKPARENT;
wantparent = flags & (CNP_LOCKPARENT|CNP_WANTPARENT);
nmp = VFSTONFS(dvp->v_mount);
np = VTONFS(dvp);
lwkt_gettoken(&nmp->nm_token);
error = 0;
newvp = NULLVP;
nfsstats.lookupcache_misses++;
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
len = cnp->cn_namelen;
nfsm_reqhead(&info, dvp, NFSPROC_LOOKUP,
NFSX_FH(info.v3) + NFSX_UNSIGNED + nfsm_rndup(len));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, len, NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_LOOKUP, cnp->cn_td,
cnp->cn_cred, &error));
if (error) {
tmp_error = nfsm_postop_attr(&info, dvp, &attrflag,
NFS_LATTR_NOSHRINK);
if (tmp_error) {
error = tmp_error;
goto nfsmout;
}
m_freem(info.mrep);
info.mrep = NULL;
goto nfsmout;
}
NEGATIVEOUT(fhsize = nfsm_getfh(&info, &fhp));
if (cnp->cn_nameiop == NAMEI_RENAME && wantparent) {
if (NFS_CMPFH(np, fhp, fhsize)) {
m_freem(info.mrep);
info.mrep = NULL;
lwkt_reltoken(&nmp->nm_token);
return (EISDIR);
}
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np, notvp);
if (error) {
m_freem(info.mrep);
info.mrep = NULL;
lwkt_reltoken(&nmp->nm_token);
return (error);
}
newvp = NFSTOV(np);
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, newvp, &attrflag,
NFS_LATTR_NOSHRINK));
ERROROUT(nfsm_postop_attr(&info, dvp, &attrflag,
NFS_LATTR_NOSHRINK));
} else {
ERROROUT(nfsm_loadattr(&info, newvp, NULL));
}
*vpp = newvp;
m_freem(info.mrep);
info.mrep = NULL;
if (!lockparent) {
vn_unlock(dvp);
cnp->cn_flags |= CNP_PDIRUNLOCK;
}
lwkt_reltoken(&nmp->nm_token);
return (0);
}
if (flags & CNP_ISDOTDOT) {
vn_unlock(dvp);
cnp->cn_flags |= CNP_PDIRUNLOCK;
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np, notvp);
if (error) {
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
cnp->cn_flags &= ~CNP_PDIRUNLOCK;
lwkt_reltoken(&nmp->nm_token);
return (error);
}
newvp = NFSTOV(np);
if (lockparent) {
error = vn_lock(dvp, LK_EXCLUSIVE | LK_FAILRECLAIM);
if (error) {
vput(newvp);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
cnp->cn_flags |= CNP_PDIRUNLOCK;
}
} else if (NFS_CMPFH(np, fhp, fhsize)) {
vref(dvp);
newvp = dvp;
} else {
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np, notvp);
if (error) {
m_freem(info.mrep);
info.mrep = NULL;
lwkt_reltoken(&nmp->nm_token);
return (error);
}
if (!lockparent) {
vn_unlock(dvp);
cnp->cn_flags |= CNP_PDIRUNLOCK;
}
newvp = NFSTOV(np);
}
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, newvp, &attrflag,
NFS_LATTR_NOSHRINK));
ERROROUT(nfsm_postop_attr(&info, dvp, &attrflag,
NFS_LATTR_NOSHRINK));
} else {
ERROROUT(nfsm_loadattr(&info, newvp, NULL));
}
#if 0
if ((cnp->cn_flags & CNP_MAKEENTRY) &&
cnp->cn_nameiop != NAMEI_DELETE) {
np->n_ctime = np->n_vattr.va_ctime.tv_sec;
}
#endif
*vpp = newvp;
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
if (error) {
if (newvp != NULLVP) {
vrele(newvp);
*vpp = NULLVP;
}
if ((cnp->cn_nameiop == NAMEI_CREATE ||
cnp->cn_nameiop == NAMEI_RENAME) &&
error == ENOENT) {
if (!lockparent) {
vn_unlock(dvp);
cnp->cn_flags |= CNP_PDIRUNLOCK;
}
if (dvp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else
error = EJUSTRETURN;
}
}
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_read(struct vop_read_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error;
lwkt_gettoken(&nmp->nm_token);
error = nfs_bioread(vp, ap->a_uio, ap->a_ioflag);
lwkt_reltoken(&nmp->nm_token);
return error;
}
static int
nfs_readlink(struct vop_readlink_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error;
if (vp->v_type != VLNK)
return (EINVAL);
lwkt_gettoken(&nmp->nm_token);
error = nfs_bioread(vp, ap->a_uio, 0);
lwkt_reltoken(&nmp->nm_token);
return error;
}
int
nfs_readlinkrpc_uio(struct vnode *vp, struct uio *uiop)
{
int error = 0, len, attrflag;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
nfsstats.rpccnt[NFSPROC_READLINK]++;
nfsm_reqhead(&info, vp, NFSPROC_READLINK, NFSX_FH(info.v3));
ERROROUT(nfsm_fhtom(&info, vp));
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_READLINK, uiop->uio_td,
nfs_vpcred(vp, ND_CHECK), &error));
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag,
NFS_LATTR_NOSHRINK));
}
if (!error) {
NEGATIVEOUT(len = nfsm_strsiz(&info, NFS_MAXPATHLEN));
if (len == NFS_MAXPATHLEN) {
struct nfsnode *np = VTONFS(vp);
if (np->n_size && np->n_size < NFS_MAXPATHLEN)
len = np->n_size;
}
ERROROUT(nfsm_mtouio(&info, uiop, len));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
return (error);
}
int
nfs_readrpc_uio(struct vnode *vp, struct uio *uiop)
{
u_int32_t *tl;
struct nfsmount *nmp;
int error = 0, len, retlen, tsiz, eof, attrflag;
struct nfsm_info info;
off_t tmp_off;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
#ifndef nolint
eof = 0;
#endif
nmp = VFSTONFS(vp->v_mount);
tsiz = uiop->uio_resid;
tmp_off = uiop->uio_offset + tsiz;
if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset)
return (EFBIG);
tmp_off = uiop->uio_offset;
while (tsiz > 0) {
nfsstats.rpccnt[NFSPROC_READ]++;
len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
nfsm_reqhead(&info, vp, NFSPROC_READ,
NFSX_FH(info.v3) + NFSX_UNSIGNED * 3);
ERROROUT(nfsm_fhtom(&info, vp));
tl = nfsm_build(&info, NFSX_UNSIGNED * 3);
if (info.v3) {
txdr_hyper(uiop->uio_offset, tl);
*(tl + 2) = txdr_unsigned(len);
} else {
*tl++ = txdr_unsigned(uiop->uio_offset);
*tl++ = txdr_unsigned(len);
*tl = 0;
}
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_READ, uiop->uio_td,
nfs_vpcred(vp, ND_READ), &error));
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag,
NFS_LATTR_NOSHRINK));
NULLOUT(tl = nfsm_dissect(&info, 2 * NFSX_UNSIGNED));
eof = fxdr_unsigned(int, *(tl + 1));
} else {
ERROROUT(nfsm_loadattr(&info, vp, NULL));
}
NEGATIVEOUT(retlen = nfsm_strsiz(&info, len));
ERROROUT(nfsm_mtouio(&info, uiop, retlen));
m_freem(info.mrep);
info.mrep = NULL;
if (retlen < len && info.v3 && eof == 0) {
ERROROUT(uiomovez(len - retlen, uiop));
retlen = len;
}
tsiz -= retlen;
if (info.v3) {
if (eof || retlen == 0)
tsiz = 0;
} else if (retlen < len) {
tsiz = 0;
}
}
nfsmout:
return (error);
}
int
nfs_writerpc_uio(struct vnode *vp, struct uio *uiop,
int *iomode, int *must_commit)
{
u_int32_t *tl;
int32_t backup;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit;
int committed = NFSV3WRITE_FILESYNC;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
#ifndef DIAGNOSTIC
if (uiop->uio_iovcnt != 1)
panic("nfs: writerpc iovcnt > 1");
#endif
*must_commit = 0;
tsiz = uiop->uio_resid;
if (uiop->uio_offset + tsiz > nmp->nm_maxfilesize)
return (EFBIG);
while (tsiz > 0) {
nfsstats.rpccnt[NFSPROC_WRITE]++;
len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
nfsm_reqhead(&info, vp, NFSPROC_WRITE,
NFSX_FH(info.v3) + 5 * NFSX_UNSIGNED +
nfsm_rndup(len));
ERROROUT(nfsm_fhtom(&info, vp));
if (info.v3) {
tl = nfsm_build(&info, 5 * NFSX_UNSIGNED);
txdr_hyper(uiop->uio_offset, tl);
tl += 2;
*tl++ = txdr_unsigned(len);
*tl++ = txdr_unsigned(*iomode);
*tl = txdr_unsigned(len);
} else {
u_int32_t x;
tl = nfsm_build(&info, 4 * NFSX_UNSIGNED);
x = txdr_unsigned((u_int32_t)uiop->uio_offset);
*tl++ = x;
*tl++ = x;
x = txdr_unsigned(len);
*tl++ = x;
*tl = x;
}
ERROROUT(nfsm_uiotom(&info, uiop, len));
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_WRITE, uiop->uio_td,
nfs_vpcred(vp, ND_WRITE), &error));
if (info.v3) {
wccflag = NFSV3_WCCCHK;
ERROROUT(nfsm_wcc_data(&info, vp, &wccflag));
if (error == 0) {
NULLOUT(tl = nfsm_dissect(&info, 2 * NFSX_UNSIGNED + NFSX_V3WRITEVERF));
rlen = fxdr_unsigned(int, *tl++);
if (rlen == 0) {
error = NFSERR_IO;
m_freem(info.mrep);
info.mrep = NULL;
break;
} else if (rlen < len) {
backup = len - rlen;
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base - backup;
uiop->uio_iov->iov_len += backup;
uiop->uio_offset -= backup;
uiop->uio_resid += backup;
len = rlen;
}
commit = fxdr_unsigned(int, *tl++);
if (committed == NFSV3WRITE_FILESYNC)
committed = commit;
else if (committed == NFSV3WRITE_DATASYNC &&
commit == NFSV3WRITE_UNSTABLE)
committed = commit;
if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0){
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
nmp->nm_state |= NFSSTA_HASWRITEVERF;
} else if (bcmp((caddr_t)tl,
(caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
*must_commit = 1;
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
}
}
} else {
ERROROUT(nfsm_loadattr(&info, vp, NULL));
}
m_freem(info.mrep);
info.mrep = NULL;
if (error)
break;
tsiz -= len;
}
nfsmout:
if (vp->v_mount->mnt_flag & MNT_ASYNC)
committed = NFSV3WRITE_FILESYNC;
*iomode = committed;
if (error)
uiop->uio_resid = tsiz;
return (error);
}
static int
nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
struct vattr *vap)
{
struct nfsv2_sattr *sp;
u_int32_t *tl;
struct vnode *newvp = NULL;
struct nfsnode *np = NULL;
struct vattr vattr;
int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0;
int rmajor, rminor;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
if (vap->va_type == VCHR || vap->va_type == VBLK) {
rmajor = txdr_unsigned(vap->va_rmajor);
rminor = txdr_unsigned(vap->va_rminor);
} else if (vap->va_type == VFIFO || vap->va_type == VSOCK) {
rmajor = nfs_xdrneg1;
rminor = nfs_xdrneg1;
} else {
return (EOPNOTSUPP);
}
if ((error = VOP_GETATTR(dvp, &vattr)) != 0) {
return (error);
}
nfsstats.rpccnt[NFSPROC_MKNOD]++;
nfsm_reqhead(&info, dvp, NFSPROC_MKNOD,
NFSX_FH(info.v3) + 4 * NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(info.v3));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, cnp->cn_namelen,
NFS_MAXNAMLEN));
if (info.v3) {
tl = nfsm_build(&info, NFSX_UNSIGNED);
*tl++ = vtonfsv3_type(vap->va_type);
nfsm_v3attrbuild(&info, vap, FALSE);
if (vap->va_type == VCHR || vap->va_type == VBLK) {
tl = nfsm_build(&info, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(vap->va_rmajor);
*tl = txdr_unsigned(vap->va_rminor);
}
} else {
sp = nfsm_build(&info, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
sp->sa_uid = nfs_xdrneg1;
sp->sa_gid = nfs_xdrneg1;
sp->sa_size = makeudev(rmajor, rminor);
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_MKNOD, cnp->cn_td,
cnp->cn_cred, &error));
if (!error) {
ERROROUT(nfsm_mtofh(&info, dvp, &newvp, &gotvp));
if (!gotvp) {
if (newvp) {
vput(newvp);
newvp = NULL;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_td, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (info.v3) {
ERROROUT(nfsm_wcc_data(&info, dvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
if (error) {
if (newvp)
vput(newvp);
} else {
*vpp = newvp;
}
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
return (error);
}
static int
nfs_mknod(struct vop_old_mknod_args *ap)
{
struct nfsmount *nmp = VFSTONFS(ap->a_dvp->v_mount);
int error;
lwkt_gettoken(&nmp->nm_token);
error = nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);
lwkt_reltoken(&nmp->nm_token);
if (error == 0)
nfs_knote(ap->a_dvp, NOTE_WRITE);
return error;
}
static u_long create_verf;
static int
nfs_create(struct vop_old_create_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct nfsmount *nmp = VFSTONFS(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
struct nfsv2_sattr *sp;
u_int32_t *tl;
struct nfsnode *np = NULL;
struct vnode *newvp = NULL;
int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0;
struct vattr vattr;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
lwkt_gettoken(&nmp->nm_token);
if (vap->va_type == VSOCK) {
error = nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap);
lwkt_reltoken(&nmp->nm_token);
return error;
}
if ((error = VOP_GETATTR(dvp, &vattr)) != 0) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
if (vap->va_vaflags & VA_EXCLUSIVE)
fmode |= O_EXCL;
again:
nfsstats.rpccnt[NFSPROC_CREATE]++;
nfsm_reqhead(&info, dvp, NFSPROC_CREATE,
NFSX_FH(info.v3) + 2 * NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(info.v3));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, cnp->cn_namelen,
NFS_MAXNAMLEN));
if (info.v3) {
tl = nfsm_build(&info, NFSX_UNSIGNED);
if (fmode & O_EXCL) {
*tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
tl = nfsm_build(&info, NFSX_V3CREATEVERF);
#ifdef INET
if (!TAILQ_EMPTY(&in_ifaddrheads[mycpuid]))
*tl++ = IA_SIN(TAILQ_FIRST(&in_ifaddrheads[mycpuid])->ia)->sin_addr.s_addr;
else
#endif
*tl++ = create_verf;
*tl = ++create_verf;
} else {
*tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
nfsm_v3attrbuild(&info, vap, FALSE);
}
} else {
sp = nfsm_build(&info, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
sp->sa_uid = nfs_xdrneg1;
sp->sa_gid = nfs_xdrneg1;
sp->sa_size = 0;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_CREATE, cnp->cn_td,
cnp->cn_cred, &error));
if (error == 0) {
ERROROUT(nfsm_mtofh(&info, dvp, &newvp, &gotvp));
if (!gotvp) {
if (newvp) {
vput(newvp);
newvp = NULL;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_td, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (info.v3) {
if (error == 0)
error = nfsm_wcc_data(&info, dvp, &wccflag);
else
(void)nfsm_wcc_data(&info, dvp, &wccflag);
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
if (error) {
if (info.v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
KKASSERT(newvp == NULL);
fmode &= ~O_EXCL;
goto again;
}
} else if (info.v3 && (fmode & O_EXCL)) {
if (vap->va_mtime.tv_sec == VNOVAL)
vfs_timestamp(&vap->va_mtime);
if (vap->va_atime.tv_sec == VNOVAL)
vap->va_atime = vap->va_mtime;
error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_td);
}
if (error == 0) {
np = VTONFS(newvp);
if (np->n_rucred == NULL)
np->n_rucred = nfs_crhold(cnp->cn_cred);
if (np->n_wucred == NULL)
np->n_wucred = nfs_crhold(cnp->cn_cred);
*ap->a_vpp = newvp;
nfs_knote(dvp, NOTE_WRITE);
} else if (newvp) {
vput(newvp);
}
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_remove(struct vop_old_remove_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct nfsmount *nmp = VFSTONFS(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
struct nfsnode *np = VTONFS(vp);
int error = 0;
struct vattr vattr;
lwkt_gettoken(&nmp->nm_token);
#ifndef DIAGNOSTIC
if (VREFCNT(vp) < 1)
panic("nfs_remove: bad v_refcnt");
#endif
if (vp->v_type == VDIR) {
error = EPERM;
} else if (VREFCNT(vp) == 1 || (np->n_sillyrename &&
VOP_GETATTR(vp, &vattr) == 0 && vattr.va_nlink > 1)) {
atomic_set_int(&vp->v_refcnt, VREF_FINALIZE);
np->n_flag |= NREMOVED;
error = nfs_vinvalbuf(vp, 0, 1);
if (error != EINTR) {
error = nfs_removerpc(dvp, cnp->cn_nameptr,
cnp->cn_namelen,
cnp->cn_cred, cnp->cn_td);
}
if (error == ENOENT)
error = 0;
} else if (!np->n_sillyrename) {
error = nfs_sillyrename(dvp, vp, cnp);
}
np->n_attrstamp = 0;
lwkt_reltoken(&nmp->nm_token);
if (error == 0) {
nfs_knote(vp, NOTE_DELETE);
nfs_knote(dvp, NOTE_WRITE);
}
return (error);
}
int
nfs_removeit(struct sillyrename *sp)
{
if (sp->s_dvp->v_type == VBAD)
return(0);
return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen,
sp->s_cred, NULL));
}
static int
nfs_removerpc(struct vnode *dvp, const char *name, int namelen,
struct ucred *cred, struct thread *td)
{
int error = 0, wccflag = NFSV3_WCCRATTR;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
nfsstats.rpccnt[NFSPROC_REMOVE]++;
nfsm_reqhead(&info, dvp, NFSPROC_REMOVE,
NFSX_FH(info.v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, name, namelen, NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_REMOVE, td, cred, &error));
if (info.v3) {
ERROROUT(nfsm_wcc_data(&info, dvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
return (error);
}
static int
nfs_rename(struct vop_old_rename_args *ap)
{
struct vnode *fvp = ap->a_fvp;
struct vnode *tvp = ap->a_tvp;
struct vnode *fdvp = ap->a_fdvp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
struct nfsmount *nmp = VFSTONFS(fdvp->v_mount);
int error;
lwkt_gettoken(&nmp->nm_token);
if (tvp) {
atomic_set_int(&tvp->v_refcnt, VREF_FINALIZE);
if (VTONFS(tvp))
VTONFS(tvp)->n_flag |= NREMOVED;
}
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
goto out;
}
if (nfs_flush_on_rename)
VOP_FSYNC(fvp, MNT_WAIT, 0);
if (tvp)
VOP_FSYNC(tvp, MNT_WAIT, 0);
if (tvp && VREFCNT(tvp) > 1 && !VTONFS(tvp)->n_sillyrename &&
tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) {
nfs_knote(tvp, NOTE_DELETE);
vput(tvp);
tvp = NULL;
} else if (tvp) {
nfs_knote(tvp, NOTE_DELETE);
}
error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
tcnp->cn_td);
out:
if (error == 0) {
nfs_knote(fdvp, NOTE_WRITE);
nfs_knote(tdvp, NOTE_WRITE);
nfs_knote(fvp, NOTE_RENAME);
}
lwkt_reltoken(&nmp->nm_token);
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
vrele(fdvp);
vrele(fvp);
if (error == ENOENT)
error = 0;
return (error);
}
static int
nfs_renameit(struct vnode *sdvp, struct componentname *scnp,
struct sillyrename *sp)
{
return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen,
sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_td));
}
static int
nfs_renamerpc(struct vnode *fdvp, const char *fnameptr, int fnamelen,
struct vnode *tdvp, const char *tnameptr, int tnamelen,
struct ucred *cred, struct thread *td)
{
int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(fdvp);
nfsstats.rpccnt[NFSPROC_RENAME]++;
nfsm_reqhead(&info, fdvp, NFSPROC_RENAME,
(NFSX_FH(info.v3) + NFSX_UNSIGNED)*2 +
nfsm_rndup(fnamelen) + nfsm_rndup(tnamelen));
ERROROUT(nfsm_fhtom(&info, fdvp));
ERROROUT(nfsm_strtom(&info, fnameptr, fnamelen, NFS_MAXNAMLEN));
ERROROUT(nfsm_fhtom(&info, tdvp));
ERROROUT(nfsm_strtom(&info, tnameptr, tnamelen, NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, fdvp, NFSPROC_RENAME, td, cred, &error));
if (info.v3) {
ERROROUT(nfsm_wcc_data(&info, fdvp, &fwccflag));
ERROROUT(nfsm_wcc_data(&info, tdvp, &twccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
VTONFS(fdvp)->n_flag |= NLMODIFIED;
VTONFS(tdvp)->n_flag |= NLMODIFIED;
if (!fwccflag)
VTONFS(fdvp)->n_attrstamp = 0;
if (!twccflag)
VTONFS(tdvp)->n_attrstamp = 0;
return (error);
}
static int
nfs_link(struct vop_old_link_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct nfsmount *nmp = VFSTONFS(tdvp->v_mount);
struct componentname *cnp = ap->a_cnp;
int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0;
struct nfsm_info info;
if (vp->v_mount != tdvp->v_mount) {
return (EXDEV);
}
lwkt_gettoken(&nmp->nm_token);
if (nfs_flush_on_hlink)
VOP_FSYNC(vp, MNT_WAIT, 0);
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
nfsstats.rpccnt[NFSPROC_LINK]++;
nfsm_reqhead(&info, vp, NFSPROC_LINK,
NFSX_FH(info.v3) * 2 + NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen));
ERROROUT(nfsm_fhtom(&info, vp));
ERROROUT(nfsm_fhtom(&info, tdvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, cnp->cn_namelen,
NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_LINK, cnp->cn_td,
cnp->cn_cred, &error));
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag,
NFS_LATTR_NOSHRINK));
ERROROUT(nfsm_wcc_data(&info, tdvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
VTONFS(tdvp)->n_flag |= NLMODIFIED;
if (!attrflag)
VTONFS(vp)->n_attrstamp = 0;
if (!wccflag)
VTONFS(tdvp)->n_attrstamp = 0;
if (error == EEXIST)
error = 0;
lwkt_reltoken(&nmp->nm_token);
if (error == 0) {
nfs_knote(vp, NOTE_LINK);
nfs_knote(tdvp, NOTE_WRITE);
}
return (error);
}
static int
nfs_symlink(struct vop_old_symlink_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct nfsmount *nmp = VFSTONFS(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
struct nfsv2_sattr *sp;
int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp;
struct vnode *newvp = NULL;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
lwkt_gettoken(&nmp->nm_token);
nfsstats.rpccnt[NFSPROC_SYMLINK]++;
slen = strlen(ap->a_target);
nfsm_reqhead(&info, dvp, NFSPROC_SYMLINK,
NFSX_FH(info.v3) + 2*NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) +
nfsm_rndup(slen) + NFSX_SATTR(info.v3));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, cnp->cn_namelen,
NFS_MAXNAMLEN));
if (info.v3) {
nfsm_v3attrbuild(&info, vap, FALSE);
}
ERROROUT(nfsm_strtom(&info, ap->a_target, slen, NFS_MAXPATHLEN));
if (info.v3 == 0) {
sp = nfsm_build(&info, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode);
sp->sa_uid = nfs_xdrneg1;
sp->sa_gid = nfs_xdrneg1;
sp->sa_size = nfs_xdrneg1;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_SYMLINK, cnp->cn_td,
cnp->cn_cred, &error));
if (info.v3) {
if (error == 0) {
ERROROUT(nfsm_mtofh(&info, dvp, &newvp, &gotvp));
}
ERROROUT(nfsm_wcc_data(&info, dvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
if (error == EEXIST)
error = 0;
if (error == 0 && newvp == NULL) {
struct nfsnode *np = NULL;
error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
cnp->cn_cred, cnp->cn_td, &np);
if (!error)
newvp = NFSTOV(np);
}
if (error) {
if (newvp)
vput(newvp);
} else {
*ap->a_vpp = newvp;
}
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
if (error == 0 && *ap->a_vpp)
nfs_knote(*ap->a_vpp, NOTE_WRITE);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_mkdir(struct vop_old_mkdir_args *ap)
{
struct vnode *dvp = ap->a_dvp;
struct vattr *vap = ap->a_vap;
struct nfsmount *nmp = VFSTONFS(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
struct nfsv2_sattr *sp;
struct nfsnode *np = NULL;
struct vnode *newvp = NULL;
struct vattr vattr;
int error = 0, wccflag = NFSV3_WCCRATTR;
int gotvp = 0;
int len;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
lwkt_gettoken(&nmp->nm_token);
if ((error = VOP_GETATTR(dvp, &vattr)) != 0) {
lwkt_reltoken(&nmp->nm_token);
return (error);
}
len = cnp->cn_namelen;
nfsstats.rpccnt[NFSPROC_MKDIR]++;
nfsm_reqhead(&info, dvp, NFSPROC_MKDIR,
NFSX_FH(info.v3) + NFSX_UNSIGNED +
nfsm_rndup(len) + NFSX_SATTR(info.v3));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, len, NFS_MAXNAMLEN));
if (info.v3) {
nfsm_v3attrbuild(&info, vap, FALSE);
} else {
sp = nfsm_build(&info, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode);
sp->sa_uid = nfs_xdrneg1;
sp->sa_gid = nfs_xdrneg1;
sp->sa_size = nfs_xdrneg1;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_MKDIR, cnp->cn_td,
cnp->cn_cred, &error));
if (error == 0) {
ERROROUT(nfsm_mtofh(&info, dvp, &newvp, &gotvp));
}
if (info.v3) {
ERROROUT(nfsm_wcc_data(&info, dvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
if (error == EEXIST || (!error && !gotvp)) {
if (newvp) {
vrele(newvp);
newvp = NULL;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred,
cnp->cn_td, &np);
if (!error) {
newvp = NFSTOV(np);
if (newvp->v_type != VDIR)
error = EEXIST;
}
}
if (error) {
if (newvp)
vrele(newvp);
} else {
nfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
*ap->a_vpp = newvp;
}
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_rmdir(struct vop_old_rmdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct nfsmount *nmp = VFSTONFS(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
if (dvp == vp)
return (EINVAL);
lwkt_gettoken(&nmp->nm_token);
nfsstats.rpccnt[NFSPROC_RMDIR]++;
nfsm_reqhead(&info, dvp, NFSPROC_RMDIR,
NFSX_FH(info.v3) + NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, cnp->cn_nameptr, cnp->cn_namelen,
NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_RMDIR, cnp->cn_td,
cnp->cn_cred, &error));
if (info.v3) {
ERROROUT(nfsm_wcc_data(&info, dvp, &wccflag));
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
VTONFS(dvp)->n_flag |= NLMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
if (error == ENOENT)
error = 0;
else
nfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_readdir(struct vop_readdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct uio *uio = ap->a_uio;
int tresid, error;
struct vattr vattr;
if (vp->v_type != VDIR)
return (EPERM);
error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_FAILRECLAIM);
if (error)
return (error);
lwkt_gettoken(&nmp->nm_token);
if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
(np->n_flag & (NLMODIFIED|NRMODIFIED)) == 0) {
if (VOP_GETATTR(vp, &vattr) == 0 &&
(np->n_flag & (NLMODIFIED|NRMODIFIED)) == 0
) {
nfsstats.direofcache_hits++;
goto done;
}
}
tresid = uio->uio_resid;
error = nfs_bioread(vp, uio, 0);
if (!error && uio->uio_resid == tresid)
nfsstats.direofcache_misses++;
done:
lwkt_reltoken(&nmp->nm_token);
vn_unlock(vp);
return (error);
}
int
nfs_readdirrpc_uio(struct vnode *vp, struct uio *uiop)
{
int len, left;
struct nfs_dirent *dp = NULL;
u_int32_t *tl;
nfsuint64 *cookiep;
caddr_t cp;
nfsuint64 cookie;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct nfsnode *dnp = VTONFS(vp);
u_quad_t fileno;
int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
int attrflag;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(vp);
#ifndef DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
(uiop->uio_resid & (DIRBLKSIZ - 1)))
panic("nfs readdirrpc bad uio");
#endif
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep)
cookie = *cookiep;
else
return (NFSERR_BAD_COOKIE);
while (more_dirs && bigenough) {
nfsstats.rpccnt[NFSPROC_READDIR]++;
nfsm_reqhead(&info, vp, NFSPROC_READDIR,
NFSX_FH(info.v3) + NFSX_READDIR(info.v3));
ERROROUT(nfsm_fhtom(&info, vp));
if (info.v3) {
tl = nfsm_build(&info, 5 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
*tl++ = cookie.nfsuquad[1];
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
} else {
tl = nfsm_build(&info, 2 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
}
*tl = txdr_unsigned(nmp->nm_readdirsize);
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_READDIR,
uiop->uio_td,
nfs_vpcred(vp, ND_READ), &error));
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag,
NFS_LATTR_NOSHRINK));
NULLOUT(tl = nfsm_dissect(&info, 2 * NFSX_UNSIGNED));
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl;
}
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
more_dirs = fxdr_unsigned(int, *tl);
while (more_dirs && bigenough) {
if (info.v3) {
NULLOUT(tl = nfsm_dissect(&info, 3 * NFSX_UNSIGNED));
fileno = fxdr_hyper(tl);
len = fxdr_unsigned(int, *(tl + 2));
} else {
NULLOUT(tl = nfsm_dissect(&info, 2 * NFSX_UNSIGNED));
fileno = fxdr_unsigned(u_quad_t, *tl++);
len = fxdr_unsigned(int, *tl);
}
if (len <= 0 || len > NFS_MAXNAMLEN) {
error = EBADRPC;
m_freem(info.mrep);
info.mrep = NULL;
goto nfsmout;
}
tlen = nfsm_rndup(len);
if (tlen == len)
tlen += 4;
left = DIRBLKSIZ - blksiz;
if ((tlen + sizeof(struct nfs_dirent)) > left) {
dp->nfs_reclen += left;
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
blksiz = 0;
}
if ((tlen + sizeof(struct nfs_dirent)) > uiop->uio_resid)
bigenough = 0;
if (bigenough) {
dp = (struct nfs_dirent *)uiop->uio_iov->iov_base;
dp->nfs_ino = fileno;
dp->nfs_namlen = len;
dp->nfs_reclen = tlen + sizeof(struct nfs_dirent);
dp->nfs_type = DT_UNKNOWN;
blksiz += dp->nfs_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += sizeof(struct nfs_dirent);
uiop->uio_resid -= sizeof(struct nfs_dirent);
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + sizeof(struct nfs_dirent);
uiop->uio_iov->iov_len -= sizeof(struct nfs_dirent);
ERROROUT(nfsm_mtouio(&info, uiop, len));
cp = uiop->uio_iov->iov_base;
tlen -= len;
*cp = '\0';
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + tlen;
uiop->uio_iov->iov_len -= tlen;
uiop->uio_offset += tlen;
uiop->uio_resid -= tlen;
} else {
ERROROUT(nfsm_adv(&info, nfsm_rndup(len)));
}
if (info.v3) {
NULLOUT(tl = nfsm_dissect(&info, 3 * NFSX_UNSIGNED));
} else {
NULLOUT(tl = nfsm_dissect(&info, 2 * NFSX_UNSIGNED));
}
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
if (info.v3)
cookie.nfsuquad[1] = *tl++;
} else if (info.v3) {
tl += 2;
} else {
tl++;
}
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
m_freem(info.mrep);
info.mrep = NULL;
}
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->nfs_reclen += left;
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
}
if (bigenough) {
dnp->n_direofoffset = uiop->uio_offset;
} else {
if (uiop->uio_resid > 0)
kprintf("EEK! readdirrpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
*cookiep = cookie;
}
nfsmout:
return (error);
}
int
nfs_readdirplusrpc_uio(struct vnode *vp, struct uio *uiop)
{
int len, left;
struct nfs_dirent *dp;
u_int32_t *tl;
struct vnode *newvp;
nfsuint64 *cookiep;
caddr_t dpossav1, dpossav2;
caddr_t cp;
struct mbuf *mdsav1, *mdsav2;
nfsuint64 cookie;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct nfsnode *dnp = VTONFS(vp), *np;
nfsfh_t *fhp;
u_quad_t fileno;
int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
int attrflag, fhsize;
struct nchandle nch;
struct nchandle dnch;
struct nlcomponent nlc;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = 1;
#ifndef nolint
dp = NULL;
#endif
#ifndef DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
(uiop->uio_resid & (DIRBLKSIZ - 1)))
panic("nfs readdirplusrpc bad uio");
#endif
cache_fromdvp(vp, NULL, 0, &dnch);
bzero(&nlc, sizeof(nlc));
newvp = NULLVP;
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep) {
cookie = *cookiep;
} else {
if (dnch.ncp)
cache_drop(&dnch);
return (NFSERR_BAD_COOKIE);
}
while (more_dirs && bigenough) {
nfsstats.rpccnt[NFSPROC_READDIRPLUS]++;
nfsm_reqhead(&info, vp, NFSPROC_READDIRPLUS,
NFSX_FH(info.v3) + 6 * NFSX_UNSIGNED);
ERROROUT(nfsm_fhtom(&info, vp));
tl = nfsm_build(&info, 6 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
*tl++ = cookie.nfsuquad[1];
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
*tl++ = txdr_unsigned(nmp->nm_readdirsize);
*tl = txdr_unsigned(nmp->nm_rsize);
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_READDIRPLUS,
uiop->uio_td,
nfs_vpcred(vp, ND_READ), &error));
ERROROUT(nfsm_postop_attr(&info, vp, &attrflag,
NFS_LATTR_NOSHRINK));
NULLOUT(tl = nfsm_dissect(&info, 3 * NFSX_UNSIGNED));
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl++;
more_dirs = fxdr_unsigned(int, *tl);
while (more_dirs && bigenough) {
NULLOUT(tl = nfsm_dissect(&info, 3 * NFSX_UNSIGNED));
fileno = fxdr_hyper(tl);
len = fxdr_unsigned(int, *(tl + 2));
if (len <= 0 || len > NFS_MAXNAMLEN) {
error = EBADRPC;
m_freem(info.mrep);
info.mrep = NULL;
goto nfsmout;
}
tlen = nfsm_rndup(len);
if (tlen == len)
tlen += 4;
left = DIRBLKSIZ - blksiz;
if ((tlen + sizeof(struct nfs_dirent)) > left) {
dp->nfs_reclen += left;
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
blksiz = 0;
}
if ((tlen + sizeof(struct nfs_dirent)) > uiop->uio_resid)
bigenough = 0;
if (bigenough) {
dp = (struct nfs_dirent *)uiop->uio_iov->iov_base;
dp->nfs_ino = fileno;
dp->nfs_namlen = len;
dp->nfs_reclen = tlen + sizeof(struct nfs_dirent);
dp->nfs_type = DT_UNKNOWN;
blksiz += dp->nfs_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += sizeof(struct nfs_dirent);
uiop->uio_resid -= sizeof(struct nfs_dirent);
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + sizeof(struct nfs_dirent);
uiop->uio_iov->iov_len -= sizeof(struct nfs_dirent);
nlc.nlc_nameptr = uiop->uio_iov->iov_base;
nlc.nlc_namelen = len;
ERROROUT(nfsm_mtouio(&info, uiop, len));
cp = uiop->uio_iov->iov_base;
tlen -= len;
*cp = '\0';
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + tlen;
uiop->uio_iov->iov_len -= tlen;
uiop->uio_offset += tlen;
uiop->uio_resid -= tlen;
} else {
ERROROUT(nfsm_adv(&info, nfsm_rndup(len)));
}
NULLOUT(tl = nfsm_dissect(&info, 3 * NFSX_UNSIGNED));
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
cookie.nfsuquad[1] = *tl++;
} else {
tl += 2;
}
attrflag = fxdr_unsigned(int, *tl);
if (attrflag) {
dpossav1 = info.dpos;
mdsav1 = info.md;
ERROROUT(nfsm_adv(&info, NFSX_V3FATTR));
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
doit = fxdr_unsigned(int, *tl);
if (doit) {
NEGATIVEOUT(fhsize = nfsm_getfh(&info, &fhp));
}
if (doit && bigenough && !nlcdegenerate(&nlc) &&
!NFS_CMPFH(dnp, fhp, fhsize)
) {
if (dnch.ncp) {
#if 0
kprintf("NFS/READDIRPLUS, ENTER %*.*s\n",
nlc.nlc_namelen, nlc.nlc_namelen,
nlc.nlc_nameptr);
#endif
nch = cache_nlookup_nonblock(&dnch, &nlc);
if (nch.ncp == NULL)
goto rdfail;
cache_setunresolved(&nch);
error = nfs_nget_nonblock(vp->v_mount, fhp,
fhsize, &np,
NULL);
if (error) {
cache_put(&nch);
goto rdfail;
}
newvp = NFSTOV(np);
dpossav2 = info.dpos;
info.dpos = dpossav1;
mdsav2 = info.md;
info.md = mdsav1;
ERROROUT(nfsm_loadattr(&info, newvp, NULL));
info.dpos = dpossav2;
info.md = mdsav2;
dp->nfs_type =
IFTODT(VTTOIF(np->n_vattr.va_type));
nfs_cache_setvp(&nch, newvp,
nfspos_cache_timeout);
vput(newvp);
newvp = NULLVP;
cache_put(&nch);
} else {
rdfail:
;
#if 0
kprintf("Warning: NFS/rddirplus, "
"UNABLE TO ENTER %*.*s\n",
nlc.nlc_namelen, nlc.nlc_namelen,
nlc.nlc_nameptr);
#endif
}
}
} else {
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
i = fxdr_unsigned(int, *tl);
ERROROUT(nfsm_adv(&info, nfsm_rndup(i)));
}
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED));
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
m_freem(info.mrep);
info.mrep = NULL;
}
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->nfs_reclen += left;
uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
}
if (bigenough) {
dnp->n_direofoffset = uiop->uio_offset;
} else {
if (uiop->uio_resid > 0)
kprintf("EEK! readdirplusrpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
*cookiep = cookie;
}
nfsmout:
if (newvp != NULLVP) {
if (newvp == vp)
vrele(newvp);
else
vput(newvp);
newvp = NULLVP;
}
if (dnch.ncp)
cache_drop(&dnch);
return (error);
}
static int
nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
{
struct sillyrename *sp;
struct nfsnode *np;
int error;
atomic_set_int(&vp->v_refcnt, VREF_FINALIZE);
np = VTONFS(vp);
np->n_flag |= NREMOVED;
cache_purge(vp);
#ifndef DIAGNOSTIC
if (vp->v_type == VDIR)
panic("nfs: sillyrename dir");
#endif
sp = kmalloc(sizeof(struct sillyrename), M_NFSREQ, M_WAITOK);
sp->s_cred = crdup(cnp->cn_cred);
sp->s_dvp = dvp;
vref(dvp);
sp->s_namlen = ksprintf(sp->s_name, ".nfsA%08x4.4",
(int)(intptr_t)cnp->cn_td);
while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_td, NULL) == 0) {
sp->s_name[4]++;
if (sp->s_name[4] > 'z') {
error = EINVAL;
goto bad;
}
}
error = nfs_renameit(dvp, cnp, sp);
if (error)
goto bad;
error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_td, &np);
np->n_sillyrename = sp;
return (0);
bad:
vrele(sp->s_dvp);
crfree(sp->s_cred);
kfree((caddr_t)sp, M_NFSREQ);
return (error);
}
static int
nfs_lookitup(struct vnode *dvp, const char *name, int len, struct ucred *cred,
struct thread *td, struct nfsnode **npp)
{
struct vnode *newvp = NULL;
struct nfsnode *np, *dnp = VTONFS(dvp);
int error = 0, fhlen, attrflag;
nfsfh_t *nfhp;
struct nfsm_info info;
info.mrep = NULL;
info.v3 = NFS_ISV3(dvp);
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
nfsm_reqhead(&info, dvp, NFSPROC_LOOKUP,
NFSX_FH(info.v3) + NFSX_UNSIGNED + nfsm_rndup(len));
ERROROUT(nfsm_fhtom(&info, dvp));
ERROROUT(nfsm_strtom(&info, name, len, NFS_MAXNAMLEN));
NEGKEEPOUT(nfsm_request(&info, dvp, NFSPROC_LOOKUP, td, cred, &error));
if (npp && !error) {
NEGATIVEOUT(fhlen = nfsm_getfh(&info, &nfhp));
if (*npp) {
np = *npp;
if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) {
kfree((caddr_t)np->n_fhp, M_NFSBIGFH);
np->n_fhp = &np->n_fh;
} else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH)
np->n_fhp =(nfsfh_t *)kmalloc(fhlen,M_NFSBIGFH,M_WAITOK);
bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen);
np->n_fhsize = fhlen;
newvp = NFSTOV(np);
} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
vref(dvp);
newvp = dvp;
} else {
error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np, NULL);
if (error) {
m_freem(info.mrep);
info.mrep = NULL;
return (error);
}
newvp = NFSTOV(np);
}
if (info.v3) {
ERROROUT(nfsm_postop_attr(&info, newvp, &attrflag,
NFS_LATTR_NOSHRINK));
if (!attrflag && *npp == NULL) {
m_freem(info.mrep);
info.mrep = NULL;
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
return (ENOENT);
}
} else {
ERROROUT(nfsm_loadattr(&info, newvp, NULL));
}
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
if (npp && *npp == NULL) {
if (error) {
if (newvp) {
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
}
} else
*npp = np;
}
return (error);
}
int
nfs_commitrpc_uio(struct vnode *vp, u_quad_t offset, int cnt, struct thread *td)
{
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, wccflag = NFSV3_WCCRATTR;
struct nfsm_info info;
u_int32_t *tl;
info.mrep = NULL;
info.v3 = 1;
if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0)
return (0);
nfsstats.rpccnt[NFSPROC_COMMIT]++;
nfsm_reqhead(&info, vp, NFSPROC_COMMIT, NFSX_FH(1));
ERROROUT(nfsm_fhtom(&info, vp));
tl = nfsm_build(&info, 3 * NFSX_UNSIGNED);
txdr_hyper(offset, tl);
tl += 2;
*tl = txdr_unsigned(cnt);
NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_COMMIT, td,
nfs_vpcred(vp, ND_WRITE), &error));
ERROROUT(nfsm_wcc_data(&info, vp, &wccflag));
if (!error) {
NULLOUT(tl = nfsm_dissect(&info, NFSX_V3WRITEVERF));
if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
NFSX_V3WRITEVERF)) {
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
error = NFSERR_STALEWRITEVERF;
}
}
m_freem(info.mrep);
info.mrep = NULL;
nfsmout:
return (error);
}
static int
nfs_bmap(struct vop_bmap_args *ap)
{
if (ap->a_doffsetp != NULL)
*ap->a_doffsetp = ap->a_loffset;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
if (ap->a_runb != NULL)
*ap->a_runb = 0;
return (0);
}
static int
nfs_strategy(struct vop_strategy_args *ap)
{
struct bio *bio = ap->a_bio;
struct bio *nbio;
struct buf *bp __debugvar = bio->bio_buf;
struct nfsmount *nmp = VFSTONFS(ap->a_vp->v_mount);
struct thread *td;
int error;
KASSERT(bp->b_cmd != BUF_CMD_DONE,
("nfs_strategy: buffer %p unexpectedly marked done", bp));
KASSERT(BUF_LOCKINUSE(bp),
("nfs_strategy: buffer %p not locked", bp));
if (bio->bio_flags & BIO_SYNC)
td = curthread;
else
td = NULL;
lwkt_gettoken(&nmp->nm_token);
nbio = push_bio(bio);
nbio->bio_offset = bio->bio_offset;
nbio->bio_flags = bio->bio_flags & BIO_SYNC;
if (bio->bio_flags & BIO_SYNC) {
error = nfs_doio(ap->a_vp, nbio, td);
} else {
nfs_asyncio(ap->a_vp, nbio);
error = 0;
}
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfs_fsync(struct vop_fsync_args *ap)
{
struct nfsmount *nmp = VFSTONFS(ap->a_vp->v_mount);
int error;
lwkt_gettoken(&nmp->nm_token);
error = nfs_flush(ap->a_vp, ap->a_waitfor, curthread, 1);
lwkt_reltoken(&nmp->nm_token);
return error;
}
#ifndef NFS_COMMITBVECSIZ
#define NFS_COMMITBVECSIZ 16
#endif
struct nfs_flush_info {
enum { NFI_FLUSHNEW, NFI_COMMIT } mode;
struct thread *td;
struct vnode *vp;
int waitfor;
int slpflag;
int slptimeo;
int loops;
struct buf *bvary[NFS_COMMITBVECSIZ];
int bvsize;
off_t beg_off;
off_t end_off;
};
static int nfs_flush_bp(struct buf *bp, void *data);
static int nfs_flush_docommit(struct nfs_flush_info *info, int error);
int
nfs_flush(struct vnode *vp, int waitfor, struct thread *td, int commit)
{
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct nfs_flush_info info;
int error;
bzero(&info, sizeof(info));
info.td = td;
info.vp = vp;
info.waitfor = waitfor;
info.slpflag = (nmp->nm_flag & NFSMNT_INT) ? PCATCH : 0;
info.loops = 0;
lwkt_gettoken(&vp->v_token);
do {
info.mode = NFI_FLUSHNEW;
error = RB_SCAN(buf_rb_tree, &vp->v_rbdirty_tree, NULL,
nfs_flush_bp, &info);
if (commit && error == 0) {
info.mode = NFI_COMMIT;
error = RB_SCAN(buf_rb_tree, &vp->v_rbdirty_tree, NULL,
nfs_flush_bp, &info);
if (info.bvsize)
error = nfs_flush_docommit(&info, error);
}
while (waitfor == MNT_WAIT &&
bio_track_active(&vp->v_track_write)) {
error = bio_track_wait(&vp->v_track_write,
info.slpflag, info.slptimeo);
if (error) {
if (nfs_sigintr(nmp, NULL, td)) {
error = -EINTR;
break;
}
if (info.slpflag == PCATCH) {
info.slpflag = 0;
info.slptimeo = 2 * hz;
}
error = 0;
}
}
++info.loops;
} while (waitfor == MNT_WAIT && commit &&
error == 0 && !RB_EMPTY(&vp->v_rbdirty_tree));
if (error < 0)
error = -error;
if (np->n_flag & NWRITEERR) {
error = np->n_error;
np->n_flag &= ~NWRITEERR;
}
lwkt_reltoken(&vp->v_token);
return (error);
}
static
int
nfs_flush_bp(struct buf *bp, void *data)
{
struct nfs_flush_info *info = data;
int lkflags;
int error;
off_t toff;
error = 0;
switch(info->mode) {
case NFI_FLUSHNEW:
error = BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT);
if (error && info->loops && info->waitfor == MNT_WAIT) {
error = BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT);
if (error) {
lkflags = LK_EXCLUSIVE | LK_SLEEPFAIL;
if (info->slpflag & PCATCH)
lkflags |= LK_PCATCH;
atomic_add_int(&bp->b_refs, 1);
error = BUF_TIMELOCK(bp, lkflags, "nfsfsync",
info->slptimeo);
atomic_add_int(&bp->b_refs, -1);
}
}
if (error) {
error = 0;
break;
}
if (bp->b_vp == info->vp &&
(bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) == B_DELWRI) {
bremfree(bp);
bawrite(bp);
} else {
BUF_UNLOCK(bp);
}
break;
case NFI_COMMIT:
if ((bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) !=
(B_DELWRI | B_NEEDCOMMIT)) {
break;
}
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT))
break;
if (bp->b_vp != info->vp ||
(bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) !=
(B_DELWRI | B_NEEDCOMMIT)) {
BUF_UNLOCK(bp);
break;
}
bremfree(bp);
bp->b_cmd = BUF_CMD_WRITE;
vfs_busy_pages(bp->b_vp, bp);
info->bvary[info->bvsize] = bp;
toff = bp->b_bio2.bio_offset + bp->b_dirtyoff;
if (info->bvsize == 0 || toff < info->beg_off)
info->beg_off = toff;
toff += (off_t)(bp->b_dirtyend - bp->b_dirtyoff);
if (info->bvsize == 0 || toff > info->end_off)
info->end_off = toff;
++info->bvsize;
if (info->bvsize == NFS_COMMITBVECSIZ) {
error = nfs_flush_docommit(info, 0);
KKASSERT(info->bvsize == 0);
}
}
return (error);
}
static
int
nfs_flush_docommit(struct nfs_flush_info *info, int error)
{
struct vnode *vp;
struct buf *bp;
off_t bytes;
int retv;
int i;
vp = info->vp;
if (info->bvsize > 0) {
bytes = info->end_off - info->beg_off;
if (bytes > 0x40000000)
bytes = 0x40000000;
if (error) {
retv = -error;
} else {
retv = nfs_commitrpc_uio(vp, info->beg_off,
(int)bytes, info->td);
if (retv == NFSERR_STALEWRITEVERF)
nfs_clearcommit(vp->v_mount);
}
for (i = 0; i < info->bvsize; ++i) {
bp = info->bvary[i];
if (retv || (bp->b_flags & B_NEEDCOMMIT) == 0) {
bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
vfs_unbusy_pages(bp);
bp->b_cmd = BUF_CMD_DONE;
bqrelse(bp);
} else {
bundirty(bp);
bp->b_flags &= ~B_ERROR;
bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
bp->b_dirtyoff = bp->b_dirtyend = 0;
biodone(&bp->b_bio1);
}
}
info->bvsize = 0;
}
return (error);
}
static int
nfs_advlock(struct vop_advlock_args *ap)
{
struct nfsnode *np = VTONFS(ap->a_vp);
return (lf_advlock(ap, &(np->n_lockf), np->n_size));
}
static int
nfs_print(struct vop_print_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
kprintf("tag VT_NFS, fileid %lld fsid 0x%x",
(long long)np->n_vattr.va_fileid, np->n_vattr.va_fsid);
if (vp->v_type == VFIFO)
fifo_printinfo(vp);
kprintf("\n");
return (0);
}
static int
nfs_laccess(struct vop_access_args *ap)
{
struct nfsmount *nmp = VFSTONFS(ap->a_vp->v_mount);
struct vattr vattr;
int error;
lwkt_gettoken(&nmp->nm_token);
error = VOP_GETATTR(ap->a_vp, &vattr);
if (error == 0) {
error = vop_helper_access(ap, vattr.va_uid, vattr.va_gid,
vattr.va_mode, 0);
}
lwkt_reltoken(&nmp->nm_token);
return (error);
}
static int
nfsfifo_read(struct vop_read_args *ap)
{
struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NACC;
getnanotime(&np->n_atim);
return (VOCALL(&fifo_vnode_vops, &ap->a_head));
}
static int
nfsfifo_write(struct vop_write_args *ap)
{
struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NUPD;
getnanotime(&np->n_mtim);
return (VOCALL(&fifo_vnode_vops, &ap->a_head));
}
static int
nfsfifo_close(struct vop_close_args *ap)
{
struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct vattr vattr;
struct timespec ts;
vn_lock(vp, LK_UPGRADE | LK_RETRY);
if (np->n_flag & (NACC | NUPD)) {
getnanotime(&ts);
if (np->n_flag & NACC)
np->n_atim = ts;
if (np->n_flag & NUPD)
np->n_mtim = ts;
np->n_flag |= NCHG;
if (VREFCNT(vp) == 1 &&
(vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
VATTR_NULL(&vattr);
if (np->n_flag & NACC)
vattr.va_atime = np->n_atim;
if (np->n_flag & NUPD)
vattr.va_mtime = np->n_mtim;
(void)VOP_SETATTR(vp, &vattr, nfs_vpcred(vp, ND_WRITE));
}
}
return (VOCALL(&fifo_vnode_vops, &ap->a_head));
}
static void filt_nfsdetach(struct knote *kn);
static int filt_nfsread(struct knote *kn, long hint);
static int filt_nfswrite(struct knote *kn, long hint);
static int filt_nfsvnode(struct knote *kn, long hint);
static struct filterops nfsread_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_nfsdetach, filt_nfsread };
static struct filterops nfswrite_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_nfsdetach, filt_nfswrite };
static struct filterops nfsvnode_filtops =
{ FILTEROP_ISFD | FILTEROP_MPSAFE,
NULL, filt_nfsdetach, filt_nfsvnode };
static int
nfs_kqfilter (struct vop_kqfilter_args *ap)
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &nfsread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &nfswrite_filtops;
break;
case EVFILT_VNODE:
kn->kn_fop = &nfsvnode_filtops;
break;
default:
return (EOPNOTSUPP);
}
kn->kn_hook = (caddr_t)vp;
knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
return(0);
}
static void
filt_nfsdetach(struct knote *kn)
{
struct vnode *vp = (void *)kn->kn_hook;
knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
}
static int
filt_nfsread(struct knote *kn, long hint)
{
struct vnode *vp = (void *)kn->kn_hook;
struct nfsnode *node = VTONFS(vp);
off_t off;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
return(1);
}
off = node->n_size - kn->kn_fp->f_offset;
kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
if (kn->kn_sfflags & NOTE_OLDAPI) {
return(1);
}
if (kn->kn_data == 0) {
kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
}
return (kn->kn_data != 0);
}
static int
filt_nfswrite(struct knote *kn, long hint)
{
if (hint == NOTE_REVOKE)
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
kn->kn_data = 0;
return (1);
}
static int
filt_nfsvnode(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
return (kn->kn_fflags != 0);
}