#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.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/conf.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/ubc.h>
#include <ufs/ufs/dir.h>
#include <vfs/vfs_support.h>
#include <sys/vm.h>
#include <machine/spl.h>
#include <vm/vm_pageout.h>
#include <sys/time.h>
#include <kern/clock.h>
#include <miscfs/fifofs/fifo.h>
#include <miscfs/specfs/specdev.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nqnfs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <vm/vm_kern.h>
#include <kern/task.h>
#include <kern/sched_prim.h>
#include <sys/kdebug.h>
#define FSDBG(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_NONE, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
#define FSDBG_TOP(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_START, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
#define FSDBG_BOT(A, B, C, D, E) \
KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_END, \
(int)(B), (int)(C), (int)(D), (int)(E), 0)
#define TRUE 1
#define FALSE 0
static int nfsspec_read __P((struct vop_read_args *));
static int nfsspec_write __P((struct vop_write_args *));
static int nfsfifo_read __P((struct vop_read_args *));
static int nfsfifo_write __P((struct vop_write_args *));
static int nfsspec_close __P((struct vop_close_args *));
static int nfsfifo_close __P((struct vop_close_args *));
#define nfs_poll vop_nopoll
static int nfs_ioctl __P((struct vop_ioctl_args *));
static int nfs_select __P((struct vop_select_args *));
static int nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int));
static int nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *));
static int nfs_lookup __P((struct vop_lookup_args *));
static int nfs_create __P((struct vop_create_args *));
static int nfs_mknod __P((struct vop_mknod_args *));
static int nfs_open __P((struct vop_open_args *));
static int nfs_close __P((struct vop_close_args *));
static int nfs_access __P((struct vop_access_args *));
static int nfs_getattr __P((struct vop_getattr_args *));
static int nfs_setattr __P((struct vop_setattr_args *));
static int nfs_read __P((struct vop_read_args *));
static int nfs_mmap __P((struct vop_mmap_args *));
static int nfs_fsync __P((struct vop_fsync_args *));
static int nfs_remove __P((struct vop_remove_args *));
static int nfs_link __P((struct vop_link_args *));
static int nfs_rename __P((struct vop_rename_args *));
static int nfs_mkdir __P((struct vop_mkdir_args *));
static int nfs_rmdir __P((struct vop_rmdir_args *));
static int nfs_symlink __P((struct vop_symlink_args *));
static int nfs_readdir __P((struct vop_readdir_args *));
static int nfs_bmap __P((struct vop_bmap_args *));
static int nfs_strategy __P((struct vop_strategy_args *));
static int nfs_lookitup __P((struct vnode *,char *,int,struct ucred *,struct proc *,struct nfsnode **));
static int nfs_sillyrename __P((struct vnode *,struct vnode *,struct componentname *));
static int nfsspec_access __P((struct vop_access_args *));
static int nfs_readlink __P((struct vop_readlink_args *));
static int nfs_print __P((struct vop_print_args *));
static int nfs_pathconf __P((struct vop_pathconf_args *));
static int nfs_advlock __P((struct vop_advlock_args *));
static int nfs_blkatoff __P((struct vop_blkatoff_args *));
static int nfs_bwrite __P((struct vop_bwrite_args *));
static int nfs_valloc __P((struct vop_valloc_args *));
static int nfs_vfree __P((struct vop_vfree_args *));
static int nfs_truncate __P((struct vop_truncate_args *));
static int nfs_update __P((struct vop_update_args *));
static int nfs_pagein __P((struct vop_pagein_args *));
static int nfs_pageout __P((struct vop_pageout_args *));
static int nfs_blktooff __P((struct vop_blktooff_args *));
static int nfs_offtoblk __P((struct vop_offtoblk_args *));
static int nfs_cmap __P((struct vop_cmap_args *));
vop_t **nfsv2_vnodeop_p;
static struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
{ &vop_default_desc, (vop_t *)vn_default_error },
{ &vop_lookup_desc, (vop_t *)nfs_lookup },
{ &vop_create_desc, (vop_t *)nfs_create },
{ &vop_mknod_desc, (vop_t *)nfs_mknod },
{ &vop_open_desc, (vop_t *)nfs_open },
{ &vop_close_desc, (vop_t *)nfs_close },
{ &vop_access_desc, (vop_t *)nfs_access },
{ &vop_getattr_desc, (vop_t *)nfs_getattr },
{ &vop_setattr_desc, (vop_t *)nfs_setattr },
{ &vop_read_desc, (vop_t *)nfs_read },
{ &vop_write_desc, (vop_t *)nfs_write },
{ &vop_lease_desc, (vop_t *)nfs_lease_check },
{ &vop_ioctl_desc, (vop_t *)nfs_ioctl },
{ &vop_select_desc, (vop_t *)nfs_select },
{ &vop_revoke_desc, (vop_t *)nfs_revoke },
{ &vop_mmap_desc, (vop_t *)nfs_mmap },
{ &vop_fsync_desc, (vop_t *)nfs_fsync },
{ &vop_seek_desc, (vop_t *)nfs_seek },
{ &vop_remove_desc, (vop_t *)nfs_remove },
{ &vop_link_desc, (vop_t *)nfs_link },
{ &vop_rename_desc, (vop_t *)nfs_rename },
{ &vop_mkdir_desc, (vop_t *)nfs_mkdir },
{ &vop_rmdir_desc, (vop_t *)nfs_rmdir },
{ &vop_symlink_desc, (vop_t *)nfs_symlink },
{ &vop_readdir_desc, (vop_t *)nfs_readdir },
{ &vop_readlink_desc, (vop_t *)nfs_readlink },
{ &vop_abortop_desc, (vop_t *)nfs_abortop },
{ &vop_inactive_desc, (vop_t *)nfs_inactive },
{ &vop_reclaim_desc, (vop_t *)nfs_reclaim },
{ &vop_lock_desc, (vop_t *)nfs_lock },
{ &vop_unlock_desc, (vop_t *)nfs_unlock },
{ &vop_bmap_desc, (vop_t *)nfs_bmap },
{ &vop_strategy_desc, (vop_t *)nfs_strategy },
{ &vop_print_desc, (vop_t *)nfs_print },
{ &vop_islocked_desc, (vop_t *)nfs_islocked },
{ &vop_pathconf_desc, (vop_t *)nfs_pathconf },
{ &vop_advlock_desc, (vop_t *)nfs_advlock },
{ &vop_blkatoff_desc, (vop_t *)nfs_blkatoff },
{ &vop_valloc_desc, (vop_t *)nfs_valloc },
{ &vop_reallocblks_desc, (vop_t *)nfs_reallocblks },
{ &vop_vfree_desc, (vop_t *)nfs_vfree },
{ &vop_truncate_desc, (vop_t *)nfs_truncate },
{ &vop_update_desc, (vop_t *)nfs_update },
{ &vop_bwrite_desc, (vop_t *)nfs_bwrite },
{ &vop_pagein_desc, (vop_t *)nfs_pagein },
{ &vop_pageout_desc, (vop_t *)nfs_pageout },
{ &vop_copyfile_desc, (vop_t *)err_copyfile },
{ &vop_blktooff_desc, (vop_t *)nfs_blktooff },
{ &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },
{ &vop_cmap_desc, (vop_t *)nfs_cmap },
{ NULL, NULL }
};
struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
{ &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(nfsv2_vnodeop_opv_desc);
#endif
vop_t **spec_nfsv2nodeop_p;
static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
{ &vop_default_desc, (vop_t *)vn_default_error },
{ &vop_lookup_desc, (vop_t *)spec_lookup },
{ &vop_create_desc, (vop_t *)spec_create },
{ &vop_mknod_desc, (vop_t *)spec_mknod },
{ &vop_open_desc, (vop_t *)spec_open },
{ &vop_close_desc, (vop_t *)nfsspec_close },
{ &vop_access_desc, (vop_t *)nfsspec_access },
{ &vop_getattr_desc, (vop_t *)nfs_getattr },
{ &vop_setattr_desc, (vop_t *)nfs_setattr },
{ &vop_read_desc, (vop_t *)nfsspec_read },
{ &vop_write_desc, (vop_t *)nfsspec_write },
{ &vop_lease_desc, (vop_t *)spec_lease_check },
{ &vop_ioctl_desc, (vop_t *)spec_ioctl },
{ &vop_select_desc, (vop_t *)spec_select },
{ &vop_revoke_desc, (vop_t *)spec_revoke },
{ &vop_mmap_desc, (vop_t *)spec_mmap },
{ &vop_fsync_desc, (vop_t *)nfs_fsync },
{ &vop_seek_desc, (vop_t *)spec_seek },
{ &vop_remove_desc, (vop_t *)spec_remove },
{ &vop_link_desc, (vop_t *)spec_link },
{ &vop_rename_desc, (vop_t *)spec_rename },
{ &vop_mkdir_desc, (vop_t *)spec_mkdir },
{ &vop_rmdir_desc, (vop_t *)spec_rmdir },
{ &vop_symlink_desc, (vop_t *)spec_symlink },
{ &vop_readdir_desc, (vop_t *)spec_readdir },
{ &vop_readlink_desc, (vop_t *)spec_readlink },
{ &vop_abortop_desc, (vop_t *)spec_abortop },
{ &vop_inactive_desc, (vop_t *)nfs_inactive },
{ &vop_reclaim_desc, (vop_t *)nfs_reclaim },
{ &vop_lock_desc, (vop_t *)nfs_lock },
{ &vop_unlock_desc, (vop_t *)nfs_unlock },
{ &vop_bmap_desc, (vop_t *)spec_bmap },
{ &vop_strategy_desc, (vop_t *)spec_strategy },
{ &vop_print_desc, (vop_t *)nfs_print },
{ &vop_islocked_desc, (vop_t *)nfs_islocked },
{ &vop_pathconf_desc, (vop_t *)spec_pathconf },
{ &vop_advlock_desc, (vop_t *)spec_advlock },
{ &vop_blkatoff_desc, (vop_t *)spec_blkatoff },
{ &vop_valloc_desc, (vop_t *)spec_valloc },
{ &vop_reallocblks_desc, (vop_t *)spec_reallocblks },
{ &vop_vfree_desc, (vop_t *)spec_vfree },
{ &vop_truncate_desc, (vop_t *)spec_truncate },
{ &vop_update_desc, (vop_t *)nfs_update },
{ &vop_bwrite_desc, (vop_t *)vn_bwrite },
{ &vop_devblocksize_desc, (vop_t *)spec_devblocksize },
{ &vop_pagein_desc, (vop_t *)nfs_pagein },
{ &vop_pageout_desc, (vop_t *)nfs_pageout },
{ &vop_blktooff_desc, (vop_t *)nfs_blktooff },
{ &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },
{ &vop_cmap_desc, (vop_t *)nfs_cmap },
{ NULL, NULL }
};
struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
{ &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(spec_nfsv2nodeop_opv_desc);
#endif
vop_t **fifo_nfsv2nodeop_p;
static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
{ &vop_default_desc, (vop_t *)vn_default_error },
{ &vop_lookup_desc, (vop_t *)fifo_lookup },
{ &vop_create_desc, (vop_t *)fifo_create },
{ &vop_mknod_desc, (vop_t *)fifo_mknod },
{ &vop_open_desc, (vop_t *)fifo_open },
{ &vop_close_desc, (vop_t *)nfsfifo_close },
{ &vop_access_desc, (vop_t *)nfsspec_access },
{ &vop_getattr_desc, (vop_t *)nfs_getattr },
{ &vop_setattr_desc, (vop_t *)nfs_setattr },
{ &vop_read_desc, (vop_t *)nfsfifo_read },
{ &vop_write_desc, (vop_t *)nfsfifo_write },
{ &vop_lease_desc, (vop_t *)fifo_lease_check },
{ &vop_ioctl_desc, (vop_t *)fifo_ioctl },
{ &vop_select_desc, (vop_t *)fifo_select },
{ &vop_revoke_desc, (vop_t *)fifo_revoke },
{ &vop_mmap_desc, (vop_t *)fifo_mmap },
{ &vop_fsync_desc, (vop_t *)nfs_fsync },
{ &vop_seek_desc, (vop_t *)fifo_seek },
{ &vop_remove_desc, (vop_t *)fifo_remove },
{ &vop_link_desc, (vop_t *)fifo_link },
{ &vop_rename_desc, (vop_t *)fifo_rename },
{ &vop_mkdir_desc, (vop_t *)fifo_mkdir },
{ &vop_rmdir_desc, (vop_t *)fifo_rmdir },
{ &vop_symlink_desc, (vop_t *)fifo_symlink },
{ &vop_readdir_desc, (vop_t *)fifo_readdir },
{ &vop_readlink_desc, (vop_t *)fifo_readlink },
{ &vop_abortop_desc, (vop_t *)fifo_abortop },
{ &vop_inactive_desc, (vop_t *)nfs_inactive },
{ &vop_reclaim_desc, (vop_t *)nfs_reclaim },
{ &vop_lock_desc, (vop_t *)nfs_lock },
{ &vop_unlock_desc, (vop_t *)nfs_unlock },
{ &vop_bmap_desc, (vop_t *)fifo_bmap },
{ &vop_strategy_desc, (vop_t *)fifo_strategy },
{ &vop_print_desc, (vop_t *)nfs_print },
{ &vop_islocked_desc, (vop_t *)nfs_islocked },
{ &vop_pathconf_desc, (vop_t *)fifo_pathconf },
{ &vop_advlock_desc, (vop_t *)fifo_advlock },
{ &vop_blkatoff_desc, (vop_t *)fifo_blkatoff },
{ &vop_valloc_desc, (vop_t *)fifo_valloc },
{ &vop_reallocblks_desc, (vop_t *)fifo_reallocblks },
{ &vop_vfree_desc, (vop_t *)fifo_vfree },
{ &vop_truncate_desc, (vop_t *)fifo_truncate },
{ &vop_update_desc, (vop_t *)nfs_update },
{ &vop_bwrite_desc, (vop_t *)vn_bwrite },
{ &vop_pagein_desc, (vop_t *)nfs_pagein },
{ &vop_pageout_desc, (vop_t *)nfs_pageout },
{ &vop_blktooff_desc, (vop_t *)nfs_blktooff },
{ &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },
{ &vop_cmap_desc, (vop_t *)nfs_cmap },
{ NULL, NULL }
};
struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
{ &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
#ifdef __FreeBSD__
VNODEOP_SET(fifo_nfsv2nodeop_opv_desc);
#endif
static int nfs_commit __P((struct vnode *vp, u_quad_t offset, int cnt,
struct ucred *cred, struct proc *procp));
static int nfs_mknodrpc __P((struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp,
struct vattr *vap));
static int nfs_removerpc __P((struct vnode *dvp, char *name, int namelen,
struct ucred *cred, struct proc *proc));
static int nfs_renamerpc __P((struct vnode *fdvp, char *fnameptr,
int fnamelen, struct vnode *tdvp,
char *tnameptr, int tnamelen,
struct ucred *cred, struct proc *proc));
static int nfs_renameit __P((struct vnode *sdvp,
struct componentname *scnp,
struct sillyrename *sp));
extern u_long nfs_true, nfs_false;
extern struct nfsstats nfsstats;
extern nfstype nfsv3_type[9];
struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON];
int nfs_numasync = 0;
#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
static int nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
#define NFSV3ACCESS_ALL (NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY \
| NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE \
| NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, RETRYWITHSLEEP, SEVER};
static int errorcount[ELAST+1];
static const short errortooutcome[ELAST+1] = {
NOACTION,
DUMP,
DUMP,
DUMPANDLOG,
RETRY,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMP,
DUMP,
DUMP,
DUMP,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMP,
DUMP,
DUMPANDLOG,
DUMP,
DUMP,
RETRY,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
RETRY,
DUMPANDLOG,
DUMP,
RETRY,
RETRY,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMP,
DUMP,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
DUMPANDLOG,
};
static short
nfs_pageouterrorhandler(error)
int error;
{
if (error > ELAST)
return(DUMP);
else
return(errortooutcome[error]);
}
static int
nfs3_access_otw(struct vnode *vp,
int wmode,
struct proc *p,
struct ucred *cred)
{
const int v3 = 1;
u_int32_t *tl;
int error = 0, attrflag;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
caddr_t bpos, dpos, cp2;
register int32_t t1, t2;
register caddr_t cp;
u_int32_t rmode;
struct nfsnode *np = VTONFS(vp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_ACCESS]++;
nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED);
nfsm_fhtom(vp, v3);
nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(wmode);
nfsm_request(vp, NFSPROC_ACCESS, p, cred, &xid);
nfsm_postop_attr(vp, attrflag, &xid);
if (!error) {
nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
rmode = fxdr_unsigned(u_int32_t, *tl);
np->n_mode = rmode;
np->n_modeuid = cred->cr_uid;
np->n_modestamp = time_second;
}
nfsm_reqdone;
return error;
}
static int
nfs_access(ap)
struct vop_access_args *ap;
{
register struct vnode *vp = ap->a_vp;
int error = 0;
u_long mode, wmode;
int v3 = NFS_ISV3(vp);
struct nfsnode *np = VTONFS(vp);
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 | NFSV3ACCESS_DELETE;
if (ap->a_mode & VEXEC)
mode |= NFSV3ACCESS_LOOKUP;
} else {
if (ap->a_mode & VWRITE)
mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
if (ap->a_mode & VEXEC)
mode |= NFSV3ACCESS_EXECUTE;
}
if (nfsaccess_cache_timeout > 0) {
wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
} else
wmode = mode;
if (time_second < np->n_modestamp + nfsaccess_cache_timeout &&
ap->a_cred->cr_uid == np->n_modeuid &&
(np->n_mode & mode) == mode) {
} else {
error = nfs3_access_otw(vp, wmode, ap->a_p,ap->a_cred);
if (!error) {
if ((np->n_mode & mode) != mode)
error = EACCES;
}
}
} else
return (nfsspec_access(ap));
if (!error && (ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG: case VDIR: case VLNK:
error = EROFS;
default:
break;
}
}
return (error);
}
static int
nfs_open(ap)
struct vop_open_args *ap;
{
register struct vnode *vp = ap->a_vp;
struct nfsnode *np = VTONFS(vp);
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
struct vattr vattr;
int error;
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) {
return (EACCES);
}
if (nmp->nm_flag & NFSMNT_NQNFS) {
if (NQNFS_CKINVALID(vp, np, ND_READ)) {
do {
error = nqnfs_getlease(vp, ND_READ, ap->a_cred,
ap->a_p);
} while (error == NQNFS_EXPIRED);
if (error)
return (error);
if (np->n_lrev != np->n_brev ||
(np->n_flag & NQNFSNONCACHE)) {
if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
ap->a_p, 1)) == EINTR)
return (error);
np->n_brev = np->n_lrev;
}
}
} else {
if (np->n_flag & NMODIFIED) {
if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
ap->a_p, 1)) == EINTR)
return (error);
np->n_attrstamp = 0;
if (vp->v_type == VDIR)
np->n_direofoffset = 0;
error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
if (error)
return (error);
np->n_mtime = vattr.va_mtime.tv_sec;
} else {
error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
if (error)
return (error);
if (np->n_mtime != vattr.va_mtime.tv_sec) {
if (vp->v_type == VDIR)
np->n_direofoffset = 0;
if ((error = nfs_vinvalbuf(vp, V_SAVE,
ap->a_cred, ap->a_p, 1)) == EINTR)
return (error);
np->n_mtime = vattr.va_mtime.tv_sec;
}
}
}
if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
np->n_attrstamp = 0;
return (0);
}
static int
nfs_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
int error = 0;
if (vp->v_type == VREG) {
#if DIAGNOSTIC
register struct sillyrename *sp = np->n_sillyrename;
if (sp)
kprintf("nfs_close: %s, dvp=%x, vp=%x, ap=%x, np=%x, sp=%x\n",
&sp->s_name[0], (unsigned)(sp->s_dvp), (unsigned)vp,
(unsigned)ap, (unsigned)np, (unsigned)sp);
#endif
if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
(np->n_flag & NMODIFIED)) {
if (NFS_ISV3(vp)) {
error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 1);
} else
error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
np->n_attrstamp = 0;
}
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
error = np->n_error;
}
}
return (error);
}
static int
nfs_getattr(ap)
struct vop_getattr_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
register caddr_t cp;
register u_long *tl;
register int t1, t2;
caddr_t bpos, dpos;
int error = 0;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
int avoidfloods;
FSDBG_TOP(513, np->n_size, np, np->n_vattr.va_size, np->n_flag);
if (np->n_flag & (NACC | NUPD))
np->n_flag |= NCHG;
if ((error = nfs_getattrcache(vp, ap->a_vap)) == 0) {
FSDBG_BOT(513, np->n_size, 0, np->n_vattr.va_size, np->n_flag);
return (0);
}
if (error != ENOENT) {
FSDBG_BOT(513, np->n_size, error, np->n_vattr.va_size,
np->n_flag);
return (error);
}
error = 0;
if (v3 && nfsaccess_cache_timeout > 0) {
if (error = nfs3_access_otw(vp, NFSV3ACCESS_ALL, ap->a_p,
ap->a_cred))
return (error);
if ((error = nfs_getattrcache(vp, ap->a_vap)) == 0)
return (0);
if (error != ENOENT)
return (error);
error = 0;
}
avoidfloods = 0;
tryagain:
nfsstats.rpccnt[NFSPROC_GETATTR]++;
nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3));
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred, &xid);
if (!error) {
nfsm_loadattr(vp, ap->a_vap, &xid);
if (!xid) {
m_freem(mrep);
FSDBG(513, -1, np, np->n_xid << 32, np->n_xid);
if (avoidfloods++ < 100)
goto tryagain;
panic("nfs_getattr: getattr flood\n");
}
if (np->n_mtime != ap->a_vap->va_mtime.tv_sec) {
FSDBG(513, -1, np, -1, vp);
if (vp->v_type == VDIR)
nfs_invaldir(vp);
error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
ap->a_p, 1);
FSDBG(513, -1, np, -2, error);
if (!error)
np->n_mtime = ap->a_vap->va_mtime.tv_sec;
}
}
nfsm_reqdone;
FSDBG_BOT(513, np->n_size, -1, np->n_vattr.va_size, error);
return (error);
}
static int
nfs_setattr(ap)
struct vop_setattr_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
register struct vattr *vap = ap->a_vap;
int error = 0;
u_quad_t tsize;
#ifndef nolint
tsize = (u_quad_t)0;
#endif
#ifdef XXX
if (vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
#endif
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);
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
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 == (u_short)VNOVAL &&
vap->va_uid == (uid_t)VNOVAL &&
vap->va_gid == (gid_t)VNOVAL)
return (0);
vap->va_size = VNOVAL;
break;
default:
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
FSDBG_TOP(512, np->n_size, vap->va_size,
np->n_vattr.va_size, np->n_flag);
if (np->n_flag & NMODIFIED) {
if (vap->va_size == 0)
error = nfs_vinvalbuf(vp, 0,
ap->a_cred, ap->a_p, 1);
else
error = nfs_vinvalbuf(vp, V_SAVE,
ap->a_cred, ap->a_p, 1);
if (error) {
printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
FSDBG_BOT(512, np->n_size, vap->va_size,
np->n_vattr.va_size, -1);
return (error);
}
} else if (np->n_size > vap->va_size) {
daddr_t obn, bn;
int biosize;
struct buf *bp;
biosize = min(vp->v_mount->mnt_stat.f_iosize,
PAGE_SIZE);
obn = (np->n_size - 1) / biosize;
bn = vap->va_size / biosize;
for ( ; obn >= bn; obn--)
if (incore(vp, obn)) {
bp = getblk(vp, obn, biosize, 0,
0, BLK_READ);
FSDBG(512, bp, bp->b_flags,
0, obn);
SET(bp->b_flags, B_INVAL);
brelse(bp);
}
}
tsize = np->n_size;
np->n_size = np->n_vattr.va_size = vap->va_size;
ubc_setsize(vp, (off_t)vap->va_size);
};
} else if ((vap->va_mtime.tv_sec != VNOVAL ||
vap->va_atime.tv_sec != VNOVAL) &&
(np->n_flag & NMODIFIED) && vp->v_type == VREG &&
(error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
ap->a_p, 1)) == EINTR)
return (error);
error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p);
FSDBG_BOT(512, np->n_size, vap->va_size, np->n_vattr.va_size, error);
if (error && vap->va_size != VNOVAL) {
int err = 0;
printf("nfs_setattr: nfs_setattrrpc %d\n", error);
np->n_size = np->n_vattr.va_size = tsize;
ubc_setsize(vp, (off_t)np->n_size);
vap->va_size = tsize;
err = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p);
if (err)
printf("nfs_setattr1: nfs_setattrrpc %d\n", err);
}
return (error);
}
static int
nfs_setattrrpc(vp, vap, cred, procp)
register struct vnode *vp;
register struct vattr *vap;
struct ucred *cred;
struct proc *procp;
{
register struct nfsv2_sattr *sp;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
u_long *tl;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_SETATTR]++;
nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3));
nfsm_fhtom(vp, v3);
if (v3) {
if (vap->va_mode != (u_short)VNOVAL) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_mode);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (vap->va_uid != (uid_t)VNOVAL) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_uid);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (vap->va_gid != (gid_t)VNOVAL) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_true;
*tl = txdr_unsigned(vap->va_gid);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (vap->va_size != VNOVAL) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = nfs_true;
txdr_hyper(&vap->va_size, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
}
if (vap->va_atime.tv_sec != VNOVAL) {
if (vap->va_atime.tv_sec != time.tv_sec) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_atime, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
if (vap->va_mtime.tv_sec != VNOVAL) {
if (vap->va_mtime.tv_sec != time.tv_sec) {
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
txdr_nfsv3time(&vap->va_mtime, tl);
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
}
} else {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
}
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = nfs_false;
} else {
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
if (vap->va_mode == (u_short)VNOVAL)
sp->sa_mode = VNOVAL;
else
sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode);
if (vap->va_uid == (uid_t)VNOVAL)
sp->sa_uid = VNOVAL;
else
sp->sa_uid = txdr_unsigned(vap->va_uid);
if (vap->va_gid == (gid_t)VNOVAL)
sp->sa_gid = VNOVAL;
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);
}
nfsm_request(vp, NFSPROC_SETATTR, procp, cred, &xid);
if (v3) {
nfsm_wcc_data(vp, wccflag, &xid);
if (!wccflag && vp->v_type != VBAD)
VTONFS(vp)->n_attrstamp = 0;
} else
nfsm_loadattr(vp, (struct vattr *)0, &xid);
nfsm_reqdone;
return (error);
}
static int
nfs_lookup(ap)
struct vop_lookup_args *ap;
{
register struct componentname *cnp = ap->a_cnp;
register struct vnode *dvp = ap->a_dvp;
register struct vnode **vpp = ap->a_vpp;
register int flags = cnp->cn_flags;
register struct vnode *newvp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
struct nfsmount *nmp;
caddr_t bpos, dpos, cp2;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
long len;
nfsfh_t *fhp;
struct nfsnode *np;
int lockparent, wantparent, error = 0, attrflag, fhsize;
int v3 = NFS_ISV3(dvp);
struct proc *p = cnp->cn_proc;
int worldbuildworkaround = 1;
u_int64_t xid;
if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
*vpp = NULLVP;
if (dvp->v_type != VDIR)
return (ENOTDIR);
lockparent = flags & LOCKPARENT;
wantparent = flags & (LOCKPARENT|WANTPARENT);
nmp = VFSTONFS(dvp->v_mount);
np = VTONFS(dvp);
if (worldbuildworkaround) {
VREF(dvp);
VREF(dvp);
}
if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
struct vattr vattr;
int vpid;
if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, p))) {
*vpp = NULLVP;
goto error_return;
}
if ((*vpp)->v_type == VBAD) {
error = EINVAL;
goto error_return;
}
newvp = *vpp;
vpid = newvp->v_id;
if (dvp == newvp) {
VREF(newvp);
error = 0;
} else if (flags & ISDOTDOT) {
VOP_UNLOCK(dvp, 0, p);
error = vget(newvp, LK_EXCLUSIVE, p);
if (!error && lockparent && (flags & ISLASTCN))
error = vn_lock(dvp, LK_EXCLUSIVE, p);
} else {
error = vget(newvp, LK_EXCLUSIVE, p);
if (!lockparent || error || !(flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
}
if (!error) {
if (vpid == newvp->v_id) {
if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, p)
&& vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) {
nfsstats.lookupcache_hits++;
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
error = 0;
goto error_return;
}
cache_purge(newvp);
}
vput(newvp);
if (lockparent && dvp != newvp && (flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
}
error = vn_lock(dvp, LK_EXCLUSIVE, p);
*vpp = NULLVP;
if (error)
goto error_return;
}
if (dvp->v_type == VBAD) {
error = EINVAL;
goto error_return;
}
error = 0;
newvp = NULLVP;
nfsstats.lookupcache_misses++;
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
len = cnp->cn_namelen;
nfsm_reqhead(dvp, NFSPROC_LOOKUP,
NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred, &xid);
if (error) {
nfsm_postop_attr(dvp, attrflag, &xid);
m_freem(mrep);
goto nfsmout;
}
nfsm_getfh(fhp, fhsize, v3);
if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
if (NFS_CMPFH(np, fhp, fhsize)) {
m_freem(mrep);
error = EISDIR;
goto error_return;
}
if ((error = nfs_nget(dvp->v_mount, fhp, fhsize, &np))) {
m_freem(mrep);
goto error_return;
}
newvp = NFSTOV(np);
if (v3) {
u_int64_t dxid = xid;
nfsm_postop_attr(newvp, attrflag, &xid);
nfsm_postop_attr(dvp, attrflag, &dxid);
} else
nfsm_loadattr(newvp, (struct vattr *)0, &xid);
*vpp = newvp;
m_freem(mrep);
cnp->cn_flags |= SAVENAME;
if (!lockparent)
VOP_UNLOCK(dvp, 0, p);
error = 0;
goto error_return;
}
if (flags & ISDOTDOT) {
VOP_UNLOCK(dvp, 0, p);
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
if (error) {
vn_lock(dvp, LK_EXCLUSIVE + LK_RETRY, p);
goto error_return;
}
newvp = NFSTOV(np);
if (lockparent && (flags & ISLASTCN) &&
(error = vn_lock(dvp, LK_EXCLUSIVE, p))) {
vput(newvp);
goto error_return;
}
} else if (NFS_CMPFH(np, fhp, fhsize)) {
VREF(dvp);
newvp = dvp;
} else {
if ((error = nfs_nget(dvp->v_mount, fhp, fhsize, &np))) {
m_freem(mrep);
goto error_return;
}
if (!lockparent || !(flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
newvp = NFSTOV(np);
}
if (v3) {
u_int64_t dxid = xid;
nfsm_postop_attr(newvp, attrflag, &xid);
nfsm_postop_attr(dvp, attrflag, &dxid);
} else
nfsm_loadattr(newvp, (struct vattr *)0, &xid);
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
if ((cnp->cn_flags & MAKEENTRY) &&
(cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
np->n_ctime = np->n_vattr.va_ctime.tv_sec;
cache_enter(dvp, newvp, cnp);
}
*vpp = newvp;
nfsm_reqdone;
if (error) {
if (newvp != NULLVP) {
vrele(newvp);
*vpp = NULLVP;
}
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN) && error == ENOENT) {
if (!lockparent)
VOP_UNLOCK(dvp, 0, p);
if (dvp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else
error = EJUSTRETURN;
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
}
error_return:
if (worldbuildworkaround) {
vrele(dvp);
vrele(dvp);
}
return (error);
}
static int
nfs_read(ap)
struct vop_read_args *ap;
{
register struct vnode *vp = ap->a_vp;
if (vp->v_type != VREG)
return (EPERM);
return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0));
}
static int
nfs_readlink(ap)
struct vop_readlink_args *ap;
{
register struct vnode *vp = ap->a_vp;
if (vp->v_type != VLNK)
return (EPERM);
return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred, 0));
}
int
nfs_readlinkrpc(vp, uiop, cred)
register struct vnode *vp;
struct uio *uiop;
struct ucred *cred;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, len, attrflag;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_READLINK]++;
nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3));
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred, &xid);
if (v3)
nfsm_postop_attr(vp, attrflag, &xid);
if (!error) {
nfsm_strsiz(len, NFS_MAXPATHLEN);
if (len == NFS_MAXPATHLEN) {
struct nfsnode *np = VTONFS(vp);
#if DIAGNOSTIC
if (!np)
panic("nfs_readlinkrpc: null np");
#endif
if (np->n_size && np->n_size < NFS_MAXPATHLEN)
len = np->n_size;
}
nfsm_mtouio(uiop, len);
}
nfsm_reqdone;
return (error);
}
int
nfs_readrpc(vp, uiop, cred)
register struct vnode *vp;
struct uio *uiop;
struct ucred *cred;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct nfsmount *nmp;
int error = 0, len, retlen, tsiz, eof, attrflag;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
#ifndef nolint
eof = 0;
#endif
nmp = VFSTONFS(vp->v_mount);
tsiz = uiop->uio_resid;
if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) &&
!v3)
return (EFBIG);
while (tsiz > 0) {
nfsstats.rpccnt[NFSPROC_READ]++;
len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3);
nfsm_fhtom(vp, v3);
nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
if (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;
}
nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred, &xid);
if (v3) {
nfsm_postop_attr(vp, attrflag, &xid);
if (error) {
m_freem(mrep);
goto nfsmout;
}
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
eof = fxdr_unsigned(int, *(tl + 1));
} else
nfsm_loadattr(vp, (struct vattr *)0, &xid);
nfsm_strsiz(retlen, nmp->nm_rsize);
nfsm_mtouio(uiop, retlen);
m_freem(mrep);
tsiz -= retlen;
if (v3) {
if (eof || retlen == 0)
tsiz = 0;
} else if (retlen < len)
tsiz = 0;
}
nfsmout:
return (error);
}
int
nfs_writerpc(vp, uiop, cred, iomode, must_commit)
register struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
int *iomode, *must_commit;
{
register u_long *tl;
register caddr_t cp;
register int t1, t2, backup;
caddr_t bpos, dpos, cp2;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit;
int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC;
u_int64_t xid;
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1)
panic("nfs_writerpc: iovcnt > 1");
#endif
*must_commit = 0;
tsiz = uiop->uio_resid;
if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3)
return (EFBIG);
while (tsiz > 0) {
nfsstats.rpccnt[NFSPROC_WRITE]++;
len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
nfsm_reqhead(vp, NFSPROC_WRITE,
NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
nfsm_fhtom(vp, v3);
if (v3) {
nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
txdr_hyper(&uiop->uio_offset, tl);
tl += 2;
*tl++ = txdr_unsigned(len);
*tl++ = txdr_unsigned(*iomode);
} else {
nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
*++tl = txdr_unsigned(uiop->uio_offset);
tl += 2;
}
*tl = txdr_unsigned(len);
nfsm_uiotom(uiop, len);
nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred, &xid);
if (v3) {
wccflag = NFSV3_WCCCHK;
nfsm_wcc_data(vp, wccflag, &xid);
if (!error) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
NFSX_V3WRITEVERF);
rlen = fxdr_unsigned(int, *tl++);
if (rlen <= 0) {
error = NFSERR_IO;
break;
} else if (rlen < len) {
backup = len - rlen;
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_flag & NFSMNT_HASWRITEVERF) == 0) {
bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
NFSX_V3WRITEVERF);
nmp->nm_flag |= NFSMNT_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
nfsm_loadattr(vp, (struct vattr *)0, &xid);
if (wccflag && vp->v_type != VBAD)
VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.tv_sec;
m_freem(mrep);
if (error)
break;
tsiz -= len;
}
nfsmout:
if (vp->v_type != VBAD && (vp->v_mount->mnt_flag & MNT_ASYNC))
committed = NFSV3WRITE_FILESYNC;
*iomode = committed;
if (error)
uiop->uio_resid = tsiz;
return (error);
}
static int
nfs_mknodrpc(dvp, vpp, cnp, vap)
register struct vnode *dvp;
register struct vnode **vpp;
register struct componentname *cnp;
register struct vattr *vap;
{
register struct nfsv2_sattr *sp;
register struct nfsv3_sattr *sp3;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
struct vnode *newvp = (struct vnode *)0;
struct nfsnode *np = (struct nfsnode *)0;
struct vattr vattr;
char *cp2;
caddr_t bpos, dpos;
int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
u_long rdev;
u_int64_t xid;
int v3 = NFS_ISV3(dvp);
if (vap->va_type == VCHR || vap->va_type == VBLK)
rdev = txdr_unsigned(vap->va_rdev);
else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
rdev = 0xffffffff;
else {
VOP_ABORTOP(dvp, cnp);
vput(dvp);
return (EOPNOTSUPP);
}
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) {
VOP_ABORTOP(dvp, cnp);
vput(dvp);
return (error);
}
nfsstats.rpccnt[NFSPROC_MKNOD]++;
nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
+ nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
if (v3) {
nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR);
*tl++ = vtonfsv3_type(vap->va_type);
sp3 = (struct nfsv3_sattr *)tl;
nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
if (vap->va_type == VCHR || vap->va_type == VBLK) {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(major(vap->va_rdev));
*tl = txdr_unsigned(minor(vap->va_rdev));
}
} else {
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
sp->sa_gid = txdr_unsigned(vattr.va_gid);
sp->sa_size = rdev;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred, &xid);
if (!error) {
nfsm_mtofh(dvp, newvp, v3, gotvp, &xid);
if (!gotvp) {
if (newvp) {
vput(newvp);
newvp = (struct vnode *)0;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (v3)
nfsm_wcc_data(dvp, wccflag, &xid);
nfsm_reqdone;
if (error) {
if (newvp)
vput(newvp);
} else {
if (cnp->cn_flags & MAKEENTRY)
cache_enter(dvp, newvp, cnp);
*vpp = newvp;
}
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
vput(dvp);
return (error);
}
static int
nfs_mknod(ap)
struct vop_mknod_args *ap;
{
struct vnode *newvp;
int error;
error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap);
if (!error && newvp)
vput(newvp);
*ap->a_vpp = 0;
return (error);
}
static u_long create_verf;
static int
nfs_create(ap)
struct vop_create_args *ap;
{
register struct vnode *dvp = ap->a_dvp;
register struct vattr *vap = ap->a_vap;
register struct componentname *cnp = ap->a_cnp;
register struct nfsv2_sattr *sp;
register struct nfsv3_sattr *sp3;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
struct nfsnode *np = (struct nfsnode *)0;
struct vnode *newvp = (struct vnode *)0;
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct vattr vattr;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
if (vap->va_type == VSOCK)
return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) {
VOP_ABORTOP(dvp, cnp);
vput(dvp);
return (error);
}
if (vap->va_vaflags & VA_EXCLUSIVE)
fmode |= O_EXCL;
again:
nfsstats.rpccnt[NFSPROC_CREATE]++;
nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
if (v3) {
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
if (fmode & O_EXCL) {
*tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
if (!TAILQ_EMPTY(&in_ifaddrhead))
*tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr;
else
*tl++ = create_verf;
*tl = ++create_verf;
} else {
*tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
nfsm_build(tl, u_long *, NFSX_V3SRVSATTR);
sp3 = (struct nfsv3_sattr *)tl;
nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
}
} else {
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
sp->sa_gid = txdr_unsigned(vattr.va_gid);
sp->sa_size = 0;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred, &xid);
if (!error) {
nfsm_mtofh(dvp, newvp, v3, gotvp, &xid);
if (!gotvp) {
if (newvp) {
vput(newvp);
newvp = (struct vnode *)0;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
if (!error)
newvp = NFSTOV(np);
}
}
if (v3)
nfsm_wcc_data(dvp, wccflag, &xid);
nfsm_reqdone;
if (error) {
if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
fmode &= ~O_EXCL;
goto again;
}
if (newvp)
vput(newvp);
} else if (v3 && (fmode & O_EXCL))
error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc);
if (!error) {
if (cnp->cn_flags & MAKEENTRY)
cache_enter(dvp, newvp, cnp);
*ap->a_vpp = newvp;
}
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
vput(dvp);
return (error);
}
static int
nfs_remove(ap)
struct vop_remove_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct vnode *dvp = ap->a_dvp;
register struct componentname *cnp = ap->a_cnp;
register struct nfsnode *np = VTONFS(vp);
int error = 0, gofree = 0;
struct vattr vattr;
#if DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("nfs_remove: no name");
if (vp->v_usecount < 1)
panic("nfs_remove: bad v_usecount");
#endif
if (UBCISVALID(vp)) {
if (UBCINFOEXISTS(vp))
gofree = (ubc_isinuse(vp, 1)) ? 0 : 1;
else {
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
vput(dvp);
vput(vp);
return (EIO);
}
} else {
if (vp->v_usecount == 1)
gofree = 1;
}
if (gofree || (np->n_sillyrename &&
VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 &&
vattr.va_nlink > 1)) {
cache_purge(vp);
error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
np->n_size = 0;
ubc_setsize(vp, (off_t)0);
if (error != EINTR)
error = nfs_removerpc(dvp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc);
if (error == ENOENT)
error = 0;
} else if (!np->n_sillyrename) {
error = nfs_sillyrename(dvp, vp, cnp);
}
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
np->n_attrstamp = 0;
vput(dvp);
VOP_UNLOCK(vp, 0, cnp->cn_proc);
ubc_uncache(vp);
vrele(vp);
return (error);
}
int
nfs_removeit(sp)
register struct sillyrename *sp;
{
return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred,
(struct proc *)0));
}
static int
nfs_removerpc(dvp, name, namelen, cred, proc)
register struct vnode *dvp;
char *name;
int namelen;
struct ucred *cred;
struct proc *proc;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_REMOVE]++;
nfsm_reqhead(dvp, NFSPROC_REMOVE,
NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
nfsm_fhtom(dvp, v3);
nfsm_strtom(name, namelen, NFS_MAXNAMLEN);
nfsm_request(dvp, NFSPROC_REMOVE, proc, cred, &xid);
if (v3)
nfsm_wcc_data(dvp, wccflag, &xid);
nfsm_reqdone;
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
return (error);
}
static int
nfs_rename(ap)
struct vop_rename_args *ap;
{
register struct vnode *fvp = ap->a_fvp;
register struct vnode *tvp = ap->a_tvp;
register struct vnode *fdvp = ap->a_fdvp;
register struct vnode *tdvp = ap->a_tdvp;
register struct componentname *tcnp = ap->a_tcnp;
register struct componentname *fcnp = ap->a_fcnp;
int error, purged=0, inuse=0;
#if DIAGNOSTIC
if ((tcnp->cn_flags & HASBUF) == 0 ||
(fcnp->cn_flags & HASBUF) == 0)
panic("nfs_rename: no name");
#endif
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
if (tvp)
VOP_UNLOCK(tvp, 0, tcnp->cn_proc);
goto out;
}
if (tvp && tvp != fvp) {
if (UBCISVALID(tvp)) {
if (UBCINFOEXISTS(tvp))
inuse = (ubc_isinuse(tvp, 1)) ? 1 : 0;
else {
error = EIO;
VOP_UNLOCK(tvp, 0, tcnp->cn_proc);
goto out;
}
} else {
if (tvp->v_usecount > 1)
inuse = 1;
}
}
if (inuse && !VTONFS(tvp)->n_sillyrename && tvp->v_type != VDIR) {
if (error = nfs_sillyrename(tdvp, tvp, tcnp)) {
goto out;
} else {
VOP_UNLOCK(tvp, 0, tcnp->cn_proc);
ubc_uncache(tvp);
vrele(tvp);
tvp = NULL;
}
}
error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
tcnp->cn_proc);
if (fvp->v_type == VDIR) {
if (tvp != NULL && tvp->v_type == VDIR) {
cache_purge(tdvp);
if (tvp == tdvp)
purged = 1;
}
cache_purge(fdvp);
}
cache_purge(fvp);
if (tvp) {
if (!purged)
cache_purge(tvp);
VOP_UNLOCK(tvp, 0, tcnp->cn_proc);
ubc_uncache(tvp);
}
out:
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vrele(tvp);
vrele(fdvp);
vrele(fvp);
if (error == ENOENT)
error = 0;
return (error);
}
static int
nfs_renameit(sdvp, scnp, sp)
struct vnode *sdvp;
struct componentname *scnp;
register 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_proc));
}
static int
nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
register struct vnode *fdvp;
char *fnameptr;
int fnamelen;
register struct vnode *tdvp;
char *tnameptr;
int tnamelen;
struct ucred *cred;
struct proc *proc;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(fdvp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_RENAME]++;
nfsm_reqhead(fdvp, NFSPROC_RENAME,
(NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
nfsm_rndup(tnamelen));
nfsm_fhtom(fdvp, v3);
nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN);
nfsm_fhtom(tdvp, v3);
nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN);
nfsm_request(fdvp, NFSPROC_RENAME, proc, cred, &xid);
if (v3) {
u_int64_t txid = xid;
nfsm_wcc_data(fdvp, fwccflag, &xid);
nfsm_wcc_data(tdvp, twccflag, &txid);
}
nfsm_reqdone;
if (fdvp->v_type != VBAD) {
VTONFS(fdvp)->n_flag |= NMODIFIED;
if (!fwccflag)
VTONFS(fdvp)->n_attrstamp = 0;
}
if (tdvp->v_type != VBAD) {
VTONFS(tdvp)->n_flag |= NMODIFIED;
if (!twccflag)
VTONFS(tdvp)->n_attrstamp = 0;
}
return (error);
}
static int
nfs_link(ap)
struct vop_link_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct vnode *tdvp = ap->a_tdvp;
register struct componentname *cnp = ap->a_cnp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
if (vp->v_mount != tdvp->v_mount) {
VOP_ABORTOP(vp, cnp);
if (tdvp == vp)
vrele(tdvp);
else
vput(tdvp);
return (EXDEV);
}
VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc);
nfsstats.rpccnt[NFSPROC_LINK]++;
nfsm_reqhead(vp, NFSPROC_LINK,
NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
nfsm_fhtom(vp, v3);
nfsm_fhtom(tdvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred, &xid);
if (v3) {
u_int64_t txid = xid;
nfsm_postop_attr(vp, attrflag, &xid);
nfsm_wcc_data(tdvp, wccflag, &txid);
}
nfsm_reqdone;
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
VTONFS(tdvp)->n_flag |= NMODIFIED;
if (!attrflag && vp->v_type != VBAD)
VTONFS(vp)->n_attrstamp = 0;
if (!wccflag && tdvp->v_type != VBAD)
VTONFS(tdvp)->n_attrstamp = 0;
vput(tdvp);
if (error == EEXIST)
error = 0;
return (error);
}
static int
nfs_symlink(ap)
struct vop_symlink_args *ap;
{
register struct vnode *dvp = ap->a_dvp;
register struct vattr *vap = ap->a_vap;
register struct componentname *cnp = ap->a_cnp;
register struct nfsv2_sattr *sp;
register struct nfsv3_sattr *sp3;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct vnode *newvp = (struct vnode *)0;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_SYMLINK]++;
slen = strlen(ap->a_target);
nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED +
nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
if (v3) {
nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid,
cnp->cn_cred->cr_gid);
}
nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
if (!v3) {
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode);
sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
sp->sa_size = -1;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred, &xid);
if (v3) {
u_int64_t dxid = xid;
if (!error)
nfsm_mtofh(dvp, newvp, v3, gotvp, &xid);
nfsm_wcc_data(dvp, wccflag, &dxid);
}
nfsm_reqdone;
if (newvp)
vput(newvp);
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
vput(dvp);
if (error == EEXIST)
error = 0;
return (error);
}
static int
nfs_mkdir(ap)
struct vop_mkdir_args *ap;
{
register struct vnode *dvp = ap->a_dvp;
register struct vattr *vap = ap->a_vap;
register struct componentname *cnp = ap->a_cnp;
register struct nfsv2_sattr *sp;
register struct nfsv3_sattr *sp3;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
register int len;
struct nfsnode *np = (struct nfsnode *)0;
struct vnode *newvp = (struct vnode *)0;
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR;
int gotvp = 0;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
struct vattr vattr;
int v3 = NFS_ISV3(dvp);
u_int64_t xid, dxid;
if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) {
VOP_ABORTOP(dvp, cnp);
vput(dvp);
return (error);
}
len = cnp->cn_namelen;
nfsstats.rpccnt[NFSPROC_MKDIR]++;
nfsm_reqhead(dvp, NFSPROC_MKDIR,
NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
if (v3) {
nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
} else {
nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode);
sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
sp->sa_gid = txdr_unsigned(vattr.va_gid);
sp->sa_size = -1;
txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
}
nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred, &xid);
dxid = xid;
if (!error)
nfsm_mtofh(dvp, newvp, v3, gotvp, &xid);
if (v3)
nfsm_wcc_data(dvp, wccflag, &dxid);
nfsm_reqdone;
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
if (error == EEXIST || (!error && !gotvp)) {
if (newvp) {
vrele(newvp);
newvp = (struct vnode *)0;
}
error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred,
cnp->cn_proc, &np);
if (!error) {
newvp = NFSTOV(np);
if (newvp->v_type != VDIR)
error = EEXIST;
}
}
if (error) {
if (newvp)
vrele(newvp);
} else
*ap->a_vpp = newvp;
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
vput(dvp);
return (error);
}
static int
nfs_rmdir(ap)
struct vop_rmdir_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct vnode *dvp = ap->a_dvp;
register struct componentname *cnp = ap->a_cnp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_RMDIR]++;
nfsm_reqhead(dvp, NFSPROC_RMDIR,
NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
nfsm_fhtom(dvp, v3);
nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred, &xid);
if (v3)
nfsm_wcc_data(dvp, wccflag, &xid);
nfsm_reqdone;
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
if (dvp->v_type != VBAD) {
VTONFS(dvp)->n_flag |= NMODIFIED;
if (!wccflag)
VTONFS(dvp)->n_attrstamp = 0;
}
cache_purge(dvp);
cache_purge(vp);
vput(vp);
vput(dvp);
if (error == ENOENT)
error = 0;
return (error);
}
static int
nfs_readdir(ap)
struct vop_readdir_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
register struct uio *uio = ap->a_uio;
int tresid, error;
struct vattr vattr;
if (vp->v_type != VDIR)
return (EPERM);
if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
(np->n_flag & NMODIFIED) == 0) {
if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
if (NQNFS_CKCACHABLE(vp, ND_READ)) {
nfsstats.direofcache_hits++;
return (0);
}
} else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 &&
np->n_mtime == vattr.va_mtime.tv_sec) {
nfsstats.direofcache_hits++;
return (0);
}
}
tresid = uio->uio_resid;
error = nfs_bioread(vp, uio, 0, ap->a_cred, 0);
if (!error && uio->uio_resid == tresid)
nfsstats.direofcache_misses++;
return (error);
}
int
nfs_readdirrpc(vp, uiop, cred)
struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
{
register int len, left;
register struct dirent *dp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
register nfsuint64 *cookiep;
caddr_t bpos, dpos, cp2;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
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;
int v3 = NFS_ISV3(vp);
u_int64_t xid;
#ifndef nolint
dp = (struct dirent *)0;
#endif
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
(uiop->uio_resid & (NFS_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(vp, NFSPROC_READDIR, NFSX_FH(v3) +
NFSX_READDIR(v3));
nfsm_fhtom(vp, v3);
if (v3) {
nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
*tl++ = cookie.nfsuquad[1];
*tl++ = dnp->n_cookieverf.nfsuquad[0];
*tl++ = dnp->n_cookieverf.nfsuquad[1];
} else {
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = cookie.nfsuquad[0];
}
*tl = txdr_unsigned(nmp->nm_readdirsize);
nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred, &xid);
if (v3) {
nfsm_postop_attr(vp, attrflag, &xid);
if (!error) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
dnp->n_cookieverf.nfsuquad[0] = *tl++;
dnp->n_cookieverf.nfsuquad[1] = *tl;
} else {
m_freem(mrep);
goto nfsmout;
}
}
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
while (more_dirs && bigenough) {
if (v3) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
fxdr_hyper(tl, &fileno);
len = fxdr_unsigned(int, *(tl + 2));
} else {
nfsm_dissect(tl, u_long *, 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(mrep);
goto nfsmout;
}
tlen = nfsm_rndup(len);
if (tlen == len)
tlen += 4;
left = DIRBLKSIZ - blksiz;
if ((tlen + DIRHDSIZ) > left) {
dp->d_reclen += left;
uiop->uio_iov->iov_base += left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
blksiz = 0;
}
if ((tlen + DIRHDSIZ) > uiop->uio_resid)
bigenough = 0;
if (bigenough) {
dp = (struct dirent *)uiop->uio_iov->iov_base;
dp->d_fileno = (int)fileno;
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ;
dp->d_type = DT_UNKNOWN;
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += DIRHDSIZ;
uiop->uio_resid -= DIRHDSIZ;
uiop->uio_iov->iov_base += DIRHDSIZ;
uiop->uio_iov->iov_len -= DIRHDSIZ;
nfsm_mtouio(uiop, len);
cp = uiop->uio_iov->iov_base;
tlen -= len;
*cp = '\0';
uiop->uio_iov->iov_base += tlen;
uiop->uio_iov->iov_len -= tlen;
uiop->uio_offset += tlen;
uiop->uio_resid -= tlen;
} else
nfsm_adv(nfsm_rndup(len));
if (v3) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
} else {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
}
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
if (v3)
cookie.nfsuquad[1] = *tl++;
} else if (v3)
tl += 2;
else
tl++;
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
m_freem(mrep);
}
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->d_reclen += left;
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)
printf("EEK! readdirrpc resid > 0\n");
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
*cookiep = cookie;
}
nfsmout:
return (error);
}
int
nfs_readdirplusrpc(vp, uiop, cred)
struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
{
register int len, left;
register struct dirent *dp;
register u_long *tl;
register caddr_t cp;
register long t1, t2;
register struct vnode *newvp;
register nfsuint64 *cookiep;
caddr_t bpos, dpos, cp2, dpossav1, dpossav2;
struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2;
struct nameidata nami, *ndp = &nami;
struct componentname *cnp = &ndp->ni_cnd;
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;
u_int64_t xid, savexid;
#ifndef nolint
dp = (struct dirent *)0;
#endif
#if DIAGNOSTIC
if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
(uiop->uio_resid & (DIRBLKSIZ - 1)))
panic("nfs_readdirplusrpc: bad uio");
#endif
ndp->ni_dvp = vp;
newvp = NULLVP;
cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
if (cookiep)
cookie = *cookiep;
else
return (NFSERR_BAD_COOKIE);
while (more_dirs && bigenough) {
nfsstats.rpccnt[NFSPROC_READDIRPLUS]++;
nfsm_reqhead(vp, NFSPROC_READDIRPLUS,
NFSX_FH(1) + 6 * NFSX_UNSIGNED);
nfsm_fhtom(vp, 1);
nfsm_build(tl, u_long *, 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);
nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred,
&xid);
savexid = xid;
nfsm_postop_attr(vp, attrflag, &xid);
if (error) {
m_freem(mrep);
goto nfsmout;
}
nfsm_dissect(tl, u_long *, 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) {
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
fxdr_hyper(tl, &fileno);
len = fxdr_unsigned(int, *(tl + 2));
if (len <= 0 || len > NFS_MAXNAMLEN) {
error = EBADRPC;
m_freem(mrep);
goto nfsmout;
}
tlen = nfsm_rndup(len);
if (tlen == len)
tlen += 4;
left = DIRBLKSIZ - blksiz;
if ((tlen + DIRHDSIZ) > left) {
dp->d_reclen += left;
uiop->uio_iov->iov_base += left;
uiop->uio_iov->iov_len -= left;
uiop->uio_offset += left;
uiop->uio_resid -= left;
blksiz = 0;
}
if ((tlen + DIRHDSIZ) > uiop->uio_resid)
bigenough = 0;
if (bigenough) {
dp = (struct dirent *)uiop->uio_iov->iov_base;
dp->d_fileno = (int)fileno;
dp->d_namlen = len;
dp->d_reclen = tlen + DIRHDSIZ;
dp->d_type = DT_UNKNOWN;
blksiz += dp->d_reclen;
if (blksiz == DIRBLKSIZ)
blksiz = 0;
uiop->uio_offset += DIRHDSIZ;
uiop->uio_resid -= DIRHDSIZ;
uiop->uio_iov->iov_base += DIRHDSIZ;
uiop->uio_iov->iov_len -= DIRHDSIZ;
cnp->cn_nameptr = uiop->uio_iov->iov_base;
cnp->cn_namelen = len;
nfsm_mtouio(uiop, len);
cp = uiop->uio_iov->iov_base;
tlen -= len;
*cp = '\0';
uiop->uio_iov->iov_base += tlen;
uiop->uio_iov->iov_len -= tlen;
uiop->uio_offset += tlen;
uiop->uio_resid -= tlen;
} else
nfsm_adv(nfsm_rndup(len));
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
if (bigenough) {
cookie.nfsuquad[0] = *tl++;
cookie.nfsuquad[1] = *tl++;
} else
tl += 2;
attrflag = fxdr_unsigned(int, *tl);
if (attrflag) {
dpossav1 = dpos;
mdsav1 = md;
nfsm_adv(NFSX_V3FATTR);
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
doit = fxdr_unsigned(int, *tl);
if (doit) {
nfsm_getfh(fhp, fhsize, 1);
if (NFS_CMPFH(dnp, fhp, fhsize)) {
VREF(vp);
newvp = vp;
np = dnp;
} else {
if ((error = nfs_nget(vp->v_mount, fhp,
fhsize, &np)))
doit = 0;
else
newvp = NFSTOV(np);
}
}
if (doit) {
dpossav2 = dpos;
dpos = dpossav1;
mdsav2 = md;
md = mdsav1;
xid = savexid;
nfsm_loadattr(newvp, (struct vattr *)0, &xid);
dpos = dpossav2;
md = mdsav2;
dp->d_type =
IFTODT(VTTOIF(np->n_vattr.va_type));
ndp->ni_vp = newvp;
cnp->cn_hash = 0;
for (cp = cnp->cn_nameptr, i = 1; i <= len;
i++, cp++)
cnp->cn_hash += (unsigned char)*cp * i;
if (cnp->cn_namelen <= NCHNAMLEN)
cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
}
} else {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
i = fxdr_unsigned(int, *tl);
nfsm_adv(nfsm_rndup(i));
}
if (newvp != NULLVP) {
vrele(newvp);
newvp = NULLVP;
}
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = fxdr_unsigned(int, *tl);
}
if (!more_dirs) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
more_dirs = (fxdr_unsigned(int, *tl) == 0);
}
m_freem(mrep);
}
if (blksiz > 0) {
left = DIRBLKSIZ - blksiz;
dp->d_reclen += left;
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)
printf("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;
}
return (error);
}
static int
nfs_sillyrename(dvp, vp, cnp)
struct vnode *dvp, *vp;
struct componentname *cnp;
{
register struct sillyrename *sp;
struct nfsnode *np;
int error;
short pid;
struct ucred *cred;
cache_purge(dvp);
np = VTONFS(vp);
#if DIAGNOSTIC
if (vp->v_type == VDIR)
panic("nfs_sillyrename: dir");
#endif
MALLOC_ZONE(sp, struct sillyrename *,
sizeof (struct sillyrename), M_NFSREQ, M_WAITOK);
sp->s_cred = crdup(cnp->cn_cred);
sp->s_dvp = dvp;
VREF(dvp);
pid = cnp->cn_proc->p_pid;
sp->s_namlen = sprintf(sp->s_name, ".nfsA%04x4.4", pid);
while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_proc, (struct nfsnode **)0) == 0) {
sp->s_name[4]++;
if (sp->s_name[4] > 'z') {
error = EINVAL;
goto bad;
}
}
if ((error = nfs_renameit(dvp, cnp, sp)))
goto bad;
error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
cnp->cn_proc, &np);
#if DIAGNOSTIC
kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
&sp->s_name[0], (unsigned)vp, (unsigned)np, (unsigned)dvp);
#endif
np->n_sillyrename = sp;
return (0);
bad:
vrele(sp->s_dvp);
cred = sp->s_cred;
sp->s_cred = NOCRED;
crfree(cred);
_FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
return (error);
}
static int
nfs_lookitup(dvp, name, len, cred, procp, npp)
register struct vnode *dvp;
char *name;
int len;
struct ucred *cred;
struct proc *procp;
struct nfsnode **npp;
{
register u_long *tl;
register caddr_t cp;
register long t1, t2;
struct vnode *newvp = (struct vnode *)0;
struct nfsnode *np, *dnp = VTONFS(dvp);
caddr_t bpos, dpos, cp2;
int error = 0, fhlen, attrflag;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
nfsfh_t *nfhp;
int v3 = NFS_ISV3(dvp);
u_int64_t xid;
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
nfsm_reqhead(dvp, NFSPROC_LOOKUP,
NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
nfsm_fhtom(dvp, v3);
nfsm_strtom(name, len, NFS_MAXNAMLEN);
nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred, &xid);
if (npp && !error) {
nfsm_getfh(nfhp, fhlen, v3);
if (*npp) {
np = *npp;
if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) {
_FREE_ZONE((caddr_t)np->n_fhp,
np->n_fhsize, M_NFSBIGFH);
np->n_fhp = &np->n_fh;
} else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH)
MALLOC_ZONE(np->n_fhp, nfsfh_t *,
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);
if (error) {
m_freem(mrep);
return (error);
}
newvp = NFSTOV(np);
}
if (v3) {
nfsm_postop_attr(newvp, attrflag, &xid);
if (!attrflag && *npp == NULL) {
m_freem(mrep);
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
return (ENOENT);
}
} else
nfsm_loadattr(newvp, (struct vattr *)0, &xid);
}
nfsm_reqdone;
if (npp && *npp == NULL) {
if (error) {
if (newvp)
if (newvp == dvp)
vrele(newvp);
else
vput(newvp);
} else
*npp = np;
}
return (error);
}
static int
nfs_commit(vp, offset, cnt, cred, procp)
register struct vnode *vp;
u_quad_t offset;
int cnt;
struct ucred *cred;
struct proc *procp;
{
register caddr_t cp;
register u_long *tl;
register int t1, t2;
register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
caddr_t bpos, dpos, cp2;
int error = 0, wccflag = NFSV3_WCCRATTR;
struct mbuf *mreq, *mrep, *md, *mb, *mb2;
u_int64_t xid;
FSDBG(521, vp, offset, cnt, nmp->nm_flag);
if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0)
return (0);
nfsstats.rpccnt[NFSPROC_COMMIT]++;
nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1));
nfsm_fhtom(vp, 1);
nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
txdr_hyper(&offset, tl);
tl += 2;
*tl = txdr_unsigned(cnt);
nfsm_request(vp, NFSPROC_COMMIT, procp, cred, &xid);
nfsm_wcc_data(vp, wccflag, &xid);
if (!error) {
nfsm_dissect(tl, u_long *, 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;
}
}
nfsm_reqdone;
return (error);
}
static int
nfs_bmap(ap)
struct vop_bmap_args *ap;
{
register struct vnode *vp = ap->a_vp;
int devBlockSize = DEV_BSIZE;
if (ap->a_vpp != NULL)
*ap->a_vpp = vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize,
devBlockSize);
if (ap->a_runp != NULL)
*ap->a_runp = 0;
#ifdef notyet
if (ap->a_runb != NULL)
*ap->a_runb = 0;
#endif
return (0);
}
static int
nfs_strategy(ap)
struct vop_strategy_args *ap;
{
register struct buf *bp = ap->a_bp;
struct ucred *cr;
struct proc *p;
int error = 0;
if (ISSET(bp->b_flags, B_PHYS))
panic("nfs_strategy: physio");
if (ISSET(bp->b_flags, B_ASYNC))
p = (struct proc *)0;
else
p = current_proc();
if (ISSET(bp->b_flags, B_READ))
cr = bp->b_rcred;
else
cr = bp->b_wcred;
if (!ISSET(bp->b_flags, B_ASYNC) || nfs_asyncio(bp, NOCRED))
error = nfs_doio(bp, cr, p);
return (error);
}
static int
nfs_mmap(ap)
struct vop_mmap_args *ap;
{
return (EINVAL);
}
static int
nfs_fsync(ap)
struct vop_fsync_args *ap;
{
return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));
}
static int
nfs_flush(vp, cred, waitfor, p, commit)
register struct vnode *vp;
struct ucred *cred;
int waitfor;
struct proc *p;
int commit;
{
register struct nfsnode *np = VTONFS(vp);
register struct buf *bp;
register int i;
struct buf *nbp;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos, err;
int passone = 1;
u_quad_t off, endoff, toff;
struct ucred* wcred = NULL;
struct buf **bvec = NULL;
#ifndef NFS_COMMITBVECSIZ
#define NFS_COMMITBVECSIZ 20
#endif
struct buf *bvec_on_stack[NFS_COMMITBVECSIZ];
int bvecsize = 0, bveccount;
kern_return_t kret;
upl_t upl;
FSDBG_TOP(517, vp, np, waitfor, commit);
if (nmp->nm_flag & NFSMNT_INT)
slpflag = PCATCH;
if (!commit)
passone = 0;
again:
FSDBG(518, vp->v_dirtyblkhd.lh_first, np->n_flag, 0, 0);
if (vp->v_dirtyblkhd.lh_first)
np->n_flag |= NMODIFIED;
off = (u_quad_t)-1;
endoff = 0;
bvecpos = 0;
if (NFS_ISV3(vp) && commit) {
s = splbio();
bveccount = 0;
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
== (B_DELWRI | B_NEEDCOMMIT))
bveccount++;
FSDBG(519, bp, bp->b_flags, bveccount, 0);
}
if (bvec != NULL && bvec != bvec_on_stack)
_FREE(bvec, M_TEMP);
if (bveccount > NFS_COMMITBVECSIZ) {
MALLOC(bvec, struct buf **,
bveccount * sizeof(struct buf *), M_TEMP,
M_NOWAIT);
if (bvec == NULL) {
bvec = bvec_on_stack;
bvecsize = NFS_COMMITBVECSIZ;
} else
bvecsize = bveccount;
} else {
bvec = bvec_on_stack;
bvecsize = NFS_COMMITBVECSIZ;
}
FSDBG(519, 0, bvecsize, bveccount, 0);
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
FSDBG(520, bp, bp->b_flags, bvecpos, bp->b_bufsize);
FSDBG(520, bp->b_validoff, bp->b_validend,
bp->b_dirtyoff, bp->b_dirtyend);
if (bvecpos >= bvecsize)
break;
if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
!= (B_DELWRI | B_NEEDCOMMIT))
continue;
bremfree(bp);
SET(bp->b_flags, B_BUSY);
if (ISSET(bp->b_flags, B_PAGELIST)) {
upl = bp->b_pagelist;
} else {
kret = ubc_create_upl(vp, ubc_blktooff(vp, bp->b_lblkno),
bp->b_bufsize, &upl,
NULL, UPL_PRECIOUS);
if (kret != KERN_SUCCESS)
panic("nfs_flush: create upl %d", kret);
#ifdef UBC_DEBUG
upl_ubc_alias_set(upl, current_act(), 1);
#endif
}
if (upl_dirty_page(ubc_upl_pageinfo(upl), 0)) {
if (!ISSET(bp->b_flags, B_PAGELIST)) {
err = ubc_upl_abort(upl, NULL);
if (err)
printf("nfs_flush: upl abort %d\n", err);
}
bp->b_dirtyoff = bp->b_validoff;
bp->b_dirtyend = bp->b_validend;
CLR(bp->b_flags, B_NEEDCOMMIT);
nbp = bp->b_vnbufs.le_next;
brelse(bp);
continue;
}
if (!ISSET(bp->b_flags, B_PAGELIST)) {
bp->b_pagelist = upl;
SET(bp->b_flags, B_PAGELIST);
ubc_upl_map(upl, (vm_address_t *)&bp->b_data);
}
nbp = bp->b_vnbufs.le_next;
if (wcred == NULL)
wcred = bp->b_wcred;
else if (wcred != bp->b_wcred)
wcred = NOCRED;
SET(bp->b_flags, B_WRITEINPROG);
bvec[bvecpos++] = bp;
toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
bp->b_dirtyoff;
if (toff < off)
off = toff;
toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
if (toff > endoff)
endoff = toff;
}
splx(s);
}
if (bvecpos > 0) {
if (wcred != NOCRED)
retv = nfs_commit(vp, off, (int)(endoff - off),
wcred, p);
else {
retv = 0;
for (i = 0; i < bvecpos; i++) {
off_t off, size;
bp = bvec[i];
FSDBG(522, bp, bp->b_blkno * DEV_BSIZE,
bp->b_dirtyoff, bp->b_dirtyend);
off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
bp->b_dirtyoff;
size = (u_quad_t)(bp->b_dirtyend
- bp->b_dirtyoff);
retv = nfs_commit(vp, off, (int)size,
bp->b_wcred, p);
if (retv) break;
}
}
if (retv == NFSERR_STALEWRITEVERF)
nfs_clearcommit(vp->v_mount);
for (i = 0; i < bvecpos; i++) {
bp = bvec[i];
FSDBG(523, bp, retv, bp->b_flags, 0);
CLR(bp->b_flags, (B_NEEDCOMMIT | B_WRITEINPROG));
if (retv) {
brelse(bp);
} else {
int oldflags = bp->b_flags;
s = splbio();
vp->v_numoutput++;
SET(bp->b_flags, B_ASYNC);
CLR(bp->b_flags,
(B_READ|B_DONE|B_ERROR|B_DELWRI));
if (ISSET(oldflags, B_DELWRI)) {
extern int nbdwrite;
nbdwrite--;
wakeup((caddr_t)&nbdwrite);
}
bp->b_dirtyoff = bp->b_dirtyend = 0;
reassignbuf(bp, vp);
splx(s);
biodone(bp);
}
}
}
loop:
if (current_thread_aborted()) {
error = EINTR;
goto done;
}
s = splbio();
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
if (ISSET(bp->b_flags, B_BUSY)) {
FSDBG(524, bp, waitfor, passone, bp->b_flags);
if (waitfor != MNT_WAIT || passone)
continue;
SET(bp->b_flags, B_WANTED);
error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
"nfsfsync", slptimeo);
splx(s);
if (error) {
if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
error = EINTR;
goto done;
}
if (slpflag == PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
}
goto loop;
}
if (!ISSET(bp->b_flags, B_DELWRI))
panic("nfs_fsync: not dirty");
FSDBG(525, bp, passone, commit, bp->b_flags);
if ((passone || !commit) && ISSET(bp->b_flags, B_NEEDCOMMIT))
continue;
bremfree(bp);
if (passone || !commit)
SET(bp->b_flags, B_BUSY|B_ASYNC);
else
SET(bp->b_flags,
B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT);
splx(s);
VOP_BWRITE(bp);
goto loop;
}
splx(s);
if (passone) {
passone = 0;
goto again;
}
if (waitfor == MNT_WAIT) {
while (vp->v_numoutput) {
vp->v_flag |= VBWAIT;
error = tsleep((caddr_t)&vp->v_numoutput,
slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
if (error) {
if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
error = EINTR;
goto done;
}
if (slpflag == PCATCH) {
slpflag = 0;
slptimeo = 2 * hz;
}
}
}
if (vp->v_dirtyblkhd.lh_first && commit) {
goto loop;
}
}
FSDBG(526, np->n_flag, np->n_error, 0, 0);
if (np->n_flag & NWRITEERR) {
error = np->n_error;
np->n_flag &= ~NWRITEERR;
}
done:
FSDBG_BOT(517, vp, np, error, 0);
if (bvec != NULL && bvec != bvec_on_stack)
_FREE(bvec, M_TEMP);
return (error);
}
static int
nfs_pathconf(ap)
struct vop_pathconf_args *ap;
{
return (EINVAL);
}
static int
nfs_advlock(ap)
struct vop_advlock_args *ap;
{
#ifdef __FreeBSD__
register struct nfsnode *np = VTONFS(ap->a_vp);
return (lf_advlock(ap, &(np->n_lockf), np->n_size));
#else
#if DIAGNOSTIC
printf("nfs_advlock: pid %d comm %s\n", current_proc()->p_pid, current_proc()->p_comm);
#endif
return (EOPNOTSUPP);
#endif
}
static int
nfs_print(ap)
struct vop_print_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
printf("tag VT_NFS, fileid %ld fsid 0x%lx",
np->n_vattr.va_fileid, np->n_vattr.va_fsid);
if (vp->v_type == VFIFO)
fifo_printinfo(vp);
printf("\n");
return (0);
}
static int
nfs_blkatoff(ap)
struct vop_blkatoff_args *ap;
{
#if DIAGNOSTIC
printf("nfs_blkatoff: unimplemented!!");
#endif
return (EOPNOTSUPP);
}
static int
nfs_valloc(ap)
struct vop_valloc_args *ap;
{
return (EOPNOTSUPP);
}
static int
nfs_vfree(ap)
struct vop_vfree_args *ap;
{
#if DIAGNOSTIC
printf("nfs_vfree: unimplemented!!");
#endif
return (EOPNOTSUPP);
}
static int
nfs_truncate(ap)
struct vop_truncate_args *ap;
{
#if DIAGNOSTIC
printf("nfs_truncate: unimplemented!!");
#endif
return (EOPNOTSUPP);
}
static int
nfs_update(ap)
struct vop_update_args *ap;
{
#if DIAGNOSTIC
printf("nfs_update: unimplemented!!");
#endif
return (EOPNOTSUPP);
}
int nfs_aio_threads = 0;
struct slock nfs_aio_slock;
TAILQ_HEAD(bqueues, buf) nfs_aio_bufq;
int nfs_aio_bufq_len = 0;
void
nfs_aio_thread()
{
struct buf *bp;
boolean_t funnel_state;
funnel_state = thread_funnel_set(kernel_flock, TRUE);
for(;;) {
simple_lock(&nfs_aio_slock);
if ((bp = nfs_aio_bufq.tqh_first)) {
TAILQ_REMOVE(&nfs_aio_bufq, bp, b_freelist);
nfs_aio_bufq_len--;
simple_unlock(&nfs_aio_slock);
nfs_writebp(bp, 1);
} else {
assert_wait(&nfs_aio_bufq, THREAD_UNINT);
simple_unlock(&nfs_aio_slock);
(void)tsleep((caddr_t)0, PRIBIO+1, "nfs_aio_bufq", 0);
}
}
(void) thread_funnel_set(kernel_flock, FALSE);
}
void
nfs_aio_thread_init()
{
if (nfs_aio_threads++ == 0) {
simple_lock_init(&nfs_aio_slock);
TAILQ_INIT(&nfs_aio_bufq);
}
kernel_thread(kernel_task, nfs_aio_thread);
}
static int
nfs_bwrite(ap)
struct vop_bwrite_args *ap;
{
extern void wakeup_one(caddr_t chan);
if (nfs_aio_threads && ISSET(ap->a_bp->b_flags, B_ASYNC)) {
simple_lock(&nfs_aio_slock);
nfs_aio_bufq_len++;
TAILQ_INSERT_TAIL(&nfs_aio_bufq, ap->a_bp, b_freelist);
simple_unlock(&nfs_aio_slock);
wakeup_one((caddr_t)&nfs_aio_bufq);
return (0);
}
return (nfs_writebp(ap->a_bp, 1));
}
int
nfs_writebp(bp, force)
register struct buf *bp;
int force;
{
int s;
register int oldflags = bp->b_flags, retv = 1;
off_t off;
upl_t upl;
kern_return_t kret;
struct vnode *vp = bp->b_vp;
upl_page_info_t *pl;
if(!ISSET(bp->b_flags, B_BUSY))
panic("nfs_writebp: buffer is not busy???");
s = splbio();
CLR(bp->b_flags, (B_READ|B_DONE|B_ERROR|B_DELWRI));
if (ISSET(oldflags, B_DELWRI)) {
extern int nbdwrite;
nbdwrite--;
wakeup((caddr_t)&nbdwrite);
}
if (ISSET(oldflags, (B_ASYNC|B_DELWRI))) {
reassignbuf(bp, vp);
}
vp->v_numoutput++;
current_proc()->p_stats->p_ru.ru_oublock++;
splx(s);
if (!ISSET(bp->b_flags, B_META) && UBCISVALID(vp) &&
!ISSET(bp->b_flags, B_PAGELIST)) {
kret = ubc_create_upl(vp, ubc_blktooff(vp, bp->b_lblkno),
bp->b_bufsize, &upl, &pl, UPL_PRECIOUS);
if (kret != KERN_SUCCESS)
panic("nfs_writebp: ubc_create_upl %d", kret);
#ifdef UBC_DEBUG
upl_ubc_alias_set(upl, current_act(), 2);
#endif
s = splbio();
bp->b_pagelist = upl;
SET(bp->b_flags, B_PAGELIST);
splx(s);
kret = ubc_upl_map(upl, (vm_address_t *)&(bp->b_data));
if (kret != KERN_SUCCESS)
panic("nfs_writebp: ubc_upl_map %d", kret);
if(bp->b_data == 0)
panic("nfs_writebp: ubc_upl_map mapped 0");
if (!upl_page_present(pl, 0))
panic("nfs_writebp: nopage");
}
if ((oldflags & (B_NEEDCOMMIT | B_WRITEINPROG)) == B_NEEDCOMMIT) {
off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
SET(bp->b_flags, B_WRITEINPROG);
retv = nfs_commit(vp, off, bp->b_dirtyend-bp->b_dirtyoff,
bp->b_wcred, bp->b_proc);
CLR(bp->b_flags, B_WRITEINPROG);
if (!retv) {
bp->b_dirtyoff = bp->b_dirtyend = 0;
CLR(bp->b_flags, B_NEEDCOMMIT);
biodone(bp);
} else if (retv == NFSERR_STALEWRITEVERF)
nfs_clearcommit(vp->v_mount);
}
if (retv) {
if (force)
SET(bp->b_flags, B_WRITEINPROG);
VOP_STRATEGY(bp);
}
if( (oldflags & B_ASYNC) == 0) {
int rtval = biowait(bp);
if (oldflags & B_DELWRI) {
s = splbio();
reassignbuf(bp, vp);
splx(s);
}
brelse(bp);
return (rtval);
}
return (0);
}
static int
nfsspec_access(ap)
struct vop_access_args *ap;
{
register struct vattr *vap;
register gid_t *gp;
register struct ucred *cred = ap->a_cred;
struct vnode *vp = ap->a_vp;
mode_t mode = ap->a_mode;
struct vattr vattr;
register int i;
int error;
if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG: case VDIR: case VLNK:
return (EROFS);
}
}
if (cred->cr_uid == 0)
return (0);
vap = &vattr;
error = VOP_GETATTR(vp, vap, cred, ap->a_p);
if (error)
return (error);
if (cred->cr_uid != vap->va_uid) {
mode >>= 3;
gp = cred->cr_groups;
for (i = 0; i < cred->cr_ngroups; i++, gp++)
if (vap->va_gid == *gp)
goto found;
mode >>= 3;
found:
;
}
error = (vap->va_mode & mode) == mode ? 0 : EACCES;
return (error);
}
static int
nfsspec_read(ap)
struct vop_read_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NACC;
np->n_atim.tv_sec = time.tv_sec;
np->n_atim.tv_nsec = time.tv_usec * 1000;
return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
}
static int
nfsspec_write(ap)
struct vop_write_args *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NUPD;
np->n_mtim.tv_sec = time.tv_sec;
np->n_mtim.tv_nsec = time.tv_usec * 1000;
return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
}
static int
nfsspec_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
struct vattr vattr;
if (np->n_flag & (NACC | NUPD)) {
np->n_flag |= NCHG;
if (vp->v_usecount == 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, ap->a_cred, ap->a_p);
}
}
return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
}
static int
nfsfifo_read(ap)
struct vop_read_args *ap;
{
extern vop_t **fifo_vnodeop_p;
register struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NACC;
np->n_atim.tv_sec = time.tv_sec;
np->n_atim.tv_nsec = time.tv_usec * 1000;
return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
}
static int
nfsfifo_write(ap)
struct vop_write_args *ap;
{
extern vop_t **fifo_vnodeop_p;
register struct nfsnode *np = VTONFS(ap->a_vp);
np->n_flag |= NUPD;
np->n_mtim.tv_sec = time.tv_sec;
np->n_mtim.tv_nsec = time.tv_usec * 1000;
return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
}
static int
nfsfifo_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct nfsnode *np = VTONFS(vp);
struct vattr vattr;
extern vop_t **fifo_vnodeop_p;
if (np->n_flag & (NACC | NUPD)) {
if (np->n_flag & NACC) {
np->n_atim.tv_sec = time.tv_sec;
np->n_atim.tv_nsec = time.tv_usec * 1000;
}
if (np->n_flag & NUPD) {
np->n_mtim.tv_sec = time.tv_sec;
np->n_mtim.tv_nsec = time.tv_usec * 1000;
}
np->n_flag |= NCHG;
if (vp->v_usecount == 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, ap->a_cred, ap->a_p);
}
}
return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
}
static int
nfs_ioctl(ap)
struct vop_ioctl_args *ap;
{
return (ENOTTY);
}
static int
nfs_select(ap)
struct vop_select_args *ap;
{
return (1);
}
static int
nfs_pagein(ap)
struct vop_pagein_args *ap;
{
register struct vnode *vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
struct ucred *cred;
register struct nfsnode *np = VTONFS(vp);
register int biosize;
register int iosize;
register int xsize;
struct vattr vattr;
struct proc *p = current_proc();
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
int error = 0;
vm_offset_t ioaddr;
struct uio auio;
struct iovec aiov;
struct uio * uio = &auio;
int nofreeupl = flags & UPL_NOCOMMIT;
FSDBG(322, f_offset, size, pl, pl_offset);
if (pl == (upl_t)NULL)
panic("nfs_pagein: no upl");
if (UBCINVALID(vp)) {
printf("nfs_pagein: invalid vnode 0x%x", (int)vp);
if (!nofreeupl)
(void) ubc_upl_abort(pl, NULL);
return (EPERM);
}
UBCINFOCHECK("nfs_pagein", vp);
if (size <= 0) {
printf("nfs_pagein: invalid size %d", size);
if (!nofreeupl)
(void) ubc_upl_abort(pl, NULL);
return (EINVAL);
}
if (f_offset < 0 || f_offset >= np->n_size ||
(f_offset & PAGE_MASK_64)) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
return (EINVAL);
}
cred = ubc_getcred(vp);
if (cred == NOCRED)
cred = ap->a_cred;
auio.uio_offset = f_offset;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_procp = NULL;
if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
(void)nfs_fsinfo(nmp, vp, cred, p);
biosize = min(vp->v_mount->mnt_stat.f_iosize, size);
if (biosize & PAGE_MASK)
panic("nfs_pagein(%x): biosize not page aligned", biosize);
ubc_upl_map(pl, &ioaddr);
ioaddr += pl_offset;
xsize = size;
do {
iosize = min(biosize, xsize);
uio->uio_resid = iosize;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_len = iosize;
aiov.iov_base = (caddr_t)ioaddr;
FSDBG(322, uio->uio_offset, uio->uio_resid, ioaddr, xsize);
#warning our nfs_pagein does not support NQNFS
#ifdef UBC_DEBUG
upl_ubc_alias_set(pl, current_act(), 2);
#endif
nfsstats.pageins++;
error = nfs_readrpc(vp, uio, cred);
if (!error) {
if (uio->uio_resid) {
int zcnt = uio->uio_resid;
int zoff = iosize - zcnt;
bzero((char *)ioaddr + zoff, zcnt);
FSDBG(324, uio->uio_offset, zoff, zcnt, ioaddr);
uio->uio_offset += zcnt;
}
ioaddr += iosize;
xsize -= iosize;
} else
FSDBG(322, uio->uio_offset, uio->uio_resid, error, -1);
if (p && (vp->v_flag & VTEXT) &&
((nmp->nm_flag & NFSMNT_NQNFS &&
NQNFS_CKINVALID(vp, np, ND_READ) &&
np->n_lrev != np->n_brev) ||
(!(nmp->nm_flag & NFSMNT_NQNFS) &&
np->n_mtime != np->n_vattr.va_mtime.tv_sec))) {
uprintf("Process killed due to text file modification\n");
psignal(p, SIGKILL);
p->p_flag |= P_NOSWAP;
}
} while (error == 0 && xsize > 0);
ubc_upl_unmap(pl);
if (!nofreeupl) {
if (error)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_ERROR |
UPL_ABORT_FREE_ON_EMPTY);
else
ubc_upl_commit_range(pl, pl_offset, size,
UPL_COMMIT_CLEAR_DIRTY |
UPL_COMMIT_FREE_ON_EMPTY);
}
return (error);
}
static int
nfs_pageout(ap)
struct vop_pageout_args *ap;
{
register struct vnode *vp = ap->a_vp;
upl_t pl = ap->a_pl;
size_t size= ap->a_size;
off_t f_offset = ap->a_f_offset;
vm_offset_t pl_offset = ap->a_pl_offset;
int flags = ap->a_flags;
int ioflag = ap->a_flags;
register int biosize;
struct proc *p = current_proc();
struct nfsnode *np = VTONFS(vp);
register struct ucred *cred;
struct buf *bp;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
daddr_t lbn;
int n = 0, on, error = 0, iomode, must_commit, s;
off_t off;
vm_offset_t ioaddr;
struct uio auio;
struct iovec aiov;
struct uio * uio = &auio;
int nofreeupl = flags & UPL_NOCOMMIT;
int iosize;
int pgsize;
FSDBG(323, f_offset, size, pl, pl_offset);
if (pl == (upl_t)NULL)
panic("nfs_pageout: no upl");
if (UBCINVALID(vp)) {
printf("nfs_pageout: invalid vnode 0x%x", (int)vp);
if (!nofreeupl)
(void) ubc_upl_abort(pl, NULL);
return (EIO);
}
UBCINFOCHECK("nfs_pageout", vp);
if (size <= 0) {
printf("nfs_pageout: invalid size %d", size);
if (!nofreeupl)
(void) ubc_upl_abort(pl, NULL);
return (EINVAL);
}
biosize = min(vp->v_mount->mnt_stat.f_iosize, size);
if (biosize & PAGE_MASK)
panic("nfs_pageout(%x): biosize not page aligned", biosize);
lbn = f_offset / PAGE_SIZE;
for (iosize = size; iosize > 0; iosize -= PAGE_SIZE, lbn++) {
s = splbio();
if (bp = incore(vp, lbn)) {
FSDBG(323, lbn*PAGE_SIZE, 1, bp, bp->b_flags);
if (ISSET(bp->b_flags, B_BUSY)) {
if (!nofreeupl)
(void) ubc_upl_abort(pl, NULL);
return(EBUSY);
}
bremfree(bp);
SET(bp->b_flags, (B_BUSY | B_INVAL));
brelse(bp);
}
splx(s);
}
cred = ubc_getcred(vp);
if (cred == NOCRED)
cred = ap->a_cred;
if (np->n_flag & NWRITEERR) {
np->n_flag &= ~NWRITEERR;
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (np->n_error);
}
if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
(void)nfs_fsinfo(nmp, vp, cred, p);
if (f_offset < 0 || f_offset >= np->n_size ||
f_offset & PAGE_MASK_64 || size & PAGE_MASK) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset, size,
UPL_ABORT_FREE_ON_EMPTY);
return (EINVAL);
}
ubc_upl_map(pl, &ioaddr);
if (f_offset + size > np->n_size)
iosize = np->n_size - f_offset;
else
iosize = size;
pgsize = (iosize + (PAGE_SIZE - 1)) & ~PAGE_MASK;
if (size > pgsize) {
if (!nofreeupl)
ubc_upl_abort_range(pl, pl_offset + pgsize,
size - pgsize,
UPL_ABORT_FREE_ON_EMPTY);
}
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = f_offset;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_resid = iosize;
auio.uio_procp = NULL;
aiov.iov_len = iosize;
aiov.iov_base = (caddr_t)ioaddr + pl_offset;
if (f_offset < np->n_size && f_offset + size > np->n_size) {
size_t io = np->n_size - f_offset;
bzero((caddr_t)(ioaddr + pl_offset + io), size - io);
FSDBG(321, np->n_size, f_offset, f_offset + io, size - io);
}
do {
#warning our nfs_pageout does not support NQNFS
nfsstats.pageouts++;
lbn = uio->uio_offset / biosize;
on = uio->uio_offset & (biosize-1);
n = min((unsigned)(biosize - on), uio->uio_resid);
again:
#if 0
bufsize = biosize;
if ((off_t)(lbn + 1) * biosize > np->n_size) {
bufsize = np->n_size - (off_t)lbn * biosize;
bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
}
#endif
vp->v_numoutput++;
iomode = NFSV3WRITE_FILESYNC;
error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
if (must_commit)
nfs_clearcommit(vp->v_mount);
vpwakeup(vp);
if (error)
goto cleanup;
if (n > 0) {
uio->uio_resid -= n;
uio->uio_offset += n;
uio->uio_iov->iov_base += n;
uio->uio_iov->iov_len -= n;
}
} while (uio->uio_resid > 0 && n > 0);
cleanup:
ubc_upl_unmap(pl);
if (!nofreeupl) {
if (error) {
int abortflags;
short action = nfs_pageouterrorhandler(error);
switch (action) {
case DUMP:
abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
break;
case DUMPANDLOG:
abortflags = UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY;
if (error <= ELAST &&
(errorcount[error] % 100 == 0))
printf("nfs_pageout: unexpected error %d. dumping vm page\n", error);
errorcount[error]++;
break;
case RETRY:
abortflags = UPL_ABORT_FREE_ON_EMPTY;
break;
case RETRYWITHSLEEP:
abortflags = UPL_ABORT_FREE_ON_EMPTY;
(void) tsleep(&lbolt, PSOCK,
"nfspageout", 0);
break;
case SEVER:
default:
printf("nfs_pageout: action %d not expected\n", action);
break;
}
ubc_upl_abort_range(pl, pl_offset, size, abortflags);
} else
ubc_upl_commit_range(pl, pl_offset, pgsize,
UPL_COMMIT_CLEAR_DIRTY |
UPL_COMMIT_FREE_ON_EMPTY);
}
return (error);
}
static int
nfs_blktooff(ap)
struct vop_blktooff_args *ap;
{
int biosize;
register struct vnode *vp = ap->a_vp;
biosize = min(vp->v_mount->mnt_stat.f_iosize, PAGE_SIZE);
*ap->a_offset = (off_t)ap->a_lblkno * biosize;
return (0);
}
static int
nfs_offtoblk(ap)
struct vop_offtoblk_args *ap;
{
int biosize;
register struct vnode *vp = ap->a_vp;
biosize = min(vp->v_mount->mnt_stat.f_iosize, PAGE_SIZE);
*ap->a_lblkno = (daddr_t)(ap->a_offset / biosize);
return (0);
}
static int
nfs_cmap(ap)
struct vop_cmap_args *ap;
{
return (EOPNOTSUPP);
}