]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_vnops.c
xnu-792.21.3.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_vnops.c
index 06f5961e339682c40e8ced030d1f7f61dadf71e9..5488aa9d62afeea007453a110395066f9a591efb 100644 (file)
@@ -1,23 +1,29 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
 /*
 /*
  * vnode op calls for Sun NFS version 2 and 3
  */
-
 #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/proc_internal.h>
+#include <sys/kauth.h>
+#include <sys/mount_internal.h>
 #include <sys/malloc.h>
-#include <sys/mbuf.h>
+#include <sys/kpi_mbuf.h>
 #include <sys/conf.h>
-#include <sys/namei.h>
-#include <sys/vnode.h>
+#include <sys/vnode_internal.h>
 #include <sys/dirent.h>
 #include <sys/fcntl.h>
 #include <sys/lockf.h>
-#include <sys/ubc.h>
+#include <sys/ubc_internal.h>
+#include <sys/attr.h>
+#include <sys/signalvar.h>
+#include <sys/uio_internal.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 <libkern/OSAtomic.h>
 
 #include <miscfs/fifofs/fifo.h>
 #include <miscfs/specfs/specdev.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsnode.h>
 #include <nfs/nfsmount.h>
+#include <nfs/nfs_lock.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 <kern/task.h>
 #include <vm/vm_kern.h>
 
+#include <kern/task.h>
+#include <kern/sched_prim.h>
+
 #include <sys/kdebug.h>
 
-#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 *));
+#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)
+
+static int     nfsspec_read(struct vnop_read_args *);
+static int     nfsspec_write(struct vnop_write_args *);
+static int     nfsfifo_read(struct vnop_read_args *);
+static int     nfsfifo_write(struct vnop_write_args *);
+static int     nfsspec_close(struct vnop_close_args *);
+static int     nfsfifo_close(struct vnop_close_args *);
+static int     nfs_ioctl(struct vnop_ioctl_args *);
+static int     nfs_select(struct vnop_select_args *);
+static int     nfs_setattrrpc(vnode_t,struct vnode_attr *,kauth_cred_t,proc_t);
+static int     nfs_lookup(struct vnop_lookup_args *);
+static int     nfs_create(struct vnop_create_args *);
+static int     nfs_mknod(struct vnop_mknod_args *);
+static int     nfs_open(struct vnop_open_args *);
+static int     nfs_close(struct vnop_close_args *);
+static int     nfs_access(struct vnop_access_args *);
+static int     nfs_vnop_getattr(struct vnop_getattr_args *);
+static int     nfs_setattr(struct vnop_setattr_args *);
+static int     nfs_read(struct vnop_read_args *);
+static int     nfs_mmap(struct vnop_mmap_args *);
+static int     nfs_fsync(struct vnop_fsync_args *);
+static int     nfs_remove(struct vnop_remove_args *);
+static int     nfs_link(struct vnop_link_args *);
+static int     nfs_rename(struct vnop_rename_args *);
+static int     nfs_mkdir(struct vnop_mkdir_args *);
+static int     nfs_rmdir(struct vnop_rmdir_args *);
+static int     nfs_symlink(struct vnop_symlink_args *);
+static int     nfs_readdir(struct vnop_readdir_args *);
+static int     nfs_lookitup(vnode_t,char *,int,kauth_cred_t,proc_t,struct nfsnode **);
+static int     nfs_sillyrename(vnode_t,vnode_t,struct componentname *,kauth_cred_t,proc_t);
+static int     nfs_readlink(struct vnop_readlink_args *);
+static int     nfs_pathconf(struct vnop_pathconf_args *);
+static int     nfs_advlock(struct vnop_advlock_args *);
+static int     nfs_pagein(struct vnop_pagein_args *);
+static int     nfs_pageout(struct vnop_pageout_args *);
+static int nfs_blktooff(struct vnop_blktooff_args *);
+static int nfs_offtoblk(struct vnop_offtoblk_args *);
+static int nfs_blockmap(struct vnop_blockmap_args *);
 
 /*
  * Global vfs data structures for nfs
  */
-vop_t **nfsv2_vnodeop_p;
+vnop_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 },      /* lookup */
-       { &vop_create_desc, (vop_t *)nfs_create },      /* create */
-       { &vop_mknod_desc, (vop_t *)nfs_mknod },        /* mknod */
-       { &vop_open_desc, (vop_t *)nfs_open },          /* open */
-       { &vop_close_desc, (vop_t *)nfs_close },        /* close */
-       { &vop_access_desc, (vop_t *)nfs_access },      /* access */
-       { &vop_getattr_desc, (vop_t *)nfs_getattr },    /* getattr */
-       { &vop_setattr_desc, (vop_t *)nfs_setattr },    /* setattr */
-       { &vop_read_desc, (vop_t *)nfs_read },          /* read */
-       { &vop_write_desc, (vop_t *)nfs_write },        /* write */
-       { &vop_lease_desc, (vop_t *)nfs_lease_check },  /* lease */
-       { &vop_ioctl_desc, (vop_t *)nfs_ioctl },        /* ioctl */
-       { &vop_select_desc, (vop_t *)nfs_select },      /* select */
-       { &vop_revoke_desc, (vop_t *)nfs_revoke },      /* revoke */
-       { &vop_mmap_desc, (vop_t *)nfs_mmap },          /* mmap */
-       { &vop_fsync_desc, (vop_t *)nfs_fsync },        /* fsync */
-       { &vop_seek_desc, (vop_t *)nfs_seek },          /* seek */
-       { &vop_remove_desc, (vop_t *)nfs_remove },      /* remove */
-       { &vop_link_desc, (vop_t *)nfs_link },          /* link */
-       { &vop_rename_desc, (vop_t *)nfs_rename },      /* rename */
-       { &vop_mkdir_desc, (vop_t *)nfs_mkdir },        /* mkdir */
-       { &vop_rmdir_desc, (vop_t *)nfs_rmdir },        /* rmdir */
-       { &vop_symlink_desc, (vop_t *)nfs_symlink },    /* symlink */
-       { &vop_readdir_desc, (vop_t *)nfs_readdir },    /* readdir */
-       { &vop_readlink_desc, (vop_t *)nfs_readlink },  /* readlink */
-       { &vop_abortop_desc, (vop_t *)nfs_abortop },    /* abortop */
-       { &vop_inactive_desc, (vop_t *)nfs_inactive },  /* inactive */
-       { &vop_reclaim_desc, (vop_t *)nfs_reclaim },    /* reclaim */
-       { &vop_lock_desc, (vop_t *)nfs_lock },          /* lock */
-       { &vop_unlock_desc, (vop_t *)nfs_unlock },      /* unlock */
-       { &vop_bmap_desc, (vop_t *)nfs_bmap },          /* bmap */
-       { &vop_strategy_desc, (vop_t *)nfs_strategy },  /* strategy */
-       { &vop_print_desc, (vop_t *)nfs_print },        /* print */
-       { &vop_islocked_desc, (vop_t *)nfs_islocked },  /* islocked */
-       { &vop_pathconf_desc, (vop_t *)nfs_pathconf },  /* pathconf */
-       { &vop_advlock_desc, (vop_t *)nfs_advlock },    /* advlock */
-       { &vop_blkatoff_desc, (vop_t *)nfs_blkatoff },  /* blkatoff */
-       { &vop_valloc_desc, (vop_t *)nfs_valloc },      /* valloc */
-       { &vop_reallocblks_desc, (vop_t *)nfs_reallocblks },    /* reallocblks */
-       { &vop_vfree_desc, (vop_t *)nfs_vfree },        /* vfree */
-       { &vop_truncate_desc, (vop_t *)nfs_truncate },  /* truncate */
-       { &vop_update_desc, (vop_t *)nfs_update },      /* update */
-       { &vop_bwrite_desc, (vop_t *)nfs_bwrite },      /* bwrite */
-       { &vop_pagein_desc, (vop_t *)nfs_pagein },              /* Pagein */
-       { &vop_pageout_desc, (vop_t *)nfs_pageout },            /* Pageout */
-       { &vop_copyfile_desc, (vop_t *)err_copyfile },          /* Copyfile */
-       { &vop_blktooff_desc, (vop_t *)nfs_blktooff },          /* blktooff */
-       { &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },          /* offtoblk */
-       { &vop_cmap_desc, (vop_t *)nfs_cmap },          /* cmap */
+       { &vnop_default_desc, (vnop_t *)vn_default_error },
+       { &vnop_lookup_desc, (vnop_t *)nfs_lookup },            /* lookup */
+       { &vnop_create_desc, (vnop_t *)nfs_create },            /* create */
+       { &vnop_mknod_desc, (vnop_t *)nfs_mknod },              /* mknod */
+       { &vnop_open_desc, (vnop_t *)nfs_open },                /* open */
+       { &vnop_close_desc, (vnop_t *)nfs_close },              /* close */
+       { &vnop_access_desc, (vnop_t *)nfs_access },            /* access */
+       { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },     /* getattr */
+       { &vnop_setattr_desc, (vnop_t *)nfs_setattr },          /* setattr */
+       { &vnop_read_desc, (vnop_t *)nfs_read },                /* read */
+       { &vnop_write_desc, (vnop_t *)nfs_write },              /* write */
+       { &vnop_ioctl_desc, (vnop_t *)nfs_ioctl },              /* ioctl */
+       { &vnop_select_desc, (vnop_t *)nfs_select },            /* select */
+       { &vnop_revoke_desc, (vnop_t *)nfs_revoke },            /* revoke */
+       { &vnop_mmap_desc, (vnop_t *)nfs_mmap },                /* mmap */
+       { &vnop_fsync_desc, (vnop_t *)nfs_fsync },              /* fsync */
+       { &vnop_remove_desc, (vnop_t *)nfs_remove },            /* remove */
+       { &vnop_link_desc, (vnop_t *)nfs_link },                /* link */
+       { &vnop_rename_desc, (vnop_t *)nfs_rename },            /* rename */
+       { &vnop_mkdir_desc, (vnop_t *)nfs_mkdir },              /* mkdir */
+       { &vnop_rmdir_desc, (vnop_t *)nfs_rmdir },              /* rmdir */
+       { &vnop_symlink_desc, (vnop_t *)nfs_symlink },          /* symlink */
+       { &vnop_readdir_desc, (vnop_t *)nfs_readdir },          /* readdir */
+       { &vnop_readlink_desc, (vnop_t *)nfs_readlink },        /* readlink */
+       { &vnop_inactive_desc, (vnop_t *)nfs_inactive },        /* inactive */
+       { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },          /* reclaim */
+       { &vnop_strategy_desc, (vnop_t *)err_strategy },        /* strategy */
+       { &vnop_pathconf_desc, (vnop_t *)nfs_pathconf },        /* pathconf */
+       { &vnop_advlock_desc, (vnop_t *)nfs_advlock },          /* advlock */
+       { &vnop_bwrite_desc, (vnop_t *)err_bwrite },            /* bwrite */
+       { &vnop_pagein_desc, (vnop_t *)nfs_pagein },            /* Pagein */
+       { &vnop_pageout_desc, (vnop_t *)nfs_pageout },          /* Pageout */
+       { &vnop_copyfile_desc, (vnop_t *)err_copyfile },        /* Copyfile */
+       { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },        /* blktooff */
+       { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },        /* offtoblk */
+       { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },        /* blockmap */
        { NULL, NULL }
 };
 struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
@@ -230,58 +218,42 @@ VNODEOP_SET(nfsv2_vnodeop_opv_desc);
 /*
  * Special device vnode ops
  */
-vop_t **spec_nfsv2nodeop_p;
+vnop_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 },     /* lookup */
-       { &vop_create_desc, (vop_t *)spec_create },     /* create */
-       { &vop_mknod_desc, (vop_t *)spec_mknod },       /* mknod */
-       { &vop_open_desc, (vop_t *)spec_open },         /* open */
-       { &vop_close_desc, (vop_t *)nfsspec_close },    /* close */
-       { &vop_access_desc, (vop_t *)nfsspec_access },  /* access */
-       { &vop_getattr_desc, (vop_t *)nfs_getattr },    /* getattr */
-       { &vop_setattr_desc, (vop_t *)nfs_setattr },    /* setattr */
-       { &vop_read_desc, (vop_t *)nfsspec_read },      /* read */
-       { &vop_write_desc, (vop_t *)nfsspec_write },    /* write */
-       { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */
-       { &vop_ioctl_desc, (vop_t *)spec_ioctl },       /* ioctl */
-       { &vop_select_desc, (vop_t *)spec_select },     /* select */
-       { &vop_revoke_desc, (vop_t *)spec_revoke },     /* revoke */
-       { &vop_mmap_desc, (vop_t *)spec_mmap },         /* mmap */
-       { &vop_fsync_desc, (vop_t *)nfs_fsync },        /* fsync */
-       { &vop_seek_desc, (vop_t *)spec_seek },         /* seek */
-       { &vop_remove_desc, (vop_t *)spec_remove },     /* remove */
-       { &vop_link_desc, (vop_t *)spec_link },         /* link */
-       { &vop_rename_desc, (vop_t *)spec_rename },     /* rename */
-       { &vop_mkdir_desc, (vop_t *)spec_mkdir },       /* mkdir */
-       { &vop_rmdir_desc, (vop_t *)spec_rmdir },       /* rmdir */
-       { &vop_symlink_desc, (vop_t *)spec_symlink },   /* symlink */
-       { &vop_readdir_desc, (vop_t *)spec_readdir },   /* readdir */
-       { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */
-       { &vop_abortop_desc, (vop_t *)spec_abortop },   /* abortop */
-       { &vop_inactive_desc, (vop_t *)nfs_inactive },  /* inactive */
-       { &vop_reclaim_desc, (vop_t *)nfs_reclaim },    /* reclaim */
-       { &vop_lock_desc, (vop_t *)nfs_lock },          /* lock */
-       { &vop_unlock_desc, (vop_t *)nfs_unlock },      /* unlock */
-       { &vop_bmap_desc, (vop_t *)spec_bmap },         /* bmap */
-       { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */
-       { &vop_print_desc, (vop_t *)nfs_print },        /* print */
-       { &vop_islocked_desc, (vop_t *)nfs_islocked },  /* islocked */
-       { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */
-       { &vop_advlock_desc, (vop_t *)spec_advlock },   /* advlock */
-       { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */
-       { &vop_valloc_desc, (vop_t *)spec_valloc },     /* valloc */
-       { &vop_reallocblks_desc, (vop_t *)spec_reallocblks },   /* reallocblks */
-       { &vop_vfree_desc, (vop_t *)spec_vfree },       /* vfree */
-       { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */
-       { &vop_update_desc, (vop_t *)nfs_update },      /* update */
-       { &vop_bwrite_desc, (vop_t *)vn_bwrite },       /* bwrite */
-       { &vop_devblocksize_desc, (vop_t *)spec_devblocksize },  /* devblocksize */
-       { &vop_pagein_desc, (vop_t *)nfs_pagein },              /* Pagein */
-       { &vop_pageout_desc, (vop_t *)nfs_pageout },            /* Pageout */
-       { &vop_blktooff_desc, (vop_t *)nfs_blktooff },          /* blktooff */
-       { &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },          /* offtoblk */
-       { &vop_cmap_desc, (vop_t *)nfs_cmap },          /* cmap */
+       { &vnop_default_desc, (vnop_t *)vn_default_error },
+       { &vnop_lookup_desc, (vnop_t *)spec_lookup },           /* lookup */
+       { &vnop_create_desc, (vnop_t *)spec_create },           /* create */
+       { &vnop_mknod_desc, (vnop_t *)spec_mknod },             /* mknod */
+       { &vnop_open_desc, (vnop_t *)spec_open },               /* open */
+       { &vnop_close_desc, (vnop_t *)nfsspec_close },          /* close */
+       { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },     /* getattr */
+       { &vnop_setattr_desc, (vnop_t *)nfs_setattr },          /* setattr */
+       { &vnop_read_desc, (vnop_t *)nfsspec_read },            /* read */
+       { &vnop_write_desc, (vnop_t *)nfsspec_write },          /* write */
+       { &vnop_ioctl_desc, (vnop_t *)spec_ioctl },             /* ioctl */
+       { &vnop_select_desc, (vnop_t *)spec_select },           /* select */
+       { &vnop_revoke_desc, (vnop_t *)spec_revoke },           /* revoke */
+       { &vnop_mmap_desc, (vnop_t *)spec_mmap },               /* mmap */
+       { &vnop_fsync_desc, (vnop_t *)nfs_fsync },              /* fsync */
+       { &vnop_remove_desc, (vnop_t *)spec_remove },           /* remove */
+       { &vnop_link_desc, (vnop_t *)spec_link },               /* link */
+       { &vnop_rename_desc, (vnop_t *)spec_rename },           /* rename */
+       { &vnop_mkdir_desc, (vnop_t *)spec_mkdir },             /* mkdir */
+       { &vnop_rmdir_desc, (vnop_t *)spec_rmdir },             /* rmdir */
+       { &vnop_symlink_desc, (vnop_t *)spec_symlink },         /* symlink */
+       { &vnop_readdir_desc, (vnop_t *)spec_readdir },         /* readdir */
+       { &vnop_readlink_desc, (vnop_t *)spec_readlink },       /* readlink */
+       { &vnop_inactive_desc, (vnop_t *)nfs_inactive },        /* inactive */
+       { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },          /* reclaim */
+       { &vnop_strategy_desc, (vnop_t *)spec_strategy },       /* strategy */
+       { &vnop_pathconf_desc, (vnop_t *)spec_pathconf },       /* pathconf */
+       { &vnop_advlock_desc, (vnop_t *)spec_advlock },         /* advlock */
+       { &vnop_bwrite_desc, (vnop_t *)vn_bwrite },             /* bwrite */
+       { &vnop_pagein_desc, (vnop_t *)nfs_pagein },            /* Pagein */
+       { &vnop_pageout_desc, (vnop_t *)nfs_pageout },          /* Pageout */
+       { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },        /* blktooff */
+       { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },        /* offtoblk */
+       { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },        /* blockmap */
        { NULL, NULL }
 };
 struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
@@ -290,57 +262,42 @@ struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
 VNODEOP_SET(spec_nfsv2nodeop_opv_desc);
 #endif
 
-vop_t **fifo_nfsv2nodeop_p;
+vnop_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 },     /* lookup */
-       { &vop_create_desc, (vop_t *)fifo_create },     /* create */
-       { &vop_mknod_desc, (vop_t *)fifo_mknod },       /* mknod */
-       { &vop_open_desc, (vop_t *)fifo_open },         /* open */
-       { &vop_close_desc, (vop_t *)nfsfifo_close },    /* close */
-       { &vop_access_desc, (vop_t *)nfsspec_access },  /* access */
-       { &vop_getattr_desc, (vop_t *)nfs_getattr },    /* getattr */
-       { &vop_setattr_desc, (vop_t *)nfs_setattr },    /* setattr */
-       { &vop_read_desc, (vop_t *)nfsfifo_read },      /* read */
-       { &vop_write_desc, (vop_t *)nfsfifo_write },    /* write */
-       { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */
-       { &vop_ioctl_desc, (vop_t *)fifo_ioctl },       /* ioctl */
-       { &vop_select_desc, (vop_t *)fifo_select },     /* select */
-       { &vop_revoke_desc, (vop_t *)fifo_revoke },     /* revoke */
-       { &vop_mmap_desc, (vop_t *)fifo_mmap },         /* mmap */
-       { &vop_fsync_desc, (vop_t *)nfs_fsync },        /* fsync */
-       { &vop_seek_desc, (vop_t *)fifo_seek },         /* seek */
-       { &vop_remove_desc, (vop_t *)fifo_remove },     /* remove */
-       { &vop_link_desc, (vop_t *)fifo_link },         /* link */
-       { &vop_rename_desc, (vop_t *)fifo_rename },     /* rename */
-       { &vop_mkdir_desc, (vop_t *)fifo_mkdir },       /* mkdir */
-       { &vop_rmdir_desc, (vop_t *)fifo_rmdir },       /* rmdir */
-       { &vop_symlink_desc, (vop_t *)fifo_symlink },   /* symlink */
-       { &vop_readdir_desc, (vop_t *)fifo_readdir },   /* readdir */
-       { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */
-       { &vop_abortop_desc, (vop_t *)fifo_abortop },   /* abortop */
-       { &vop_inactive_desc, (vop_t *)nfs_inactive },  /* inactive */
-       { &vop_reclaim_desc, (vop_t *)nfs_reclaim },    /* reclaim */
-       { &vop_lock_desc, (vop_t *)nfs_lock },          /* lock */
-       { &vop_unlock_desc, (vop_t *)nfs_unlock },      /* unlock */
-       { &vop_bmap_desc, (vop_t *)fifo_bmap },         /* bmap */
-       { &vop_strategy_desc, (vop_t *)fifo_badop },    /* strategy */
-       { &vop_print_desc, (vop_t *)nfs_print },        /* print */
-       { &vop_islocked_desc, (vop_t *)nfs_islocked },  /* islocked */
-       { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */
-       { &vop_advlock_desc, (vop_t *)fifo_advlock },   /* advlock */
-       { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */
-       { &vop_valloc_desc, (vop_t *)fifo_valloc },     /* valloc */
-       { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks },   /* reallocblks */
-       { &vop_vfree_desc, (vop_t *)fifo_vfree },       /* vfree */
-       { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */
-       { &vop_update_desc, (vop_t *)nfs_update },      /* update */
-       { &vop_bwrite_desc, (vop_t *)vn_bwrite },       /* bwrite */
-       { &vop_pagein_desc, (vop_t *)nfs_pagein },              /* Pagein */
-       { &vop_pageout_desc, (vop_t *)nfs_pageout },            /* Pageout */
-       { &vop_blktooff_desc, (vop_t *)nfs_blktooff },          /* blktooff */
-       { &vop_offtoblk_desc, (vop_t *)nfs_offtoblk },          /* offtoblk */
-       { &vop_cmap_desc, (vop_t *)nfs_cmap },          /* cmap */
+       { &vnop_default_desc, (vnop_t *)vn_default_error },
+       { &vnop_lookup_desc, (vnop_t *)fifo_lookup },           /* lookup */
+       { &vnop_create_desc, (vnop_t *)fifo_create },           /* create */
+       { &vnop_mknod_desc, (vnop_t *)fifo_mknod },             /* mknod */
+       { &vnop_open_desc, (vnop_t *)fifo_open },               /* open */
+       { &vnop_close_desc, (vnop_t *)nfsfifo_close },          /* close */
+       { &vnop_getattr_desc, (vnop_t *)nfs_vnop_getattr },     /* getattr */
+       { &vnop_setattr_desc, (vnop_t *)nfs_setattr },          /* setattr */
+       { &vnop_read_desc, (vnop_t *)nfsfifo_read },            /* read */
+       { &vnop_write_desc, (vnop_t *)nfsfifo_write },          /* write */
+       { &vnop_ioctl_desc, (vnop_t *)fifo_ioctl },             /* ioctl */
+       { &vnop_select_desc, (vnop_t *)fifo_select },           /* select */
+       { &vnop_revoke_desc, (vnop_t *)fifo_revoke },           /* revoke */
+       { &vnop_mmap_desc, (vnop_t *)fifo_mmap },               /* mmap */
+       { &vnop_fsync_desc, (vnop_t *)nfs_fsync },              /* fsync */
+       { &vnop_remove_desc, (vnop_t *)fifo_remove },           /* remove */
+       { &vnop_link_desc, (vnop_t *)fifo_link },               /* link */
+       { &vnop_rename_desc, (vnop_t *)fifo_rename },           /* rename */
+       { &vnop_mkdir_desc, (vnop_t *)fifo_mkdir },             /* mkdir */
+       { &vnop_rmdir_desc, (vnop_t *)fifo_rmdir },             /* rmdir */
+       { &vnop_symlink_desc, (vnop_t *)fifo_symlink },         /* symlink */
+       { &vnop_readdir_desc, (vnop_t *)fifo_readdir },         /* readdir */
+       { &vnop_readlink_desc, (vnop_t *)fifo_readlink },       /* readlink */
+       { &vnop_inactive_desc, (vnop_t *)nfs_inactive },        /* inactive */
+       { &vnop_reclaim_desc, (vnop_t *)nfs_reclaim },          /* reclaim */
+       { &vnop_strategy_desc, (vnop_t *)fifo_strategy },       /* strategy */
+       { &vnop_pathconf_desc, (vnop_t *)fifo_pathconf },       /* pathconf */
+       { &vnop_advlock_desc, (vnop_t *)fifo_advlock },         /* advlock */
+       { &vnop_bwrite_desc, (vnop_t *)vn_bwrite },             /* bwrite */
+       { &vnop_pagein_desc, (vnop_t *)nfs_pagein },            /* Pagein */
+       { &vnop_pageout_desc, (vnop_t *)nfs_pageout },          /* Pageout */
+       { &vnop_blktooff_desc, (vnop_t *)nfs_blktooff },        /* blktooff */
+       { &vnop_offtoblk_desc, (vnop_t *)nfs_offtoblk },        /* offtoblk */
+       { &vnop_blockmap_desc, (vnop_t *)nfs_blockmap },        /* blockmap */
        { NULL, NULL }
 };
 struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
@@ -349,30 +306,35 @@ struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
 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));
+static int     nfs_mknodrpc(vnode_t dvp, vnode_t *vpp,
+                               struct componentname *cnp,
+                               struct vnode_attr *vap,
+                               kauth_cred_t cred, proc_t p);
+static int     nfs_removerpc(vnode_t dvp, char *name, int namelen,
+                               kauth_cred_t cred, proc_t proc);
+static int     nfs_renamerpc(vnode_t fdvp, char *fnameptr,
+                               int fnamelen, vnode_t tdvp,
+                               char *tnameptr, int tnamelen,
+                               kauth_cred_t cred, proc_t proc);
 
 /*
  * Global variables
  */
+extern u_long nfs_xdrneg1;
 extern u_long nfs_true, nfs_false;
 extern struct nfsstats nfsstats;
 extern nfstype nfsv3_type[9];
-struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+proc_t nfs_iodwant[NFS_MAXASYNCDAEMON];
 struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON];
+
+lck_grp_t *nfs_iod_lck_grp;
+lck_grp_attr_t *nfs_iod_lck_grp_attr;
+lck_attr_t *nfs_iod_lck_attr;
+lck_mtx_t *nfs_iod_mutex;
+
 int nfs_numasync = 0;
+int nfs_ioddelwri = 0;
+
 #define        DIRHDSIZ        (sizeof (struct dirent) - (MAXNAMLEN + 1))
 
 static int     nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
@@ -383,41 +345,169 @@ static int       nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
                         | NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE     \
                         | NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP)
                          
-                         
+
+/* 
+ * the following are needed only by nfs_pageout to know how to handle errors
+ * see nfs_pageout comments on explanation of actions.
+ * the errors here are copied from errno.h and errors returned by servers
+ * are expected to match the same numbers here. If not, our actions maybe
+ * erroneous.
+ */
+enum actiontype {NOACTION, DUMP, DUMPANDLOG, RETRY, RETRYWITHSLEEP, SEVER};
+
+static int errorcount[ELAST+1]; /* better be zeros when initialized */
+
+static const short errortooutcome[ELAST+1] = {
+       NOACTION,
+       DUMP,                   /* EPERM        1       Operation not permitted */
+       DUMP,                   /* ENOENT       2       No such file or directory */
+       DUMPANDLOG,             /* ESRCH        3       No such process */
+       RETRY,                  /* EINTR        4       Interrupted system call */
+       DUMP,                   /* EIO          5       Input/output error */
+       DUMP,                   /* ENXIO        6       Device not configured */
+       DUMPANDLOG,             /* E2BIG        7       Argument list too long */
+       DUMPANDLOG,             /* ENOEXEC      8       Exec format error */
+       DUMPANDLOG,             /* EBADF        9       Bad file descriptor */
+       DUMPANDLOG,             /* ECHILD       10      No child processes */
+       DUMPANDLOG,             /* EDEADLK      11      Resource deadlock avoided - was EAGAIN */
+       RETRY,                  /* ENOMEM       12      Cannot allocate memory */
+       DUMP,                   /* EACCES       13      Permission denied */
+       DUMPANDLOG,             /* EFAULT       14      Bad address */
+       DUMPANDLOG,             /* ENOTBLK      15      POSIX - Block device required */
+       RETRY,                  /* EBUSY        16      Device busy */
+       DUMP,                   /* EEXIST       17      File exists */
+       DUMP,                   /* EXDEV        18      Cross-device link */
+       DUMP,                   /* ENODEV       19      Operation not supported by device */
+       DUMP,                   /* ENOTDIR      20      Not a directory */
+       DUMP,                   /* EISDIR       21      Is a directory */
+       DUMP,                   /* EINVAL       22      Invalid argument */
+       DUMPANDLOG,             /* ENFILE       23      Too many open files in system */
+       DUMPANDLOG,             /* EMFILE       24      Too many open files */
+       DUMPANDLOG,             /* ENOTTY       25      Inappropriate ioctl for device */
+       DUMPANDLOG,             /* ETXTBSY      26      Text file busy - POSIX */
+       DUMP,                   /* EFBIG        27      File too large */
+       DUMP,                   /* ENOSPC       28      No space left on device */
+       DUMPANDLOG,             /* ESPIPE       29      Illegal seek */
+       DUMP,                   /* EROFS        30      Read-only file system */
+       DUMP,                   /* EMLINK       31      Too many links */
+       RETRY,                  /* EPIPE        32      Broken pipe */
+       /* math software */
+       DUMPANDLOG,             /* EDOM                         33      Numerical argument out of domain */
+       DUMPANDLOG,             /* ERANGE                       34      Result too large */
+       RETRY,                  /* EAGAIN/EWOULDBLOCK   35      Resource temporarily unavailable */
+       DUMPANDLOG,             /* EINPROGRESS          36      Operation now in progress */
+       DUMPANDLOG,             /* EALREADY                     37      Operation already in progress */
+       /* ipc/network software -- argument errors */
+       DUMPANDLOG,             /* ENOTSOC                      38      Socket operation on non-socket */
+       DUMPANDLOG,             /* EDESTADDRREQ         39      Destination address required */
+       DUMPANDLOG,             /* EMSGSIZE                     40      Message too long */
+       DUMPANDLOG,             /* EPROTOTYPE           41      Protocol wrong type for socket */
+       DUMPANDLOG,             /* ENOPROTOOPT          42      Protocol not available */
+       DUMPANDLOG,             /* EPROTONOSUPPORT      43      Protocol not supported */
+       DUMPANDLOG,             /* ESOCKTNOSUPPORT      44      Socket type not supported */
+       DUMPANDLOG,             /* ENOTSUP                      45      Operation not supported */
+       DUMPANDLOG,             /* EPFNOSUPPORT         46      Protocol family not supported */
+       DUMPANDLOG,             /* EAFNOSUPPORT         47      Address family not supported by protocol family */
+       DUMPANDLOG,             /* EADDRINUSE           48      Address already in use */
+       DUMPANDLOG,             /* EADDRNOTAVAIL        49      Can't assign requested address */
+       /* ipc/network software -- operational errors */
+       RETRY,                  /* ENETDOWN                     50      Network is down */
+       RETRY,                  /* ENETUNREACH          51      Network is unreachable */
+       RETRY,                  /* ENETRESET            52      Network dropped connection on reset */
+       RETRY,                  /* ECONNABORTED         53      Software caused connection abort */
+       RETRY,                  /* ECONNRESET           54      Connection reset by peer */
+       RETRY,                  /* ENOBUFS                      55      No buffer space available */
+       RETRY,                  /* EISCONN                      56      Socket is already connected */
+       RETRY,                  /* ENOTCONN                     57      Socket is not connected */
+       RETRY,                  /* ESHUTDOWN            58      Can't send after socket shutdown */
+       RETRY,                  /* ETOOMANYREFS         59      Too many references: can't splice */
+       RETRY,                  /* ETIMEDOUT            60      Operation timed out */
+       RETRY,                  /* ECONNREFUSED         61      Connection refused */
+
+       DUMPANDLOG,             /* ELOOP                        62      Too many levels of symbolic links */
+       DUMP,                   /* ENAMETOOLONG         63      File name too long */
+       RETRY,                  /* EHOSTDOWN            64      Host is down */ 
+       RETRY,                  /* EHOSTUNREACH         65      No route to host */
+       DUMP,                   /* ENOTEMPTY            66      Directory not empty */
+       /* quotas & mush */     
+       DUMPANDLOG,             /* PROCLIM                      67      Too many processes */
+       DUMPANDLOG,             /* EUSERS                       68      Too many users */
+       DUMPANDLOG,             /* EDQUOT                       69      Disc quota exceeded */   
+       /* Network File System */
+       DUMP,                   /* ESTALE                       70      Stale NFS file handle */
+       DUMP,                   /* EREMOTE                      71      Too many levels of remote in path */
+       DUMPANDLOG,             /* EBADRPC                      72      RPC struct is bad */
+       DUMPANDLOG,             /* ERPCMISMATCH         73      RPC version wrong */
+       DUMPANDLOG,             /* EPROGUNAVAIL         74      RPC prog. not avail */
+       DUMPANDLOG,             /* EPROGMISMATCH        75      Program version wrong */
+       DUMPANDLOG,             /* EPROCUNAVAIL         76      Bad procedure for program */
+
+       DUMPANDLOG,             /* ENOLCK                       77      No locks available */
+       DUMPANDLOG,             /* ENOSYS                       78      Function not implemented */
+       DUMPANDLOG,             /* EFTYPE                       79      Inappropriate file type or format */  
+       DUMPANDLOG,             /* EAUTH                        80      Authentication error */
+       DUMPANDLOG,             /* ENEEDAUTH            81      Need authenticator */
+       /* Intelligent device errors */
+       DUMPANDLOG,             /* EPWROFF                      82      Device power is off */
+       DUMPANDLOG,             /* EDEVERR                      83      Device error, e.g. paper out */
+       DUMPANDLOG,             /* EOVERFLOW            84      Value too large to be stored in data type */
+       /* Program loading errors */
+       DUMPANDLOG,             /* EBADEXEC                     85      Bad executable */
+       DUMPANDLOG,             /* EBADARCH                     86      Bad CPU type in executable */
+       DUMPANDLOG,             /* ESHLIBVERS           87      Shared library version mismatch */
+       DUMPANDLOG,             /* EBADMACHO            88      Malformed Macho file */
+};
+
+
+static short
+nfs_pageouterrorhandler(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)  
+nfs3_access_otw(vnode_t vp,  
+               int wmode,
+               proc_t p,
+               kauth_cred_t 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);
-
-        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);
-        nfsm_postop_attr(vp, attrflag);
-        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;
+       const int v3 = 1;
+       u_long *tl;
+       int error = 0, attrflag;
+
+       mbuf_t mreq, mrep, md, mb, mb2;
+       caddr_t bpos, dpos, cp2;
+       register long t1, t2;
+       register caddr_t cp;
+       u_int32_t rmode;
+       struct nfsnode *np = VTONFS(vp);
+       u_int64_t xid;
+       struct timeval now;
+
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED);
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_ACCESS]);   
+       nfsm_fhtom(vp, v3);
+       nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+       *tl = txdr_unsigned(wmode);
+       nfsm_request(vp, NFSPROC_ACCESS, p, cred, &xid);
+       if (mrep) {
+               nfsm_postop_attr_update(vp, 1, attrflag, &xid);
+       }
+       if (!error) {
+               nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+               rmode = fxdr_unsigned(u_int32_t, *tl);
+               np->n_mode = rmode;
+               np->n_modeuid = kauth_cred_getuid(cred);
+               microuptime(&now);
+               np->n_modestamp = now.tv_sec;
+       }
+       nfsm_reqdone;
+       return error;
 }
 
 /*
@@ -428,18 +518,20 @@ nfs3_access_otw(struct vnode *vp,
  */
 static int
 nfs_access(ap)
-       struct vop_access_args /* {
-               struct vnode *a_vp;
-               int  a_mode;
-               struct ucred *a_cred;
-               struct proc *a_p;
+       struct vnop_access_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_mode;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-        int error = 0;
-        u_long mode, wmode;
+       vnode_t vp = ap->a_vp;
+       int error = 0, dorpc;
+       u_long mode, wmode;
        int v3 = NFS_ISV3(vp);
-        struct nfsnode *np = VTONFS(vp);
+       struct nfsnode *np = VTONFS(vp);
+       struct timeval now;
+       kauth_cred_t cred;
 
        /*
         * For nfs v3, do an access rpc, otherwise you are stuck emulating
@@ -450,69 +542,102 @@ nfs_access(ap)
         * in the cache.
         */
        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)
+               /*
+                * Convert KAUTH primitives to NFS access rights.
+                */
+               mode = 0;
+               if (vnode_isdir(vp)) {
+                       /* directory */
+                       if (ap->a_action &
+                           (KAUTH_VNODE_LIST_DIRECTORY |
+                           KAUTH_VNODE_READ_EXTATTRIBUTES))
+                               mode |= NFSV3ACCESS_READ;
+                       if (ap->a_action & KAUTH_VNODE_SEARCH)
                                mode |= NFSV3ACCESS_LOOKUP;
+                       if (ap->a_action &
+                           (KAUTH_VNODE_ADD_FILE |
+                           KAUTH_VNODE_ADD_SUBDIRECTORY))
+                               mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
+                       if (ap->a_action & KAUTH_VNODE_DELETE_CHILD)
+                               mode |= NFSV3ACCESS_MODIFY;
                } else {
-                       if (ap->a_mode & VWRITE)
-                               mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
-                       if (ap->a_mode & VEXEC)
+                       /* file */
+                       if (ap->a_action &
+                           (KAUTH_VNODE_READ_DATA |
+                           KAUTH_VNODE_READ_EXTATTRIBUTES))
+                               mode |= NFSV3ACCESS_READ;
+                       if (ap->a_action & KAUTH_VNODE_WRITE_DATA)
+                               mode |= NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND;
+                       if (ap->a_action & KAUTH_VNODE_APPEND_DATA)
+                               mode |= NFSV3ACCESS_EXTEND;
+                       if (ap->a_action & KAUTH_VNODE_EXECUTE)
                                mode |= NFSV3ACCESS_EXECUTE;
                }
-                /* XXX safety belt, only make blanket request if caching */
-                if (nfsaccess_cache_timeout > 0) {
-                        wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
-                                NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
-                                NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
-                } else {
-                        wmode = mode;
-                }
+               /* common */
+               if (ap->a_action & KAUTH_VNODE_DELETE)
+                       mode |= NFSV3ACCESS_DELETE;
+               if (ap->a_action &
+                   (KAUTH_VNODE_WRITE_ATTRIBUTES |
+                   KAUTH_VNODE_WRITE_EXTATTRIBUTES |
+                   KAUTH_VNODE_WRITE_SECURITY))
+                       mode |= NFSV3ACCESS_MODIFY;
+               /* XXX this is pretty dubious */
+               if (ap->a_action & KAUTH_VNODE_CHANGE_OWNER)
+                       mode |= NFSV3ACCESS_MODIFY;
+
+               /* if caching, always ask for every right */
+               if (nfsaccess_cache_timeout > 0) {
+                       wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY |
+                               NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE |
+                               NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
+               } else
+                       wmode = mode;
                 
-                /*
-                 * Does our cached result allow us to give a definite yes to
-                 * this request?
-                 */     
-                if ((time_second < (np->n_modestamp + nfsaccess_cache_timeout)) &&
-                    (ap->a_cred->cr_uid == np->n_modeuid) &&
-                    ((np->n_mode & mode) == mode)) {
-                        /* nfsstats.accesscache_hits++; */
-                } else {
-                        /*
-                         * Either a no, or a don't know.  Go to the wire.
-                         */
-                       /* nfsstats.accesscache_misses++; */
-                       error = nfs3_access_otw(vp, wmode, ap->a_p,ap->a_cred);
-                       if (!error) {
-                            if ((np->n_mode & mode) != mode)
+               cred = vfs_context_ucred(ap->a_context);
+
+               /*
+                * Does our cached result allow us to give a definite yes to
+                * this request?
+                */     
+               dorpc = 1;
+               if (NMODEVALID(np)) {
+                       microuptime(&now);
+                       if ((now.tv_sec < (np->n_modestamp + nfsaccess_cache_timeout)) &&
+                           (kauth_cred_getuid(cred) == np->n_modeuid) &&
+                           ((np->n_mode & mode) == mode)) {
+                               /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_hits); */
+                               dorpc = 0;
+                       }
+               }
+               if (dorpc) {
+                       /* Either a no, or a don't know.  Go to the wire. */
+                       /* OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
+                       error = nfs3_access_otw(vp, wmode, vfs_context_proc(ap->a_context), cred);
+               }
+               if (!error) {
+                       /*
+                        * If we asked for DELETE but didn't get it, the server
+                        * may simply not support returning that bit (possible
+                        * on UNIX systems).  So, we'll assume that it is OK,
+                        * and just let any subsequent delete action fail if it
+                        * really isn't deletable.
+                        */
+                       if ((mode & NFSV3ACCESS_DELETE) &&
+                           !(np->n_mode & NFSV3ACCESS_DELETE))
+                               np->n_mode |= NFSV3ACCESS_DELETE;
+                       if ((np->n_mode & mode) != mode)
                                error = EACCES;
-                        }
-                }
-       } else
-               return (nfsspec_access(ap)); /* NFSv2 case checks for EROFS here*/
-       /* CSM - moved EROFS check down per NetBSD rev 1.71.  So you
-        * get the correct error value with layered filesystems. 
-         * EKN - moved the return(error) below this so it does get called.*/
-       /*
-        * Disallow write attempts on filesystems mounted read-only;
-        * unless the file is a socket, fifo, or a block or character
-        * device resident on the filesystem.
-        */
-       if (!error && (ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
-               switch (vp->v_type) {
-               case VREG: case VDIR: case VLNK:
+               }
+       } else {
+               /* v2 */
+               if ((ap->a_action & KAUTH_VNODE_WRITE_RIGHTS) && vfs_isrdonly(vnode_mount(vp))) {
                        error = EROFS;
-                default: 
-                        break;
+               } else {
+                       error = 0;
                }
        }
-        return (error);
+
+       return (error);
 }
 
 /*
@@ -523,72 +648,72 @@ nfs_access(ap)
  * if consistency is lost.
  */
 /* ARGSUSED */
+
 static int
 nfs_open(ap)
-       struct vop_open_args /* {
-               struct vnode *a_vp;
-               int  a_mode;
-               struct ucred *a_cred;
-               struct proc *a_p;
+       struct vnop_open_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_mode;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
+       vnode_t vp = ap->a_vp;
        struct nfsnode *np = VTONFS(vp);
-       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
-       struct vattr vattr;
+       struct nfs_vattr nvattr;
+       kauth_cred_t cred;
+       proc_t p;
+       enum vtype vtype;
        int error;
 
-       if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
-{ printf("open eacces vtyp=%d\n",vp->v_type);
+       vtype = vnode_vtype(vp);
+       if (vtype != VREG && vtype != VDIR && vtype != VLNK) {
                return (EACCES);
-}
-       /*
-        * Get a valid lease. If cached data is stale, flush it.
-        */
-       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)
+       }
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       if (np->n_flag & NNEEDINVALIDATE) {
+               np->n_flag &= ~NNEEDINVALIDATE;
+               nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
+       }
+       if (np->n_flag & NMODIFIED) {
+               if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
                        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;
-                   }
+               NATTRINVALIDATE(np);
+               if (vtype == VDIR)
+                       np->n_direofoffset = 0;
+               error = nfs_getattr(vp, &nvattr, cred, p);
+               if (error)
+                       return (error);
+               if (vtype == VDIR) {
+                       /* if directory changed, purge any name cache entries */
+                       if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
+                               cache_purge(vp);
+                       np->n_ncmtime = nvattr.nva_mtime;
                }
+               np->n_mtime = nvattr.nva_mtime;
        } 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)
+               error = nfs_getattr(vp, &nvattr, cred, p);
+               if (error)
+                       return (error);
+               if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, !=)) {
+                       if (vtype == 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;
+                               nfs_invaldir(vp);
+                               /* purge name cache entries */
+                               if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=))
+                                       cache_purge(vp);
                        }
+                       if ((error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1)) == EINTR)
+                               return (error);
+                       if (vtype == VDIR)
+                               np->n_ncmtime = nvattr.nva_mtime;
+                       np->n_mtime = nvattr.nva_mtime;
                }
        }
-       if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
-               np->n_attrstamp = 0; /* For Open/Close consistency */
+       NATTRINVALIDATE(np); /* For Open/Close consistency */
        return (0);
 }
 
@@ -612,32 +737,29 @@ nfs_open(ap)
  * The current code does the following:
  * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
  * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
- *                     or commit them (this satisfies 1 and 2 except for the
- *                     case where the server crashes after this close but
- *                     before the commit RPC, which is felt to be "good
- *                     enough". Changing the last argument to nfs_flush() to
- *                     a 1 would force a commit operation, if it is felt a
- *                     commit is necessary now.
- * for NQNFS         - do nothing now, since 2 is dealt with via leases and
- *                     1 should be dealt with via an fsync() system call for
- *                     cases where write errors are important.
+ *                     them.
  */
 /* ARGSUSED */
 static int
 nfs_close(ap)
-       struct vop_close_args /* {
+       struct vnop_close_args /* {
                struct vnodeop_desc *a_desc;
-               struct vnode *a_vp;
-               int  a_fflag;
-               struct ucred *a_cred;
-               struct proc *a_p;
+               vnode_t a_vp;
+               int a_fflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-       register struct nfsnode *np = VTONFS(vp);
+       vnode_t vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct nfsmount *nmp;
+       kauth_cred_t cred;
+       proc_t p;
        int error = 0;
 
-       if (vp->v_type == VREG) {
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       if (vnode_vtype(vp) == VREG) {
 #if DIAGNOSTIC
            register struct sillyrename *sp = np->n_sillyrename;
            if (sp)
@@ -645,14 +767,26 @@ nfs_close(ap)
                        &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)) {
+           nmp = VFSTONFS(vnode_mount(vp));
+           if (!nmp)
+               return (ENXIO);
+           if (np->n_flag & NNEEDINVALIDATE) {
+               np->n_flag &= ~NNEEDINVALIDATE;
+               nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
+           }
+           if (np->n_flag & NMODIFIED) {
                if (NFS_ISV3(vp)) {
-                   error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0);
-                   np->n_flag &= ~NMODIFIED;
-               } else
-                   error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
-               np->n_attrstamp = 0;
+                   error = nfs_flush(vp, MNT_WAIT, cred, p, 0);
+                    /*
+                     * We cannot clear the NMODIFIED bit in np->n_flag due to
+                     * potential races with other processes
+                    * NMODIFIED is a hint
+                     */
+                   /* np->n_flag &= ~NMODIFIED; */
+               } else {
+                   error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+               }
+               NATTRINVALIDATE(np);
            }
            if (np->n_flag & NWRITEERR) {
                np->n_flag &= ~NWRITEERR;
@@ -662,85 +796,229 @@ nfs_close(ap)
        return (error);
 }
 
+
+int
+nfs_getattr_no_vnode(
+       mount_t mp,
+       u_char *fhp,
+       int fhsize,
+       kauth_cred_t cred,
+       proc_t p,
+       struct nfs_vattr *nvap,
+       u_int64_t *xidp)
+{
+       mbuf_t mreq, mrep, md, mb, mb2;
+       caddr_t bpos, dpos;
+       int t2;
+       u_long *tl;
+       caddr_t cp;
+       struct nfsmount *nmp = VFSTONFS(mp);
+       int v3 = (nmp->nm_flag & NFSMNT_NFSV3);
+       int hsiz;
+       int error = 0;
+
+       // XXX fix this to use macros once the macros get cleaned up
+       //nfsm_reqhead(NFSX_FH(v3));
+               hsiz = NFSX_FH(v3);
+               mb = NULL;
+               if (hsiz >= nfs_mbuf_minclsize)
+                       error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
+               else
+                       error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
+               if (error)
+                       return (error);
+               bpos = mbuf_data(mb);
+               mreq = mb;
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
+       //nfsm_fhtom(vp, v3);
+             if (v3) {
+                       t2 = nfsm_rndup(fhsize) + NFSX_UNSIGNED;
+                       if (t2 <= mbuf_trailingspace(mb)) {
+                               nfsm_build(tl, u_long *, t2);
+                               *tl++ = txdr_unsigned(fhsize);
+                               *(tl + ((t2>>2) - 2)) = 0;
+                               bcopy((caddr_t)fhp,(caddr_t)tl, fhsize);
+                       } else if ((t2 = nfsm_strtmbuf(&mb, &bpos, (caddr_t)fhp, fhsize))) {
+                               error = t2;
+                               mbuf_freem(mreq);
+                               goto nfsmout;
+                       }
+               } else {
+                       nfsm_build(cp, caddr_t, NFSX_V2FH);
+                       bcopy((caddr_t)fhp, cp, NFSX_V2FH);
+               }
+       //nfsm_request(vp, NFSPROC_GETATTR, p, cred, xidp);
+               if ((error = nfs_request(NULL, mp, mreq, NFSPROC_GETATTR, p, cred, &mrep, &md, &dpos, xidp))) {
+                       if (error & NFSERR_RETERR)
+                               error &= ~NFSERR_RETERR;
+                       else
+                               goto nfsmout;
+               }
+       if (!error) {
+               //nfsm_loadattr(vp, nvap, xidp);
+               error = nfs_parsefattr(&md, &dpos, v3, nvap);
+               if (error) {
+                       mbuf_freem(mrep);
+                       goto nfsmout;
+               }
+       }
+       nfsm_reqdone;
+       return (error);
+}
+
 /*
  * nfs getattr call from vfs.
  */
-static int
-nfs_getattr(ap)
-       struct vop_getattr_args /* {
-               struct vnode *a_vp;
-               struct vattr *a_vap;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *ap;
+int
+nfs_getattr(
+       vnode_t vp,
+       struct nfs_vattr *nvap,
+       kauth_cred_t cred,
+       proc_t p)
 {
-       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;
+       struct nfsnode *np = VTONFS(vp);
+       caddr_t cp;
+       u_long *tl;
+       int t1, t2;
        caddr_t bpos, dpos;
        int error = 0;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       int v3 = NFS_ISV3(vp);
-       
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
+       int avoidfloods;
+
+       FSDBG_TOP(513, np->n_size, np, np->n_vattr.nva_size, np->n_flag);
+
        /*
         * Update local times for special files.
         */
        if (np->n_flag & (NACC | NUPD))
                np->n_flag |= NCHG;
-
-       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 513)) | DBG_FUNC_START,
-                    (int)np->n_size, 0, (int)np->n_vattr.va_size, np->n_flag, 0);
-
        /*
         * First look in the cache.
         */
-       if ((error = nfs_getattrcache(vp, ap->a_vap)) == 0) {
-               KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 513)) | DBG_FUNC_END,
-                            (int)np->n_size, 0, (int)np->n_vattr.va_size, np->n_flag, 0);
-
+       if ((error = nfs_getattrcache(vp, nvap)) == 0) {
+               FSDBG_BOT(513, np->n_size, 0, np->n_vattr.nva_size, np->n_flag);
                return (0);
        }
-       if (error != ENOENT)
+       if (error != ENOENT) {
+               FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size,
+                         np->n_flag);
                return (error);
+       }
+
+       if (!VFSTONFS(vnode_mount(vp))) {
+               FSDBG_BOT(513, np->n_size, ENXIO, np->n_vattr.nva_size, np->n_flag);
+               return (ENXIO);
+       }
+       v3 = NFS_ISV3(vp);
        error = 0;
-        
-       if (v3 && nfsaccess_cache_timeout > 0) {
-               /*  nfsstats.accesscache_misses++; */
-               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)
+
+       /*
+        * Try to get both the attributes and access info by making an
+        * ACCESS call and seeing if it returns updated attributes.
+        * But don't bother if we aren't caching access info or if the
+        * attributes returned wouldn't be cached.
+        */
+       if (v3 && (nfsaccess_cache_timeout > 0) &&
+           (nfs_attrcachetimeout(vp) > 0)) {
+               /*  OSAddAtomic(1, (SInt32*)&nfsstats.accesscache_misses); */
+               if ((error = nfs3_access_otw(vp, NFSV3ACCESS_ALL, p, cred)))
+                       return (error);
+               if ((error = nfs_getattrcache(vp, nvap)) == 0)
                        return (0);
                if (error != ENOENT)
                        return (error);
                error = 0;
        }
-
-       nfsstats.rpccnt[NFSPROC_GETATTR]++;
-       nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3));
+       avoidfloods = 0;
+tryagain:
+       nfsm_reqhead(NFSX_FH(v3));
+       if (error) {
+               FSDBG_BOT(513, np->n_size, error, np->n_vattr.nva_size, np->n_flag);
+               return (error);
+       }
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_GETATTR]);
        nfsm_fhtom(vp, v3);
-       nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred);
+       nfsm_request(vp, NFSPROC_GETATTR, p, cred, &xid);
        if (!error) {
-               nfsm_loadattr(vp, ap->a_vap);
-               if (np->n_mtime != ap->a_vap->va_mtime.tv_sec) {
-                       NFSTRACE(NFSTRC_GA_INV, vp);
-                       if (vp->v_type == VDIR)
+               nfsm_loadattr(vp, v3, nvap, &xid);
+               if (!xid) { /* out-of-order rpc - attributes were dropped */
+                       mbuf_freem(mrep);
+                       mrep = NULL;
+                       FSDBG(513, -1, np, np->n_xid << 32, np->n_xid);
+                       if (avoidfloods++ < 100)
+                               goto tryagain;
+                       /*
+                        * avoidfloods>1 is bizarre.  at 100 pull the plug
+                        */
+                       panic("nfs_getattr: getattr flood\n");
+               }
+               if (nfstimespeccmp(&np->n_mtime, &nvap->nva_mtime, !=)) {
+                       enum vtype vtype = vnode_vtype(vp);
+                       FSDBG(513, -1, np, -1, vp);
+                       if (vtype == VDIR) {
                                nfs_invaldir(vp);
-                       error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
-                                             ap->a_p, 1);
+                               /* purge name cache entries */
+                               if (nfstimespeccmp(&np->n_ncmtime, &nvap->nva_mtime, !=))
+                                       cache_purge(vp);
+                       }
+                       error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                       FSDBG(513, -1, np, -2, error);
                        if (!error) {
-                               NFSTRACE(NFSTRC_GA_INV1, vp);
-                               np->n_mtime = ap->a_vap->va_mtime.tv_sec;
-                       } else {
-                               NFSTRACE(NFSTRC_GA_INV2, error);
+                               if (vtype == VDIR)
+                                       np->n_ncmtime = nvap->nva_mtime;
+                               np->n_mtime = nvap->nva_mtime;
                        }
                }
        }
        nfsm_reqdone;
 
-       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 513)) | DBG_FUNC_END,
-                    (int)np->n_size, -1, (int)np->n_vattr.va_size, error, 0);
+       FSDBG_BOT(513, np->n_size, -1, np->n_vattr.nva_size, error);
+       return (error);
+}
+
+
+static int
+nfs_vnop_getattr(
+       struct vnop_getattr_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               struct vnode_attr *a_vap;
+               vfs_context_t a_context;
+       } */ *ap)
+{
+       int error;
+       struct nfs_vattr nva;
+       struct vnode_attr *vap = ap->a_vap;
+
+       error = nfs_getattr(ap->a_vp, &nva,
+               vfs_context_ucred(ap->a_context),
+               vfs_context_proc(ap->a_context));
+       if (error)
+               return (error);
+
+       /* copy nva to *a_vap */
+       VATTR_RETURN(vap, va_type, nva.nva_type);
+       VATTR_RETURN(vap, va_mode, nva.nva_mode);
+       VATTR_RETURN(vap, va_rdev, nva.nva_rdev);
+       VATTR_RETURN(vap, va_uid, nva.nva_uid);
+       VATTR_RETURN(vap, va_gid, nva.nva_gid);
+       VATTR_RETURN(vap, va_nlink, nva.nva_nlink);
+       VATTR_RETURN(vap, va_fileid, nva.nva_fileid);
+       VATTR_RETURN(vap, va_data_size, nva.nva_size);
+       VATTR_RETURN(vap, va_data_alloc, nva.nva_bytes);
+       VATTR_RETURN(vap, va_iosize, nva.nva_blocksize);  /* should this just be f_iosize? */
+       VATTR_RETURN(vap, va_fsid, nva.nva_fsid);
+       vap->va_access_time.tv_sec = nva.nva_atime.tv_sec;
+       vap->va_access_time.tv_nsec = nva.nva_atime.tv_nsec;
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       vap->va_modify_time.tv_sec = nva.nva_mtime.tv_sec;
+       vap->va_modify_time.tv_nsec = nva.nva_mtime.tv_nsec;
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+       vap->va_change_time.tv_sec = nva.nva_ctime.tv_sec;
+       vap->va_change_time.tv_nsec = nva.nva_ctime.tv_nsec;
+       VATTR_SET_SUPPORTED(vap, va_change_time);
 
        return (error);
 }
@@ -750,120 +1028,174 @@ nfs_getattr(ap)
  */
 static int
 nfs_setattr(ap)
-       struct vop_setattr_args /* {
+       struct vnop_setattr_args /* {
                struct vnodeop_desc *a_desc;
-               struct vnode *a_vp;
-               struct vattr *a_vap;
-               struct ucred *a_cred;
-               struct proc *a_p;
+               vnode_t a_vp;
+               struct vnode_attr *a_vap;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-       register struct nfsnode *np = VTONFS(vp);
-       register struct vattr *vap = ap->a_vap;
+       vnode_t vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct vnode_attr *vap = ap->a_vap;
        int error = 0;
        u_quad_t tsize;
+       kauth_cred_t cred;
+       proc_t p;
 
 #ifndef nolint
        tsize = (u_quad_t)0;
 #endif
-       /*
-        * Disallow write attempts if the filesystem is mounted read-only.
-        */
-       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))
+
+       /* Setting of flags is not supported. */
+       if (VATTR_IS_ACTIVE(vap, va_flags))
+               return (ENOTSUP);
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       VATTR_SET_SUPPORTED(vap, va_mode);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_data_size);
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+
+       /* Disallow write attempts if the filesystem is mounted read-only. */
+       if ((VATTR_IS_ACTIVE(vap, va_flags) || VATTR_IS_ACTIVE(vap, va_mode) ||
+            VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid) ||
+            VATTR_IS_ACTIVE(vap, va_access_time) ||
+            VATTR_IS_ACTIVE(vap, va_modify_time)) &&
+           vnode_vfsisrdonly(vp))
                return (EROFS);
-       if (vap->va_size != VNOVAL) {
-               switch (vp->v_type) {
+
+       if (VATTR_IS_ACTIVE(vap, va_data_size)) {
+               switch (vnode_vtype(vp)) {
                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)
+                       if (!VATTR_IS_ACTIVE(vap, va_modify_time) &&
+                           !VATTR_IS_ACTIVE(vap, va_access_time) &&
+                           !VATTR_IS_ACTIVE(vap, va_mode) &&
+                           !VATTR_IS_ACTIVE(vap, va_uid) &&
+                           !VATTR_IS_ACTIVE(vap, va_gid))
                                return (0);
-                       vap->va_size = VNOVAL;
+                       VATTR_CLEAR_ACTIVE(vap, va_data_size);
                        break;
                default:
                        /*
                         * Disallow write attempts if the filesystem is
                         * mounted read-only.
                         */
-                       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+                       if (vnode_vfsisrdonly(vp))
                                return (EROFS);
-                       np->n_flag |= NMODIFIED;
-                       tsize = np->n_size;
-                       
-                       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 512)) | DBG_FUNC_START,
-                                    (int)np->n_size, (int)vap->va_size, (int)np->n_vattr.va_size, np->n_flag, 0);
-
-                       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 (UBCISVALID(vp))
-                               ubc_setsize(vp, (off_t)vap->va_size); /* XXX check error */
-
-                       if (error) {
-                               printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
-
-#if DIAGNOSTIC
-                               kprintf("nfs_setattr: nfs_vinvalbuf %d\n",
-                                       error);
-#endif /* DIAGNOSTIC */
-                               if (UBCISVALID(vp))
-                                       ubc_setsize(vp, (off_t)tsize); /* XXX check error */
-
-                               KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 512)) | DBG_FUNC_END,
-                                            (int)np->n_size, (int)vap->va_size, (int)np->n_vattr.va_size, -1, 0);
-
-                               return (error);
+                       FSDBG_TOP(512, np->n_size, vap->va_data_size,
+                                 np->n_vattr.nva_size, np->n_flag);
+                       if (np->n_flag & NMODIFIED) {
+                               if (vap->va_data_size == 0)
+                                       error = nfs_vinvalbuf(vp, 0, cred, p, 1);
+                               else
+                                       error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+                               if (error) {
+                                       printf("nfs_setattr: nfs_vinvalbuf %d\n", error);
+                                       FSDBG_BOT(512, np->n_size, vap->va_data_size,
+                                                 np->n_vattr.nva_size, -1);
+                                       return (error);
+                               }
+                       } else if (np->n_size > vap->va_data_size) { /* shrinking? */
+                               daddr64_t obn, bn;
+                               int biosize, neweofoff, mustwrite;
+                               struct nfsbuf *bp;
+
+                               biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
+                               obn = (np->n_size - 1) / biosize;
+                               bn = vap->va_data_size / biosize; 
+                               for ( ; obn >= bn; obn--) {
+                                       if (!nfs_buf_is_incore(vp, obn))
+                                               continue;
+                                       error = nfs_buf_get(vp, obn, biosize, 0, NBLK_READ, &bp);
+                                       if (error)
+                                               continue;
+                                       if (obn != bn) {
+                                               FSDBG(512, bp, bp->nb_flags, 0, obn);
+                                               SET(bp->nb_flags, NB_INVAL);
+                                               nfs_buf_release(bp, 1);
+                                               continue;
+                                       }
+                                       mustwrite = 0;
+                                       neweofoff = vap->va_data_size - NBOFF(bp);
+                                       /* check for any dirty data before the new EOF */
+                                       if (bp->nb_dirtyend && bp->nb_dirtyoff < neweofoff) {
+                                               /* clip dirty range to EOF */
+                                               if (bp->nb_dirtyend > neweofoff)
+                                                       bp->nb_dirtyend = neweofoff;
+                                               mustwrite++;
+                                       }
+                                       bp->nb_dirty &= (1 << round_page_32(neweofoff)/PAGE_SIZE) - 1;
+                                       if (bp->nb_dirty)
+                                               mustwrite++;
+                                       if (!mustwrite) {
+                                               FSDBG(512, bp, bp->nb_flags, 0, obn);
+                                               SET(bp->nb_flags, NB_INVAL);
+                                               nfs_buf_release(bp, 1);
+                                               continue;
+                                       }
+                                       /* gotta write out dirty data before invalidating */
+                                       /* (NB_STABLE indicates that data writes should be FILESYNC) */
+                                       /* (NB_NOCACHE indicates buffer should be discarded) */
+                                       CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL | NB_ASYNC | NB_READ));
+                                       SET(bp->nb_flags, NB_STABLE | NB_NOCACHE);
+                                       if (bp->nb_wcred == NOCRED) {
+                                               kauth_cred_ref(cred);
+                                               bp->nb_wcred = cred;
+                                       }
+                                       error = nfs_buf_write(bp);
+                                       // Note: bp has been released
+                                       if (error) {
+                                               FSDBG(512, bp, 0xd00dee, 0xbad, error);
+                                               np->n_error = error;
+                                               np->n_flag |= NWRITEERR;
+                                               /*
+                                                * There was a write error and we need to
+                                                * invalidate attrs and flush buffers in
+                                                * order to sync up with the server.
+                                                * (if this write was extending the file,
+                                                * we may no longer know the correct size)
+                                                */
+                                               NATTRINVALIDATE(np);
+                                               nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cred, p, 1);
+                                               error = 0;
+                                       }
+                               }
                        }
-                       np->n_size = np->n_vattr.va_size = 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);
-
-       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 512)) | DBG_FUNC_END,
-                    (int)np->n_size, (int)vap->va_size, (int)np->n_vattr.va_size, error, 0);
-
-       if (error && vap->va_size != VNOVAL) {
+                       tsize = np->n_size;
+                       np->n_size = np->n_vattr.nva_size = vap->va_data_size;
+                       ubc_setsize(vp, (off_t)vap->va_data_size); /* XXX error? */
+               }
+       } else if ((VATTR_IS_ACTIVE(vap, va_modify_time) ||
+                   VATTR_IS_ACTIVE(vap, va_access_time)) &&
+                  (np->n_flag & NMODIFIED) && (vnode_vtype(vp) == VREG)) {
+               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
+               if (error == EINTR)
+                       return (error);
+       }
+       if (VATTR_IS_ACTIVE(vap, va_mode)) {
+               NMODEINVALIDATE(np);
+       }
+       error = nfs_setattrrpc(vp, vap, cred, p);
+       FSDBG_BOT(512, np->n_size, vap->va_data_size, np->n_vattr.nva_size, error);
+       if (error && VATTR_IS_ACTIVE(vap, va_data_size)) {
                /* make every effort to resync file size w/ server... */
-               int err = 0; /* preserve "error" for return */
+               int err; /* preserve "error" for return */
 
-               printf("nfs_setattr: nfs_setattrrpc %d\n", error);
-#if DIAGNOSTIC
-               kprintf("nfs_setattr: nfs_setattrrpc %d\n", error);
-#endif /* DIAGNOSTIC */
-               np->n_size = np->n_vattr.va_size = tsize;
-               if (UBCISVALID(vp))
-                       ubc_setsize(vp, (off_t)np->n_size); /* XXX check error */
-               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);
-#if DIAGNOSTIC
-               if (err)
-                       kprintf("nfs_setattr nfs_setattrrpc %d\n", err);
-#endif /* DIAGNOSTIC */
+               np->n_size = np->n_vattr.nva_size = tsize;
+               ubc_setsize(vp, (off_t)np->n_size); /* XXX check error */
+               vap->va_data_size = tsize;
+               err = nfs_setattrrpc(vp, vap, cred, p);
+               printf("nfs_setattr: nfs_setattrrpc %d %d\n", error, err);
        }
        return (error);
 }
@@ -873,25 +1205,33 @@ nfs_setattr(ap)
  */
 static int
 nfs_setattrrpc(vp, vap, cred, procp)
-       register struct vnode *vp;
-       register struct vattr *vap;
-       struct ucred *cred;
-       struct proc *procp;
+       vnode_t vp;
+       struct vnode_attr *vap;
+       kauth_cred_t cred;
+       proc_t 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);
+       int error = 0, wccpostattr = 0;
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
+       struct timeval now;
+
+       if (!VFSTONFS(vnode_mount(vp)))
+               return (ENXIO);
+       v3 = NFS_ISV3(vp);
 
-       nfsstats.rpccnt[NFSPROC_SETATTR]++;
-       nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3));
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_SATTR(v3));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SETATTR]);
        nfsm_fhtom(vp, v3);
        if (v3) {
-               if (vap->va_mode != (u_short)VNOVAL) {
+               if (VATTR_IS_ACTIVE(vap, va_mode)) {
                        nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
                        *tl++ = nfs_true;
                        *tl = txdr_unsigned(vap->va_mode);
@@ -899,7 +1239,7 @@ nfs_setattrrpc(vp, vap, cred, procp)
                        nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                        *tl = nfs_false;
                }
-               if (vap->va_uid != (uid_t)VNOVAL) {
+               if (VATTR_IS_ACTIVE(vap, va_uid)) {
                        nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
                        *tl++ = nfs_true;
                        *tl = txdr_unsigned(vap->va_uid);
@@ -907,7 +1247,7 @@ nfs_setattrrpc(vp, vap, cred, procp)
                        nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                        *tl = nfs_false;
                }
-               if (vap->va_gid != (gid_t)VNOVAL) {
+               if (VATTR_IS_ACTIVE(vap, va_gid)) {
                        nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
                        *tl++ = nfs_true;
                        *tl = txdr_unsigned(vap->va_gid);
@@ -915,19 +1255,20 @@ nfs_setattrrpc(vp, vap, cred, procp)
                        nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                        *tl = nfs_false;
                }
-               if (vap->va_size != VNOVAL) {
+               if (VATTR_IS_ACTIVE(vap, va_data_size)) {
                        nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
                        *tl++ = nfs_true;
-                       txdr_hyper(&vap->va_size, tl);
+                       txdr_hyper(&vap->va_data_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) {
+               microtime(&now);
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       if (vap->va_access_time.tv_sec != now.tv_sec) {
                                nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
                                *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
-                               txdr_nfsv3time(&vap->va_atime, tl);
+                               txdr_nfsv3time(&vap->va_access_time, tl);
                        } else {
                                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
@@ -936,11 +1277,11 @@ nfs_setattrrpc(vp, vap, cred, procp)
                        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) {
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       if (vap->va_modify_time.tv_sec != now.tv_sec) {
                                nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
                                *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
-                               txdr_nfsv3time(&vap->va_mtime, tl);
+                               txdr_nfsv3time(&vap->va_modify_time, tl);
                        } else {
                                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                                *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
@@ -952,30 +1293,57 @@ nfs_setattrrpc(vp, vap, cred, procp)
                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                *tl = nfs_false;
        } else {
+               struct timespec neg1time = { -1, -1 };
                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;
+               if (VATTR_IS_ACTIVE(vap, va_mode))
+                       sp->sa_mode = vtonfsv2_mode(vnode_vtype(vp), vap->va_mode);
                else
+                       sp->sa_mode = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_uid))
                        sp->sa_uid = txdr_unsigned(vap->va_uid);
-               if (vap->va_gid == (gid_t)VNOVAL)
-                       sp->sa_gid = VNOVAL;
                else
+                       sp->sa_uid = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_gid))
                        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);
+               else
+                       sp->sa_gid = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_data_size))
+                       sp->sa_size = txdr_unsigned(vap->va_data_size);
+               else
+                       sp->sa_size = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_atime);
+               }
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_mtime);
+               }
        }
-       nfsm_request(vp, NFSPROC_SETATTR, procp, cred);
+       nfsm_request(vp, NFSPROC_SETATTR, procp, cred, &xid);
        if (v3) {
-               nfsm_wcc_data(vp, wccflag);
-               if ((!wccflag) && (vp->v_type != VBAD)) /* EINVAL set on VBAD vnode */
-                       VTONFS(vp)->n_attrstamp = 0;
-       } else
-               nfsm_loadattr(vp, (struct vattr *)0);
+               struct timespec premtime = { 0, 0 };
+               if (mrep) {
+                       nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
+               }
+               /* if file hadn't changed, update cached mtime */
+               if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==)) {
+                       VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
+               }
+               /* if directory hadn't changed, update namecache mtime */
+               if ((vnode_vtype(vp) == VDIR) &&
+                   nfstimespeccmp(&VTONFS(vp)->n_ncmtime, &premtime, ==)) {
+                       VTONFS(vp)->n_ncmtime = VTONFS(vp)->n_vattr.nva_mtime;
+               }
+               if (!wccpostattr)
+                       NATTRINVALIDATE(VTONFS(vp));
+       } else {
+               if (mrep) {
+                       nfsm_loadattr(vp, v3, NULL, &xid);
+               }
+       }
        nfsm_reqdone;
        return (error);
 }
@@ -987,251 +1355,233 @@ nfs_setattrrpc(vp, vap, cred, procp)
  */
 static int
 nfs_lookup(ap)
-       struct vop_lookup_args /* {
+       struct vnop_lookup_args /* {
                struct vnodeop_desc *a_desc;
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
+               vnode_t a_dvp;
+               vnode_t *a_vpp;
                struct componentname *a_cnp;
+               vfs_context_t a_context;
        } */ *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;
+       struct componentname *cnp = ap->a_cnp;
+       vnode_t dvp = ap->a_dvp;
+       vnode_t *vpp = ap->a_vpp;
+       int flags = cnp->cn_flags;
+       vnode_t newvp;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
        caddr_t bpos, dpos, cp2;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       mbuf_t mreq, mrep, md, mb, mb2;
        long len;
-       nfsfh_t *fhp;
-       struct nfsnode *np;
-       int lockparent, wantparent, error = 0, attrflag, fhsize;
+       u_char *fhp;
+       struct nfsnode *dnp, *np;
+       int wantparent, error, attrflag, dattrflag, fhsize, fhisdvp;
        int v3 = NFS_ISV3(dvp);
-       struct proc *p = cnp->cn_proc;
-        int worldbuildworkaround = 1;
+       u_int64_t xid, dxid;
+       struct nfs_vattr nvattr;
+       kauth_cred_t cred;
+       proc_t p;
+       int ngflags;
 
-       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;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
        wantparent = flags & (LOCKPARENT|WANTPARENT);
-       nmp = VFSTONFS(dvp->v_mount);
-       np = VTONFS(dvp);
-        
-        if (worldbuildworkaround) {
-        /* temporary workaround for world builds to not have dvp go
-            VBAD on during server calls in this routine. When
-            the real ref counting problem is found take this out.
-            Note if this was later and before the nfsm_request
-            set up, the workaround did not work (NOTE other difference
-            was I only put one VREF in that time. Thus it needs
-            to be above the cache_lookup branch or with 2 VREFS. Not
-            sure which. Can't play with world builds right now to see
-            which.  VOP_ACCESS could also make it go to server.  - EKN */
-            VREF(dvp);   /* hang on to this dvp - EKN */
-            VREF(dvp);   /* hang on tight - EKN  */
-        }
-
-       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;
-                        if (worldbuildworkaround) {
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                        }
-                       return (error);
-               }
-                
-                /* got to check to make sure the vnode didn't go away if access went to server */
-                if ((*vpp)->v_type == VBAD) {
-                       if (worldbuildworkaround) {
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                        }
-                        return(EINVAL);
-                }
+       dnp = VTONFS(dvp);
 
-               newvp = *vpp;
-               vpid = newvp->v_id;
+       error = nfs_getattr(dvp, &nvattr, cred, p);
+       if (error)
+               goto error_return;
+       if (nfstimespeccmp(&dnp->n_ncmtime, &nvattr.nva_mtime, !=)) {
                /*
-                * See the comment starting `Step through' in ufs/ufs_lookup.c
-                * for an explanation of the locking protocol
+                * This directory has changed on us.
+                * Purge any name cache entries.
                 */
-               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);
+               cache_purge(dvp);
+               dnp->n_ncmtime = nvattr.nva_mtime;
+       }
+
+       error = cache_lookup(dvp, vpp, cnp);
+       switch (error) {
+       case ENOENT:
+               /* negative cache entry same as cache miss */
+               error = 0;
+               /* FALLTHROUGH */
+       case 0:
+               /* cache miss */
+               break;
+       case -1:
+               /* cache hit, not really an error */
+       {
+               struct vnop_access_args naa;
+
+               OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_hits);
+
+               /* check for directory access */
+               naa.a_vp = dvp;
+               naa.a_action = KAUTH_VNODE_SEARCH;
+               naa.a_context = ap->a_context;
+
+               /* compute actual success/failure based on accessibility */
+               error = nfs_access(&naa);
+       }
+               /* FALLTHROUGH */
+       default:
+               /* unexpected error from cache_lookup */
+               goto error_return;
+       }
+       
+       /* check for lookup of "." */
+       if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
+               /* skip lookup, we know who we are */
+               fhisdvp = 1;
+               fhp = NULL;
+               fhsize = 0;
+               mrep = NULL;
+               goto found;
+       }
+
+       /* do we know this name is too long? */
+       if (v3) {
+               /* For NFSv3: need uniform pathconf info to test pc_namemax */
+               struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
+               if (!nmp) {
+                       error = ENXIO;
+                       goto error_return;
                }
-               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;
-                                        
-                                if (worldbuildworkaround) {
-                                    vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                                    vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                                }
-                                
-                               return (0);
-                          }
-                          cache_purge(newvp);
-                        }
-                       vput(newvp);
-                       if (lockparent && dvp != newvp && (flags & ISLASTCN))
-                               VOP_UNLOCK(dvp, 0, p);
+               if (((nmp->nm_state & (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) ==
+                       (NFSSTA_GOTFSINFO|NFSSTA_GOTPATHCONF)) &&
+                    (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS) &&
+                    (cnp->cn_namelen > (long)nmp->nm_fsinfo.namemax)) {
+                       error = ENAMETOOLONG;
+                       goto error_return;
                }
-               error = vn_lock(dvp, LK_EXCLUSIVE, p);
-               *vpp = NULLVP;
-               if (error) {
-                        if (worldbuildworkaround) {
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                        }
-                       return (error);
-                }
+       } else if (cnp->cn_namelen > NFS_MAXNAMLEN) {
+               error = ENAMETOOLONG;
+               goto error_return;
        }
-        
-       /* 
-         * Got to check to make sure the vnode didn't go away if VOP_GETATTR went to server
-        * or callers prior to this blocked and had it go VBAD.
-         */
-       if (dvp->v_type == VBAD) {                   
-            if (worldbuildworkaround) {
-                vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-                }
-            return(EINVAL);
-        }
 
        error = 0;
        newvp = NULLVP;
-       nfsstats.lookupcache_misses++;
-       nfsstats.rpccnt[NFSPROC_LOOKUP]++;
+
+       OSAddAtomic(1, (SInt32*)&nfsstats.lookupcache_misses);
        len = cnp->cn_namelen;
-       nfsm_reqhead(dvp, NFSPROC_LOOKUP,
-               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       if (error)
+               goto error_return;
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
-       nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
-        
-        /* this two lines set dvp refcounts back to where they were
-        * before we took extra 2 VREFS to avoid VBAD vnode on dvp
-        * during server calls for world builds. Remove when real
-        * fix is found. - EKN */
-        if (worldbuildworkaround) {
-            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-            vrele(dvp);  /* end of hanging on tight to dvp - EKN */
-            }
+       nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
+       /* nfsm_request for NFSv2 causes you to goto to nfsmout upon errors */
+       nfsm_request(dvp, NFSPROC_LOOKUP, p, cred, &xid); 
 
        if (error) {
-               nfsm_postop_attr(dvp, attrflag);
-               m_freem(mrep);
+               if (mrep) {
+                       nfsm_postop_attr_update(dvp, v3, dattrflag, &xid);
+                       mbuf_freem(mrep);
+               }
                goto nfsmout;
        }
+
+       /* get the filehandle */
        nfsm_getfh(fhp, fhsize, v3);
+       /* is the file handle the same as this directory's file handle? */
+       fhisdvp = NFS_CMPFH(dnp, fhp, fhsize);
+
+       /* get attributes */
+       if (v3) {
+               dxid = xid;
+               nfsm_postop_attr_get(v3, attrflag, &nvattr);
+               nfsm_postop_attr_update(dvp, v3, dattrflag, &dxid);
+               if (!attrflag && (!fhisdvp || !dattrflag)) {
+                       /* We need valid attributes in order */
+                       /* to call nfs_nget/vnode_create().  */
+                       error = nfs_getattr_no_vnode(vnode_mount(dvp),
+                                       fhp, fhsize, cred, p, &nvattr, &xid);
+                       if (error) {
+                               mbuf_freem(mrep);
+                               goto error_return;
+                       }
+               }
+       } else {
+               nfsm_attr_get(v3, &nvattr);
+       }
+
+found:
 
        /*
         * Handle RENAME case...
         */
        if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
-               if (NFS_CMPFH(np, fhp, fhsize)) {
-                       m_freem(mrep);
-                       return (EISDIR);
+               if (fhisdvp) {
+                       mbuf_freem(mrep);
+                       error = EISDIR;
+                       goto error_return;
                }
-               if ((error = nfs_nget(dvp->v_mount, fhp, fhsize, &np))) {
-                       m_freem(mrep);
-                       return (error);
+               error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
+                               &nvattr, &xid, 0, &np);
+               if (error) {
+                       mbuf_freem(mrep);
+                       goto error_return;
                }
-               newvp = NFSTOV(np);
-               if (v3) {
-                       nfsm_postop_attr(newvp, attrflag);
-                       nfsm_postop_attr(dvp, attrflag);
-               } else
-                       nfsm_loadattr(newvp, (struct vattr *)0);
-               *vpp = newvp;
-               m_freem(mrep);
-               cnp->cn_flags |= SAVENAME;
-               if (!lockparent)
-                       VOP_UNLOCK(dvp, 0, p);
-               return (0);
+               *vpp = NFSTOV(np);
+               mbuf_freem(mrep);
+
+               goto error_return;
        }
 
-       if (flags & ISDOTDOT) {
-               VOP_UNLOCK(dvp, 0, p);
-               error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
+       if ((cnp->cn_flags & MAKEENTRY) &&
+           (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))
+               ngflags = NG_MAKEENTRY;
+       else
+               ngflags = 0;
+
+       if (fhisdvp) {
+               error = vnode_get(dvp);
                if (error) {
-                       vn_lock(dvp, LK_EXCLUSIVE + LK_RETRY, p);
-                       return (error);
-               }
-               newvp = NFSTOV(np);
-               if (lockparent && (flags & ISLASTCN) &&
-                   (error = vn_lock(dvp, LK_EXCLUSIVE, p))) {
-                       vput(newvp);
-                       return (error);
+                       mbuf_freem(mrep);
+                       goto error_return;
                }
-       } else if (NFS_CMPFH(np, fhp, fhsize)) {
-               VREF(dvp);
                newvp = dvp;
+               /* test fhp to see if we have valid attributes in nvattr */
+               if (fhp && (dnp->n_xid <= xid)) {
+                       error = nfs_loadattrcache(dnp, &nvattr, &xid, 0);
+                       if (error) {
+                               vnode_put(dvp);
+                               mbuf_freem(mrep);
+                               goto error_return;
+                       }
+               }
        } else {
-               if ((error = nfs_nget(dvp->v_mount, fhp, fhsize, &np))) {
-                       m_freem(mrep);
-                       return (error);
+               error = nfs_nget(vnode_mount(dvp), dvp, cnp, fhp, fhsize,
+                               &nvattr, &xid, ngflags, &np);
+               if (error) {
+                       mbuf_freem(mrep);
+                       goto error_return;
                }
-               if (!lockparent || !(flags & ISLASTCN))
-                       VOP_UNLOCK(dvp, 0, p);
                newvp = NFSTOV(np);
        }
-       if (v3) {
-               nfsm_postop_attr(newvp, attrflag);
-               nfsm_postop_attr(dvp, attrflag);
-       } else
-               nfsm_loadattr(newvp, (struct vattr *)0);
-       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;
+//     if (error == 0 && *vpp != NULL && *vpp != dvp)
+//             nfs_unlock(VTONFS(*vpp));
+
        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)
+                       if (vnode_mount(dvp) && vnode_vfsisrdonly(dvp))
                                error = EROFS;
                        else
                                error = EJUSTRETURN;
                }
-               if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
-                       cnp->cn_flags |= SAVENAME;
+       }
+error_return:
+       if (error && *vpp) {
+               vnode_put(*vpp);
+               *vpp = NULLVP;
        }
        return (error);
 }
@@ -1242,36 +1592,39 @@ nfs_lookup(ap)
  */
 static int
 nfs_read(ap)
-       struct vop_read_args /* {
-               struct vnode *a_vp;
+       struct vnop_read_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               int  a_ioflag;
-               struct ucred *a_cred;
+               int a_ioflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-
-       if (vp->v_type != VREG)
+       if (vnode_vtype(ap->a_vp) != VREG)
                return (EPERM);
-       return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0));
+       return (nfs_bioread(ap->a_vp, ap->a_uio, ap->a_ioflag,
+               vfs_context_ucred(ap->a_context),
+               vfs_context_proc(ap->a_context)));
 }
 
+
 /*
  * nfs readlink call
  */
 static int
 nfs_readlink(ap)
-       struct vop_readlink_args /* {
-               struct vnode *a_vp;
+       struct vnop_readlink_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               struct ucred *a_cred;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-
-       if (vp->v_type != VLNK)
+       if (vnode_vtype(ap->a_vp) != VLNK)
                return (EPERM);
-       return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred, 0));
+       return (nfs_bioread(ap->a_vp, ap->a_uio, 0,
+               vfs_context_ucred(ap->a_context),
+               vfs_context_proc(ap->a_context)));
 }
 
 /*
@@ -1279,29 +1632,37 @@ nfs_readlink(ap)
  * Called by nfs_doio() from below the buffer cache.
  */
 int
-nfs_readlinkrpc(vp, uiop, cred)
-       register struct vnode *vp;
-       struct uio *uiop;
-       struct ucred *cred;
+nfs_readlinkrpc(
+       vnode_t vp,
+       struct uio *uiop,
+       kauth_cred_t cred,
+       proc_t p)
 {
        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);
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
 
-       nfsstats.rpccnt[NFSPROC_READLINK]++;
-       nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3));
+       if (!VFSTONFS(vnode_mount(vp)))
+               return (ENXIO);
+       v3 = NFS_ISV3(vp);
+
+       nfsm_reqhead(NFSX_FH(v3));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READLINK]);
        nfsm_fhtom(vp, v3);
-       nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
-       if (v3)
-               nfsm_postop_attr(vp, attrflag);
+       nfsm_request(vp, NFSPROC_READLINK, p, cred, &xid);
+       if (v3 && mrep)
+               nfsm_postop_attr_update(vp, v3, attrflag, &xid);
        if (!error) {
-               nfsm_strsiz(len, NFS_MAXPATHLEN);
-               if (len == NFS_MAXPATHLEN) {
-                        struct nfsnode *np = VTONFS(vp);
+               nfsm_strsiz(len, NFS_MAXPATHLEN, v3);
+               if (len >= NFS_MAXPATHLEN) {
+                       struct nfsnode *np = VTONFS(vp);
 #if DIAGNOSTIC
                         if (!np)
                                 panic("nfs_readlinkrpc: null np");
@@ -1320,31 +1681,41 @@ nfs_readlinkrpc(vp, uiop, cred)
  * Ditto above
  */
 int
-nfs_readrpc(vp, uiop, cred)
-       register struct vnode *vp;
-       struct uio *uiop;
-       struct ucred *cred;
+nfs_readrpc(
+       vnode_t vp,
+       struct uio *uiop,
+       kauth_cred_t cred,
+       proc_t p)
 {
        register u_long *tl;
        register caddr_t cp;
        register long t1, t2;
        caddr_t bpos, dpos, cp2;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       mbuf_t mreq, mrep, md, mb, mb2;
        struct nfsmount *nmp;
-       int error = 0, len, retlen, tsiz, eof, attrflag;
-       int v3 = NFS_ISV3(vp);
-
-#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)
+       int error = 0, len, retlen, tsiz, eof = 0, attrflag;
+       int v3, nmrsize;
+       u_int64_t xid;
+
+       FSDBG_TOP(536, vp, uiop->uio_offset, uio_uio_resid(uiop), 0);
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp)
+               return (ENXIO);
+       v3 = NFS_ISV3(vp);
+       nmrsize = nmp->nm_rsize;
+
+       // LP64todo - fix this
+       tsiz = uio_uio_resid(uiop);
+        if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
+               FSDBG_BOT(536, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
                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);
+               len = (tsiz > nmrsize) ? nmrsize : tsiz;
+               nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED * 3);
+               if (error)
+                       break;
+               OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READ]);
                nfsm_fhtom(vp, v3);
                nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
                if (v3) {
@@ -1355,20 +1726,30 @@ nfs_readrpc(vp, uiop, cred)
                        *tl++ = txdr_unsigned(len);
                        *tl = 0;
                }
-               nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
+               FSDBG(536, vp, uiop->uio_offset, len, 0);
+               nfsm_request(vp, NFSPROC_READ, p, cred, &xid);
                if (v3) {
-                       nfsm_postop_attr(vp, attrflag);
+                       if (mrep) {
+                               nfsm_postop_attr_update(vp, v3, attrflag, &xid);
+                       }
                        if (error) {
-                               m_freem(mrep);
+                               mbuf_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);
-               nfsm_strsiz(retlen, nmp->nm_rsize);
-               nfsm_mtouio(uiop, retlen);
-               m_freem(mrep);
+               } else {
+                       if (mrep) {
+                               nfsm_loadattr(vp, v3, NULL, &xid);
+                       }
+               }
+               if (mrep) {
+                       nfsm_strsiz(retlen, nmrsize, 0);
+                       nfsm_mtouio(uiop, retlen);
+                       mbuf_freem(mrep);
+               } else {
+                       retlen = 0;
+               }
                tsiz -= retlen;
                if (v3) {
                        if (eof || retlen == 0)
@@ -1377,6 +1758,7 @@ nfs_readrpc(vp, uiop, cred)
                        tsiz = 0;
        }
 nfsmout:
+       FSDBG_BOT(536, vp, eof, uio_uio_resid(uiop), error);
        return (error);
 }
 
@@ -1384,34 +1766,51 @@ nfsmout:
  * nfs write call
  */
 int
-nfs_writerpc(vp, uiop, cred, iomode, must_commit)
-       register struct vnode *vp;
-       register struct uio *uiop;
-       struct ucred *cred;
-       int *iomode, *must_commit;
+nfs_writerpc(
+       vnode_t vp,
+       struct uio *uiop,
+       kauth_cred_t cred,
+       proc_t p,
+       int *iomode,
+       uint64_t *wverfp)
 {
        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;
+       mbuf_t mreq, mrep, md, mb, mb2;
+       struct nfsmount *nmp;
+       int error = 0, len, tsiz, updatemtime = 0, wccpostattr = 0, rlen, commit;
+       int v3, committed = NFSV3WRITE_FILESYNC;
+       u_int64_t xid, wverf;
+       mount_t mp;
 
 #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)
+       FSDBG_TOP(537, vp, uiop->uio_offset, uio_uio_resid(uiop), *iomode);
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp)
+               return (ENXIO);
+       v3 = NFS_ISV3(vp);
+       // LP64todo - fix this
+       tsiz = uio_uio_resid(uiop);
+        if (((u_int64_t)uiop->uio_offset + (unsigned int)tsiz > 0xffffffff) && !v3) {
+               FSDBG_BOT(537, vp, uiop->uio_offset, uio_uio_resid(uiop), EFBIG);
                return (EFBIG);
+       }
        while (tsiz > 0) {
-               nfsstats.rpccnt[NFSPROC_WRITE]++;
+               nmp = VFSTONFS(vnode_mount(vp));
+               if (!nmp) {
+                       error = ENXIO;
+                       break;
+               }
                len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
-               nfsm_reqhead(vp, NFSPROC_WRITE,
-                       NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
+               nfsm_reqhead(NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
+               if (error)
+                       break;
+               OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_WRITE]);
                nfsm_fhtom(vp, v3);
                if (v3) {
                        nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
@@ -1425,11 +1824,19 @@ nfs_writerpc(vp, uiop, cred, iomode, must_commit)
                        tl += 2;
                }
                *tl = txdr_unsigned(len);
+               FSDBG(537, vp, uiop->uio_offset, len, 0);
                nfsm_uiotom(uiop, len);
-               nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
+               nfsm_request(vp, NFSPROC_WRITE, p, cred, &xid);
+               nmp = VFSTONFS(vnode_mount(vp));
+               if (!nmp)
+                       error = ENXIO;
                if (v3) {
-                       wccflag = NFSV3_WCCCHK;
-                       nfsm_wcc_data(vp, wccflag);
+                       if (mrep) {
+                               struct timespec premtime;
+                               nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
+                               if (nfstimespeccmp(&VTONFS(vp)->n_mtime, &premtime, ==))
+                                       updatemtime = 1;
+                       }
                        if (!error) {
                                nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
                                        NFSX_V3WRITEVERF);
@@ -1439,10 +1846,10 @@ nfs_writerpc(vp, uiop, cred, iomode, must_commit)
                                        break;
                                } else if (rlen < len) {
                                        backup = len - rlen;
-                                       uiop->uio_iov->iov_base -= backup;
-                                       uiop->uio_iov->iov_len += backup;
+                                       uio_iov_base_add(uiop, -backup);
+                                       uio_iov_len_add(uiop, backup);
                                        uiop->uio_offset -= backup;
-                                       uiop->uio_resid += backup;
+                                       uio_uio_resid_add(uiop, backup);
                                        len = rlen;
                                }
                                commit = fxdr_unsigned(int, *tl++);
@@ -1456,39 +1863,43 @@ nfs_writerpc(vp, uiop, cred, iomode, must_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);
+                               fxdr_hyper(tl, &wverf);
+                               if (wverfp)
+                                       *wverfp = wverf;
+                               if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
+                                   nmp->nm_verf = wverf;
+                                   nmp->nm_state |= NFSSTA_HASWRITEVERF;
+                               } else if (wverf != nmp->nm_verf) {
+                                   nmp->nm_verf = wverf;
                                }
                        }
-               } else
-                   nfsm_loadattr(vp, (struct vattr *)0);
-               if ((wccflag) && (vp->v_type != VBAD))  /* EINVAL set on VBAD vnode */
-                   VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.tv_sec;
-               m_freem(mrep);
-                /*
-                 * we seem to have a case where we end up looping on shutdown and taking down nfs servers.
-                 * For V3, error cases, there is no way to terminate loop, if the len was 0, meaning,
-                 * nmp->nm_wsize was trashed. FreeBSD has this fix in it. Let's try it.
-                 */
-                if (error)
-                    break;
-                tsiz -= len;
+               } else {
+                       if (mrep) {
+                               nfsm_loadattr(vp, v3, NULL, &xid);
+                       }
+               }
+
+               if (updatemtime)
+                       VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.nva_mtime;
+               mbuf_freem(mrep);
+               /*
+                * we seem to have a case where we end up looping on shutdown
+                * and taking down nfs servers.  For V3, error cases, there is
+                * no way to terminate loop, if the len was 0, meaning,
+                * nmp->nm_wsize was trashed. FreeBSD has this fix in it.
+                * Let's try it.
+                */
+               if (error)
+                       break;
+               tsiz -= len;
        }
 nfsmout:
-        /* does it make sense to even say it was committed if we had an error? EKN */
-        /* okay well just don't on bad vnodes then. EINVAL will be returned on bad vnodes */
-        if ((vp->v_type != VBAD) && (vp->v_mount->mnt_flag & MNT_ASYNC))
+        if ((mp = vnode_mount(vp)) && (vfs_flags(mp) & MNT_ASYNC))
                committed = NFSV3WRITE_FILESYNC;
         *iomode = committed;
        if (error)
-               uiop->uio_resid = tsiz;
+               uio_uio_resid_set(uiop, tsiz);
+       FSDBG_BOT(537, vp, committed, uio_uio_resid(uiop), error);
        return (error);
 }
 
@@ -1498,97 +1909,121 @@ nfsmout:
  * mode set to specify the file type and the size field for rdev.
  */
 static int
-nfs_mknodrpc(dvp, vpp, cnp, vap)
-       register struct vnode *dvp;
-       register struct vnode **vpp;
-       register struct componentname *cnp;
-       register struct vattr *vap;
+nfs_mknodrpc(
+       vnode_t dvp,
+       vnode_t *vpp,
+       struct componentname *cnp,
+       struct vnode_attr *vap,
+       kauth_cred_t cred,
+       proc_t p)
 {
        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;
+       vnode_t newvp = (vnode_t)0;
        struct nfsnode *np = (struct nfsnode *)0;
-       struct vattr vattr;
+       struct nfs_vattr nvattr;
        char *cp2;
        caddr_t bpos, dpos;
-       int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int error = 0, wccpostattr = 0, gotvp = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
        u_long rdev;
+       u_int64_t xid;
        int v3 = NFS_ISV3(dvp);
+       int gotuid, gotgid;
 
-       if (vap->va_type == VCHR || vap->va_type == VBLK)
+       if (!VATTR_IS_ACTIVE(vap, va_type))
+               return (EINVAL);
+       if (vap->va_type == VCHR || vap->va_type == VBLK) {
+               if (!VATTR_IS_ACTIVE(vap, va_rdev))
+                       return (EINVAL);
                rdev = txdr_unsigned(vap->va_rdev);
-       else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
+       else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
                rdev = 0xffffffff;
        else {
-               VOP_ABORTOP(dvp, cnp);
-               vput(dvp);
-               return (EOPNOTSUPP);
+               return (ENOTSUP);
        }
-       if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc))) {
-               VOP_ABORTOP(dvp, cnp);
-               vput(dvp);
+       nfsm_reqhead(NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
+               nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
+       if (error)
                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));
+
+       VATTR_SET_SUPPORTED(vap, va_mode);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_data_size);
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+       gotuid = VATTR_IS_ACTIVE(vap, va_uid);
+       gotgid = VATTR_IS_ACTIVE(vap, va_gid);
+
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKNOD]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
        if (v3) {
-               nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR);
+               nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                *tl++ = vtonfsv3_type(vap->va_type);
-               sp3 = (struct nfsv3_sattr *)tl;
-               nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
+               nfsm_v3sattr(vap);
                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 {
+               struct timespec neg1time = { -1, -1 };
                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_mode = vtonfsv2_mode(vap->va_type,
+                       (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
+               sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
+               sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
                sp->sa_size = rdev;
-               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
-               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_atime);
+               }
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_mtime);
+               }
        }
-       nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred);
+       nfsm_request(dvp, NFSPROC_MKNOD, p, cred, &xid);
+       /* XXX no EEXIST kludge here? */
        if (!error) {
-               nfsm_mtofh(dvp, newvp, v3, gotvp);
+               nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
                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);
+                           cnp->cn_namelen, cred, p, &np);
                        if (!error)
                                newvp = NFSTOV(np);
                }
        }
-       if (v3)
-               nfsm_wcc_data(dvp, wccflag);
+       if (v3 && mrep)
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
+       if (!error && (gotuid || gotgid) &&
+           (!newvp || nfs_getattrcache(newvp, &nvattr) ||
+            (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
+            (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
+               /* clear ID bits if server didn't use them (or we can't tell) */
+               VATTR_CLEAR_SUPPORTED(vap, va_uid);
+               VATTR_CLEAR_SUPPORTED(vap, va_gid);
+       }
        nfsm_reqdone;
        if (error) {
                if (newvp)
-                       vput(newvp);
+                       vnode_put(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) { /* EINVAL set on VBAD vnode */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-               VTONFS(dvp)->n_attrstamp = 0;
-            }
-       vput(dvp);
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
        return (error);
 }
 
@@ -1599,19 +2034,21 @@ nfs_mknodrpc(dvp, vpp, cnp, vap)
 /* ARGSUSED */
 static int
 nfs_mknod(ap)
-       struct vop_mknod_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
+       struct vnop_mknod_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_dvp;
+               vnode_t *a_vpp;
                struct componentname *a_cnp;
-               struct vattr *a_vap;
+               struct vnode_attr *a_vap;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       struct vnode *newvp;
        int error;
 
-       error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap);
-       if (!error)
-               vput(newvp);
+       error = nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap,
+                       vfs_context_ucred(ap->a_context),
+                       vfs_context_proc(ap->a_context));
+
        return (error);
 }
 
@@ -1621,48 +2058,66 @@ static u_long create_verf;
  */
 static int
 nfs_create(ap)
-       struct vop_create_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
+       struct vnop_create_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_dvp;
+               vnode_t *a_vpp;
                struct componentname *a_cnp;
-               struct vattr *a_vap;
+               struct vnode_attr *a_vap;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t dvp = ap->a_dvp;
+       struct vnode_attr *vap = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       struct nfs_vattr nvattr;
+       struct nfsv2_sattr *sp;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
        struct nfsnode *np = (struct nfsnode *)0;
-       struct vnode *newvp = (struct vnode *)0;
+       vnode_t newvp = (vnode_t)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 error = 0, wccpostattr = 0, gotvp = 0, fmode = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
        int v3 = NFS_ISV3(dvp);
+       int gotuid, gotgid;
+       u_int64_t xid;
+       kauth_cred_t cred;
+       proc_t p;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       if (!VATTR_IS_ACTIVE(vap, va_type))
+               return (EINVAL);
 
        /*
         * Oops, not for me..
         */
        if (vap->va_type == VSOCK)
-               return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
+               return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap, cred, p));
+
+       VATTR_SET_SUPPORTED(vap, va_mode);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_data_size);
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+       gotuid = VATTR_IS_ACTIVE(vap, va_uid);
+       gotgid = VATTR_IS_ACTIVE(vap, va_gid);
 
-       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_reqhead(NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
                nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_CREATE]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
        if (v3) {
                nfsm_build(tl, u_long *, NFSX_UNSIGNED);
                if (fmode & O_EXCL) {
@@ -1675,35 +2130,39 @@ again:
                    *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);
+                   nfsm_v3sattr(vap);
                }
        } else {
+               struct timespec neg1time = { -1, -1 };
                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_mode = vtonfsv2_mode(vap->va_type,
+                       (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
+               sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
+               sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
                sp->sa_size = 0;
-               txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
-               txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_atime);
+               }
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_mtime);
+               }
        }
-       nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
+       nfsm_request(dvp, NFSPROC_CREATE, p, cred, &xid);
        if (!error) {
-               nfsm_mtofh(dvp, newvp, v3, gotvp);
+               nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
                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);
+                           cnp->cn_namelen, cred, p, &np);
                        if (!error)
                                newvp = NFSTOV(np);
                }
        }
-       if (v3)
-               nfsm_wcc_data(dvp, wccflag);
+       if (v3 && mrep)
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
        nfsm_reqdone;
        if (error) {
                if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
@@ -1711,21 +2170,36 @@ again:
                        goto again;
                }
                if (newvp)
-                       vput(newvp);
-       } else if (v3 && (fmode & O_EXCL))
-               error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc);
+                       vnode_put(newvp);
+       } else if (v3 && (fmode & O_EXCL)) {
+               error = nfs_setattrrpc(newvp, vap, cred, p);
+               if (error && (gotuid || gotgid)) {
+                       /* it's possible the server didn't like our attempt to set IDs. */
+                       /* so, let's try it again without those */
+                       VATTR_CLEAR_ACTIVE(vap, va_uid);
+                       VATTR_CLEAR_ACTIVE(vap, va_gid);
+                       error = nfs_setattrrpc(newvp, vap, cred, p);
+               }
+               if (error)
+                       vnode_put(newvp);
+       }
        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) { /* EINVAL set on VBAD vnode */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-                    VTONFS(dvp)->n_attrstamp = 0;
-        }
-       vput(dvp);
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
+       if (!error && (gotuid || gotgid) &&
+           (!newvp || nfs_getattrcache(newvp, &nvattr) ||
+            (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
+            (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
+               /* clear ID bits if server didn't use them (or we can't tell) */
+               VATTR_CLEAR_SUPPORTED(vap, va_uid);
+               VATTR_CLEAR_SUPPORTED(vap, va_gid);
+       }
        return (error);
 }
 
@@ -1734,7 +2208,7 @@ again:
  * To try and make nfs semantics closer to ufs semantics, a file that has
  * other processes using the vnode is renamed instead of removed and then
  * removed later on the last close.
- * - If v_usecount > 1
+ * - If vnode_isinuse()
  *       If a rename is not already in the works
  *          call nfs_sillyrename() to set it up
  *     else
@@ -1742,50 +2216,52 @@ again:
  */
 static int
 nfs_remove(ap)
-       struct vop_remove_args /* {
+       struct vnop_remove_args /* {
                struct vnodeop_desc *a_desc;
-               struct vnode * a_dvp;
-               struct vnode * a_vp;
-               struct componentname * a_cnp;
+               vnode_t a_dvp;
+               vnode_t a_vp;
+               struct componentname *a_cnp;
+               int a_flags;
+               vfs_context_t a_context;
        } */ *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;
-       struct vattr vattr;
-       int file_deleted = 0;
-
-#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 (vp->v_usecount == 1 || 
-               (UBCISVALID(vp)&&(vp->v_usecount==2)) || 
-               (np->n_sillyrename &&
-           VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 &&
-           vattr.va_nlink > 1)) {
+       vnode_t vp = ap->a_vp;
+       vnode_t dvp = ap->a_dvp;
+       struct componentname *cnp = ap->a_cnp;
+       struct nfsnode *np = VTONFS(vp);
+       int error = 0, gofree = 0;
+       struct nfs_vattr nvattr;
+       kauth_cred_t cred;
+       proc_t p;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       gofree = vnode_isinuse(vp, 0) ? 0 : 1;
+       if ((ap->a_flags & VNODE_REMOVE_NODELETEBUSY) && !gofree) {
+               /* Caller requested Carbon delete semantics, but file is busy */
+               return (EBUSY);
+       }
+       if (gofree || (np->n_sillyrename &&
+               nfs_getattr(vp, &nvattr, cred, p) == 0 &&
+               nvattr.nva_nlink > 1)) {
                /*
                 * Purge the name cache so that the chance of a lookup for
                 * the name succeeding while the remove is in progress is
-                * minimized. Without node locking it can still happen, such
-                * that an I/O op returns ESTALE, but since you get this if
-                * another host removes the file..
+                * minimized.
                 */
                cache_purge(vp);
                /*
                 * throw away biocache buffers, mainly to avoid
                 * unnecessary delayed writes later.
                 */
-               error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
-               ubc_setsize(vp, (off_t)0);
+               error = nfs_vinvalbuf(vp, 0, cred, p, 1);
+               np->n_size = 0;
+               ubc_setsize(vp, (off_t)0); /* XXX check error */
                /* Do the rpc */
                if (error != EINTR)
                        error = nfs_removerpc(dvp, cnp->cn_nameptr,
-                               cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc);
+                               cnp->cn_namelen, cred, p);
                /*
                 * Kludge City: If the first reply to the remove rpc is lost..
                 *   the reply to the retransmitted request will be ENOENT
@@ -1794,26 +2270,27 @@ nfs_remove(ap)
                 */
                if (error == ENOENT)
                        error = 0;
-               file_deleted = 1;
+               if (!error) {
+                       /*
+                        * remove nfsnode from hash now so we can't accidentally find it
+                        * again if another object gets created with the same filehandle
+                        * before this vnode gets reclaimed
+                        */
+                       lck_mtx_lock(nfs_node_hash_mutex);
+                       LIST_REMOVE(np, n_hash);
+                       np->n_flag &= ~NHASHED;
+                       lck_mtx_unlock(nfs_node_hash_mutex);
+               }
+               if (!error && !np->n_sillyrename) {
+                       /* clear flags now: won't get nfs_inactive for recycled vnode */
+                       /* clear all flags other than these */
+                       np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
+                       vnode_recycle(vp);
+               }
        } 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);
-
-
-       if (vp == dvp)
-               vrele(vp);
-       else
-               vput(vp);
-
-       if (file_deleted && UBCINFOEXISTS(vp)) {
-               (void) ubc_uncache(vp); 
-               ubc_release(vp);
-               /* WARNING vp may not be valid after this */
+               error = nfs_sillyrename(dvp, vp, cnp, cred, p);
        }
+       NATTRINVALIDATE(np);
 
        return (error);
 }
@@ -1822,12 +2299,9 @@ nfs_remove(ap)
  * nfs file remove rpc called from nfs_inactive
  */
 int
-nfs_removeit(sp)
-       register struct sillyrename *sp;
+nfs_removeit(struct sillyrename *sp)
 {
-
-       return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred,
-               (struct proc *)0));
+       return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, NULL));
 }
 
 /*
@@ -1835,34 +2309,42 @@ nfs_removeit(sp)
  */
 static int
 nfs_removerpc(dvp, name, namelen, cred, proc)
-       register struct vnode *dvp;
+       vnode_t dvp;
        char *name;
        int namelen;
-       struct ucred *cred;
-       struct proc *proc;
+       kauth_cred_t cred;
+       proc_t 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);
+       int error = 0, wccpostattr = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
 
-       nfsstats.rpccnt[NFSPROC_REMOVE]++;
-       nfsm_reqhead(dvp, NFSPROC_REMOVE,
-               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
+       if (!VFSTONFS(vnode_mount(dvp)))
+               return (ENXIO);
+       v3 = NFS_ISV3(dvp);
+
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_REMOVE]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(name, namelen, NFS_MAXNAMLEN);
-       nfsm_request(dvp, NFSPROC_REMOVE, proc, cred);
-       if (v3)
-               nfsm_wcc_data(dvp, wccflag);
+       nfsm_strtom(name, namelen, NFS_MAXNAMLEN, v3);
+       nfsm_request(dvp, NFSPROC_REMOVE, proc, cred, &xid);
+       if (v3 && mrep)
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
        nfsm_reqdone;
-        if (dvp->v_type != VBAD) { /* EINVAL set on VBAD vnode */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-               VTONFS(dvp)->n_attrstamp = 0;
-        }
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
        return (error);
 }
 
@@ -1871,31 +2353,39 @@ nfs_removerpc(dvp, name, namelen, cred, proc)
  */
 static int
 nfs_rename(ap)
-       struct vop_rename_args  /* {
-               struct vnode *a_fdvp;
-               struct vnode *a_fvp;
+       struct vnop_rename_args  /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_fdvp;
+               vnode_t a_fvp;
                struct componentname *a_fcnp;
-               struct vnode *a_tdvp;
-               struct vnode *a_tvp;
+               vnode_t a_tdvp;
+               vnode_t a_tvp;
                struct componentname *a_tcnp;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t fvp = ap->a_fvp;
+       vnode_t tvp = ap->a_tvp;
+       vnode_t fdvp = ap->a_fdvp;
+       vnode_t tdvp = ap->a_tdvp;
+       struct componentname *tcnp = ap->a_tcnp;
+       struct componentname *fcnp = ap->a_fcnp;
+       int error, inuse=0;
+       mount_t fmp, tdmp, tmp;
+       struct nfsnode *tnp;
+       kauth_cred_t cred;
+       proc_t p;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       tnp = tvp ? VTONFS(tvp) : NULL;
 
-#if DIAGNOSTIC
-       if ((tcnp->cn_flags & HASBUF) == 0 ||
-           (fcnp->cn_flags & HASBUF) == 0)
-               panic("nfs_rename: no name");
-#endif
        /* Check for cross-device rename */
-       if ((fvp->v_mount != tdvp->v_mount) ||
-           (tvp && (fvp->v_mount != tvp->v_mount))) {
+       fmp = vnode_mount(fvp);
+       tmp = tvp ? vnode_mount(tvp) : NULL;
+       tdmp = vnode_mount(tdvp);
+       if ((fmp != tdmp) || (tvp && (fmp != tmp))) {
                error = EXDEV;
                goto out;
        }
@@ -1904,32 +2394,58 @@ nfs_rename(ap)
         * If the tvp exists and is in use, sillyrename it before doing the
         * rename of the new file over it.
         * XXX Can't sillyrename a directory.
+        * Don't sillyrename if source and target are same vnode (hard
+        * links or case-variants)
         */
-       if (tvp && (tvp->v_usecount>(UBCISVALID(tvp) ? 2 : 1)) &&
-                !VTONFS(tvp)->n_sillyrename &&
-               tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) {
-               vput(tvp);
-               tvp = NULL;
+       if (tvp && tvp != fvp) {
+               inuse = vnode_isinuse(tvp, 0);
+       }
+       if (inuse && !tnp->n_sillyrename && vnode_vtype(tvp) != VDIR) {
+               if  ((error = nfs_sillyrename(tdvp, tvp, tcnp, cred, p))) {
+                       /* sillyrename failed. Instead of pressing on, return error */
+                       goto out; /* should not be ENOENT. */
+               } else {
+                       /* sillyrename succeeded.*/
+                       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);
+               tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, cred, p);
+
+       /*
+        * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
+        */
+       if (error == ENOENT)
+               error = 0;
 
-       if (fvp->v_type == VDIR) {
-               if (tvp != NULL && tvp->v_type == VDIR)
-                       cache_purge(tdvp);
-               cache_purge(fdvp);
+       if (!error && tvp && tvp != fvp && !tnp->n_sillyrename) {
+               /*
+                * remove nfsnode from hash now so we can't accidentally find it
+                * again if another object gets created with the same filehandle
+                * before this vnode gets reclaimed
+                */
+               lck_mtx_lock(nfs_node_hash_mutex);
+               LIST_REMOVE(tnp, n_hash);
+               tnp->n_flag &= ~NHASHED;
+               lck_mtx_unlock(nfs_node_hash_mutex);
        }
+       
+       /* purge the old name cache entries and enter the new one */
+       cache_purge(fvp);
+       if (tvp) {
+               cache_purge(tvp);
+               if (!error && !tnp->n_sillyrename) {
+                       /* clear flags now: won't get nfs_inactive for recycled vnode */
+                       /* clear all flags other than these */
+                       tnp->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
+                       vnode_recycle(tvp);
+               }
+       }
+       if (!error)
+               cache_enter(tdvp, fvp, tcnp);
+
 out:
-       if (tdvp == tvp)
-               vrele(tdvp);
-       else
-               vput(tdvp);
-       if (tvp)
-               vput(tvp);
-       vrele(fdvp);
-       vrele(fvp);
        /*
         * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
         */
@@ -1939,64 +2455,62 @@ out:
 }
 
 /*
- * nfs file rename rpc called from nfs_remove() above
- */
-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));
-}
-
-/*
- * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit().
+ * Do an nfs rename rpc. Called from nfs_rename() and nfs_sillyrename().
  */
 static int
 nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
-       register struct vnode *fdvp;
+       vnode_t fdvp;
        char *fnameptr;
        int fnamelen;
-       register struct vnode *tdvp;
+       vnode_t tdvp;
        char *tnameptr;
        int tnamelen;
-       struct ucred *cred;
-       struct proc *proc;
+       kauth_cred_t cred;
+       proc_t 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);
-
-       nfsstats.rpccnt[NFSPROC_RENAME]++;
-       nfsm_reqhead(fdvp, NFSPROC_RENAME,
-               (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
-               nfsm_rndup(tnamelen));
+       int error = 0, fwccpostattr = 0, twccpostattr = 0;
+       struct timespec fpremtime = { 0, 0 }, tpremtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
+
+       if (!VFSTONFS(vnode_mount(fdvp)))
+               return (ENXIO);
+       v3 = NFS_ISV3(fdvp);
+
+       nfsm_reqhead((NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
+                     nfsm_rndup(tnamelen));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RENAME]);
        nfsm_fhtom(fdvp, v3);
-       nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN);
+       nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN, v3);
        nfsm_fhtom(tdvp, v3);
-       nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN);
-       nfsm_request(fdvp, NFSPROC_RENAME, proc, cred);
-       if (v3) {
-               nfsm_wcc_data(fdvp, fwccflag);
-               nfsm_wcc_data(tdvp, twccflag);
+       nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN, v3);
+       nfsm_request(fdvp, NFSPROC_RENAME, proc, cred, &xid);
+       if (v3 && mrep) {
+               u_int64_t txid = xid;
+
+               nfsm_wcc_data(fdvp, &fpremtime, fwccpostattr, &xid);
+               nfsm_wcc_data(tdvp, &tpremtime, twccpostattr, &txid);
        }
        nfsm_reqdone;
-        if (fdvp->v_type != VBAD) { /* EINVAL set on VBAD vnode */
-            VTONFS(fdvp)->n_flag |= NMODIFIED;
-            if (!fwccflag)
-               VTONFS(fdvp)->n_attrstamp = 0;
-        }
-        if (tdvp->v_type != VBAD) { /* EINVAL set on VBAD vnode */
-            VTONFS(tdvp)->n_flag |= NMODIFIED;
-            if (!twccflag)
-                    VTONFS(tdvp)->n_attrstamp = 0;
-        }
+       VTONFS(fdvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(fdvp)->n_ncmtime, &fpremtime, ==))
+               VTONFS(fdvp)->n_ncmtime = VTONFS(fdvp)->n_vattr.nva_mtime;
+       if (!fwccpostattr)
+               NATTRINVALIDATE(VTONFS(fdvp));
+       VTONFS(tdvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &tpremtime, ==))
+               VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
+       if (!twccpostattr)
+               NATTRINVALIDATE(VTONFS(tdvp));
        return (error);
 }
 
@@ -2005,59 +2519,69 @@ nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
  */
 static int
 nfs_link(ap)
-       struct vop_link_args /* {
-               struct vnode *a_vp;
-               struct vnode *a_tdvp;
+       struct vnop_link_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               vnode_t a_tdvp;
                struct componentname *a_cnp;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t vp = ap->a_vp;
+       vnode_t tdvp = ap->a_tdvp;
+       struct componentname *cnp = ap->a_cnp;
+       u_long *tl;
+       caddr_t cp;
+       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);
-
-       if (vp->v_mount != tdvp->v_mount) {
-               VOP_ABORTOP(vp, cnp);
-               if (tdvp == vp)
-                       vrele(tdvp);
-               else
-                       vput(tdvp);
+       int error = 0, wccpostattr = 0, attrflag = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
+       int v3;
+       u_int64_t xid;
+       kauth_cred_t cred;
+       proc_t p;
+
+       if (vnode_mount(vp) != vnode_mount(tdvp)) {
                return (EXDEV);
        }
 
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       v3 = NFS_ISV3(vp);
+
        /*
         * Push all writes to the server, so that the attribute cache
         * doesn't get "out of sync" with the server.
         * XXX There should be a better way!
         */
-       VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc);
+       nfs_flush(vp, MNT_WAIT, cred, p, 0);
 
-       nfsstats.rpccnt[NFSPROC_LINK]++;
-       nfsm_reqhead(vp, NFSPROC_LINK,
-               NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       nfsm_reqhead(NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LINK]);
        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);
-       if (v3) {
-               nfsm_postop_attr(vp, attrflag);
-               nfsm_wcc_data(tdvp, wccflag);
+       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
+       nfsm_request(vp, NFSPROC_LINK, p, cred, &xid);
+       if (v3 && mrep) {
+               u_int64_t txid = xid;
+
+               nfsm_postop_attr_update(vp, v3, attrflag, &xid);
+               nfsm_wcc_data(tdvp, &premtime, wccpostattr, &txid);
        }
        nfsm_reqdone;
-       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
 
        VTONFS(tdvp)->n_flag |= NMODIFIED;
-       if ((!attrflag) && (vp->v_type != VBAD))  /* EINVAL set on VBAD vnode */
-               VTONFS(vp)->n_attrstamp = 0;
-       if ((!wccflag) && (tdvp->v_type != VBAD))  /* EINVAL set on VBAD vnode */
-               VTONFS(tdvp)->n_attrstamp = 0;
-       vput(tdvp);
+       if (!attrflag)
+               NATTRINVALIDATE(VTONFS(vp));
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(tdvp)->n_ncmtime, &premtime, ==))
+               VTONFS(tdvp)->n_ncmtime = VTONFS(tdvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(tdvp));
        /*
         * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
         */
@@ -2071,70 +2595,127 @@ nfs_link(ap)
  */
 static int
 nfs_symlink(ap)
-       struct vop_symlink_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
+       struct vnop_symlink_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_dvp;
+               vnode_t *a_vpp;
                struct componentname *a_cnp;
-               struct vattr *a_vap;
+               struct vnode_attr *a_vap;
                char *a_target;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t dvp = ap->a_dvp;
+       struct vnode_attr *vap = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       struct nfs_vattr nvattr;
+       struct nfsv2_sattr *sp;
+       u_long *tl;
+       caddr_t cp;
+       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 slen, error = 0, wccpostattr = 0, gotvp = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
+       vnode_t newvp = (vnode_t)0;
        int v3 = NFS_ISV3(dvp);
+       int gotuid, gotgid;
+       u_int64_t xid;
+       kauth_cred_t cred;
+       proc_t p;
+       struct nfsnode *np = NULL;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
 
-       nfsstats.rpccnt[NFSPROC_SYMLINK]++;
        slen = strlen(ap->a_target);
-       nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED +
+       nfsm_reqhead(NFSX_FH(v3) + 2*NFSX_UNSIGNED +
            nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
+       if (error)
+               return (error);
+
+       VATTR_SET_SUPPORTED(vap, va_mode);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_data_size);
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+       gotuid = VATTR_IS_ACTIVE(vap, va_uid);
+       gotgid = VATTR_IS_ACTIVE(vap, va_gid);
+
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_SYMLINK]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
+       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
        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_v3sattr(vap);
        }
-       nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
+       nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN, v3);
        if (!v3) {
+               struct timespec neg1time = { -1, -1 };
                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);
+               sp->sa_mode = vtonfsv2_mode(VLNK,
+                       (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
+               sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
+               sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
+               sp->sa_size = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_atime);
+               }
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_mtime);
+               }
        }
-       nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
-       if (v3) {
+       nfsm_request(dvp, NFSPROC_SYMLINK, p, cred, &xid);
+       if (v3 && mrep) {
+               u_int64_t dxid = xid;
+
                if (!error)
-                       nfsm_mtofh(dvp, newvp, v3, gotvp);
-               nfsm_wcc_data(dvp, wccflag);
+                       nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
        }
        nfsm_reqdone;
-       if (newvp)
-               vput(newvp);
-       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-        if (dvp->v_type != VBAD) { /* EINVAL set on VBAD vnode */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-               VTONFS(dvp)->n_attrstamp = 0;
-        }
-       vput(dvp);
+
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
+
        /*
-        * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
+        * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
+        * if we can succeed in looking up the symlink.
         */
-       if (error == EEXIST)
-               error = 0;
+       if ((error == EEXIST) || (!error && !gotvp)) {
+               if (newvp) {
+                       vnode_put(newvp);
+                       newvp = NULL;
+               }
+               error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cred, p, &np);
+               if (!error) {
+                       newvp = NFSTOV(np);
+                       if (vnode_vtype(newvp) != VLNK)
+                               error = EEXIST;
+               }
+       }
+       if (!error && (gotuid || gotgid) &&
+           (!newvp || nfs_getattrcache(newvp, &nvattr) ||
+            (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
+            (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
+               /* clear ID bits if server didn't use them (or we can't tell) */
+               VATTR_CLEAR_SUPPORTED(vap, va_uid);
+               VATTR_CLEAR_SUPPORTED(vap, va_gid);
+       }
+       if (error) {
+               if (newvp)
+                       vnode_put(newvp);
+       } else {
+               *ap->a_vpp = newvp;
+       }
        return (error);
 }
 
@@ -2143,89 +2724,121 @@ nfs_symlink(ap)
  */
 static int
 nfs_mkdir(ap)
-       struct vop_mkdir_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
+       struct vnop_mkdir_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_dvp;
+               vnode_t *a_vpp;
                struct componentname *a_cnp;
-               struct vattr *a_vap;
+               struct vnode_attr *a_vap;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t dvp = ap->a_dvp;
+       struct vnode_attr *vap = ap->a_vap;
+       struct componentname *cnp = ap->a_cnp;
+       struct nfs_vattr nvattr;
+       struct nfsv2_sattr *sp;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
+       int len;
        struct nfsnode *np = (struct nfsnode *)0;
-       struct vnode *newvp = (struct vnode *)0;
+       vnode_t newvp = (vnode_t)0;
        caddr_t bpos, dpos, cp2;
-       int error = 0, wccflag = NFSV3_WCCRATTR;
+       int error = 0, wccpostattr = 0;
+       struct timespec premtime = { 0, 0 };
        int gotvp = 0;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       struct vattr vattr;
+       mbuf_t mreq, mrep, md, mb, mb2;
        int v3 = NFS_ISV3(dvp);
+       int gotuid, gotgid;
+       u_int64_t xid, dxid;
+       kauth_cred_t cred;
+       proc_t p;
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
 
-       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_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
+       if (error)
+               return (error);
+
+       VATTR_SET_SUPPORTED(vap, va_mode);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_data_size);
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_modify_time);
+       gotuid = VATTR_IS_ACTIVE(vap, va_uid);
+       gotgid = VATTR_IS_ACTIVE(vap, va_gid);
+
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_MKDIR]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
+       nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN, v3);
        if (v3) {
-               nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
-               nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
+               nfsm_v3sattr(vap);
        } else {
+               struct timespec neg1time = { -1, -1 };
                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);
+               sp->sa_mode = vtonfsv2_mode(VDIR,
+                       (VATTR_IS_ACTIVE(vap, va_mode) ? vap->va_mode : 0600));
+               sp->sa_uid = gotuid ? (u_long)txdr_unsigned(vap->va_uid) : nfs_xdrneg1;
+               sp->sa_gid = gotgid ? (u_long)txdr_unsigned(vap->va_gid) : nfs_xdrneg1;
+               sp->sa_size = nfs_xdrneg1;
+               if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+                       txdr_nfsv2time(&vap->va_access_time, &sp->sa_atime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_atime);
+               }
+               if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+                       txdr_nfsv2time(&vap->va_modify_time, &sp->sa_mtime);
+               } else {
+                       txdr_nfsv2time(&neg1time, &sp->sa_mtime);
+               }
        }
-       nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
+       nfsm_request(dvp, NFSPROC_MKDIR, p, cred, &xid);
+       dxid = xid;
        if (!error)
-               nfsm_mtofh(dvp, newvp, v3, gotvp);
-       if (v3)
-               nfsm_wcc_data(dvp, wccflag);
+               nfsm_mtofh(dvp, cnp, newvp, v3, &xid, gotvp);
+       if (v3 && mrep)
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &dxid);
        nfsm_reqdone;
-        if (dvp->v_type != VBAD) { /* EINVAL set on this case */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-               VTONFS(dvp)->n_attrstamp = 0;
-        }
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
        /*
         * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
         * if we can succeed in looking up the directory.
         */
        if (error == EEXIST || (!error && !gotvp)) {
                if (newvp) {
-                       vrele(newvp);
-                       newvp = (struct vnode *)0;
+                       vnode_put(newvp);
+                       newvp = NULL;
                }
-               error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred,
-                       cnp->cn_proc, &np);
+               error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cred, p, &np);
                if (!error) {
                        newvp = NFSTOV(np);
-                       if (newvp->v_type != VDIR)
+                       if (vnode_vtype(newvp) != VDIR)
                                error = EEXIST;
                }
        }
+       if (!error && (gotuid || gotgid) &&
+           (!newvp || nfs_getattrcache(newvp, &nvattr) ||
+            (gotuid && (nvattr.nva_uid != vap->va_uid)) ||
+            (gotgid && (nvattr.nva_gid != vap->va_gid)))) {
+               /* clear ID bits if server didn't use them (or we can't tell) */
+               VATTR_CLEAR_SUPPORTED(vap, va_uid);
+               VATTR_CLEAR_SUPPORTED(vap, va_gid);
+       }
        if (error) {
                if (newvp)
-                       vrele(newvp);
-       } else
+                       vnode_put(newvp);
+       } else {
                *ap->a_vpp = newvp;
-       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-       vput(dvp);
+       }
        return (error);
 }
 
@@ -2234,47 +2847,65 @@ nfs_mkdir(ap)
  */
 static int
 nfs_rmdir(ap)
-       struct vop_rmdir_args /* {
-               struct vnode *a_dvp;
-               struct vnode *a_vp;
+       struct vnop_rmdir_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_dvp;
+               vnode_t a_vp;
                struct componentname *a_cnp;
+               vfs_context_t a_context;
        } */ *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;
+       vnode_t vp = ap->a_vp;
+       vnode_t dvp = ap->a_dvp;
+       struct componentname *cnp = ap->a_cnp;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
        caddr_t bpos, dpos, cp2;
-       int error = 0, wccflag = NFSV3_WCCRATTR;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
+       int error = 0, wccpostattr = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
        int v3 = NFS_ISV3(dvp);
+       u_int64_t xid;
+       kauth_cred_t cred;
+       proc_t p;
 
-       nfsstats.rpccnt[NFSPROC_RMDIR]++;
-       nfsm_reqhead(dvp, NFSPROC_RMDIR,
-               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_RMDIR]);
        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);
-       if (v3)
-               nfsm_wcc_data(dvp, wccflag);
+       nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN, v3);
+       nfsm_request(dvp, NFSPROC_RMDIR, p, cred, &xid);
+       if (v3 && mrep)
+               nfsm_wcc_data(dvp, &premtime, wccpostattr, &xid);
        nfsm_reqdone;
-       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-        if (dvp->v_type != VBAD) { /* EINVAL set on this case */
-            VTONFS(dvp)->n_flag |= NMODIFIED;
-            if (!wccflag)
-               VTONFS(dvp)->n_attrstamp = 0;
-        }
-       cache_purge(dvp);
+       VTONFS(dvp)->n_flag |= NMODIFIED;
+       /* if directory hadn't changed, update namecache mtime */
+       if (nfstimespeccmp(&VTONFS(dvp)->n_ncmtime, &premtime, ==))
+               VTONFS(dvp)->n_ncmtime = VTONFS(dvp)->n_vattr.nva_mtime;
+       if (!wccpostattr)
+               NATTRINVALIDATE(VTONFS(dvp));
        cache_purge(vp);
-       vput(vp);
-       vput(dvp);
        /*
         * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
         */
        if (error == ENOENT)
                error = 0;
+       if (!error) {
+               /*
+                * remove nfsnode from hash now so we can't accidentally find it
+                * again if another object gets created with the same filehandle
+                * before this vnode gets reclaimed
+                */
+               lck_mtx_lock(nfs_node_hash_mutex);
+               LIST_REMOVE(VTONFS(vp), n_hash);
+               VTONFS(vp)->n_flag &= ~NHASHED;
+               lck_mtx_unlock(nfs_node_hash_mutex);
+       }
        return (error);
 }
 
@@ -2283,45 +2914,56 @@ nfs_rmdir(ap)
  */
 static int
 nfs_readdir(ap)
-       struct vop_readdir_args /* {
-               struct vnode *a_vp;
+       struct vnop_readdir_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               struct ucred *a_cred;
+               int *a_eofflag;
+               int *a_ncookies;
+               u_long **a_cookies;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-       register struct nfsnode *np = VTONFS(vp);
-       register struct uio *uio = ap->a_uio;
+       vnode_t vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct uio *uio = ap->a_uio;
        int tresid, error;
-       struct vattr vattr;
+       struct nfs_vattr nvattr;
+       kauth_cred_t cred;
+       proc_t p;
 
-       if (vp->v_type != VDIR)
+       if (vnode_vtype(vp) != VDIR)
                return (EPERM);
+
+       cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
        /*
         * First, check for hit on the EOF offset cache
         */
        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++;
+               if (!nfs_getattr(vp, &nvattr, cred, p)) {
+                       if (nfstimespeccmp(&np->n_mtime, &nvattr.nva_mtime, ==)) {
+                               OSAddAtomic(1, (SInt32*)&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);
+                       if (nfstimespeccmp(&np->n_ncmtime, &nvattr.nva_mtime, !=)) {
+                               /* directory changed, purge any name cache entries */
+                               cache_purge(vp);
+                       }
                }
        }
 
        /*
         * Call nfs_bioread() to do the real work.
         */
-       tresid = uio->uio_resid;
-       error = nfs_bioread(vp, uio, 0, ap->a_cred, 0);
+       // LP64todo - fix this
+       tresid = uio_uio_resid(uio);
+       error = nfs_bioread(vp, uio, 0, cred, p);
 
-       if (!error && uio->uio_resid == tresid)
-               nfsstats.direofcache_misses++;
+       if (!error && uio_uio_resid(uio) == tresid)
+               OSAddAtomic(1, (SInt32*)&nfsstats.direofcache_misses);
        return (error);
 }
 
@@ -2330,36 +2972,42 @@ nfs_readdir(ap)
  * Called from below the buffer cache by nfs_doio().
  */
 int
-nfs_readdirrpc(vp, uiop, cred)
-       struct vnode *vp;
-       register struct uio *uiop;
-       struct ucred *cred;
-
+nfs_readdirrpc(
+       vnode_t vp,
+       struct uio *uiop,
+       kauth_cred_t cred,
+       proc_t p)
 {
-       register int len, left;
+       register int len, skiplen, 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;
+       mbuf_t mreq, mrep, md, mb, mb2;
        nfsuint64 cookie;
-       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       struct nfsmount *nmp;
        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);
+       int v3, nmreaddirsize;
+       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)))
+               (uio_uio_resid(uiop) & (NFS_DIRBLKSIZ - 1)))
                panic("nfs_readdirrpc: bad uio");
 #endif
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp)
+               return (ENXIO);
+       v3 = NFS_ISV3(vp);
+       nmreaddirsize = nmp->nm_readdirsize;
 
        /*
         * If there is no cookie, assume directory was stale.
@@ -2375,9 +3023,10 @@ nfs_readdirrpc(vp, uiop, cred)
         * The stopping criteria is EOF or buffer full.
         */
        while (more_dirs && bigenough) {
-               nfsstats.rpccnt[NFSPROC_READDIR]++;
-               nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) +
-                       NFSX_READDIR(v3));
+               nfsm_reqhead(NFSX_FH(v3) + NFSX_READDIR(v3));
+               if (error)
+                       goto nfsmout;
+               OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIR]);
                nfsm_fhtom(vp, v3);
                if (v3) {
                        nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
@@ -2389,18 +3038,23 @@ nfs_readdirrpc(vp, uiop, cred)
                        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);
+               *tl = txdr_unsigned(nmreaddirsize);
+               nfsm_request(vp, NFSPROC_READDIR, p, cred, &xid);
                if (v3) {
-                       nfsm_postop_attr(vp, attrflag);
+                       if (mrep) {
+                               nfsm_postop_attr_update(vp, v3, 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);
+                               mbuf_freem(mrep);
                                goto nfsmout;
                        }
+               } else if (!mrep) {
+                       // XXX assert error?
+                       goto nfsmout;
                }
                nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                more_dirs = fxdr_unsigned(int, *tl);
@@ -2416,27 +3070,36 @@ nfs_readdirrpc(vp, uiop, cred)
                                fileno = fxdr_unsigned(u_quad_t, *tl++);
                                len = fxdr_unsigned(int, *tl);
                        }
-                       if (len <= 0 || len > NFS_MAXNAMLEN) {
+                       /* Note: v3 supports longer names, but struct dirent doesn't */
+                       /* so we just truncate the names to fit */
+                       if (len <= 0) {
                                error = EBADRPC;
-                               m_freem(mrep);
+                               mbuf_freem(mrep);
                                goto nfsmout;
                        }
+                       if (len > MAXNAMLEN) {
+                               skiplen = len - MAXNAMLEN;
+                               len = MAXNAMLEN;
+                       } else {
+                               skiplen = 0;
+                       }
                        tlen = nfsm_rndup(len);
                        if (tlen == len)
                                tlen += 4;      /* To ensure null termination */
                        left = DIRBLKSIZ - blksiz;
-                       if ((tlen + DIRHDSIZ) > left) {
+                       if ((tlen + (int)DIRHDSIZ) > left) {
                                dp->d_reclen += left;
-                               uiop->uio_iov->iov_base += left;
-                               uiop->uio_iov->iov_len -= left;
+                               uio_iov_base_add(uiop, left);
+                               uio_iov_len_add(uiop, -left);
                                uiop->uio_offset += left;
-                               uiop->uio_resid -= left;
+                               uio_uio_resid_add(uiop, -left);
                                blksiz = 0;
                        }
-                       if ((tlen + DIRHDSIZ) > uiop->uio_resid)
+                       if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
                                bigenough = 0;
                        if (bigenough) {
-                               dp = (struct dirent *)uiop->uio_iov->iov_base;
+                               // LP64todo - fix this!
+                               dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
                                dp->d_fileno = (int)fileno;
                                dp->d_namlen = len;
                                dp->d_reclen = tlen + DIRHDSIZ;
@@ -2445,19 +3108,28 @@ nfs_readdirrpc(vp, uiop, cred)
                                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;
+#if LP64KERN
+                               uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
+                               uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
+#else
+                               uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
+                               uio_iov_len_add(uiop, -((int)DIRHDSIZ));
+#endif
+                               uio_iov_base_add(uiop, DIRHDSIZ);
                                nfsm_mtouio(uiop, len);
-                               cp = uiop->uio_iov->iov_base;
+                               // LP64todo - fix this!
+                               cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
                                tlen -= len;
                                *cp = '\0';     /* null terminate */
-                               uiop->uio_iov->iov_base += tlen;
-                               uiop->uio_iov->iov_len -= tlen;
+                               uio_iov_base_add(uiop, tlen);
+                               uio_iov_len_add(uiop, -tlen);
                                uiop->uio_offset += tlen;
-                               uiop->uio_resid -= tlen;
-                       } else
+                               uio_uio_resid_add(uiop, -tlen);
+                       } else {
                                nfsm_adv(nfsm_rndup(len));
+                       }
+                       if (skiplen)
+                               nfsm_adv(nfsm_rndup(skiplen));
                        if (v3) {
                                nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
                        } else {
@@ -2480,7 +3152,7 @@ nfs_readdirrpc(vp, uiop, cred)
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
                }
-               m_freem(mrep);
+               mbuf_freem(mrep);
        }
        /*
         * Fill last record, iff any, out to a multiple of DIRBLKSIZ
@@ -2489,10 +3161,10 @@ nfs_readdirrpc(vp, uiop, cred)
        if (blksiz > 0) {
                left = DIRBLKSIZ - blksiz;
                dp->d_reclen += left;
-               uiop->uio_iov->iov_base += left;
-               uiop->uio_iov->iov_len -= left;
+               uio_iov_base_add(uiop, left);
+               uio_iov_len_add(uiop, -left);
                uiop->uio_offset += left;
-               uiop->uio_resid -= left;
+               uio_uio_resid_add(uiop, -left);
        }
 
        /*
@@ -2502,10 +3174,11 @@ nfs_readdirrpc(vp, uiop, cred)
        if (bigenough)
                dnp->n_direofoffset = uiop->uio_offset;
        else {
-               if (uiop->uio_resid > 0)
+               if (uio_uio_resid(uiop) > 0)
                        printf("EEK! readdirrpc resid > 0\n");
                cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
-               *cookiep = cookie;
+               if (cookiep)
+                       *cookiep = cookie;
        }
 nfsmout:
        return (error);
@@ -2515,39 +3188,47 @@ nfsmout:
  * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
  */
 int
-nfs_readdirplusrpc(vp, uiop, cred)
-       struct vnode *vp;
-       register struct uio *uiop;
-       struct ucred *cred;
+nfs_readdirplusrpc(
+       vnode_t vp,
+       struct uio *uiop,
+       kauth_cred_t cred,
+       proc_t p)
 {
-       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;
+       int len, skiplen, left;
+       struct dirent *dp;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
+       vnode_t newvp;
+       nfsuint64 *cookiep;
+       caddr_t bpos, dpos, cp2;
+       mbuf_t mreq, mrep, md, mb, mb2;
+       struct componentname cn, *cnp = &cn;
        nfsuint64 cookie;
-       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       struct nfsmount *nmp;
        struct nfsnode *dnp = VTONFS(vp), *np;
-       nfsfh_t *fhp;
+       u_char *fhp;
        u_quad_t fileno;
        int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
-       int attrflag, fhsize;
+       int attrflag, fhsize, nmreaddirsize, nmrsize;
+       u_int64_t xid, savexid;
+       struct nfs_vattr nvattr;
 
 #ifndef nolint
        dp = (struct dirent *)0;
 #endif
 #if DIAGNOSTIC
        if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
-               (uiop->uio_resid & (DIRBLKSIZ - 1)))
+               (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)))
                panic("nfs_readdirplusrpc: bad uio");
 #endif
-       ndp->ni_dvp = vp;
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp)
+               return (ENXIO);
+       nmreaddirsize = nmp->nm_readdirsize;
+       nmrsize = nmp->nm_rsize;
+
+       bzero(cnp, sizeof(*cnp));
        newvp = NULLVP;
 
        /*
@@ -2564,21 +3245,25 @@ nfs_readdirplusrpc(vp, uiop, cred)
         * The stopping criteria is EOF or buffer full.
         */
        while (more_dirs && bigenough) {
-               nfsstats.rpccnt[NFSPROC_READDIRPLUS]++;
-               nfsm_reqhead(vp, NFSPROC_READDIRPLUS,
-                       NFSX_FH(1) + 6 * NFSX_UNSIGNED);
+               nfsm_reqhead(NFSX_FH(1) + 6 * NFSX_UNSIGNED);
+               if (error)
+                       goto nfsmout;
+               OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_READDIRPLUS]);
                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);
-               nfsm_postop_attr(vp, attrflag);
+               *tl++ = txdr_unsigned(nmreaddirsize);
+               *tl = txdr_unsigned(nmrsize);
+               nfsm_request(vp, NFSPROC_READDIRPLUS, p, cred, &xid);
+               savexid = xid;
+               if (mrep) {
+                       nfsm_postop_attr_update(vp, 1, attrflag, &xid);
+               }
                if (error) {
-                       m_freem(mrep);
+                       mbuf_freem(mrep);
                        goto nfsmout;
                }
                nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
@@ -2591,27 +3276,36 @@ nfs_readdirplusrpc(vp, uiop, cred)
                        nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
                        fxdr_hyper(tl, &fileno);
                        len = fxdr_unsigned(int, *(tl + 2));
-                       if (len <= 0 || len > NFS_MAXNAMLEN) {
+                       /* Note: v3 supports longer names, but struct dirent doesn't */
+                       /* so we just truncate the names to fit */
+                       if (len <= 0) {
                                error = EBADRPC;
-                               m_freem(mrep);
+                               mbuf_freem(mrep);
                                goto nfsmout;
                        }
+                       if (len > MAXNAMLEN) {
+                               skiplen = len - MAXNAMLEN;
+                               len = MAXNAMLEN;
+                       } else {
+                               skiplen = 0;
+                       }
                        tlen = nfsm_rndup(len);
                        if (tlen == len)
                                tlen += 4;      /* To ensure null termination*/
                        left = DIRBLKSIZ - blksiz;
-                       if ((tlen + DIRHDSIZ) > left) {
+                       if ((tlen + (int)DIRHDSIZ) > left) {
                                dp->d_reclen += left;
-                               uiop->uio_iov->iov_base += left;
-                               uiop->uio_iov->iov_len -= left;
+                               uio_iov_base_add(uiop, left);
+                               uio_iov_len_add(uiop, -left);
                                uiop->uio_offset += left;
-                               uiop->uio_resid -= left;
+                               uio_uio_resid_add(uiop, -left);
                                blksiz = 0;
                        }
-                       if ((tlen + DIRHDSIZ) > uiop->uio_resid)
+                       if ((tlen + (int)DIRHDSIZ) > uio_uio_resid(uiop))
                                bigenough = 0;
                        if (bigenough) {
-                               dp = (struct dirent *)uiop->uio_iov->iov_base;
+                               // LP64todo - fix this!
+                               dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
                                dp->d_fileno = (int)fileno;
                                dp->d_namlen = len;
                                dp->d_reclen = tlen + DIRHDSIZ;
@@ -2620,21 +3314,30 @@ nfs_readdirplusrpc(vp, uiop, cred)
                                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;
+#if LP64KERN
+                               uio_uio_resid_add(uiop, -((int64_t)DIRHDSIZ));
+                               uio_iov_len_add(uiop, -((int64_t)DIRHDSIZ));
+#else
+                               uio_uio_resid_add(uiop, -((int)DIRHDSIZ));
+                               uio_iov_len_add(uiop, -((int)DIRHDSIZ));
+#endif
+                               uio_iov_base_add(uiop, DIRHDSIZ);
+                               // LP64todo - fix this!
+                               cnp->cn_nameptr = CAST_DOWN(caddr_t, uio_iov_base(uiop));
                                cnp->cn_namelen = len;
                                nfsm_mtouio(uiop, len);
-                               cp = uiop->uio_iov->iov_base;
+                               cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
                                tlen -= len;
                                *cp = '\0';
-                               uiop->uio_iov->iov_base += tlen;
-                               uiop->uio_iov->iov_len -= tlen;
+                               uio_iov_base_add(uiop, tlen);
+                               uio_iov_len_add(uiop, -tlen);
                                uiop->uio_offset += tlen;
-                               uiop->uio_resid -= tlen;
-                       } else
+                               uio_uio_resid_add(uiop, -tlen);
+                       } else {
                                nfsm_adv(nfsm_rndup(len));
+                       }
+                       if (skiplen)
+                               nfsm_adv(nfsm_rndup(skiplen));
                        nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
                        if (bigenough) {
                                cookie.nfsuquad[0] = *tl++;
@@ -2649,42 +3352,54 @@ nfs_readdirplusrpc(vp, uiop, cred)
                         */
                        attrflag = fxdr_unsigned(int, *tl);
                        if (attrflag) {
-                           dpossav1 = dpos;
-                           mdsav1 = md;
-                           nfsm_adv(NFSX_V3FATTR);
+                           /* grab attributes */
+                           nfsm_attr_get(1, &nvattr);
+                           dp->d_type = IFTODT(VTTOIF(nvattr.nva_type));
+                           /* check for file handle */
                            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;
+                                   error = vnode_ref(vp);
+                                   if (error) {
+                                       doit = 0;
+                                   } else {
+                                       newvp = vp;
+                                       np = dnp;
+                                   }
+                               } else if (!bigenough ||
+                                       (cnp->cn_namelen == 2 &&
+                                        cnp->cn_nameptr[1] == '.' &&
+                                        cnp->cn_nameptr[0] == '.')) {
+                                   /*
+                                    * XXXmacko I don't think this ".." thing is a problem anymore.
+                                    * don't doit if we can't guarantee
+                                    * that this entry is NOT ".." because
+                                    * we would have to drop the lock on
+                                    * the directory before getting the
+                                    * lock on the ".." vnode... and we
+                                    * don't want to drop the dvp lock in
+                                    * the middle of a readdirplus.
+                                    */
+                                   doit = 0;
                                } else {
-                                   if ((error = nfs_nget(vp->v_mount, fhp,
-                                       fhsize, &np)))
+                                   cnp->cn_hash = 0;
+
+                                   error = nfs_nget(vnode_mount(vp), vp, cnp,
+                                               fhp, fhsize, &nvattr, &xid,
+                                               NG_MAKEENTRY, &np);
+                                   if (error)
                                        doit = 0;
                                    else
                                        newvp = NFSTOV(np);
                                }
                            }
-                           if (doit) {
-                               dpossav2 = dpos;
-                               dpos = dpossav1;
-                               mdsav2 = md;
-                               md = mdsav1;
-                               nfsm_loadattr(newvp, (struct vattr *)0);
-                               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);
+                           /* update attributes if not already updated */
+                           if (doit && bigenough && (np->n_xid <= savexid)) {
+                               xid = savexid;
+                               nfs_loadattrcache(np, &nvattr, &xid, 0);
+                               /* any error can be ignored */
                            }
                        } else {
                            /* Just skip over the file handle */
@@ -2693,7 +3408,10 @@ nfs_readdirplusrpc(vp, uiop, cred)
                            nfsm_adv(nfsm_rndup(i));
                        }
                        if (newvp != NULLVP) {
-                           vrele(newvp);
+                           if (newvp == vp)
+                               vnode_rele(newvp);
+                           else
+                               vnode_put(newvp);
                            newvp = NULLVP;
                        }
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
@@ -2706,7 +3424,7 @@ nfs_readdirplusrpc(vp, uiop, cred)
                        nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
                        more_dirs = (fxdr_unsigned(int, *tl) == 0);
                }
-               m_freem(mrep);
+               mbuf_freem(mrep);
        }
        /*
         * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
@@ -2715,10 +3433,10 @@ nfs_readdirplusrpc(vp, uiop, cred)
        if (blksiz > 0) {
                left = DIRBLKSIZ - blksiz;
                dp->d_reclen += left;
-               uiop->uio_iov->iov_base += left;
-               uiop->uio_iov->iov_len -= left;
+               uio_iov_base_add(uiop, left);
+               uio_iov_len_add(uiop, -left);
                uiop->uio_offset += left;
-               uiop->uio_resid -= left;
+               uio_uio_resid_add(uiop, -left);
        }
 
        /*
@@ -2728,19 +3446,13 @@ nfs_readdirplusrpc(vp, uiop, cred)
        if (bigenough)
                dnp->n_direofoffset = uiop->uio_offset;
        else {
-               if (uiop->uio_resid > 0)
+               if (uio_uio_resid(uiop) > 0)
                        printf("EEK! readdirplusrpc resid > 0\n");
                cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
-               *cookiep = cookie;
+               if (cookiep)
+                       *cookiep = cookie;
        }
 nfsmout:
-       if (newvp != NULLVP) {
-               if (newvp == vp)
-                       vrele(newvp);
-               else
-                       vput(newvp);
-               newvp = NULLVP;
-       }
        return (error);
 }
 
@@ -2752,46 +3464,82 @@ nfsmout:
  * to create the same funny name between the nfs_lookitup() fails and the
  * nfs_rename() completes, but...
  */
+
+/* format of "random" names and next name to try */
+/* (note: shouldn't exceed size of sillyrename.s_name) */
+static char sillyrename_name[] = ".nfsAAA%04x4.4";
+
 static int
-nfs_sillyrename(dvp, vp, cnp)
-       struct vnode *dvp, *vp;
-       struct componentname *cnp;
+nfs_sillyrename(
+       vnode_t dvp,
+       vnode_t vp,
+       struct componentname *cnp,
+       kauth_cred_t cred,
+       proc_t p)
 {
        register struct sillyrename *sp;
        struct nfsnode *np;
        int error;
        short pid;
-       struct ucred *cred;
+       kauth_cred_t tmpcred;
+       int i, j, k;
 
-       cache_purge(dvp);
+       cache_purge(vp);
        np = VTONFS(vp);
 #if DIAGNOSTIC
-       if (vp->v_type == VDIR)
+       if (vnode_vtype(vp) == 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);
+       if (!sp)
+               return (ENOMEM);
+       kauth_cred_ref(cred);
+       sp->s_cred = cred;
        sp->s_dvp = dvp;
-       VREF(dvp);
+       error = vnode_ref(dvp);
+       if (error)
+               goto bad_norele;
 
        /* Fudge together a funny name */
-       pid = cnp->cn_proc->p_pid;
-       sp->s_namlen = sprintf(sp->s_name, ".nfsA%04x4.4", pid);
+       pid = proc_pid(p);
+       sp->s_namlen = sprintf(sp->s_name, sillyrename_name, pid);
 
        /* Try lookitups until we get one that isn't there */
-       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;
+       i = j = k = 0;
+       while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, NULL) == 0) {
+               if (sp->s_name[4]++ >= 'z')
+                       sp->s_name[4] = 'A';
+               if (++i > ('z' - 'A' + 1)) {
+                       i = 0;
+                       if (sp->s_name[5]++ >= 'z')
+                               sp->s_name[5] = 'A';
+                       if (++j > ('z' - 'A' + 1)) {
+                               j = 0;
+                               if (sp->s_name[6]++ >= 'z')
+                                       sp->s_name[6] = 'A';
+                               if (++k > ('z' - 'A' + 1)) {
+                                       error = EINVAL;
+                                       goto bad;
+                               }
+                       }
+               }
+       }
+       /* make note of next "random" name to try */
+       if ((sillyrename_name[4] = (sp->s_name[4] + 1)) > 'z') {
+               sillyrename_name[4] = 'A';
+               if ((sillyrename_name[5] = (sp->s_name[5] + 1)) > 'z') {
+                       sillyrename_name[5] = 'A';
+                       if ((sillyrename_name[6] = (sp->s_name[6] + 1)) > 'z')
+                               sillyrename_name[6] = 'A';
                }
        }
-       if ((error = nfs_renameit(dvp, cnp, sp)))
+       /* now, do the rename */
+       error = nfs_renamerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen,
+                               dvp, sp->s_name, sp->s_namlen, sp->s_cred, p);
+       if (error)
                goto bad;
-       error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
-               cnp->cn_proc, &np);
+       error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, p, &np);
 #if DIAGNOSTIC
        kprintf("sillyrename: %s, vp=%x, np=%x, dvp=%x\n",
                &sp->s_name[0], (unsigned)vp, (unsigned)np, (unsigned)dvp);
@@ -2799,11 +3547,12 @@ nfs_sillyrename(dvp, vp, cnp)
        np->n_sillyrename = sp;
        return (0);
 bad:
-       vrele(sp->s_dvp);
-       cred = sp->s_cred;
+       vnode_rele(sp->s_dvp);
+bad_norele:
+       tmpcred = sp->s_cred;
        sp->s_cred = NOCRED;
-       crfree(cred);
-       _FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
+       kauth_cred_rele(tmpcred);
+       FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
        return (error);
 }
 
@@ -2817,76 +3566,119 @@ bad:
  */
 static int
 nfs_lookitup(dvp, name, len, cred, procp, npp)
-       register struct vnode *dvp;
+       vnode_t dvp;
        char *name;
        int len;
-       struct ucred *cred;
-       struct proc *procp;
+       kauth_cred_t cred;
+       proc_t procp;
        struct nfsnode **npp;
 {
-       register u_long *tl;
-       register caddr_t cp;
-       register long t1, t2;
-       struct vnode *newvp = (struct vnode *)0;
+       u_long *tl;
+       caddr_t cp;
+       long t1, t2;
+       vnode_t newvp = (vnode_t)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);
+       mbuf_t mreq, mrep, md, mb, mb2;
+       u_char *nfhp;
+       int v3;
+       u_int64_t xid, dxid, savedxid;
+       struct nfs_vattr nvattr;
+
+       if (!VFSTONFS(vnode_mount(dvp)))
+               return (ENXIO);
+       v3 = NFS_ISV3(dvp);
 
-       nfsstats.rpccnt[NFSPROC_LOOKUP]++;
-       nfsm_reqhead(dvp, NFSPROC_LOOKUP,
-               NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       nfsm_reqhead(NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_LOOKUP]);
        nfsm_fhtom(dvp, v3);
-       nfsm_strtom(name, len, NFS_MAXNAMLEN);
-       nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred);
+       nfsm_strtom(name, len, NFS_MAXNAMLEN, v3);
+       nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred, &xid);
        if (npp && !error) {
+               savedxid = xid;
                nfsm_getfh(nfhp, fhlen, v3);
+               /* get attributes */
+               if (v3) {
+                       nfsm_postop_attr_get(v3, attrflag, &nvattr);
+                       if (!attrflag) {
+                               /* We need valid attributes in order */
+                               /* to call nfs_nget/vnode_create().  */
+                               error = nfs_getattr_no_vnode(vnode_mount(dvp),
+                                               nfhp, fhlen, cred, procp, &nvattr, &xid);
+                               if (error) {
+                                       mbuf_freem(mrep);
+                                       goto nfsmout;
+                               }
+                       }
+                       dxid = savedxid;
+                       nfsm_postop_attr_update(dvp, v3, attrflag, &dxid);
+               } else {
+                       nfsm_attr_get(v3, &nvattr);
+               }
                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);
+                   if (fhlen != np->n_fhsize) {
+                       u_char *oldbuf = (np->n_fhsize > NFS_SMALLFH) ? np->n_fhp : NULL;
+                       if (fhlen > NFS_SMALLFH) {
+                           MALLOC_ZONE(np->n_fhp, u_char *, fhlen, M_NFSBIGFH, M_WAITOK);
+                           if (!np->n_fhp) {
+                               np->n_fhp = oldbuf;
+                               error = ENOMEM;
+                               mbuf_freem(mrep);
+                               goto nfsmout;
+                           }
+                       } else {
+                           np->n_fhp = &np->n_fh[0];
+                       }
+                       if (oldbuf) {
+                           FREE_ZONE(oldbuf, np->n_fhsize, M_NFSBIGFH);
+                       }
+                   }
+                   bcopy(nfhp, np->n_fhp, fhlen);
                    np->n_fhsize = fhlen;
                    newvp = NFSTOV(np);
+                   error = nfs_loadattrcache(np, &nvattr, &xid, 0);
+                   if (error) {
+                       mbuf_freem(mrep);
+                       goto nfsmout;
+                   }
                } else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
-                   VREF(dvp);
                    newvp = dvp;
+                   if (dnp->n_xid <= savedxid) {
+                       dxid = savedxid;
+                       error = nfs_loadattrcache(dnp, &nvattr, &dxid, 0);
+                       if (error) {
+                           mbuf_freem(mrep);
+                           goto nfsmout;
+                       }
+                   }
                } else {
-                   error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np);
+                   struct componentname cn, *cnp = &cn;
+                   bzero(cnp, sizeof(*cnp));
+                   cnp->cn_nameptr = name;
+                   cnp->cn_namelen = len;
+
+                   error = nfs_nget(vnode_mount(dvp), dvp, cnp, nfhp, fhlen,
+                               &nvattr, &xid, NG_MAKEENTRY, &np);
                    if (error) {
-                       m_freem(mrep);
+                       mbuf_freem(mrep);
                        return (error);
                    }
                    newvp = NFSTOV(np);
                }
-               if (v3) {
-                       nfsm_postop_attr(newvp, attrflag);
-                       if (!attrflag && *npp == NULL) {
-                               m_freem(mrep);
-                               if (newvp == dvp)
-                                       vrele(newvp);
-                               else
-                                       vput(newvp);
-                               return (ENOENT);
-                       }
-               } else
-                       nfsm_loadattr(newvp, (struct vattr *)0);
        }
        nfsm_reqdone;
        if (npp && *npp == NULL) {
                if (error) {
-                       if (newvp)
+                       if (newvp) {
                                if (newvp == dvp)
-                                       vrele(newvp);
+                                       vnode_rele(newvp);
                                else
-                                       vput(newvp);
+                                       vnode_put(newvp);
+                       }
                } else
                        *npp = np;
        }
@@ -2896,39 +3688,48 @@ nfs_lookitup(dvp, name, len, cred, procp, npp)
 /*
  * Nfs Version 3 commit rpc
  */
-static int
-nfs_commit(vp, offset, cnt, cred, procp)
-       register struct vnode *vp;
+int
+nfs_commit(vp, offset, count, cred, procp)
+       vnode_t vp;
        u_quad_t offset;
-       int cnt;
-       struct ucred *cred;
-       struct proc *procp;
+       u_int32_t count;
+       kauth_cred_t cred;
+       proc_t procp;
 {
-       register caddr_t cp;
-       register u_long *tl;
-       register int t1, t2;
-       register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       caddr_t cp;
+       u_long *tl;
+       int t1, t2;
+       struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
        caddr_t bpos, dpos, cp2;
-       int error = 0, wccflag = NFSV3_WCCRATTR;
-       struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-       
-       if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0)
+       int error = 0, wccpostattr = 0;
+       struct timespec premtime = { 0, 0 };
+       mbuf_t mreq, mrep, md, mb, mb2;
+       u_int64_t xid, wverf;
+
+       FSDBG(521, vp, offset, count, nmp->nm_state);
+       if (!nmp)
+               return (ENXIO);
+       if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0)
                return (0);
-       nfsstats.rpccnt[NFSPROC_COMMIT]++;
-       nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1));
+       nfsm_reqhead(NFSX_FH(1));
+       if (error)
+               return (error);
+       OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_COMMIT]);
        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);
-       nfsm_wcc_data(vp, wccflag);
+       *tl = txdr_unsigned(count);
+       nfsm_request(vp, NFSPROC_COMMIT, procp, cred, &xid);
+       if (mrep) {
+               nfsm_wcc_data(vp, &premtime, wccpostattr, &xid);
+               /* XXX can we do anything useful with the wcc info? */
+       }
        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);
+               fxdr_hyper(tl, &wverf);
+               if (wverf != nmp->nm_verf) {
+                       nmp->nm_verf = wverf;
                        error = NFSERR_STALEWRITEVERF;
                }
        }
@@ -2936,76 +3737,20 @@ nfs_commit(vp, offset, cnt, cred, procp)
        return (error);
 }
 
-/*
- * Kludge City..
- * - make nfs_bmap() essentially a no-op that does no translation
- * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc
- *   (Maybe I could use the process's page mapping, but I was concerned that
- *    Kernel Write might not be enabled and also figured copyout() would do
- *    a lot more work than bcopy() and also it currently happens in the
- *    context of the swapper process (2).
- */
-static int
-nfs_bmap(ap)
-       struct vop_bmap_args /* {
-               struct vnode *a_vp;
-               daddr_t  a_bn;
-               struct vnode **a_vpp;
-               daddr_t *a_bnp;
-               int *a_runp;
-               int *a_runb;
-       } */ *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);
-}
-
-/*
- * Strategy routine.
- * For async requests when nfsiod(s) are running, queue the request by
- * calling nfs_asyncio(), otherwise just all nfs_doio() to do the
- * request.
- */
 static int
-nfs_strategy(ap)
-       struct vop_strategy_args *ap;
+nfs_blockmap(
+       __unused struct vnop_blockmap_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               off_t a_foffset;
+               size_t a_size;
+               daddr64_t *a_bpn;
+               size_t *a_run;
+               void *a_poff;
+               int a_flags;
+       } */ *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();     /* XXX */
-       if (ISSET(bp->b_flags, B_READ))
-               cr = bp->b_rcred;
-       else
-               cr = bp->b_wcred;
-       /*
-        * If the op is asynchronous and an i/o daemon is waiting
-        * queue the request, wake it up and wait for completion
-        * otherwise just do it ourselves.
-        */
-       if (!ISSET(bp->b_flags, B_ASYNC) || nfs_asyncio(bp, NOCRED))
-               error = nfs_doio(bp, cr, p);
-       return (error);
+       return (ENOTSUP);
 }
 
 /*
@@ -3013,15 +3758,16 @@ nfs_strategy(ap)
  *
  * NB Currently unsupported.
  */
-/* ARGSUSED */
+/*ARGSUSED*/
 static int
-nfs_mmap(ap)
-       struct vop_mmap_args /* {
-               struct vnode *a_vp;
-               int  a_fflags;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *ap;
+nfs_mmap(
+       __unused struct vnop_mmap_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_fflags;
+               kauth_cred_t a_cred;
+               proc_t a_p;
+       } */ *ap)
 {
 
        return (EINVAL);
@@ -3033,160 +3779,129 @@ nfs_mmap(ap)
 /* ARGSUSED */
 static int
 nfs_fsync(ap)
-       struct vop_fsync_args /* {
+       struct vnop_fsync_args /* {
                struct vnodeop_desc *a_desc;
-               struct vnode * a_vp;
-               struct ucred * a_cred;
-               int  a_waitfor;
-               struct proc * a_p;
+               vnode_t a_vp;
+               int a_waitfor;
+               vfs_context_t a_context;
        } */ *ap;
 {
+       kauth_cred_t cred = vfs_context_ucred(ap->a_context);
+       proc_t p = vfs_context_proc(ap->a_context);
+       struct nfsnode *np = VTONFS(ap->a_vp);
+       int error;
 
-       return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));
+       np->n_flag |= NWRBUSY;
+       error = nfs_flush(ap->a_vp, ap->a_waitfor, cred, p, 0);
+       np->n_flag &= ~NWRBUSY;
+       return (error);
 }
-
-/*
- * Flush all the blocks associated with a vnode.
- *     Walk through the buffer pool and push any dirty pages
- *     associated with the vnode.
- */
-static int
-nfs_flush(vp, cred, waitfor, p, commit)
-       register struct vnode *vp;
-       struct ucred *cred;
-       int waitfor;
-       struct proc *p;
-       int commit;
+int
+nfs_flushcommits(vnode_t vp, proc_t p, int nowait)
 {
-       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;
+       struct nfsnode *np = VTONFS(vp);
+       struct nfsbuf *bp;
+       struct nfsbuflists blist, commitlist;
+       int error = 0, retv, wcred_set, flags;
        u_quad_t off, endoff, toff;
-       struct ucred* wcred = NULL;
-       struct buf **bvec = NULL;
-        void * object;
-        kern_return_t kret;
-        upl_t *upls = NULL;
-
+       u_int32_t count;
+       kauth_cred_t wcred = NULL;
 
-#ifndef NFS_COMMITBVECSIZ
-#define NFS_COMMITBVECSIZ      20
-#endif
-       struct buf *bvec_on_stack[NFS_COMMITBVECSIZ];
-        struct upl_t *upls_on_stack[NFS_COMMITBVECSIZ]; 
-        int bvecsize = 0, bveccount, buplpos;
-
-       if (nmp->nm_flag & NFSMNT_INT)
-               slpflag = PCATCH;
-       if (!commit)
-               passone = 0;
+       FSDBG_TOP(557, vp, np, 0, 0);
 
        /*
-        * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the
+        * A nb_flags == (NB_DELWRI | NB_NEEDCOMMIT) block has been written to the
         * server, but nas not been committed to stable storage on the server
-        * yet. On the first pass, the byte range is worked out and the commit
-        * rpc is done. On the second pass, nfs_writebp() is called to do the
-        * job.
+        * yet. The byte range is worked out for as many nfsbufs as we can handle
+        * and the commit rpc is done.
         */
-again:
-       if (vp->v_dirtyblkhd.lh_first)
+       if (!LIST_EMPTY(&np->n_dirtyblkhd))
                np->n_flag |= NMODIFIED;
+
        off = (u_quad_t)-1;
        endoff = 0;
-       bvecpos = 0;
-        buplpos = 0;
-       if (NFS_ISV3(vp) && commit) {
-               s = splbio();
-               /*
-                * Count up how many buffers waiting for a commit.
-                */
-               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++;
-               }
-               /*
-                * Allocate space to remember the list of bufs to commit.  It is
-                * important to use M_NOWAIT here to avoid a race with nfs_write.
-                * If we can't get memory (for whatever reason), we will end up
-                * committing the buffers one-by-one in the loop below.
-                */
-               if (bveccount > NFS_COMMITBVECSIZ) {
-                       if (bvec != NULL && bvec != bvec_on_stack)
-                               _FREE(bvec, M_TEMP);
-                       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;
-                        /* allocate the upl structure before the loop based on buffers to commit */
-                       if (upls != NULL && upls != upls_on_stack)
-                                       _FREE(upls, M_TEMP);
-                       MALLOC(upls, struct upl_t *,
-                            bveccount * sizeof(upl_t), M_TEMP, M_NOWAIT);
-                        if (upls == NULL)
-                            upls = upls_on_stack;
-               } else {
-                       if (bvec && bvec != bvec_on_stack)
-                               _FREE(bvec, M_TEMP);
-                       bvec = bvec_on_stack;
-                       bvecsize = NFS_COMMITBVECSIZ;
-                       if (upls && upls != upls_on_stack)
-                                       _FREE(upls, M_TEMP);
-                        upls = upls_on_stack;
-               }
+       wcred_set = 0;
+       LIST_INIT(&commitlist);
 
-               for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
-                       nbp = bp->b_vnbufs.le_next;
-                       if (bvecpos >= bvecsize)
-                               break;
-                       if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
-                               != (B_DELWRI | B_NEEDCOMMIT))
+       if (!VFSTONFS(vnode_mount(vp))) {
+               error = ENXIO;
+               goto done;
+       }
+       if (!NFS_ISV3(vp)) {
+               error = EINVAL;
+               goto done;
+       }
+
+       flags = NBI_DIRTY;
+       if (nowait)
+               flags |= NBI_NOWAIT;
+       lck_mtx_lock(nfs_buf_mutex);
+       if (!nfs_buf_iterprepare(np, &blist, flags)) {
+               while ((bp = LIST_FIRST(&blist))) {
+                       LIST_REMOVE(bp, nb_vnbufs);
+                       LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
+                       error = nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0);
+                       if (error)
                                continue;
-                       bremfree(bp);
+                       if (ISSET(bp->nb_flags, NB_NEEDCOMMIT))
+                               nfs_buf_check_write_verifier(np, bp);
+                       if (((bp->nb_flags & (NB_DELWRI | NB_NEEDCOMMIT))
+                               != (NB_DELWRI | NB_NEEDCOMMIT))) {
+                               nfs_buf_drop(bp);
+                               continue;
+                       }
+                       nfs_buf_remfree(bp);
+                       lck_mtx_unlock(nfs_buf_mutex);
                        /*
-                        * Work out if all buffers are using the same cred
-                        * so we can deal with them all with one commit.
+                        * we need a upl to see if the page has been
+                        * dirtied (think mmap) since the unstable write, and
+                        * also to prevent vm from paging it during our commit rpc
+                        */
+                       if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
+                               retv = nfs_buf_upl_setup(bp);
+                               if (retv) {
+                                       /* unable to create upl */
+                                       /* vm object must no longer exist */
+                                       /* this could be fatal if we need */
+                                       /* to write the data again, we'll see...  */
+                                       printf("nfs_flushcommits: upl create failed %d\n", retv);
+                                       bp->nb_valid = bp->nb_dirty = 0;
+                               }
+                       }
+                       nfs_buf_upl_check(bp);
+                       lck_mtx_lock(nfs_buf_mutex);
+
+                       FSDBG(557, bp, bp->nb_flags, bp->nb_valid, bp->nb_dirty);
+                       FSDBG(557, bp->nb_validoff, bp->nb_validend,
+                             bp->nb_dirtyoff, bp->nb_dirtyend);
+
+                       /*
+                        * We used to check for dirty pages here; if there were any
+                        * we'd abort the commit and force the entire buffer to be
+                        * written again.
+                        *
+                        * Instead of doing that, we now go ahead and commit the dirty
+                        * range, and then leave the buffer around with dirty pages
+                        * that will be written out later.
                         */
-                       if (wcred == NULL)
-                               wcred = bp->b_wcred;
-                       else if (wcred != bp->b_wcred)
-                               wcred = NOCRED;
-                       SET(bp->b_flags, (B_BUSY | B_WRITEINPROG));
 
                        /*
-                        * we need vm_fault_list_request so if vm decides to
-                        * do paging while we are waiting on commit rpc,
-                        * that it doesn't pick these pages.
+                        * Work out if all buffers are using the same cred
+                        * so we can deal with them all with one commit.
+                        *
+                        * XXX creds in bp's must be obtained by kauth_cred_ref on
+                        *     the same original cred in order for them to be equal.
                         */
-                       if (!ISSET(bp->b_flags, B_PAGELIST)) {
-                               /* if pagelist exists, assume vm pages are locked/busy already */                               off_t file_offset = ubc_blktooff(vp, bp->b_lblkno);
-                               object = ubc_getobject(vp, (UBC_NOREACTIVATE|UBC_HOLDOBJECT));
-                               if (object == (void*)NULL)
-                                       panic("nfs_getcacheblk: NULL vmobject");
-                               if(bp->b_bufsize & 0xfff)
-                                       panic("nfs_getcacheblk: list request is less than 4k");
-                               kret = vm_fault_list_request(
-                                               object, (vm_object_offset_t)file_offset,
-                                               bp->b_bufsize, &(upls[buplpos]), NULL, 0,
-                                               (int)(UPL_NO_SYNC | UPL_CLEAN_IN_PLACE |UPL_PRECIOUS |
-                                               UPL_SET_INTERNAL));
-                               if (kret != KERN_SUCCESS) 
-                                       panic("nfs_getcacheblk: get pagelists failed with (%d)", kret);
-                                    
-#ifdef UBC_DEBUG
-                               upl_ubc_alias_set(pl, ioaddr, 1);
-#endif /* UBC_DEBUG */
-                               buplpos++; /* not same as bvecpos if upl existed already */
+                       if (wcred_set == 0) {
+                               wcred = bp->nb_wcred;
+                               if (wcred == NOCRED)
+                                       panic("nfs: needcommit w/out wcred");
+                               wcred_set = 1;
+                       } else if ((wcred_set == 1) && wcred != bp->nb_wcred) {
+                               wcred_set = -1;
                        }
+                       SET(bp->nb_flags, NB_WRITEINPROG);
 
                        /*
                         * A list of these buffers is kept so that the
@@ -3195,169 +3910,319 @@ again:
                         * may be a race between the commit rpc and new
                         * uncommitted writes on the file.
                         */
-                       bvec[bvecpos++] = bp;
-                       toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
-                               bp->b_dirtyoff;
+                       LIST_REMOVE(bp, nb_vnbufs);
+                       LIST_INSERT_HEAD(&commitlist, bp, nb_vnbufs);
+                       toff = NBOFF(bp) + bp->nb_dirtyoff;
                        if (toff < off)
                                off = toff;
-                       toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
+                       toff += (u_quad_t)(bp->nb_dirtyend - bp->nb_dirtyoff);
                        if (toff > endoff)
                                endoff = toff;
                }
-               splx(s);
+               nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
+       }
+       lck_mtx_unlock(nfs_buf_mutex);
+
+       if (LIST_EMPTY(&commitlist)) {
+               error = ENOBUFS;
+               goto done;
        }
-       if (bvecpos > 0) {
+
+       /*
+        * Commit data on the server, as required.
+        * If all bufs are using the same wcred, then use that with
+        * one call for all of them, otherwise commit each one
+        * separately.
+        */
+       if (wcred_set == 1) {
                /*
-                * Commit data on the server, as required.
-                * If all bufs are using the same wcred, then use that with
-                * one call for all of them, otherwise commit each one
-                * separately.
+                * Note, it's possible the commit range could be >2^32-1.
+                * If it is, we'll send one commit that covers the whole file.
                 */
-               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];
-                               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 ((endoff - off) > 0xffffffff)
+                       count = 0; 
+               else
+                       count = (endoff - off); 
+               retv = nfs_commit(vp, off, count, wcred, p);
+       } else {
+               retv = 0;
+               LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
+                       toff = NBOFF(bp) + bp->nb_dirtyoff;
+                       count = bp->nb_dirtyend - bp->nb_dirtyoff;
+                       retv = nfs_commit(vp, toff, count, bp->nb_wcred, p);
+                       if (retv)
+                               break;
                }
+       }
 
-               if (retv == NFSERR_STALEWRITEVERF)
-                       nfs_clearcommit(vp->v_mount);
-                        
-                for (i = 0; i < buplpos; i++) {
-                    /*
-                    * before the VOP_BWRITE and biodone(ASYNC)/brelse, we have to undo
-                    * holding the vm page or we we will deadlock on another vm_fault_list_request.
-                    * Here's a convenient place to put it. 
-                    * Better if we could hold it by setting the PAGELIST flag and kernel_upl_map
-                    * as does nfs_writebp. Then normal biodones and brelse will clean it up and 
-                    * we can avoid this abort. For now make minimal changse and test this out.
-                    */
-                    err = kernel_upl_abort(upls[i], NULL); 
-                    if (err)
-                        printf("nfs_flush: kernel_upl_abort %d\n", err);
-                    }
+       /*
+        * Now, either mark the blocks I/O done or mark the
+        * blocks dirty, depending on whether the commit
+        * succeeded.
+        */
+       while ((bp = LIST_FIRST(&commitlist))) {
+               LIST_REMOVE(bp, nb_vnbufs);
+               FSDBG(557, bp, retv, bp->nb_flags, bp->nb_dirty);
+               CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_WRITEINPROG));
+               np->n_needcommitcnt--;
+               CHECK_NEEDCOMMITCNT(np);
+
+               if (retv) {
+                       /* move back to dirty list */
+                       lck_mtx_lock(nfs_buf_mutex);
+                       LIST_INSERT_HEAD(&VTONFS(vp)->n_dirtyblkhd, bp, nb_vnbufs);
+                       lck_mtx_unlock(nfs_buf_mutex);
+                       nfs_buf_release(bp, 1);
+                       continue;
+               }
 
-               /*
-                * Now, either mark the blocks I/O done or mark the
-                * blocks dirty, depending on whether the commit
-                * succeeded.
-                */
-               for (i = 0; i < bvecpos; i++) {
-                        
-                       bp = bvec[i];
-                       CLR(bp->b_flags, (B_NEEDCOMMIT | B_WRITEINPROG));
-                       if (retv) {
-                           brelse(bp);
-                       } else {
-                           vp->v_numoutput++;
-                           SET(bp->b_flags, B_ASYNC);
-                           s = splbio();
-                           CLR(bp->b_flags, (B_READ|B_DONE|B_ERROR|B_DELWRI));
-                           bp->b_dirtyoff = bp->b_dirtyend = 0;
-                           reassignbuf(bp, vp);
-                           splx(s);
-                           biodone(bp);
-                       }
+               vnode_startwrite(vp);
+               if (ISSET(bp->nb_flags, NB_DELWRI)) {
+                       OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
+                       NFSBUFCNTCHK(0);
+                       wakeup(&nfs_nbdwrite);
                }
+               CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
+               /* if block still has dirty pages, we don't want it to */
+               /* be released in nfs_buf_iodone().  So, don't set NB_ASYNC. */
+               if (!bp->nb_dirty)
+                       SET(bp->nb_flags, NB_ASYNC);
+
+               /* move to clean list */
+               lck_mtx_lock(nfs_buf_mutex);
+               LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
+               lck_mtx_unlock(nfs_buf_mutex);
+
+               bp->nb_dirtyoff = bp->nb_dirtyend = 0;
+
+               nfs_buf_iodone(bp);
+               if (bp->nb_dirty) {
+                       /* throw it back in as a delayed write buffer */
+                       CLR(bp->nb_flags, NB_DONE);
+                       nfs_buf_write_delayed(bp, p);
+               }
+       }
 
+done:
+       FSDBG_BOT(557, vp, np, 0, error);
+       return (error);
+}
+
+/*
+ * Flush all the blocks associated with a vnode.
+ *     Walk through the buffer pool and push any dirty pages
+ *     associated with the vnode.
+ */
+int
+nfs_flush(
+       vnode_t vp,
+       int waitfor,
+       __unused kauth_cred_t cred,
+       proc_t p,
+       int ignore_writeerr)
+{
+       struct nfsnode *np = VTONFS(vp);
+       struct nfsbuf *bp;
+       struct nfsbuflists blist;
+       struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
+       int error = 0, error2, slptimeo = 0, slpflag = 0;
+       int flags, passone = 1;
+
+       FSDBG_TOP(517, vp, np, waitfor, 0);
+
+       if (!nmp) {
+               error = ENXIO;
+               goto done;
        }
+       if (nmp->nm_flag & NFSMNT_INT)
+               slpflag = PCATCH;
 
        /*
-        * Start/do any write(s) that are required.
-         * There is a window here where B_BUSY protects the buffer. The vm pages have been
-         * freed up, yet B_BUSY is set. Don't think you will hit any busy/incore problems while
-         * we sleep, but not absolutely sure. Keep an eye on it. Otherwise we will have to hold
-         * vm page across this locked. - EKN
+        * On the first pass, start async/unstable writes on all
+        * delayed write buffers.  Then wait for all writes to complete
+        * and call nfs_flushcommits() to commit any uncommitted buffers.
+        * On all subsequent passes, start STABLE writes on any remaining
+        * dirty buffers.  Then wait for all writes to complete.
         */
-loop:
-       if (current_thread_aborted()) {
-               error = EINTR;
+again:
+       lck_mtx_lock(nfs_buf_mutex);
+       FSDBG(518, LIST_FIRST(&np->n_dirtyblkhd), np->n_flag, 0, 0);
+       if (!LIST_EMPTY(&np->n_dirtyblkhd))
+               np->n_flag |= NMODIFIED;
+       if (!VFSTONFS(vnode_mount(vp))) {
+               lck_mtx_unlock(nfs_buf_mutex);
+               error = ENXIO;
                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)) {
-                       if (waitfor != MNT_WAIT || passone)
+
+       /* Start/do any write(s) that are required. */
+       if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
+               while ((bp = LIST_FIRST(&blist))) {
+                       LIST_REMOVE(bp, nb_vnbufs);
+                       LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
+                       flags = (passone || (waitfor != MNT_WAIT)) ? NBAC_NOWAIT : 0;
+                       if (flags != NBAC_NOWAIT)
+                               nfs_buf_refget(bp);
+                       while ((error = nfs_buf_acquire(bp, flags, slpflag, slptimeo))) {
+                               FSDBG(524, bp, flags, bp->nb_lflags, bp->nb_flags);
+                               if (error == EBUSY)
+                                       break;
+                               if (error) {
+                                       error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
+                                       if (error2) {
+                                               if (flags != NBAC_NOWAIT)
+                                                       nfs_buf_refrele(bp);
+                                               nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
+                                               lck_mtx_unlock(nfs_buf_mutex);
+                                               error = error2;
+                                               goto done;
+                                       }
+                                       if (slpflag == PCATCH) {
+                                               slpflag = 0;
+                                               slptimeo = 2 * hz;
+                                       }
+                               }
+                       }
+                       if (flags != NBAC_NOWAIT)
+                               nfs_buf_refrele(bp);
+                       if (error == EBUSY)
                                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;
+                       if (!bp->nb_vp) {
+                               /* buffer is no longer valid */
+                               nfs_buf_drop(bp);
+                               continue;
+                       }
+                       if (ISSET(bp->nb_flags, NB_NEEDCOMMIT))
+                               nfs_buf_check_write_verifier(np, bp);
+                       if (!ISSET(bp->nb_flags, NB_DELWRI))
+                               panic("nfs_flush: not dirty");
+                       FSDBG(525, bp, passone, bp->nb_lflags, bp->nb_flags);
+                       if ((passone || (waitfor != MNT_WAIT)) &&
+                           ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
+                               nfs_buf_drop(bp);
+                               continue;
+                       }
+                       nfs_buf_remfree(bp);
+                       lck_mtx_unlock(nfs_buf_mutex);
+                       if (ISSET(bp->nb_flags, NB_ERROR)) {
+                               np->n_error = bp->nb_error ? bp->nb_error : EIO;
+                               np->n_flag |= NWRITEERR;
+                               nfs_buf_release(bp, 1);
+                               lck_mtx_lock(nfs_buf_mutex);
+                               continue;
+                       }
+                       SET(bp->nb_flags, NB_ASYNC);
+                       if (!passone) {
+                               /* NB_STABLE forces this to be written FILESYNC */
+                               SET(bp->nb_flags, NB_STABLE);
+                       }
+                       nfs_buf_write(bp);
+                       lck_mtx_lock(nfs_buf_mutex);
+               }
+               nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
+       }
+       lck_mtx_unlock(nfs_buf_mutex);
+
+       if (waitfor == MNT_WAIT) {
+               while ((error = vnode_waitforwrites(vp, 0, slpflag, slptimeo, "nfsflush"))) {
+                       error2 = nfs_sigintr(VFSTONFS(vnode_mount(vp)), NULL, p);
+                       if (error2) {
+                               error = error2;
                                goto done;
-                           }
-                           if (slpflag == PCATCH) {
+                       }
+                       if (slpflag == PCATCH) {
                                slpflag = 0;
                                slptimeo = 2 * hz;
-                           }
                        }
-                       goto loop;
                }
-               if (!ISSET(bp->b_flags, B_DELWRI))
-                       panic("nfs_fsync: not dirty");
-               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;
+       if (NFS_ISV3(vp)) {
+               /* loop while it looks like there are still buffers to be */
+               /* commited and nfs_flushcommits() seems to be handling them. */
+               while (np->n_needcommitcnt)
+                       if (nfs_flushcommits(vp, p, 0))
+                               break;
        }
-       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;
-               }
+               if (!LIST_EMPTY(&np->n_dirtyblkhd))
+                       goto again;
+               /* if we have no dirty blocks, we can clear the modified flag */
+               np->n_flag &= ~NMODIFIED;
        }
-       if (np->n_flag & NWRITEERR) {
+
+       FSDBG(526, np->n_flag, np->n_error, 0, 0);
+       if (!ignore_writeerr && (np->n_flag & NWRITEERR)) {
                error = np->n_error;
                np->n_flag &= ~NWRITEERR;
        }
 done:
-       if (bvec != NULL && bvec != bvec_on_stack)
-               _FREE(bvec, M_TEMP);
-        if (upls != NULL && upls != upls_on_stack)
-                _FREE(upls, M_TEMP);
+       FSDBG_BOT(517, vp, np, error, 0);
+       return (error);
+}
+
+/*
+ * Do an nfs pathconf rpc.
+ */
+int
+nfs_pathconfrpc(
+       vnode_t vp,
+       struct nfsv3_pathconf *pc,
+       kauth_cred_t cred,
+       proc_t procp)
+{
+       mbuf_t mreq, mrep, md, mb, mb2;
+       caddr_t bpos, dpos, cp, cp2;
+       int32_t t1, t2;
+       u_long *tl;
+       u_int64_t xid;
+       int attrflag, error = 0;
+       struct nfsv3_pathconf *mpc;
+
+       /* fetch pathconf info from server */
+       nfsm_reqhead(NFSX_FH(1));
+       if (error)
+               return (error);
+       nfsm_fhtom(vp, 1);
+       nfsm_request(vp, NFSPROC_PATHCONF, procp, cred, &xid);
+       nfsm_postop_attr_update(vp, 1, attrflag, &xid);
+       if (!error) {
+               nfsm_dissect(mpc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
+               pc->pc_linkmax = fxdr_unsigned(long, mpc->pc_linkmax);
+               pc->pc_namemax = fxdr_unsigned(long, mpc->pc_namemax);
+               pc->pc_chownrestricted = fxdr_unsigned(long, mpc->pc_chownrestricted);
+               pc->pc_notrunc = fxdr_unsigned(long, mpc->pc_notrunc);
+               pc->pc_caseinsensitive = fxdr_unsigned(long, mpc->pc_caseinsensitive);
+               pc->pc_casepreserving = fxdr_unsigned(long, mpc->pc_casepreserving);
+       }
+       nfsm_reqdone;
+
        return (error);
 }
 
+void
+nfs_pathconf_cache(struct nfsmount *nmp, struct nfsv3_pathconf *pc)
+{
+       nmp->nm_state |= NFSSTA_GOTPATHCONF;
+       nmp->nm_fsinfo.linkmax = pc->pc_linkmax;
+       nmp->nm_fsinfo.namemax = pc->pc_namemax;
+       nmp->nm_fsinfo.pcflags = 0;
+       if (pc->pc_notrunc)
+               nmp->nm_fsinfo.pcflags |= NFSPCINFO_NOTRUNC;
+       if (pc->pc_chownrestricted)
+               nmp->nm_fsinfo.pcflags |= NFSPCINFO_CHOWN_RESTRICTED;
+       if (pc->pc_caseinsensitive)
+               nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_INSENSITIVE;
+       if (pc->pc_casepreserving)
+               nmp->nm_fsinfo.pcflags |= NFSPCINFO_CASE_PRESERVING;
+}
+
 /*
  * Return POSIX pathconf information applicable to nfs.
  *
@@ -3367,434 +4232,207 @@ done:
 /* ARGSUSED */
 static int
 nfs_pathconf(ap)
-       struct vop_pathconf_args /* {
-               struct vnode *a_vp;
+       struct vnop_pathconf_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                int a_name;
-               int *a_retval;
-       } */ *ap;
-{
-
-       return (EINVAL);
-}
-
-/*
- * NFS advisory byte-level locks.
- * Currently unsupported.
- */
-static int
-nfs_advlock(ap)
-       struct vop_advlock_args /* {
-               struct vnode *a_vp;
-               caddr_t  a_id;
-               int  a_op;
-               struct flock *a_fl;
-               int  a_flags;
-       } */ *ap;
-{
-#ifdef __FreeBSD__
-       register struct nfsnode *np = VTONFS(ap->a_vp);
-
-       /*
-        * The following kludge is to allow diskless support to work
-        * until a real NFS lockd is implemented. Basically, just pretend
-        * that this is a local lock.
-        */
-       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
-}
-
-/*
- * Print out the contents of an nfsnode.
- */
-static int
-nfs_print(ap)
-       struct vop_print_args /* {
-               struct vnode *a_vp;
-       } */ *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);
-}
-
-/*
- * NFS directory offset lookup.
- * Currently unsupported.
- */
-static int
-nfs_blkatoff(ap)
-       struct vop_blkatoff_args /* {
-               struct vnode *a_vp;
-               off_t a_offset;
-               char **a_res;
-               struct buf **a_bpp;
-       } */ *ap;
-{
-
-#if DIAGNOSTIC
-       printf("nfs_blkatoff: unimplemented!!");
-#endif
-       return (EOPNOTSUPP);
-}
-
-/*
- * NFS flat namespace allocation.
- * Currently unsupported.
- */
-static int
-nfs_valloc(ap)
-       struct vop_valloc_args /* {
-               struct vnode *a_pvp;
-               int a_mode;
-               struct ucred *a_cred;
-               struct vnode **a_vpp;
-       } */ *ap;
-{
-
-       return (EOPNOTSUPP);
-}
-
-/*
- * NFS flat namespace free.
- * Currently unsupported.
- */
-static int
-nfs_vfree(ap)
-       struct vop_vfree_args /* {
-               struct vnode *a_pvp;
-               ino_t a_ino;
-               int a_mode;
-       } */ *ap;
-{
-
-#if DIAGNOSTIC
-       printf("nfs_vfree: unimplemented!!");
-#endif
-       return (EOPNOTSUPP);
-}
-
-/*
- * NFS file truncation.
- */
-static int
-nfs_truncate(ap)
-       struct vop_truncate_args /* {
-               struct vnode *a_vp;
-               off_t a_length;
-               int a_flags;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *ap;
-{
-
-       /* Use nfs_setattr */
-#if DIAGNOSTIC
-       printf("nfs_truncate: unimplemented!!");
-#endif
-       return (EOPNOTSUPP);
-}
-
-/*
- * NFS update.
- */
-static int
-nfs_update(ap)
-       struct vop_update_args /* {
-               struct vnode *a_vp;
-               struct timeval *a_ta;
-               struct timeval *a_tm;
-               int a_waitfor;
+               register_t *a_retval;
+               vfs_context_t a_context;
        } */ *ap;
 {
+       vnode_t vp = ap->a_vp;
+       struct nfsmount *nmp;
+       struct nfsv3_pathconf pc;
+       int error = 0, cached;
 
-       /* Use nfs_setattr */
-#if DIAGNOSTIC
-       printf("nfs_update: unimplemented!!");
-#endif
-       return (EOPNOTSUPP);
-}
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp)
+               return (ENXIO);
+       if (!NFS_ISV3(vp))
+               return (EINVAL);
 
-int                            nfs_aio_threads = 0; /* 1 per nfd (arbitrary) */
-struct slock                   nfs_aio_slock;
-TAILQ_HEAD(bqueues, buf)       nfs_aio_bufq;
-int                            nfs_aio_bufq_len = 0; /* diagnostic only */
+       switch (ap->a_name) {
+       case _PC_LINK_MAX:
+       case _PC_NAME_MAX:
+       case _PC_CHOWN_RESTRICTED:
+       case _PC_NO_TRUNC:
+       case _PC_CASE_SENSITIVE:
+       case _PC_CASE_PRESERVING:
+               break;
+       default:
+               /* don't bother contacting the server if we know the answer */
+               return (EINVAL);
+       }
 
-void
-nfs_aio_thread()
-{      /* see comment below in nfs_bwrite() for some rationale */
-       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 { /* nothing to do - goodnight */
-                       assert_wait(&nfs_aio_bufq, THREAD_UNINT);
-                       simple_unlock(&nfs_aio_slock);
-                       (void)tsleep((caddr_t)0, PRIBIO+1, "nfs_aio_bufq", 0);
+       if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
+               /* no pathconf info cached */
+               kauth_cred_t cred = vfs_context_ucred(ap->a_context);
+               proc_t p = vfs_context_proc(ap->a_context);
+               error = nfs_pathconfrpc(vp, &pc, cred, p);
+               if (error)
+                       return (error);
+               nmp = VFSTONFS(vnode_mount(vp));
+               if (!nmp)
+                       return (ENXIO);
+               if (!(nmp->nm_state & NFSSTA_GOTFSINFO)) {
+                       nfs_fsinfo(nmp, vp, cred, p);
+                       nmp = VFSTONFS(vnode_mount(vp));
+                       if (!nmp)
+                               return (ENXIO);
+               }
+               if ((nmp->nm_state & NFSSTA_GOTFSINFO) &&
+                   (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS)) {
+                       /* all files have the same pathconf info, */
+                       /* so cache a copy of the results */
+                       nfs_pathconf_cache(nmp, &pc);
                }
        }
-       (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);
+       cached = (nmp->nm_state & NFSSTA_GOTPATHCONF);
+
+       switch (ap->a_name) {
+       case _PC_LINK_MAX:
+               *ap->a_retval = cached ? nmp->nm_fsinfo.linkmax : pc.pc_linkmax;
+               break;
+       case _PC_NAME_MAX:
+               *ap->a_retval = cached ? nmp->nm_fsinfo.namemax : pc.pc_namemax;
+               break;
+       case _PC_CHOWN_RESTRICTED:
+               if (cached)
+                       *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CHOWN_RESTRICTED) ? 1 : 0;
+               else
+                       *ap->a_retval = pc.pc_chownrestricted;
+               break;
+       case _PC_NO_TRUNC:
+               if (cached)
+                       *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_NOTRUNC) ? 1 : 0;
+               else
+                       *ap->a_retval = pc.pc_notrunc;
+               break;
+       case _PC_CASE_SENSITIVE:
+               if (cached)
+                       *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_INSENSITIVE) ? 0 : 1;
+               else
+                       *ap->a_retval = !pc.pc_caseinsensitive;
+               break;
+       case _PC_CASE_PRESERVING:
+               if (cached)
+                       *ap->a_retval = (nmp->nm_fsinfo.pcflags & NFSPCINFO_CASE_PRESERVING) ? 1 : 0;
+               else
+                       *ap->a_retval = pc.pc_casepreserving;
+               break;
+       default:
+               error = EINVAL;
        }
-       kernel_thread(kernel_task, nfs_aio_thread);
-}
 
+       return (error);
+}
 
 /*
- * Just call nfs_writebp() with the force argument set to 1.
+ * NFS advisory byte-level locks (client)
  */
 static int
-nfs_bwrite(ap)
-       struct vop_bwrite_args /* {
-               struct vnode *a_bp;
+nfs_advlock(ap)
+       struct vnop_advlock_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               caddr_t a_id;
+               int a_op;
+               struct flock *a_fl;
+               int a_flags;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       extern void wakeup_one(caddr_t chan);
-
-       /*
-        * nfs_writebp will issue a synchronous rpc to if B_ASYNC then
-        * to avoid distributed deadlocks we handoff the write to the
-        * nfs_aio threads.  Doing so allows us to complete the
-        * current request, rather than blocking on a server which may
-        * be ourself (or blocked on ourself).
-        *
-        * Note the loopback deadlocks happened when the thread
-        * invoking us was nfsd, and also when it was the pagedaemon.
-        *
-        * This solution has one known problem.  If *ALL* buffers get
-        * on the nfs_aio queue then no forward progress can be made
-        * until one of those writes complete.  And if the current
-        * nfs_aio writes-in-progress block due to a non-responsive server we
-        * are in a deadlock circle.  Probably the cure is to limit the
-        * async write concurrency in getnewbuf as in FreeBSD 3.2.
-        */
-       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));
+       return (nfs_dolock(ap));
 }
 
 /*
- * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless
- * the force flag is one and it also handles the B_NEEDCOMMIT flag.
+ * write (or commit) the given NFS buffer
  */
 int
-nfs_writebp(bp, force)
-       register struct buf *bp;
-       int force;
+nfs_buf_write(struct nfsbuf *bp)
 {
-       int s;
-       register int oldflags = bp->b_flags, retv = 1;
-       off_t off;
-       upl_t upl;
-       void * object;
-       kern_return_t kret;
-       struct vnode *vp = bp->b_vp;
-       upl_page_info_t *pl;
+       int oldflags = bp->nb_flags, rv = 0;
+       vnode_t vp = bp->nb_vp;
+       struct nfsnode *np = VTONFS(vp);
+       kauth_cred_t cr;
+       proc_t p = current_proc(); // XXX
 
-       if(!ISSET(bp->b_flags, B_BUSY))
-               panic("nfs_writebp: buffer is not busy???");
+       FSDBG_TOP(553, bp, NBOFF(bp), bp->nb_flags, 0);
 
-       s = splbio();
-       CLR(bp->b_flags, (B_READ|B_DONE|B_ERROR|B_DELWRI));
+       if (!ISSET(bp->nb_lflags, NBL_BUSY))
+               panic("nfs_buf_write: buffer is not busy???");
 
-       if (ISSET(oldflags, (B_ASYNC|B_DELWRI))) {
-               reassignbuf(bp, vp);
+       CLR(bp->nb_flags, (NB_READ|NB_DONE|NB_ERROR|NB_DELWRI));
+       if (ISSET(oldflags, NB_DELWRI)) {
+               OSAddAtomic(-1, (SInt32*)&nfs_nbdwrite);
+               NFSBUFCNTCHK(0);
+               wakeup(&nfs_nbdwrite);
        }
 
-       vp->v_numoutput++;
-       current_proc()->p_stats->p_ru.ru_oublock++;
-       splx(s);
-        
-        /* 
-         * Since the B_BUSY flag is set, we need to lock the page before doing nfs_commit.
-         * Otherwise we may block and get a busy incore pages during a vm pageout.
-         * Move the existing code up before the commit.
-         */
-
-        if (!ISSET(bp->b_flags, B_META) && UBCISVALID(vp)) {
-    
-            if (!ISSET(bp->b_flags, B_PAGELIST)) {
-
-                               off_t file_offset = ubc_blktooff(vp, bp->b_lblkno);
-
-                               object = ubc_getobject(vp, (UBC_NOREACTIVATE|UBC_HOLDOBJECT));
-                               if (object == (void*)NULL)
-                                       panic("nfs_writebp: NULL vmobject");
+       /* move to clean list */
+       if (ISSET(oldflags, (NB_ASYNC|NB_DELWRI))) {
+               lck_mtx_lock(nfs_buf_mutex);
+               if (bp->nb_vnbufs.le_next != NFSNOLIST)
+                       LIST_REMOVE(bp, nb_vnbufs);
+               LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
+               lck_mtx_unlock(nfs_buf_mutex);
+       }
+       vnode_startwrite(vp);
 
-                               if(bp->b_bufsize & 0xfff)
-                                       panic("nfs_writebp: list request is with less than 4k");
-       
-                               kret = vm_fault_list_request(object, (vm_object_offset_t)file_offset, 
-                                                       bp->b_bufsize, &upl, NULL, 0, 
-                                                       (int)(UPL_NO_SYNC | UPL_CLEAN_IN_PLACE | UPL_PRECIOUS | UPL_SET_INTERNAL));
-                               if (kret != KERN_SUCCESS) {
-                                       panic("nfs_writebp: get pagelists failed with (%d)", kret);
-                               }
-                    
-#ifdef UBC_DEBUG
-                    upl_ubc_alias_set(pl, ioaddr, 2);
-#endif /* UBC_DEBUG */
-
-                    s = splbio();
-
-                    pl = UPL_GET_INTERNAL_PAGE_LIST(upl);
-                    bp->b_pagelist = upl;
-                    SET(bp->b_flags, B_PAGELIST);
-                    splx(s);
-                    
-                    kret = kernel_upl_map(kernel_map, upl, 
-                            (vm_address_t *)&(bp->b_data));
-                    if (kret != KERN_SUCCESS) {
-                            panic("nfs_writebp: kernel_upl_map() failed with (%d)", kret);
-                    }
-                    if(bp->b_data == 0) 
-                            panic("nfs_writebp: upl_map mapped 0");
-                    if (!upl_page_present(pl, 0)) {
-                            /* 
-                                * may be the page got paged out.
-                                * let's just read it in. It is marked
-                                * busy so we should not have any one
-                                * yanking this page underneath the fileIO
-                                */
-                            panic("nfs_writebp: nopage");
-                    }
-            }
-        }
+       if (p && p->p_stats)
+               p->p_stats->p_ru.ru_oublock++;
 
        /*
-        * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not
-        * an actual write will have to be scheduled via. VOP_STRATEGY().
-        * If B_WRITEINPROG is already set, then push it with a write anyhow.
+        * For async requests when nfsiod(s) are running, queue the request by
+        * calling nfs_asyncio(), otherwise just all nfs_doio() to do the request.
         */
-       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);  /* on B_ASYNC will brelse the buffer */
-                        
-               } 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);
+       if (ISSET(bp->nb_flags, NB_ASYNC))
+               p = NULL;
+       if (ISSET(bp->nb_flags, NB_READ))
+               cr = bp->nb_rcred;
+       else
+               cr = bp->nb_wcred;
+       if (!ISSET(bp->nb_flags, NB_ASYNC) || nfs_asyncio(bp, NOCRED))
+               rv = nfs_doio(bp, cr, p);
+
+       if ((oldflags & NB_ASYNC) == 0) {
+               rv = nfs_buf_iowait(bp);
+               /* move to clean list */
+               if (oldflags & NB_DELWRI) {
+                       lck_mtx_lock(nfs_buf_mutex);
+                       if (bp->nb_vnbufs.le_next != NFSNOLIST)
+                               LIST_REMOVE(bp, nb_vnbufs);
+                       LIST_INSERT_HEAD(&VTONFS(vp)->n_cleanblkhd, bp, nb_vnbufs);
+                       lck_mtx_unlock(nfs_buf_mutex);
+               }
+               oldflags = bp->nb_flags;
+               FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
+               if (cr) {
+                       kauth_cred_ref(cr);
+               }
+               nfs_buf_release(bp, 1);
+               if (ISSET(oldflags, NB_ERROR) && !(np->n_flag & NFLUSHINPROG)) {
+                       /*
+                        * There was a write error and we need to
+                        * invalidate attrs and flush buffers in
+                        * order to sync up with the server.
+                        * (if this write was extending the file,
+                        * we may no longer know the correct size)
+                        *
+                        * But we couldn't call vinvalbuf while holding
+                        * the buffer busy.  So we call vinvalbuf() after
+                        * releasing the buffer.
+                        */
+                       nfs_vinvalbuf(vp, V_SAVE|V_IGNORE_WRITEERR, cr, p, 1);
                }
-               brelse(bp);
-               return (rtval);
+               if (cr)
+                       kauth_cred_rele(cr);
+               return (rv);
        } 
 
-       return (0);
-}
-
-/*
- * nfs special file access vnode op.
- * Essentially just get vattr and then imitate iaccess() since the device is
- * local to the client.
- */
-static int
-nfsspec_access(ap)
-       struct vop_access_args /* {
-               struct vnode *a_vp;
-               int  a_mode;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *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;
-
-       /*
-        * Disallow write attempts on filesystems mounted read-only;
-        * unless the file is a socket, fifo, or a block or character
-        * device resident on the filesystem.
-        */
-       if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
-               switch (vp->v_type) {
-               case VREG: case VDIR: case VLNK:
-                       return (EROFS);
-               }
-       }
-       /*
-        * If you're the super-user,
-        * you always get access.
-        */
-       if (cred->cr_uid == 0)
-               return (0);
-       vap = &vattr;
-       error = VOP_GETATTR(vp, vap, cred, ap->a_p);
-       if (error)
-               return (error);
-       /*
-        * Access check is based on only one of owner, group, public.
-        * If not owner, then check group. If not a member of the
-        * group, then check public access.
-        */
-       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);
+       FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, rv);
+       return (rv);
 }
 
 /*
@@ -3802,22 +4440,25 @@ found:
  */
 static int
 nfsspec_read(ap)
-       struct vop_read_args /* {
-               struct vnode *a_vp;
+       struct vnop_read_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               int  a_ioflag;
-               struct ucred *a_cred;
+               int a_ioflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
        register struct nfsnode *np = VTONFS(ap->a_vp);
+       struct timeval now;
 
        /*
         * Set access flag.
         */
        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));
+       microtime(&now);
+       np->n_atim.tv_sec = now.tv_sec;
+       np->n_atim.tv_nsec = now.tv_usec * 1000;
+       return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap));
 }
 
 /*
@@ -3825,22 +4466,25 @@ nfsspec_read(ap)
  */
 static int
 nfsspec_write(ap)
-       struct vop_write_args /* {
-               struct vnode *a_vp;
+       struct vnop_write_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               int  a_ioflag;
-               struct ucred *a_cred;
+               int a_ioflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
        register struct nfsnode *np = VTONFS(ap->a_vp);
+       struct timeval now;
 
        /*
         * Set update flag.
         */
        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));
+       microtime(&now);
+       np->n_mtim.tv_sec = now.tv_sec;
+       np->n_mtim.tv_nsec = now.tv_usec * 1000;
+       return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap));
 }
 
 /*
@@ -3850,54 +4494,62 @@ nfsspec_write(ap)
  */
 static int
 nfsspec_close(ap)
-       struct vop_close_args /* {
-               struct vnode *a_vp;
-               int  a_fflag;
-               struct ucred *a_cred;
-               struct proc *a_p;
+       struct vnop_close_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_fflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-       register struct nfsnode *np = VTONFS(vp);
-       struct vattr vattr;
+       vnode_t vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct vnode_attr vattr;
+       mount_t mp;
 
        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);
+               if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
+                       VATTR_INIT(&vattr);
+                       if (np->n_flag & NACC) {
+                               vattr.va_access_time = np->n_atim;
+                               VATTR_SET_ACTIVE(&vattr, va_access_time);
+                       }
+                       if (np->n_flag & NUPD) {
+                               vattr.va_modify_time = np->n_mtim;
+                               VATTR_SET_ACTIVE(&vattr, va_modify_time);
+                       }
+                       vnode_setattr(vp, &vattr, ap->a_context);
                }
        }
-       return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
+       return (VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap));
 }
 
+extern vnop_t **fifo_vnodeop_p;
+
 /*
  * Read wrapper for fifos.
  */
 static int
 nfsfifo_read(ap)
-       struct vop_read_args /* {
-               struct vnode *a_vp;
+       struct vnop_read_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               int  a_ioflag;
-               struct ucred *a_cred;
+               int a_ioflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-        extern vop_t **fifo_vnodeop_p;
        register struct nfsnode *np = VTONFS(ap->a_vp);
+       struct timeval now;
 
        /*
         * Set access flag.
         */
        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));
+       microtime(&now);
+       np->n_atim.tv_sec = now.tv_sec;
+       np->n_atim.tv_nsec = now.tv_usec * 1000;
+       return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_read), ap));
 }
 
 /*
@@ -3905,23 +4557,25 @@ nfsfifo_read(ap)
  */
 static int
 nfsfifo_write(ap)
-       struct vop_write_args /* {
-               struct vnode *a_vp;
+       struct vnop_write_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
                struct uio *a_uio;
-               int  a_ioflag;
-               struct ucred *a_cred;
+               int a_ioflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-        extern vop_t **fifo_vnodeop_p;
        register struct nfsnode *np = VTONFS(ap->a_vp);
+       struct timeval now;
 
        /*
         * Set update flag.
         */
        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));
+       microtime(&now);
+       np->n_mtim.tv_sec = now.tv_sec;
+       np->n_mtim.tv_nsec = now.tv_usec * 1000;
+       return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_write), ap));
 }
 
 /*
@@ -3931,44 +4585,58 @@ nfsfifo_write(ap)
  */
 static int
 nfsfifo_close(ap)
-       struct vop_close_args /* {
-               struct vnode *a_vp;
-               int  a_fflag;
-               struct ucred *a_cred;
-               struct proc *a_p;
+       struct vnop_close_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_fflag;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
-       register struct nfsnode *np = VTONFS(vp);
-       struct vattr vattr;
-        extern vop_t **fifo_vnodeop_p;
+       vnode_t vp = ap->a_vp;
+       struct nfsnode *np = VTONFS(vp);
+       struct vnode_attr vattr;
+       struct timeval now;
+       mount_t mp;
 
        if (np->n_flag & (NACC | NUPD)) {
+               microtime(&now);
                if (np->n_flag & NACC) {
-                       np->n_atim.tv_sec = time.tv_sec;
-                       np->n_atim.tv_nsec = time.tv_usec * 1000;
+                       np->n_atim.tv_sec = now.tv_sec;
+                       np->n_atim.tv_nsec = now.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_mtim.tv_sec = now.tv_sec;
+                       np->n_mtim.tv_nsec = now.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);
+               if (!vnode_isinuse(vp, 1) && (mp = vnode_mount(vp)) && !vfs_isrdonly(mp)) {
+                       VATTR_INIT(&vattr);
+                       if (np->n_flag & NACC) {
+                               vattr.va_access_time = np->n_atim;
+                               VATTR_SET_ACTIVE(&vattr, va_access_time);
+                       }
+                       if (np->n_flag & NUPD) {
+                               vattr.va_modify_time = np->n_mtim;
+                               VATTR_SET_ACTIVE(&vattr, va_modify_time);
+                       }
+                       vnode_setattr(vp, &vattr, ap->a_context);
                }
        }
-       return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
+       return (VOCALL(fifo_vnodeop_p, VOFFSET(vnop_close), ap));
 }
 
+/*ARGSUSED*/
 static int
-nfs_ioctl(ap)
-       struct vop_ioctl_args *ap;
+nfs_ioctl(
+       __unused struct vnop_ioctl_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               u_long a_command;
+               caddr_t a_data;
+               int a_fflag;
+               kauth_cred_t a_cred;
+               proc_t a_p;
+       } */ *ap)
 {
 
        /*
@@ -3978,9 +4646,18 @@ nfs_ioctl(ap)
        return (ENOTTY);
 }
 
+/*ARGSUSED*/
 static int
-nfs_select(ap)
-       struct vop_select_args *ap;
+nfs_select(
+       __unused struct vnop_select_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               int a_which;
+               int a_fflags;
+               kauth_cred_t a_cred;
+               void *a_wql;
+               proc_t a_p;
+       } */ *ap)
 {
 
        /*
@@ -3989,7 +4666,6 @@ nfs_select(ap)
        return (1);
 }
 
-/* XXX Eliminate use of struct bp here */
 /*
  * Vnode op for pagein using getblk_pages
  * derived from nfs_bioread()
@@ -3997,273 +4673,158 @@ nfs_select(ap)
  */
 static int
 nfs_pagein(ap)
-       struct vop_pagein_args /* {
-               struct vnode *a_vp,
-               upl_t   a_pl,
-               vm_offset_t   a_pl_offset,
-               off_t         a_f_offset,
-               size_t        a_size,
-               struct ucred *a_cred,
-               int           a_flags
+       struct vnop_pagein_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               upl_t a_pl;
+               vm_offset_t a_pl_offset;
+               off_t a_f_offset;
+               size_t a_size;
+               int a_flags;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
+       vnode_t 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 xsize;
-       struct vattr vattr;
-       struct proc *p = current_proc();
-       struct nfsmount *nmp = VFSTONFS(vp->v_mount);
+       kauth_cred_t cred;
+       proc_t p;
+       struct nfsnode *np = VTONFS(vp);
+       int biosize, xsize, iosize;
+       struct nfsmount *nmp;
        int error = 0;
        vm_offset_t ioaddr;
        struct uio      auio;
-       struct iovec    aiov;
+       struct iovec_32 aiov;
        struct uio * uio = &auio;
-       int nocommit = flags & UPL_NOCOMMIT;
+       int nofreeupl = flags & UPL_NOCOMMIT;
+       upl_page_info_t *plinfo;
 
-       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 322)) | DBG_FUNC_NONE,
-                    (int)f_offset, size, pl, pl_offset, 0);
+       FSDBG(322, vp, f_offset, size, flags);
+       if (pl == (upl_t)NULL)
+               panic("nfs_pagein: no upl");
 
        if (UBCINVALID(vp)) {
-#if DIAGNOSTIC
-               panic("nfs_pagein: invalid vp");
-#endif /* DIAGNOSTIC */
+               printf("nfs_pagein: invalid vnode 0x%x", (int)vp);
+               if (!nofreeupl)
+                       (void) ubc_upl_abort(pl, 0); 
                return (EPERM);
        }
-
        UBCINFOCHECK("nfs_pagein", vp);
-       if(pl == (upl_t)NULL) {
-               panic("nfs_pagein: no upl");
-       }
-
-       cred = ubc_getcred(vp);
-       if (cred == NOCRED)
-               cred = ap->a_cred;
 
-       if (size <= 0)
+       if (size <= 0) {
+               printf("nfs_pagein: invalid size %d", size);
+               if (!nofreeupl)
+                       (void) ubc_upl_abort(pl, 0); 
                return (EINVAL);
-
-       if (f_offset < 0 || f_offset >= np->n_size 
-                                       || (f_offset & PAGE_MASK_64)) {
-               if (!nocommit)
-                       kernel_upl_abort_range(pl, pl_offset, size, 
+       }
+       if (f_offset < 0 || f_offset >= (off_t)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);
        }
 
-       auio.uio_iov = &aiov;
-       auio.uio_iovcnt = 1;
+       cred = ubc_getcred(vp);
+       if (cred == NOCRED)
+               cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
+
        auio.uio_offset = f_offset;
+#if 1   /* LP64todo - can't use new segment flags until the drivers are ready */
        auio.uio_segflg = UIO_SYSSPACE;
+#else
+       auio.uio_segflg = UIO_SYSSPACE32;
+#endif 
        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);
+       auio.uio_procp = p;
 
-#if 0 /* Why bother? */
-/* DO NOT BOTHER WITH "approximately maintained cache consistency" */
-/* Does not make sense in paging paths -- Umesh*/
-       /*
-        * For nfs, cache consistency can only be maintained approximately.
-        * Although RFC1094 does not specify the criteria, the following is
-        * believed to be compatible with the reference port.
-        * For nqnfs, full cache consistency is maintained within the loop.
-        * For nfs:
-        * If the file's modify time on the server has changed since the
-        * last read rpc or you have written to the file,
-        * you may have lost data cache consistency with the
-        * server, so flush all of the file's data out of the cache.
-        * Then force a getattr rpc to ensure that you have up to date
-        * attributes.
-        * NB: This implies that cache data can be read when up to
-        * NFS_ATTRTIMEO seconds out of date. If you find that you need current
-        * attributes this could be forced by setting n_attrstamp to 0 before
-        * the VOP_GETATTR() call.
-        */
-       if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) {
-               if (np->n_flag & NMODIFIED) {
-                       np->n_attrstamp = 0;
-                       error = VOP_GETATTR(vp, &vattr, cred, p);
-                       if (error) {
-                               if (!nocommit)
-                                       kernel_upl_abort_range(pl, pl_offset, 
-                                               size, 
-                                               UPL_ABORT_ERROR |
-                                               UPL_ABORT_FREE_ON_EMPTY);
-                               return (error);
-                       }
-                       np->n_mtime = vattr.va_mtime.tv_sec;
-               } else {
-                       error = VOP_GETATTR(vp, &vattr, cred, p);
-                       if (error){
-                               if (!nocommit)
-                                       kernel_upl_abort_range(pl, pl_offset, size,  
-                                               UPL_ABORT_ERROR |
-                                               UPL_ABORT_FREE_ON_EMPTY);
-                               return (error);
-                       }
-                       if (np->n_mtime != vattr.va_mtime.tv_sec) {
-                               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
-                               if (error){
-                                       if (!nocommit)
-                                               kernel_upl_abort_range(pl, pl_offset, size, 
-                                                               UPL_ABORT_ERROR |
-                                                               UPL_ABORT_FREE_ON_EMPTY);
-                                       return (error);
-                               }
-                               np->n_mtime = vattr.va_mtime.tv_sec;
-                       }
-               }
+       nmp = VFSTONFS(vnode_mount(vp));
+       if (!nmp) {
+               if (!nofreeupl)
+                       ubc_upl_abort_range(pl, pl_offset, size, 
+                               UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
+               return (ENXIO);
        }
-#endif 0 /* Why bother? */
+       if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
+               (void)nfs_fsinfo(nmp, vp, cred, p);
+       biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
 
-       kernel_upl_map(kernel_map, pl, &ioaddr);
+       plinfo = ubc_upl_pageinfo(pl);
+       ubc_upl_map(pl, &ioaddr);
        ioaddr += pl_offset;
        xsize = size;
 
        do {
-               uio->uio_resid = min(biosize, xsize);
-               aiov.iov_len  = uio->uio_resid;
-               aiov.iov_base = (caddr_t)ioaddr;
-
-               KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 322)) | DBG_FUNC_NONE,
-                       (int)uio->uio_offset, uio->uio_resid, ioaddr, xsize, 0);
-
-#warning nfs_pagein does not support NQNFS yet.
-#if 0 /* why bother? */
-/* NO RESOURCES TO FIX NQNFS CASE */
-/* We need to deal with this later -- Umesh */
                /*
-                * Get a valid lease. If cached data is stale, flush it.
+                * It would be nice to be able to issue all these requests
+                * in parallel instead of waiting for each one to complete
+                * before sending the next one.
+                * XXX Should we align these requests to block boundaries?
                 */
-               if (nmp->nm_flag & NFSMNT_NQNFS) {
-                       if (NQNFS_CKINVALID(vp, np, ND_READ)) {
-                               do {
-                                       error = nqnfs_getlease(vp, ND_READ, cred, p);
-                               } while (error == NQNFS_EXPIRED);
-                               if (error){
-                                       kernel_upl_unmap(kernel_map, pl);
-                                       if (!nocommit)
-                                               kernel_upl_abort_range(pl, pl_offset,
-                                                       size ,UPL_ABORT_ERROR |
-                                               UPL_ABORT_FREE_ON_EMPTY);
-
-                                       return (error);
-                               }
-                               if (np->n_lrev != np->n_brev ||
-                                       (np->n_flag & NQNFSNONCACHE)) {
-                                       error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
-                                       if (error) {
-                                               kernel_upl_unmap(kernel_map, pl);
-                                       if (!nocommit)
-                                               kernel_upl_abort_range(pl,
-                                                               pl_offset,size ,
-                                                               UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
-                                               return (error);
-                                       }
-                                       np->n_brev = np->n_lrev;
-                               }
-                       }
-               }
-#endif 0 /* why bother? */
-
-               if (np->n_flag & NQNFSNONCACHE) {
-                       error = nfs_readrpc(vp, uio, cred);
-                       kernel_upl_unmap(kernel_map, pl);
-
-                       if (!nocommit) {
-                               if(error) 
-                                       kernel_upl_abort_range(pl, pl_offset, size ,
-                                               UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
-                               else
-                                       kernel_upl_commit_range(pl, 
-                                               pl_offset, size, 
-                                               UPL_COMMIT_CLEAR_DIRTY 
-                                                  | UPL_COMMIT_FREE_ON_EMPTY,
-                                               UPL_GET_INTERNAL_PAGE_LIST(pl),
-                                               MAX_UPL_TRANSFER);
-                       }
-                       return (error);
-               }
-
+               iosize = min(biosize, xsize);
+               aiov.iov_len  = iosize;
+               aiov.iov_base = (uintptr_t)ioaddr;
+               auio.uio_iovs.iov32p = &aiov;
+               auio.uio_iovcnt = 1;
+               uio_uio_resid_set(&auio, iosize);
+
+               FSDBG(322, uio->uio_offset, uio_uio_resid(uio), ioaddr, xsize);
                /*
                 * With UBC we get here only when the file data is not in the VM
                 * page cache, so go ahead and read in.
                 */
-#ifdef UBC_DEBUG
-               upl_ubc_alias_set(pl, ioaddr, 2);
-#endif /* UBC_DEBUG */
-               nfsstats.pageins++;
-               error = nfs_readrpc(vp, uio, cred);
+#ifdef UPL_DEBUG
+               upl_ubc_alias_set(pl, current_thread(), 2);
+#endif /* UPL_DEBUG */
+               OSAddAtomic(1, (SInt32*)&nfsstats.pageins);
 
-               if (!error) {
-                       int zoff;
-                       int zcnt;
+               error = nfs_readrpc(vp, uio, cred, p);
 
-                       if (uio->uio_resid) {
+               if (!error) {
+                       if (uio_uio_resid(uio)) {
                                /*
-                                * If uio_resid > 0, there is a hole in the file and
-                                * no writes after the hole have been pushed to
-                                * the server yet... or we're at the EOF
+                                * If uio_resid > 0, there is a hole in the file
+                                * and no writes after the hole have been pushed
+                                * to the server yet... or we're at the EOF
                                 * Just zero fill the rest of the valid area.
                                 */
-                               zcnt = uio->uio_resid;
-                               zoff = biosize - zcnt;
+                               // LP64todo - fix this
+                               int zcnt = uio_uio_resid(uio);
+                               int zoff = iosize - zcnt;
                                bzero((char *)ioaddr + zoff, zcnt);
 
-                               KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 324)) | DBG_FUNC_NONE,
-                                       (int)uio->uio_offset, zoff, zcnt, ioaddr, 0);
-
+                               FSDBG(324, uio->uio_offset, zoff, zcnt, ioaddr);
                                uio->uio_offset += zcnt;
                        }
-                       ioaddr += biosize;      
-                       xsize  -= biosize;
-               } else
-                       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 322)) | DBG_FUNC_NONE,
-                               (int)uio->uio_offset, uio->uio_resid, error, -1, 0);
-
-               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;
+                       ioaddr += iosize;       
+                       xsize  -= iosize;
+               } else {
+                       FSDBG(322, uio->uio_offset, uio_uio_resid(uio), error, -1);
                }
 
+               nmp = VFSTONFS(vnode_mount(vp));
        } while (error == 0 && xsize > 0);
 
-       kernel_upl_unmap(kernel_map, pl);
+       ubc_upl_unmap(pl);
 
-       if (!nocommit) {
+       if (!nofreeupl) {
                if (error) 
-                       kernel_upl_abort_range(pl, pl_offset, size, 
-                               UPL_ABORT_ERROR |  UPL_ABORT_FREE_ON_EMPTY);
+                       ubc_upl_abort_range(pl, pl_offset, size, 
+                                           UPL_ABORT_ERROR |
+                                           UPL_ABORT_FREE_ON_EMPTY);
                else
-                       kernel_upl_commit_range(pl, pl_offset, size,
-                               UPL_COMMIT_CLEAR_DIRTY 
-                                               | UPL_COMMIT_FREE_ON_EMPTY,
-                               UPL_GET_INTERNAL_PAGE_LIST(pl), 
-                               MAX_UPL_TRANSFER);
+                       ubc_upl_commit_range(pl, pl_offset, size,
+                                            UPL_COMMIT_CLEAR_DIRTY |
+                                            UPL_COMMIT_FREE_ON_EMPTY);
        }
-
        return (error);
 }
 
+
 /*
  * Vnode op for pageout using UPL
  * Derived from nfs_write()
@@ -4271,362 +4832,349 @@ nfs_pagein(ap)
  */
 static int
 nfs_pageout(ap)
-       struct vop_pageout_args /* {
-               struct vnode *a_vp,
-               upl_t   a_pl,
-               vm_offset_t   a_pl_offset,
-               off_t         a_f_offset,
-               size_t        a_size,
-               struct ucred *a_cred,
-               int           a_flags
+       struct vnop_pageout_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               upl_t a_pl;
+               vm_offset_t a_pl_offset;
+               off_t a_f_offset;
+               size_t a_size;
+               int a_flags;
+               vfs_context_t a_context;
        } */ *ap;
 {
-       register struct vnode *vp = ap->a_vp;
+       vnode_t 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 bufsize;
-       int n = 0, on, error = 0, iomode, must_commit, s;
+       kauth_cred_t cred;
+       proc_t p;
+       struct nfsbuf *bp;
+       struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
+       daddr64_t lbn;
+       int error = 0, iomode;
        off_t off;
        vm_offset_t ioaddr;
        struct uio      auio;
-       struct iovec    aiov;
-       struct uio * uio = &auio;
-       int nocommit = flags & UPL_NOCOMMIT;
-       int iosize;
-       int pgsize;
+       struct iovec_32 aiov;
+       int nofreeupl = flags & UPL_NOCOMMIT;
+       size_t biosize, iosize, pgsize, xsize;
 
-       KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 323)) | DBG_FUNC_NONE,
-               (int)f_offset, size, pl, pl_offset, 0);
+       FSDBG(323, f_offset, size, pl, pl_offset);
+
+       if (pl == (upl_t)NULL)
+               panic("nfs_pageout: no upl");
 
        if (UBCINVALID(vp)) {
-#if DIAGNOSTIC
-               panic("nfs_pageout: invalid vnode");
-#endif
+               printf("nfs_pageout: invalid vnode 0x%x", (int)vp);
+               if (!nofreeupl)
+                       ubc_upl_abort(pl, 0); 
                return (EIO);
        }
        UBCINFOCHECK("nfs_pageout", vp);
 
-       if (size <= 0)
+       if (size <= 0) {
+               printf("nfs_pageout: invalid size %d", size);
+               if (!nofreeupl)
+                       ubc_upl_abort(pl, 0); 
                return (EINVAL);
-
-       if (pl == (upl_t)NULL) {
-               panic("nfs_pageout: no upl");
        }
 
-       /*
-        * I use nm_rsize, not nm_wsize so that all buffer cache blocks
-        * will be the same size within a filesystem. nfs_writerpc will
-        * still use nm_wsize when sizing the rpc's.
-        */
-        biosize = min(vp->v_mount->mnt_stat.f_iosize, size);
-
-        if (biosize & PAGE_MASK)
-                panic("nfs_pageout(%x): biosize not page aligned", biosize);
-
+       if (!nmp) {
+               if (!nofreeupl)
+                       ubc_upl_abort(pl, UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
+               return (ENXIO);
+       }
+       biosize = vfs_statfs(vnode_mount(vp))->f_iosize;
 
        /*
-        * Check to see whether the buffer is incore
-        * If incore and not busy invalidate it from the cache
-        * we should not find it BUSY, since we always do a 
-        * vm_fault_list_request in 'getblk' before returning
-        * which would block on the page busy status
+        * Check to see whether the buffer is incore.
+        * If incore and not busy, invalidate it from the cache.
         */
-        lbn = f_offset / PAGE_SIZE; /* to match the size getblk uses */
-        
-       for (iosize = size; iosize > 0; iosize -= PAGE_SIZE, lbn++) {
-
-               s = splbio();
-               if (bp = incore(vp, lbn)) {
-                       if (ISSET(bp->b_flags, B_BUSY)) {
-                                /* don't panic incore. just tell vm we are busy */
-                               (void) kernel_upl_abort(pl, NULL); 
-                                return(EBUSY);
-                                };
-
-                       bremfree(bp);
-                       SET(bp->b_flags, (B_BUSY | B_INVAL));
-                       brelse(bp);
+       for (iosize = 0; iosize < size; iosize += xsize) {
+               off = f_offset + iosize;
+               /* need make sure we do things on block boundaries */
+               xsize = biosize - (off % biosize);
+               if (off + xsize > f_offset + size)
+                       xsize = f_offset + size - off;
+               lbn = ubc_offtoblk(vp, off);
+               lck_mtx_lock(nfs_buf_mutex);
+               if ((bp = nfs_buf_incore(vp, lbn))) {
+                       FSDBG(323, off, bp, bp->nb_lflags, bp->nb_flags);
+                       if (nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0)) {
+                               lck_mtx_unlock(nfs_buf_mutex);
+                               /* no panic. just tell vm we are busy */
+                               if (!nofreeupl)
+                                       ubc_upl_abort(pl, 0); 
+                               return (EBUSY);
+                       }
+                       if (bp->nb_dirtyend > 0) {
+                               /*
+                                * if there's a dirty range in the buffer, check
+                                * to see if it extends beyond the pageout region
+                                *
+                                * if the dirty region lies completely within the
+                                * pageout region, we just invalidate the buffer
+                                * because it's all being written out now anyway.
+                                *
+                                * if any of the dirty region lies outside the
+                                * pageout region, we'll try to clip the dirty
+                                * region to eliminate the portion that's being
+                                * paged out.  If that's not possible, because
+                                * the dirty region extends before and after the
+                                * pageout region, then we'll just return EBUSY.
+                                */
+                               off_t boff, start, end;
+                               boff = NBOFF(bp);
+                               start = off;
+                               end = off + xsize;
+                               /* clip end to EOF */
+                               if (end > (off_t)np->n_size)
+                                       end = np->n_size;
+                               start -= boff;
+                               end -= boff;
+                               if ((bp->nb_dirtyoff < start) &&
+                                   (bp->nb_dirtyend > end)) {
+                                   /* not gonna be able to clip the dirty region */
+                                   FSDBG(323, vp, bp, 0xd00deebc, EBUSY);
+                                   nfs_buf_drop(bp);
+                                   lck_mtx_unlock(nfs_buf_mutex);
+                                   if (!nofreeupl)
+                                       ubc_upl_abort(pl, 0); 
+                                   return (EBUSY);
+                               }
+                               if ((bp->nb_dirtyoff < start) ||
+                                   (bp->nb_dirtyend > end)) {
+                                   /* clip dirty region, if necessary */
+                                   if (bp->nb_dirtyoff < start)
+                                       bp->nb_dirtyend = min(bp->nb_dirtyend, start);
+                                   if (bp->nb_dirtyend > end)
+                                       bp->nb_dirtyoff = max(bp->nb_dirtyoff, end);
+                                   FSDBG(323, bp, bp->nb_dirtyoff, bp->nb_dirtyend, 0xd00dee00);
+                                   /* we're leaving this block dirty */
+                                   nfs_buf_drop(bp);
+                                   lck_mtx_unlock(nfs_buf_mutex);
+                                   continue;
+                               }
+                       }
+                       nfs_buf_remfree(bp);
+                       lck_mtx_unlock(nfs_buf_mutex);
+                       SET(bp->nb_flags, NB_INVAL);
+                       if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
+                               CLR(bp->nb_flags, NB_NEEDCOMMIT);
+                               np->n_needcommitcnt--;
+                               CHECK_NEEDCOMMITCNT(np);
+                       }
+                       nfs_buf_release(bp, 1);
+               } else {
+                       lck_mtx_unlock(nfs_buf_mutex);
                }
-               splx(s);
        }
 
        cred = ubc_getcred(vp);
        if (cred == NOCRED)
-               cred = ap->a_cred;
+               cred = vfs_context_ucred(ap->a_context);
+       p = vfs_context_proc(ap->a_context);
 
        if (np->n_flag & NWRITEERR) {
                np->n_flag &= ~NWRITEERR;
-               if (!nocommit)
-                       kernel_upl_abort_range(pl, pl_offset, size, 
-                               UPL_ABORT_FREE_ON_EMPTY);
+               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 (!nocommit)
-                       kernel_upl_abort_range(pl, pl_offset, size, 
-                               UPL_ABORT_FREE_ON_EMPTY);
+       if ((nmp->nm_flag & NFSMNT_NFSV3) && !(nmp->nm_state & NFSSTA_GOTFSINFO))
+               nfs_fsinfo(nmp, vp, cred, p);
+
+       if (f_offset < 0 || f_offset >= (off_t)np->n_size ||
+           f_offset & PAGE_MASK_64 || size & PAGE_MASK_64) {
+               if (!nofreeupl)
+                       ubc_upl_abort_range(pl, pl_offset, size,
+                                           UPL_ABORT_FREE_ON_EMPTY);
                return (EINVAL);
        }
 
-       kernel_upl_map(kernel_map, pl, &ioaddr);
+       ubc_upl_map(pl, &ioaddr);
+       ioaddr += pl_offset;
 
-       if ((f_offset + size) > np->n_size)
-               iosize = np->n_size - f_offset;
+       if ((u_quad_t)f_offset + size > np->n_size)
+               xsize = np->n_size - f_offset;
        else
-               iosize = size;
-
-       pgsize = (iosize + (PAGE_SIZE - 1)) & ~PAGE_MASK;
+               xsize = size;
 
+       pgsize = round_page_64(xsize);
        if (size > pgsize) {
-               if (!nocommit)
-                       kernel_upl_abort_range(pl, pl_offset + pgsize, size - pgsize,
-                               UPL_ABORT_FREE_ON_EMPTY);
+               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;
 
        /* 
         * check for partial page and clear the
         * contents past end of the file before
         * releasing it in the VM page cache
         */
-       if ((f_offset < np->n_size) && (f_offset + size) > np->n_size) {
+       if ((u_quad_t)f_offset < np->n_size && (u_quad_t)f_offset + size > np->n_size) {
                size_t io = np->n_size - f_offset;
-
-               bzero((caddr_t)(ioaddr + pl_offset + io), size - io);
-
-               KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 321)) | DBG_FUNC_NONE,
-                       (int)np->n_size, (int)f_offset, (int)f_offset + io, size - io, 0);
+               bzero((caddr_t)(ioaddr + io), size - io);
+               FSDBG(321, np->n_size, f_offset, f_offset + io, size - io);
        }
 
-       do {
-
-#warning nfs_pageout does not support NQNFS yet.
-#if 0 /* why bother? */
-/* NO RESOURCES TO FIX NQNFS CASE */
-/* We need to deal with this later -- Umesh */
+       auio.uio_offset = f_offset;
+#if 1   /* LP64todo - can't use new segment flags until the drivers are ready */
+       auio.uio_segflg = UIO_SYSSPACE;
+#else
+       auio.uio_segflg = UIO_SYSSPACE32;
+#endif 
+       auio.uio_rw = UIO_READ;
+       auio.uio_procp = p;
 
+       do {
                /*
-                * Check for a valid write lease.
+                * It would be nice to be able to issue all these requests
+                * in parallel instead of waiting for each one to complete
+                * before sending the next one.
+                * XXX Should we align these requests to block boundaries?
                 */
-               if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-                   NQNFS_CKINVALID(vp, np, ND_WRITE)) {
-                       do {
-                               error = nqnfs_getlease(vp, ND_WRITE, cred, p);
-                       } while (error == NQNFS_EXPIRED);
-                       if (error) {
-                               kernel_upl_unmap(kernel_map, pl);
-                               if (!nocommit)
-                                       kernel_upl_abort_range(pl, pl_offset, size, 
-                                               UPL_ABORT_FREE_ON_EMPTY);
-                               return (error);
-                       }
-                       if (np->n_lrev != np->n_brev ||
-                           (np->n_flag & NQNFSNONCACHE)) {
-                               error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
-                               if (error) {
-                                       kernel_upl_unmap(kernel_map, pl);
-                                       if (!nocommit)
-                                               kernel_upl_abort_range(pl, 
-                                               pl_offset, size, 
-                                               UPL_ABORT_FREE_ON_EMPTY);
-                                       return (error);
-                               }
-                               np->n_brev = np->n_lrev;
-                       }
-               }
-#endif 0 /* why bother? */
-
-               if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) {
-                       iomode = NFSV3WRITE_FILESYNC;
-                       error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
-                       if (must_commit)
-                               nfs_clearcommit(vp->v_mount);
-                       kernel_upl_unmap(kernel_map, pl);
-                        
-                        /* see comments below after other nfs_writerpc and ESTALE */
-                        if (error == ESTALE) {
-                            kernel_upl_abort_range(pl, pl_offset, size, 
-                                UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
-                        } else {
-                            if (!nocommit) {
-                                if(error)
-                                    kernel_upl_abort_range(pl, pl_offset, size, 
-                                    UPL_ABORT_FREE_ON_EMPTY);
-                                else
-                                    kernel_upl_commit_range(pl, 
-                                        pl_offset, size, 
-                                        UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_FREE_ON_EMPTY,
-                                        UPL_GET_INTERNAL_PAGE_LIST(pl), MAX_UPL_TRANSFER);
-                            }
-                        }
-                       return (error);
-               }
-               nfsstats.pageouts++;
-               lbn = uio->uio_offset / biosize;
-               on = uio->uio_offset & (biosize-1);
-               n = min((unsigned)(biosize - on), uio->uio_resid);
-again:
-               bufsize = biosize;
-#if 0
-               if ((lbn + 1) * biosize > np->n_size) {
-                       bufsize = np->n_size - lbn * biosize;
-                       bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
-               }
-#endif
-               vp->v_numoutput++;
-
-               np->n_flag |= NMODIFIED;
+               iosize = min(biosize, xsize);
+               uio_uio_resid_set(&auio, iosize);
+               aiov.iov_len = iosize;
+               aiov.iov_base = (uintptr_t)ioaddr;
+               auio.uio_iovs.iov32p = &aiov;
+               auio.uio_iovcnt = 1;
 
-#if 0 /* why bother? */
-/* NO RESOURCES TO FIX NQNFS CASE */
-/* We need to deal with this later -- Umesh */
-               /*
-                * Check for valid write lease and get one as required.
-                * In case getblk() and/or bwrite() delayed us.
-                */
-               if ((nmp->nm_flag & NFSMNT_NQNFS) &&
-                   NQNFS_CKINVALID(vp, np, ND_WRITE)) {
-                       do {
-                               error = nqnfs_getlease(vp, ND_WRITE, cred, p);
-                       } while (error == NQNFS_EXPIRED);
-                       if (error)
-                               goto cleanup;
+               FSDBG(323, auio.uio_offset, uio_uio_resid(&auio), ioaddr, xsize);
+               OSAddAtomic(1, (SInt32*)&nfsstats.pageouts);
 
-                       if (np->n_lrev != np->n_brev ||
-                           (np->n_flag & NQNFSNONCACHE)) {
-                                       error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
-                                       if (error) {
-                                               kernel_upl_unmap(kernel_map, pl);
-                                               if (!nocommit)
-                                                       kernel_upl_abort_range(pl, 
-                                                       pl_offset,
-                                                       size,
-                                                 UPL_ABORT_FREE_ON_EMPTY);
-
-                                               return (error);
-                                       }
-                                       np->n_brev = np->n_lrev;
-                                       goto again;
-                       }
-               }
-#endif 0 /* why bother? */
+               vnode_startwrite(vp);
 
+               /* NMODIFIED would be set here if doing unstable writes */
                iomode = NFSV3WRITE_FILESYNC;
-               error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
-               if (must_commit)
-                       nfs_clearcommit(vp->v_mount);
-               vp->v_numoutput--;
-
+               error = nfs_writerpc(vp, &auio, cred, p, &iomode, NULL);
+               vnode_writedone(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);
+               /* Note: no need to check uio_resid, because */
+               /* it'll only be set if there was an error. */
+               ioaddr += iosize;
+               xsize -= iosize;
+       } while (xsize > 0);
 
 cleanup:
-       kernel_upl_unmap(kernel_map, pl);
-       /* 
-       * EStale is special. In this case, we want vm to dump out
-       * the pages. Better yet, sever the object so we don't come
-       * back here on each page of the object to page out. For now,
-       * just dump. 
-       * XXX What about !nocommit case? Should ESTALE only be checked
-       * in that portion? - EKN
-       */
-       if (error == ESTALE) {
-            kernel_upl_abort_range(pl, pl_offset, size, 
-                UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY);
-       } else {
-            if (!nocommit) {
-                if(error)
-                    kernel_upl_abort_range(pl, pl_offset, pgsize, 
-                        UPL_ABORT_FREE_ON_EMPTY);
-                else
-                    kernel_upl_commit_range(pl, pl_offset, pgsize,
-                        UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_FREE_ON_EMPTY,
-                    UPL_GET_INTERNAL_PAGE_LIST(pl), MAX_UPL_TRANSFER);
-            }
+       ubc_upl_unmap(pl);
+       /*
+        * We've had several different solutions on what to do when the pageout
+        * gets an error. If we don't handle it, and return an error to the 
+        * caller, vm, it will retry . This can end in endless looping 
+        * between vm and here doing retries of the same page. Doing a dump
+        * back to vm, will get it out of vm's knowledge and we lose whatever
+        * data existed. This is risky, but in some cases necessary. For
+        * example, the initial fix here was to do that for ESTALE. In that case
+        * the server is telling us that the file is no longer the same. We 
+        * would not want to keep paging out to that. We also saw some 151 
+        * errors from Auspex server and NFSv3 can return errors higher than
+        * ELAST. Those along with NFS known server errors we will "dump" from
+        * vm.  Errors we don't expect to occur, we dump and log for further
+        * analysis. Errors that could be transient, networking ones,
+        * we let vm "retry". Lastly, errors that we retry, but may have potential
+        * to storm the network, we "retrywithsleep". "sever" will be used in
+        * in the future to dump all pages of object for cases like ESTALE.
+        * All this is the basis for the states returned and first guesses on
+        * error handling. Tweaking expected as more statistics are gathered.
+        * Note, in the long run we may need another more robust solution to
+        * have some kind of persistant store when the vm cannot dump nor keep
+        * retrying as a solution, but this would be a file architectural change
+        */
+         
+       if (!nofreeupl) { /* otherwise stacked file system has to handle this */
+               if (error) {
+                       int abortflags = 0; 
+                       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;
+                                       /* pri unused. PSOCK for placeholder. */
+                                       tsleep(&lbolt, PSOCK, "nfspageout", 0);
+                                       break;
+                               case SEVER: /* not implemented */
+                               default:
+                                       printf("nfs_pageout: action %d not expected\n", action);
+                                       break;
+                       }
+                               
+                       ubc_upl_abort_range(pl, pl_offset, size, abortflags);
+                       /* return error in all cases above */
+                       
+               } else 
+                       ubc_upl_commit_range(pl, pl_offset, pgsize,
+                                            UPL_COMMIT_CLEAR_DIRTY |
+                                            UPL_COMMIT_FREE_ON_EMPTY);
        }
-
        return (error);
 }
 
 /* Blktooff derives file offset given a logical block number */
 static int
 nfs_blktooff(ap)
-       struct vop_blktooff_args /* {
-               struct vnode *a_vp;
-               daddr_t a_lblkno;
-               off_t *a_offset;    
+       struct vnop_blktooff_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               daddr64_t a_lblkno;
+               off_t *a_offset;
        } */ *ap;
 {
        int biosize;
-       register struct vnode *vp = ap->a_vp;
+       vnode_t vp = ap->a_vp;
+       mount_t mp = vnode_mount(vp);
+
+       if (!mp)
+               return (ENXIO);
 
-       biosize = min(vp->v_mount->mnt_stat.f_iosize, PAGE_SIZE); /* nfs_bio.c */
+       biosize = vfs_statfs(mp)->f_iosize;
 
-       *ap->a_offset = (off_t)(ap->a_lblkno *  biosize);
+       *ap->a_offset = (off_t)(ap->a_lblkno * biosize);
 
        return (0);
 }
 
-/* Blktooff derives file offset given a logical block number */
 static int
 nfs_offtoblk(ap)
-       struct vop_offtoblk_args /* {
-               struct vnode *a_vp;
-               off_t a_offset;    
-               daddr_t *a_lblkno;
+       struct vnop_offtoblk_args /* {
+               struct vnodeop_desc *a_desc;
+               vnode_t a_vp;
+               off_t a_offset;
+               daddr64_t *a_lblkno;
        } */ *ap;
 {
        int biosize;
-       register struct vnode *vp = ap->a_vp;
+       vnode_t vp = ap->a_vp;
+       mount_t mp = vnode_mount(vp);
 
-       biosize = min(vp->v_mount->mnt_stat.f_iosize, PAGE_SIZE); /* nfs_bio.c */
+       if (!mp)
+               return (ENXIO);
 
-       *ap->a_lblkno = (daddr_t)(ap->a_offset /  biosize);
+       biosize = vfs_statfs(mp)->f_iosize;
+
+       *ap->a_lblkno = (daddr64_t)(ap->a_offset / biosize);
 
        return (0);
 }
-static int
-nfs_cmap(ap)
-       struct vop_cmap_args /* {
-               struct vnode *a_vp;
-               off_t a_offset;    
-               size_t a_size;
-               daddr_t *a_bpn;
-               size_t *a_run;
-               void *a_poff;
-       } */ *ap;
-{
-       return (EOPNOTSUPP);
-}
+