xref: /csrg-svn/sys/nfs/nfs_vnops.c (revision 69743)
138414Smckusick /*
263486Sbostic  * Copyright (c) 1989, 1993
363486Sbostic  *	The Regents of the University of California.  All rights reserved.
438414Smckusick  *
538414Smckusick  * This code is derived from software contributed to Berkeley by
638414Smckusick  * Rick Macklem at The University of Guelph.
738414Smckusick  *
844514Sbostic  * %sccs.include.redist.c%
938414Smckusick  *
10*69743Smckusick  *	@(#)nfs_vnops.c	8.16 (Berkeley) 05/27/95
1138414Smckusick  */
1238414Smckusick 
1368653Smckusick 
1438414Smckusick /*
1568653Smckusick  * vnode op calls for Sun NFS version 2 and 3
1638414Smckusick  */
1738414Smckusick 
1853322Smckusick #include <sys/param.h>
1953322Smckusick #include <sys/kernel.h>
2053322Smckusick #include <sys/systm.h>
2168653Smckusick #include <sys/resourcevar.h>
2268653Smckusick #include <sys/proc.h>
2353322Smckusick #include <sys/mount.h>
2453322Smckusick #include <sys/buf.h>
2553322Smckusick #include <sys/malloc.h>
2653322Smckusick #include <sys/mbuf.h>
2753322Smckusick #include <sys/conf.h>
2853322Smckusick #include <sys/namei.h>
2953322Smckusick #include <sys/vnode.h>
3054740Smckusick #include <sys/dirent.h>
3168653Smckusick #include <sys/fcntl.h>
3268653Smckusick #include <ufs/ufs/dir.h>
3347573Skarels 
3453322Smckusick #include <vm/vm.h>
3538414Smckusick 
3655041Smckusick #include <miscfs/specfs/specdev.h>
3755041Smckusick #include <miscfs/fifofs/fifo.h>
3855041Smckusick 
3953322Smckusick #include <nfs/rpcv2.h>
4068653Smckusick #include <nfs/nfsproto.h>
4153322Smckusick #include <nfs/nfs.h>
4253322Smckusick #include <nfs/nfsnode.h>
4353322Smckusick #include <nfs/nfsmount.h>
4453322Smckusick #include <nfs/xdr_subs.h>
4553322Smckusick #include <nfs/nfsm_subs.h>
4653322Smckusick #include <nfs/nqnfs.h>
4753322Smckusick 
4868653Smckusick #include <net/if.h>
4968653Smckusick #include <netinet/in.h>
5068653Smckusick #include <netinet/in_var.h>
5168653Smckusick 
5238414Smckusick /* Defs */
5338414Smckusick #define	TRUE	1
5438414Smckusick #define	FALSE	0
5538414Smckusick 
5648054Smckusick /*
5748054Smckusick  * Global vfs data structures for nfs
5848054Smckusick  */
5953554Sheideman int (**nfsv2_vnodeop_p)();
6053554Sheideman struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = {
6153554Sheideman 	{ &vop_default_desc, vn_default_error },
6253806Smckusick 	{ &vop_lookup_desc, nfs_lookup },	/* lookup */
6353806Smckusick 	{ &vop_create_desc, nfs_create },	/* create */
6453554Sheideman 	{ &vop_mknod_desc, nfs_mknod },		/* mknod */
6553554Sheideman 	{ &vop_open_desc, nfs_open },		/* open */
6653554Sheideman 	{ &vop_close_desc, nfs_close },		/* close */
6753806Smckusick 	{ &vop_access_desc, nfs_access },	/* access */
6853806Smckusick 	{ &vop_getattr_desc, nfs_getattr },	/* getattr */
6953806Smckusick 	{ &vop_setattr_desc, nfs_setattr },	/* setattr */
7053554Sheideman 	{ &vop_read_desc, nfs_read },		/* read */
7153554Sheideman 	{ &vop_write_desc, nfs_write },		/* write */
7267651Smckusick 	{ &vop_lease_desc, nfs_lease_check },	/* lease */
7353554Sheideman 	{ &vop_ioctl_desc, nfs_ioctl },		/* ioctl */
7453806Smckusick 	{ &vop_select_desc, nfs_select },	/* select */
7568418Smckusick 	{ &vop_revoke_desc, nfs_revoke },	/* revoke */
7653554Sheideman 	{ &vop_mmap_desc, nfs_mmap },		/* mmap */
7753554Sheideman 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
7853554Sheideman 	{ &vop_seek_desc, nfs_seek },		/* seek */
7953806Smckusick 	{ &vop_remove_desc, nfs_remove },	/* remove */
8053554Sheideman 	{ &vop_link_desc, nfs_link },		/* link */
8153806Smckusick 	{ &vop_rename_desc, nfs_rename },	/* rename */
8253554Sheideman 	{ &vop_mkdir_desc, nfs_mkdir },		/* mkdir */
8353554Sheideman 	{ &vop_rmdir_desc, nfs_rmdir },		/* rmdir */
8453806Smckusick 	{ &vop_symlink_desc, nfs_symlink },	/* symlink */
8553806Smckusick 	{ &vop_readdir_desc, nfs_readdir },	/* readdir */
8653806Smckusick 	{ &vop_readlink_desc, nfs_readlink },	/* readlink */
8753806Smckusick 	{ &vop_abortop_desc, nfs_abortop },	/* abortop */
8853806Smckusick 	{ &vop_inactive_desc, nfs_inactive },	/* inactive */
8953806Smckusick 	{ &vop_reclaim_desc, nfs_reclaim },	/* reclaim */
9053554Sheideman 	{ &vop_lock_desc, nfs_lock },		/* lock */
9153806Smckusick 	{ &vop_unlock_desc, nfs_unlock },	/* unlock */
9253554Sheideman 	{ &vop_bmap_desc, nfs_bmap },		/* bmap */
9353806Smckusick 	{ &vop_strategy_desc, nfs_strategy },	/* strategy */
9453554Sheideman 	{ &vop_print_desc, nfs_print },		/* print */
9553806Smckusick 	{ &vop_islocked_desc, nfs_islocked },	/* islocked */
9660400Smckusick 	{ &vop_pathconf_desc, nfs_pathconf },	/* pathconf */
9753806Smckusick 	{ &vop_advlock_desc, nfs_advlock },	/* advlock */
9853806Smckusick 	{ &vop_blkatoff_desc, nfs_blkatoff },	/* blkatoff */
9953806Smckusick 	{ &vop_valloc_desc, nfs_valloc },	/* valloc */
10066079Shibler 	{ &vop_reallocblks_desc, nfs_reallocblks },	/* reallocblks */
10153554Sheideman 	{ &vop_vfree_desc, nfs_vfree },		/* vfree */
10253806Smckusick 	{ &vop_truncate_desc, nfs_truncate },	/* truncate */
10353806Smckusick 	{ &vop_update_desc, nfs_update },	/* update */
10468653Smckusick 	{ &vop_bwrite_desc, nfs_bwrite },
10553554Sheideman 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
10638414Smckusick };
10753554Sheideman struct vnodeopv_desc nfsv2_vnodeop_opv_desc =
10853554Sheideman 	{ &nfsv2_vnodeop_p, nfsv2_vnodeop_entries };
10938414Smckusick 
11048054Smckusick /*
11148054Smckusick  * Special device vnode ops
11248054Smckusick  */
11353554Sheideman int (**spec_nfsv2nodeop_p)();
11453554Sheideman struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = {
11553554Sheideman 	{ &vop_default_desc, vn_default_error },
11653806Smckusick 	{ &vop_lookup_desc, spec_lookup },	/* lookup */
11753806Smckusick 	{ &vop_create_desc, spec_create },	/* create */
11853806Smckusick 	{ &vop_mknod_desc, spec_mknod },	/* mknod */
11953554Sheideman 	{ &vop_open_desc, spec_open },		/* open */
12053806Smckusick 	{ &vop_close_desc, nfsspec_close },	/* close */
12156364Smckusick 	{ &vop_access_desc, nfsspec_access },	/* access */
12253806Smckusick 	{ &vop_getattr_desc, nfs_getattr },	/* getattr */
12353806Smckusick 	{ &vop_setattr_desc, nfs_setattr },	/* setattr */
12453806Smckusick 	{ &vop_read_desc, nfsspec_read },	/* read */
12553806Smckusick 	{ &vop_write_desc, nfsspec_write },	/* write */
12667651Smckusick 	{ &vop_lease_desc, spec_lease_check },	/* lease */
12753806Smckusick 	{ &vop_ioctl_desc, spec_ioctl },	/* ioctl */
12853806Smckusick 	{ &vop_select_desc, spec_select },	/* select */
12968418Smckusick 	{ &vop_revoke_desc, spec_revoke },	/* revoke */
13053554Sheideman 	{ &vop_mmap_desc, spec_mmap },		/* mmap */
13154451Smckusick 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
13253554Sheideman 	{ &vop_seek_desc, spec_seek },		/* seek */
13353806Smckusick 	{ &vop_remove_desc, spec_remove },	/* remove */
13453554Sheideman 	{ &vop_link_desc, spec_link },		/* link */
13553806Smckusick 	{ &vop_rename_desc, spec_rename },	/* rename */
13653806Smckusick 	{ &vop_mkdir_desc, spec_mkdir },	/* mkdir */
13753806Smckusick 	{ &vop_rmdir_desc, spec_rmdir },	/* rmdir */
13853806Smckusick 	{ &vop_symlink_desc, spec_symlink },	/* symlink */
13953806Smckusick 	{ &vop_readdir_desc, spec_readdir },	/* readdir */
14053806Smckusick 	{ &vop_readlink_desc, spec_readlink },	/* readlink */
14153806Smckusick 	{ &vop_abortop_desc, spec_abortop },	/* abortop */
14253806Smckusick 	{ &vop_inactive_desc, nfs_inactive },	/* inactive */
14353806Smckusick 	{ &vop_reclaim_desc, nfs_reclaim },	/* reclaim */
14453554Sheideman 	{ &vop_lock_desc, nfs_lock },		/* lock */
14553806Smckusick 	{ &vop_unlock_desc, nfs_unlock },	/* unlock */
14653554Sheideman 	{ &vop_bmap_desc, spec_bmap },		/* bmap */
14753806Smckusick 	{ &vop_strategy_desc, spec_strategy },	/* strategy */
14853554Sheideman 	{ &vop_print_desc, nfs_print },		/* print */
14953806Smckusick 	{ &vop_islocked_desc, nfs_islocked },	/* islocked */
15060400Smckusick 	{ &vop_pathconf_desc, spec_pathconf },	/* pathconf */
15153806Smckusick 	{ &vop_advlock_desc, spec_advlock },	/* advlock */
15253806Smckusick 	{ &vop_blkatoff_desc, spec_blkatoff },	/* blkatoff */
15353806Smckusick 	{ &vop_valloc_desc, spec_valloc },	/* valloc */
15466079Shibler 	{ &vop_reallocblks_desc, spec_reallocblks },	/* reallocblks */
15553806Smckusick 	{ &vop_vfree_desc, spec_vfree },	/* vfree */
15653806Smckusick 	{ &vop_truncate_desc, spec_truncate },	/* truncate */
15753806Smckusick 	{ &vop_update_desc, nfs_update },	/* update */
15857809Smckusick 	{ &vop_bwrite_desc, vn_bwrite },
15953554Sheideman 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
16038414Smckusick };
16153554Sheideman struct vnodeopv_desc spec_nfsv2nodeop_opv_desc =
16253554Sheideman 	{ &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries };
16338414Smckusick 
16453554Sheideman int (**fifo_nfsv2nodeop_p)();
16553554Sheideman struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = {
16653554Sheideman 	{ &vop_default_desc, vn_default_error },
16753806Smckusick 	{ &vop_lookup_desc, fifo_lookup },	/* lookup */
16853806Smckusick 	{ &vop_create_desc, fifo_create },	/* create */
16953806Smckusick 	{ &vop_mknod_desc, fifo_mknod },	/* mknod */
17053554Sheideman 	{ &vop_open_desc, fifo_open },		/* open */
17153806Smckusick 	{ &vop_close_desc, nfsfifo_close },	/* close */
17256364Smckusick 	{ &vop_access_desc, nfsspec_access },	/* access */
17353806Smckusick 	{ &vop_getattr_desc, nfs_getattr },	/* getattr */
17453806Smckusick 	{ &vop_setattr_desc, nfs_setattr },	/* setattr */
17553806Smckusick 	{ &vop_read_desc, nfsfifo_read },	/* read */
17653806Smckusick 	{ &vop_write_desc, nfsfifo_write },	/* write */
17767651Smckusick 	{ &vop_lease_desc, fifo_lease_check },	/* lease */
17853806Smckusick 	{ &vop_ioctl_desc, fifo_ioctl },	/* ioctl */
17953806Smckusick 	{ &vop_select_desc, fifo_select },	/* select */
18068418Smckusick 	{ &vop_revoke_desc, fifo_revoke },	/* revoke */
18153554Sheideman 	{ &vop_mmap_desc, fifo_mmap },		/* mmap */
18254451Smckusick 	{ &vop_fsync_desc, nfs_fsync },		/* fsync */
18353554Sheideman 	{ &vop_seek_desc, fifo_seek },		/* seek */
18453806Smckusick 	{ &vop_remove_desc, fifo_remove },	/* remove */
18553554Sheideman 	{ &vop_link_desc, fifo_link },		/* link */
18653806Smckusick 	{ &vop_rename_desc, fifo_rename },	/* rename */
18753806Smckusick 	{ &vop_mkdir_desc, fifo_mkdir },	/* mkdir */
18853806Smckusick 	{ &vop_rmdir_desc, fifo_rmdir },	/* rmdir */
18953806Smckusick 	{ &vop_symlink_desc, fifo_symlink },	/* symlink */
19053806Smckusick 	{ &vop_readdir_desc, fifo_readdir },	/* readdir */
19153806Smckusick 	{ &vop_readlink_desc, fifo_readlink },	/* readlink */
19253806Smckusick 	{ &vop_abortop_desc, fifo_abortop },	/* abortop */
19353806Smckusick 	{ &vop_inactive_desc, nfs_inactive },	/* inactive */
19453806Smckusick 	{ &vop_reclaim_desc, nfs_reclaim },	/* reclaim */
19553554Sheideman 	{ &vop_lock_desc, nfs_lock },		/* lock */
19653806Smckusick 	{ &vop_unlock_desc, nfs_unlock },	/* unlock */
19753554Sheideman 	{ &vop_bmap_desc, fifo_bmap },		/* bmap */
19853806Smckusick 	{ &vop_strategy_desc, fifo_badop },	/* strategy */
19953554Sheideman 	{ &vop_print_desc, nfs_print },		/* print */
20053806Smckusick 	{ &vop_islocked_desc, nfs_islocked },	/* islocked */
20160400Smckusick 	{ &vop_pathconf_desc, fifo_pathconf },	/* pathconf */
20253806Smckusick 	{ &vop_advlock_desc, fifo_advlock },	/* advlock */
20353806Smckusick 	{ &vop_blkatoff_desc, fifo_blkatoff },	/* blkatoff */
20453806Smckusick 	{ &vop_valloc_desc, fifo_valloc },	/* valloc */
20566079Shibler 	{ &vop_reallocblks_desc, fifo_reallocblks },	/* reallocblks */
20653806Smckusick 	{ &vop_vfree_desc, fifo_vfree },	/* vfree */
20753806Smckusick 	{ &vop_truncate_desc, fifo_truncate },	/* truncate */
20853806Smckusick 	{ &vop_update_desc, nfs_update },	/* update */
20957809Smckusick 	{ &vop_bwrite_desc, vn_bwrite },
21053554Sheideman 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
21140294Smckusick };
21253554Sheideman struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc =
21353554Sheideman 	{ &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries };
21440294Smckusick 
21556289Smckusick void nqnfs_clientlease();
21668653Smckusick int nfs_commit();
21756289Smckusick 
21848054Smckusick /*
21952196Smckusick  * Global variables
22048054Smckusick  */
22168653Smckusick extern u_long nfs_true, nfs_false;
22268653Smckusick extern struct nfsstats nfsstats;
22368653Smckusick extern nfstype nfsv3_type[9];
22441905Smckusick struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
22546988Smckusick int nfs_numasync = 0;
22654740Smckusick #define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
22738414Smckusick 
22838414Smckusick /*
22938414Smckusick  * nfs null call from vfs.
23038414Smckusick  */
23152234Sheideman int
nfs_null(vp,cred,procp)23252196Smckusick nfs_null(vp, cred, procp)
23338414Smckusick 	struct vnode *vp;
23438414Smckusick 	struct ucred *cred;
23552196Smckusick 	struct proc *procp;
23638414Smckusick {
23739488Smckusick 	caddr_t bpos, dpos;
23839488Smckusick 	int error = 0;
23939488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb;
24038414Smckusick 
24152196Smckusick 	nfsm_reqhead(vp, NFSPROC_NULL, 0);
24252196Smckusick 	nfsm_request(vp, NFSPROC_NULL, procp, cred);
24338414Smckusick 	nfsm_reqdone;
24438414Smckusick 	return (error);
24538414Smckusick }
24638414Smckusick 
24738414Smckusick /*
24838414Smckusick  * nfs access vnode op.
24968653Smckusick  * For nfs version 2, just return ok. File accesses may fail later.
25068653Smckusick  * For nfs version 3, use the access rpc to check accessibility. If file modes
25168653Smckusick  * are changed on the server, accesses might still fail later.
25238414Smckusick  */
25352234Sheideman int
nfs_access(ap)25453806Smckusick nfs_access(ap)
25554668Smckusick 	struct vop_access_args /* {
25654668Smckusick 		struct vnode *a_vp;
25754668Smckusick 		int  a_mode;
25854668Smckusick 		struct ucred *a_cred;
25954668Smckusick 		struct proc *a_p;
26054668Smckusick 	} */ *ap;
26138414Smckusick {
26256364Smckusick 	register struct vnode *vp = ap->a_vp;
26356364Smckusick 	register u_long *tl;
26456364Smckusick 	register caddr_t cp;
26568653Smckusick 	register int t1, t2;
26668653Smckusick 	caddr_t bpos, dpos, cp2;
26768653Smckusick 	int error = 0, attrflag;
26856364Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
26968653Smckusick 	u_long mode, rmode;
27068653Smckusick 	int v3 = NFS_ISV3(vp);
27138414Smckusick 
27238414Smckusick 	/*
273*69743Smckusick 	 * Disallow write attempts on filesystems mounted read-only;
274*69743Smckusick 	 * unless the file is a socket, fifo, or a block or character
275*69743Smckusick 	 * device resident on the filesystem.
276*69743Smckusick 	 */
277*69743Smckusick 	if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
278*69743Smckusick 		switch (vp->v_type) {
279*69743Smckusick 		case VREG: case VDIR: case VLNK:
280*69743Smckusick 			return (EROFS);
281*69743Smckusick 		}
282*69743Smckusick 	}
283*69743Smckusick 	/*
28468653Smckusick 	 * For nfs v3, do an access rpc, otherwise you are stuck emulating
28556708Smckusick 	 * ufs_access() locally using the vattr. This may not be correct,
28656708Smckusick 	 * since the server may apply other access criteria such as
28756708Smckusick 	 * client uid-->server uid mapping that we do not know about, but
28856708Smckusick 	 * this is better than just returning anything that is lying about
28956708Smckusick 	 * in the cache.
29038414Smckusick 	 */
29168653Smckusick 	if (v3) {
29268653Smckusick 		nfsstats.rpccnt[NFSPROC_ACCESS]++;
29368653Smckusick 		nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED);
29468653Smckusick 		nfsm_fhtom(vp, v3);
29568653Smckusick 		nfsm_build(tl, u_long *, NFSX_UNSIGNED);
29656364Smckusick 		if (ap->a_mode & VREAD)
29768653Smckusick 			mode = NFSV3ACCESS_READ;
29856364Smckusick 		else
29968653Smckusick 			mode = 0;
30068653Smckusick 		if (vp->v_type == VDIR) {
30168653Smckusick 			if (ap->a_mode & VWRITE)
30268653Smckusick 				mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND |
30368653Smckusick 					 NFSV3ACCESS_DELETE);
30468653Smckusick 			if (ap->a_mode & VEXEC)
30568653Smckusick 				mode |= NFSV3ACCESS_LOOKUP;
30668653Smckusick 		} else {
30768653Smckusick 			if (ap->a_mode & VWRITE)
30868653Smckusick 				mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
30968653Smckusick 			if (ap->a_mode & VEXEC)
31068653Smckusick 				mode |= NFSV3ACCESS_EXECUTE;
31168653Smckusick 		}
31268653Smckusick 		*tl = txdr_unsigned(mode);
31368653Smckusick 		nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred);
31468653Smckusick 		nfsm_postop_attr(vp, attrflag);
31568653Smckusick 		if (!error) {
31668653Smckusick 			nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
31768653Smckusick 			rmode = fxdr_unsigned(u_long, *tl);
31868653Smckusick 			/*
31968653Smckusick 			 * The NFS V3 spec does not clarify whether or not
32068653Smckusick 			 * the returned access bits can be a superset of
32168653Smckusick 			 * the ones requested, so...
32268653Smckusick 			 */
32368653Smckusick 			if ((rmode & mode) != mode)
32468653Smckusick 				error = EACCES;
32568653Smckusick 		}
32656364Smckusick 		nfsm_reqdone;
32738884Smacklem 		return (error);
32856364Smckusick 	} else
32956708Smckusick 		return (nfsspec_access(ap));
33038414Smckusick }
33138414Smckusick 
33238414Smckusick /*
33338414Smckusick  * nfs open vnode op
33456289Smckusick  * Check to see if the type is ok
33552196Smckusick  * and that deletion is not in progress.
33656289Smckusick  * For paged in text files, you will need to flush the page cache
33756289Smckusick  * if consistency is lost.
33838414Smckusick  */
33939488Smckusick /* ARGSUSED */
34052234Sheideman int
nfs_open(ap)34153806Smckusick nfs_open(ap)
34254668Smckusick 	struct vop_open_args /* {
34354668Smckusick 		struct vnode *a_vp;
34454668Smckusick 		int  a_mode;
34554668Smckusick 		struct ucred *a_cred;
34654668Smckusick 		struct proc *a_p;
34754668Smckusick 	} */ *ap;
34838414Smckusick {
34953806Smckusick 	register struct vnode *vp = ap->a_vp;
35056289Smckusick 	struct nfsnode *np = VTONFS(vp);
35156289Smckusick 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
35256289Smckusick 	struct vattr vattr;
35356289Smckusick 	int error;
35438414Smckusick 
35553806Smckusick 	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
35668653Smckusick { printf("open eacces vtyp=%d\n",vp->v_type);
35738414Smckusick 		return (EACCES);
35868653Smckusick }
35968653Smckusick 	/*
36068653Smckusick 	 * Get a valid lease. If cached data is stale, flush it.
36168653Smckusick 	 */
36268653Smckusick 	if (nmp->nm_flag & NFSMNT_NQNFS) {
36368653Smckusick 		if (NQNFS_CKINVALID(vp, np, ND_READ)) {
36456289Smckusick 		    do {
36568653Smckusick 			error = nqnfs_getlease(vp, ND_READ, ap->a_cred,
36668653Smckusick 			    ap->a_p);
36756289Smckusick 		    } while (error == NQNFS_EXPIRED);
36856289Smckusick 		    if (error)
36956289Smckusick 			return (error);
37059706Smckusick 		    if (np->n_lrev != np->n_brev ||
37159706Smckusick 			(np->n_flag & NQNFSNONCACHE)) {
37257791Smckusick 			if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
37357791Smckusick 				ap->a_p, 1)) == EINTR)
37457791Smckusick 				return (error);
37556289Smckusick 			(void) vnode_pager_uncache(vp);
37656289Smckusick 			np->n_brev = np->n_lrev;
37756289Smckusick 		    }
37856289Smckusick 		}
37968653Smckusick 	} else {
38056289Smckusick 		if (np->n_flag & NMODIFIED) {
38157791Smckusick 			if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
38257791Smckusick 				ap->a_p, 1)) == EINTR)
38357791Smckusick 				return (error);
38456289Smckusick 			(void) vnode_pager_uncache(vp);
38556289Smckusick 			np->n_attrstamp = 0;
38668653Smckusick 			if (vp->v_type == VDIR)
38768653Smckusick 				np->n_direofoffset = 0;
38868653Smckusick 			error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
38968653Smckusick 			if (error)
39056289Smckusick 				return (error);
39156289Smckusick 			np->n_mtime = vattr.va_mtime.ts_sec;
39256289Smckusick 		} else {
39368653Smckusick 			error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p);
39468653Smckusick 			if (error)
39556289Smckusick 				return (error);
39656289Smckusick 			if (np->n_mtime != vattr.va_mtime.ts_sec) {
39768653Smckusick 				if (vp->v_type == VDIR)
39868653Smckusick 					np->n_direofoffset = 0;
39957791Smckusick 				if ((error = nfs_vinvalbuf(vp, V_SAVE,
40057791Smckusick 					ap->a_cred, ap->a_p, 1)) == EINTR)
40157791Smckusick 					return (error);
40256289Smckusick 				(void) vnode_pager_uncache(vp);
40356289Smckusick 				np->n_mtime = vattr.va_mtime.ts_sec;
40456289Smckusick 			}
40556289Smckusick 		}
40668653Smckusick 	}
40768653Smckusick 	if ((nmp->nm_flag & NFSMNT_NQNFS) == 0)
40856289Smckusick 		np->n_attrstamp = 0; /* For Open/Close consistency */
40952196Smckusick 	return (0);
41038414Smckusick }
41138414Smckusick 
41238414Smckusick /*
41338414Smckusick  * nfs close vnode op
41468653Smckusick  * What an NFS client should do upon close after writing is a debatable issue.
41568653Smckusick  * Most NFS clients push delayed writes to the server upon close, basically for
41668653Smckusick  * two reasons:
41768653Smckusick  * 1 - So that any write errors may be reported back to the client process
41868653Smckusick  *     doing the close system call. By far the two most likely errors are
41968653Smckusick  *     NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
42068653Smckusick  * 2 - To put a worst case upper bound on cache inconsistency between
42168653Smckusick  *     multiple clients for the file.
42268653Smckusick  * There is also a consistency problem for Version 2 of the protocol w.r.t.
42368653Smckusick  * not being able to tell if other clients are writing a file concurrently,
42468653Smckusick  * since there is no way of knowing if the changed modify time in the reply
42568653Smckusick  * is only due to the write for this client.
42668653Smckusick  * (NFS Version 3 provides weak cache consistency data in the reply that
42768653Smckusick  *  should be sufficient to detect and handle this case.)
42868653Smckusick  *
42968653Smckusick  * The current code does the following:
43068653Smckusick  * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
43168653Smckusick  * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
43268653Smckusick  *                     or commit them (this satisfies 1 and 2 except for the
43368653Smckusick  *                     case where the server crashes after this close but
43468653Smckusick  *                     before the commit RPC, which is felt to be "good
43568653Smckusick  *                     enough". Changing the last argument to nfs_flush() to
43668653Smckusick  *                     a 1 would force a commit operation, if it is felt a
43768653Smckusick  *                     commit is necessary now.
43868653Smckusick  * for NQNFS         - do nothing now, since 2 is dealt with via leases and
43968653Smckusick  *                     1 should be dealt with via an fsync() system call for
44068653Smckusick  *                     cases where write errors are important.
44138414Smckusick  */
44239488Smckusick /* ARGSUSED */
44352234Sheideman int
nfs_close(ap)44453806Smckusick nfs_close(ap)
44554451Smckusick 	struct vop_close_args /* {
44654451Smckusick 		struct vnodeop_desc *a_desc;
44754451Smckusick 		struct vnode *a_vp;
44854451Smckusick 		int  a_fflag;
44954451Smckusick 		struct ucred *a_cred;
45054451Smckusick 		struct proc *a_p;
45154451Smckusick 	} */ *ap;
45238414Smckusick {
45353806Smckusick 	register struct vnode *vp = ap->a_vp;
45453806Smckusick 	register struct nfsnode *np = VTONFS(vp);
45539341Smckusick 	int error = 0;
45638414Smckusick 
45753806Smckusick 	if (vp->v_type == VREG) {
45853806Smckusick 	    if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 &&
45953629Smckusick 		(np->n_flag & NMODIFIED)) {
46068653Smckusick 		if (NFS_ISV3(vp))
46168653Smckusick 		    error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0);
46268653Smckusick 		else
46368653Smckusick 		    error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1);
46441905Smckusick 		np->n_attrstamp = 0;
46553629Smckusick 	    }
46653629Smckusick 	    if (np->n_flag & NWRITEERR) {
46753629Smckusick 		np->n_flag &= ~NWRITEERR;
46853629Smckusick 		error = np->n_error;
46953629Smckusick 	    }
47038884Smacklem 	}
47138414Smckusick 	return (error);
47238414Smckusick }
47338414Smckusick 
47438414Smckusick /*
47538414Smckusick  * nfs getattr call from vfs.
47638414Smckusick  */
47752234Sheideman int
nfs_getattr(ap)47853805Smckusick nfs_getattr(ap)
47954668Smckusick 	struct vop_getattr_args /* {
48054668Smckusick 		struct vnode *a_vp;
48154668Smckusick 		struct vattr *a_vap;
48254668Smckusick 		struct ucred *a_cred;
48354668Smckusick 		struct proc *a_p;
48454668Smckusick 	} */ *ap;
48538414Smckusick {
48653805Smckusick 	register struct vnode *vp = ap->a_vp;
48753805Smckusick 	register struct nfsnode *np = VTONFS(vp);
48839488Smckusick 	register caddr_t cp;
48968653Smckusick 	register u_long *tl;
49068653Smckusick 	register int t1, t2;
49139488Smckusick 	caddr_t bpos, dpos;
49239488Smckusick 	int error = 0;
49339488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
49468653Smckusick 	int v3 = NFS_ISV3(vp);
49538414Smckusick 
49653805Smckusick 	/*
49753805Smckusick 	 * Update local times for special files.
49853805Smckusick 	 */
49956329Smckusick 	if (np->n_flag & (NACC | NUPD))
50053805Smckusick 		np->n_flag |= NCHG;
50153805Smckusick 	/*
50253805Smckusick 	 * First look in the cache.
50353805Smckusick 	 */
50453805Smckusick 	if (nfs_getattrcache(vp, ap->a_vap) == 0)
50538414Smckusick 		return (0);
50638414Smckusick 	nfsstats.rpccnt[NFSPROC_GETATTR]++;
50768653Smckusick 	nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3));
50868653Smckusick 	nfsm_fhtom(vp, v3);
50953805Smckusick 	nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred);
51068653Smckusick 	if (!error)
51168653Smckusick 		nfsm_loadattr(vp, ap->a_vap);
51238414Smckusick 	nfsm_reqdone;
51338414Smckusick 	return (error);
51438414Smckusick }
51538414Smckusick 
51638414Smckusick /*
51738414Smckusick  * nfs setattr call.
51838414Smckusick  */
51952234Sheideman int
nfs_setattr(ap)52053806Smckusick nfs_setattr(ap)
52154451Smckusick 	struct vop_setattr_args /* {
52254451Smckusick 		struct vnodeop_desc *a_desc;
52354451Smckusick 		struct vnode *a_vp;
52454451Smckusick 		struct vattr *a_vap;
52554451Smckusick 		struct ucred *a_cred;
52654451Smckusick 		struct proc *a_p;
52754451Smckusick 	} */ *ap;
52838414Smckusick {
52968653Smckusick 	register struct vnode *vp = ap->a_vp;
53068653Smckusick 	register struct nfsnode *np = VTONFS(vp);
53168653Smckusick 	register struct vattr *vap = ap->a_vap;
53268653Smckusick 	int error = 0;
53368653Smckusick 	u_quad_t tsize;
53468653Smckusick 
53568653Smckusick #ifndef nolint
53668653Smckusick 	tsize = (u_quad_t)0;
53768653Smckusick #endif
538*69743Smckusick 	/*
539*69743Smckusick 	 * Disallow write attempts if the filesystem is mounted read-only.
540*69743Smckusick 	 */
541*69743Smckusick   	if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
542*69743Smckusick 	    vap->va_gid != (gid_t)VNOVAL || vap->va_atime.ts_sec != VNOVAL ||
543*69743Smckusick 	    vap->va_mtime.ts_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
544*69743Smckusick 	    (vp->v_mount->mnt_flag & MNT_RDONLY))
545*69743Smckusick 		return (EROFS);
54668653Smckusick 	if (vap->va_size != VNOVAL) {
54768653Smckusick  		switch (vp->v_type) {
54868653Smckusick  		case VDIR:
54968653Smckusick  			return (EISDIR);
55068653Smckusick  		case VCHR:
55168653Smckusick  		case VBLK:
552*69743Smckusick  		case VSOCK:
553*69743Smckusick  		case VFIFO:
55468653Smckusick 			if (vap->va_mtime.ts_sec == VNOVAL &&
55568653Smckusick 			    vap->va_atime.ts_sec == VNOVAL &&
55668653Smckusick 			    vap->va_mode == (u_short)VNOVAL &&
55768653Smckusick 			    vap->va_uid == (uid_t)VNOVAL &&
55868653Smckusick 			    vap->va_gid == (gid_t)VNOVAL)
55968653Smckusick 				return (0);
56068653Smckusick  			vap->va_size = VNOVAL;
56168653Smckusick  			break;
56268653Smckusick  		default:
563*69743Smckusick 			/*
564*69743Smckusick 			 * Disallow write attempts if the filesystem is
565*69743Smckusick 			 * mounted read-only.
566*69743Smckusick 			 */
567*69743Smckusick 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
568*69743Smckusick 				return (EROFS);
56968653Smckusick  			if (np->n_flag & NMODIFIED) {
57068653Smckusick  			    if (vap->va_size == 0)
57168653Smckusick  				error = nfs_vinvalbuf(vp, 0,
57268653Smckusick  					ap->a_cred, ap->a_p, 1);
57368653Smckusick  			    else
57468653Smckusick  				error = nfs_vinvalbuf(vp, V_SAVE,
57568653Smckusick  					ap->a_cred, ap->a_p, 1);
57668653Smckusick  			    if (error)
57768653Smckusick  				return (error);
57868653Smckusick  			}
57968653Smckusick  			tsize = np->n_size;
58068653Smckusick  			np->n_size = np->n_vattr.va_size = vap->va_size;
58168653Smckusick  			vnode_pager_setsize(vp, (u_long)np->n_size);
58268653Smckusick   		};
58368653Smckusick   	} else if ((vap->va_mtime.ts_sec != VNOVAL ||
58468653Smckusick 		vap->va_atime.ts_sec != VNOVAL) && (np->n_flag & NMODIFIED) &&
58568653Smckusick 		vp->v_type == VREG &&
58668653Smckusick   		(error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred,
58768653Smckusick 		 ap->a_p, 1)) == EINTR)
58868653Smckusick 		return (error);
58968653Smckusick 	error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p);
59068653Smckusick 	if (error) {
59168653Smckusick 		np->n_size = np->n_vattr.va_size = tsize;
59268653Smckusick 		vnode_pager_setsize(vp, (u_long)np->n_size);
59368653Smckusick 	}
59468653Smckusick 	return (error);
59568653Smckusick }
59668653Smckusick 
59768653Smckusick /*
59868653Smckusick  * Do an nfs setattr rpc.
59968653Smckusick  */
60068653Smckusick int
nfs_setattrrpc(vp,vap,cred,procp)60168653Smckusick nfs_setattrrpc(vp, vap, cred, procp)
60268653Smckusick 	register struct vnode *vp;
60368653Smckusick 	register struct vattr *vap;
60468653Smckusick 	struct ucred *cred;
60568653Smckusick 	struct proc *procp;
60668653Smckusick {
60738884Smacklem 	register struct nfsv2_sattr *sp;
60839488Smckusick 	register caddr_t cp;
60968653Smckusick 	register long t1, t2;
61052196Smckusick 	caddr_t bpos, dpos, cp2;
61152196Smckusick 	u_long *tl;
61268653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR;
61339488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
61468653Smckusick 	u_quad_t frev;
61568653Smckusick 	int v3 = NFS_ISV3(vp);
61638414Smckusick 
61768653Smckusick 	nfsstats.rpccnt[NFSPROC_SETATTR]++;
61868653Smckusick 	nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3));
61968653Smckusick 	nfsm_fhtom(vp, v3);
62068653Smckusick 	if (v3) {
62168653Smckusick 		if (vap->va_mode != (u_short)VNOVAL) {
62268653Smckusick 			nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
62368653Smckusick 			*tl++ = nfs_true;
62468653Smckusick 			*tl = txdr_unsigned(vap->va_mode);
62568653Smckusick 		} else {
62668653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
62768653Smckusick 			*tl = nfs_false;
62868653Smckusick 		}
62968653Smckusick 		if (vap->va_uid != (uid_t)VNOVAL) {
63068653Smckusick 			nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
63168653Smckusick 			*tl++ = nfs_true;
63268653Smckusick 			*tl = txdr_unsigned(vap->va_uid);
63368653Smckusick 		} else {
63468653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
63568653Smckusick 			*tl = nfs_false;
63668653Smckusick 		}
63768653Smckusick 		if (vap->va_gid != (gid_t)VNOVAL) {
63868653Smckusick 			nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
63968653Smckusick 			*tl++ = nfs_true;
64068653Smckusick 			*tl = txdr_unsigned(vap->va_gid);
64168653Smckusick 		} else {
64268653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
64368653Smckusick 			*tl = nfs_false;
64468653Smckusick 		}
64557791Smckusick 		if (vap->va_size != VNOVAL) {
64668653Smckusick 			nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
64768653Smckusick 			*tl++ = nfs_true;
64868653Smckusick 			txdr_hyper(&vap->va_size, tl);
64968653Smckusick 		} else {
65068653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
65168653Smckusick 			*tl = nfs_false;
65268653Smckusick 		}
65368653Smckusick 		if (vap->va_atime.ts_sec != VNOVAL) {
65468653Smckusick 			if (vap->va_atime.ts_sec != time.tv_sec) {
65568653Smckusick 				nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
65668653Smckusick 				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
65768653Smckusick 				txdr_nfsv3time(&vap->va_atime, tl);
65868653Smckusick 			} else {
65968653Smckusick 				nfsm_build(tl, u_long *, NFSX_UNSIGNED);
66068653Smckusick 				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
66157791Smckusick 			}
66268653Smckusick 		} else {
66368653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
66468653Smckusick 			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
66568653Smckusick 		}
66668653Smckusick 		if (vap->va_mtime.ts_sec != VNOVAL) {
66768653Smckusick 			if (vap->va_mtime.ts_sec != time.tv_sec) {
66868653Smckusick 				nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
66968653Smckusick 				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
67068653Smckusick 				txdr_nfsv3time(&vap->va_atime, tl);
67168653Smckusick 			} else {
67268653Smckusick 				nfsm_build(tl, u_long *, NFSX_UNSIGNED);
67368653Smckusick 				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
67468653Smckusick 			}
67568653Smckusick 		} else {
67668653Smckusick 			nfsm_build(tl, u_long *, NFSX_UNSIGNED);
67768653Smckusick 			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
67868653Smckusick 		}
67968653Smckusick 		nfsm_build(tl, u_long *, NFSX_UNSIGNED);
68068653Smckusick 		*tl = nfs_false;
68156289Smckusick 	} else {
68268653Smckusick 		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
68368653Smckusick 		if (vap->va_mode == (u_short)VNOVAL)
68468653Smckusick 			sp->sa_mode = VNOVAL;
68568653Smckusick 		else
68668653Smckusick 			sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode);
68768653Smckusick 		if (vap->va_uid == (uid_t)VNOVAL)
68868653Smckusick 			sp->sa_uid = VNOVAL;
68968653Smckusick 		else
69068653Smckusick 			sp->sa_uid = txdr_unsigned(vap->va_uid);
69168653Smckusick 		if (vap->va_gid == (gid_t)VNOVAL)
69268653Smckusick 			sp->sa_gid = VNOVAL;
69368653Smckusick 		else
69468653Smckusick 			sp->sa_gid = txdr_unsigned(vap->va_gid);
69568653Smckusick 		sp->sa_size = txdr_unsigned(vap->va_size);
69668653Smckusick 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
69768653Smckusick 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
69856289Smckusick 	}
69968653Smckusick 	nfsm_request(vp, NFSPROC_SETATTR, procp, cred);
70068653Smckusick 	if (v3) {
70168653Smckusick 		nfsm_wcc_data(vp, wccflag);
70268653Smckusick 	} else
70368653Smckusick 		nfsm_loadattr(vp, (struct vattr *)0);
70438414Smckusick 	nfsm_reqdone;
70538414Smckusick 	return (error);
70638414Smckusick }
70738414Smckusick 
70838414Smckusick /*
70938414Smckusick  * nfs lookup call, one step at a time...
71038414Smckusick  * First look in cache
71138414Smckusick  * If not found, unlock the directory nfsnode and do the rpc
71238414Smckusick  */
71352234Sheideman int
nfs_lookup(ap)71453806Smckusick nfs_lookup(ap)
71554451Smckusick 	struct vop_lookup_args /* {
71654451Smckusick 		struct vnodeop_desc *a_desc;
71754451Smckusick 		struct vnode *a_dvp;
71854451Smckusick 		struct vnode **a_vpp;
71954451Smckusick 		struct componentname *a_cnp;
72054451Smckusick 	} */ *ap;
72138414Smckusick {
72253806Smckusick 	register struct componentname *cnp = ap->a_cnp;
72353806Smckusick 	register struct vnode *dvp = ap->a_dvp;
72454668Smckusick 	register struct vnode **vpp = ap->a_vpp;
72555184Smckusick 	register int flags = cnp->cn_flags;
72669424Smckusick 	register struct proc *p = cnp->cn_proc;
72768653Smckusick 	register struct vnode *newvp;
72848054Smckusick 	register u_long *tl;
72939488Smckusick 	register caddr_t cp;
73039488Smckusick 	register long t1, t2;
73152196Smckusick 	struct nfsmount *nmp;
73239488Smckusick 	caddr_t bpos, dpos, cp2;
73339488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
73438414Smckusick 	long len;
73568653Smckusick 	nfsfh_t *fhp;
73638414Smckusick 	struct nfsnode *np;
73768653Smckusick 	int lockparent, wantparent, error = 0, attrflag, fhsize;
73868653Smckusick 	int v3 = NFS_ISV3(dvp);
73938414Smckusick 
740*69743Smckusick 	if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
741*69743Smckusick 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
742*69743Smckusick 		return (EROFS);
74368653Smckusick 	*vpp = NULLVP;
74453806Smckusick 	if (dvp->v_type != VDIR)
74538414Smckusick 		return (ENOTDIR);
74655184Smckusick 	lockparent = flags & LOCKPARENT;
74755184Smckusick 	wantparent = flags & (LOCKPARENT|WANTPARENT);
74853806Smckusick 	nmp = VFSTONFS(dvp->v_mount);
74953806Smckusick 	np = VTONFS(dvp);
75054668Smckusick 	if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
75138884Smacklem 		struct vattr vattr;
75238884Smacklem 		int vpid;
75338884Smacklem 
75468653Smckusick 		newvp = *vpp;
75568653Smckusick 		vpid = newvp->v_id;
75638414Smckusick 		/*
75738884Smacklem 		 * See the comment starting `Step through' in ufs/ufs_lookup.c
75838884Smacklem 		 * for an explanation of the locking protocol
75938414Smckusick 		 */
76068653Smckusick 		if (dvp == newvp) {
76168653Smckusick 			VREF(newvp);
76239441Smckusick 			error = 0;
76352196Smckusick 		} else
76469424Smckusick 			error = vget(newvp, LK_EXCLUSIVE, p);
76539441Smckusick 		if (!error) {
76668653Smckusick 			if (vpid == newvp->v_id) {
76768653Smckusick 			   if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
76868653Smckusick 			    && vattr.va_ctime.ts_sec == VTONFS(newvp)->n_ctime) {
76939441Smckusick 				nfsstats.lookupcache_hits++;
77053806Smckusick 				if (cnp->cn_nameiop != LOOKUP &&
77155184Smckusick 				    (flags & ISLASTCN))
77253806Smckusick 					cnp->cn_flags |= SAVENAME;
77339441Smckusick 				return (0);
77440251Smckusick 			   }
77568653Smckusick 			   cache_purge(newvp);
77639441Smckusick 			}
77768653Smckusick 			vrele(newvp);
77838884Smacklem 		}
77954668Smckusick 		*vpp = NULLVP;
78052196Smckusick 	}
78139341Smckusick 	error = 0;
78268653Smckusick 	newvp = NULLVP;
78338414Smckusick 	nfsstats.lookupcache_misses++;
78438414Smckusick 	nfsstats.rpccnt[NFSPROC_LOOKUP]++;
78553806Smckusick 	len = cnp->cn_namelen;
78668653Smckusick 	nfsm_reqhead(dvp, NFSPROC_LOOKUP,
78768653Smckusick 		NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
78868653Smckusick 	nfsm_fhtom(dvp, v3);
78953806Smckusick 	nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
79053806Smckusick 	nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred);
79138414Smckusick 	if (error) {
79268653Smckusick 		nfsm_postop_attr(dvp, attrflag);
79368653Smckusick 		m_freem(mrep);
79468653Smckusick 		goto nfsmout;
79538414Smckusick 	}
79668653Smckusick 	nfsm_getfh(fhp, fhsize, v3);
79738414Smckusick 
79838414Smckusick 	/*
79952196Smckusick 	 * Handle RENAME case...
80038414Smckusick 	 */
80155184Smckusick 	if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
80268653Smckusick 		if (NFS_CMPFH(np, fhp, fhsize)) {
80338414Smckusick 			m_freem(mrep);
80438414Smckusick 			return (EISDIR);
80538414Smckusick 		}
80668653Smckusick 		if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) {
80738414Smckusick 			m_freem(mrep);
80838414Smckusick 			return (error);
80938414Smckusick 		}
81038414Smckusick 		newvp = NFSTOV(np);
81168653Smckusick 		if (v3) {
81268653Smckusick 			nfsm_postop_attr(newvp, attrflag);
81368653Smckusick 			nfsm_postop_attr(dvp, attrflag);
81468653Smckusick 		} else
81568653Smckusick 			nfsm_loadattr(newvp, (struct vattr *)0);
81654668Smckusick 		*vpp = newvp;
81745037Smckusick 		m_freem(mrep);
81853806Smckusick 		cnp->cn_flags |= SAVENAME;
81938414Smckusick 		return (0);
82038414Smckusick 	}
82138414Smckusick 
82268653Smckusick 	if (NFS_CMPFH(np, fhp, fhsize)) {
82353806Smckusick 		VREF(dvp);
82453806Smckusick 		newvp = dvp;
82538414Smckusick 	} else {
82668653Smckusick 		if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) {
82738414Smckusick 			m_freem(mrep);
82838414Smckusick 			return (error);
82938414Smckusick 		}
83038414Smckusick 		newvp = NFSTOV(np);
83138414Smckusick 	}
83268653Smckusick 	if (v3) {
83368653Smckusick 		nfsm_postop_attr(newvp, attrflag);
83468653Smckusick 		nfsm_postop_attr(dvp, attrflag);
83568653Smckusick 	} else
83668653Smckusick 		nfsm_loadattr(newvp, (struct vattr *)0);
83755184Smckusick 	if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
83853806Smckusick 		cnp->cn_flags |= SAVENAME;
83955184Smckusick 	if ((cnp->cn_flags & MAKEENTRY) &&
84055184Smckusick 	    (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
84168653Smckusick 		np->n_ctime = np->n_vattr.va_ctime.ts_sec;
84268653Smckusick 		cache_enter(dvp, newvp, cnp);
84340251Smckusick 	}
84468653Smckusick 	*vpp = newvp;
84568653Smckusick 	nfsm_reqdone;
84668653Smckusick 	if (error) {
84768653Smckusick 		if (newvp != NULLVP)
84868653Smckusick 			vrele(newvp);
84968653Smckusick 		if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
850*69743Smckusick 		    (flags & ISLASTCN) && error == ENOENT) {
851*69743Smckusick 			if (dvp->v_mount->mnt_flag & MNT_RDONLY)
852*69743Smckusick 				error = EROFS;
853*69743Smckusick 			else
854*69743Smckusick 				error = EJUSTRETURN;
855*69743Smckusick 		}
85668653Smckusick 		if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
85768653Smckusick 			cnp->cn_flags |= SAVENAME;
85868653Smckusick 	}
85968653Smckusick 	return (error);
86038414Smckusick }
86138414Smckusick 
86238414Smckusick /*
86341905Smckusick  * nfs read call.
86441905Smckusick  * Just call nfs_bioread() to do the work.
86541905Smckusick  */
86652234Sheideman int
nfs_read(ap)86753806Smckusick nfs_read(ap)
86854668Smckusick 	struct vop_read_args /* {
86954668Smckusick 		struct vnode *a_vp;
87054668Smckusick 		struct uio *a_uio;
87154668Smckusick 		int  a_ioflag;
87254668Smckusick 		struct ucred *a_cred;
87354668Smckusick 	} */ *ap;
87441905Smckusick {
87553806Smckusick 	register struct vnode *vp = ap->a_vp;
87653806Smckusick 
87753806Smckusick 	if (vp->v_type != VREG)
87841905Smckusick 		return (EPERM);
87953806Smckusick 	return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
88041905Smckusick }
88141905Smckusick 
88241905Smckusick /*
88338414Smckusick  * nfs readlink call
88438414Smckusick  */
88552234Sheideman int
nfs_readlink(ap)88653806Smckusick nfs_readlink(ap)
88754668Smckusick 	struct vop_readlink_args /* {
88854668Smckusick 		struct vnode *a_vp;
88954668Smckusick 		struct uio *a_uio;
89054668Smckusick 		struct ucred *a_cred;
89154668Smckusick 	} */ *ap;
89241905Smckusick {
89353806Smckusick 	register struct vnode *vp = ap->a_vp;
89453806Smckusick 
89553806Smckusick 	if (vp->v_type != VLNK)
89641905Smckusick 		return (EPERM);
89753806Smckusick 	return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred));
89841905Smckusick }
89941905Smckusick 
90041905Smckusick /*
90141905Smckusick  * Do a readlink rpc.
90241905Smckusick  * Called by nfs_doio() from below the buffer cache.
90341905Smckusick  */
90452234Sheideman int
nfs_readlinkrpc(vp,uiop,cred)90548054Smckusick nfs_readlinkrpc(vp, uiop, cred)
90639488Smckusick 	register struct vnode *vp;
90738414Smckusick 	struct uio *uiop;
90838414Smckusick 	struct ucred *cred;
90938414Smckusick {
91048054Smckusick 	register u_long *tl;
91139488Smckusick 	register caddr_t cp;
91268653Smckusick 	register long t1, t2;
91339488Smckusick 	caddr_t bpos, dpos, cp2;
91468653Smckusick 	int error = 0, len, attrflag;
91539488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
91668653Smckusick 	int v3 = NFS_ISV3(vp);
91738414Smckusick 
91838414Smckusick 	nfsstats.rpccnt[NFSPROC_READLINK]++;
91968653Smckusick 	nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3));
92068653Smckusick 	nfsm_fhtom(vp, v3);
92152196Smckusick 	nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred);
92268653Smckusick 	if (v3)
92368653Smckusick 		nfsm_postop_attr(vp, attrflag);
92468653Smckusick 	if (!error) {
92568653Smckusick 		nfsm_strsiz(len, NFS_MAXPATHLEN);
92668653Smckusick 		nfsm_mtouio(uiop, len);
92768653Smckusick 	}
92838414Smckusick 	nfsm_reqdone;
92938414Smckusick 	return (error);
93038414Smckusick }
93138414Smckusick 
93238414Smckusick /*
93341905Smckusick  * nfs read rpc call
93441905Smckusick  * Ditto above
93538414Smckusick  */
93652234Sheideman int
nfs_readrpc(vp,uiop,cred)93748054Smckusick nfs_readrpc(vp, uiop, cred)
93839488Smckusick 	register struct vnode *vp;
93938414Smckusick 	struct uio *uiop;
94038414Smckusick 	struct ucred *cred;
94138414Smckusick {
94248054Smckusick 	register u_long *tl;
94339488Smckusick 	register caddr_t cp;
94468653Smckusick 	register long t1, t2;
94539488Smckusick 	caddr_t bpos, dpos, cp2;
94639488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
94738414Smckusick 	struct nfsmount *nmp;
94868653Smckusick 	int error = 0, len, retlen, tsiz, eof, attrflag;
94968653Smckusick 	int v3 = NFS_ISV3(vp);
95038414Smckusick 
95168653Smckusick #ifndef nolint
95268653Smckusick 	eof = 0;
95368653Smckusick #endif
95441398Smckusick 	nmp = VFSTONFS(vp->v_mount);
95538414Smckusick 	tsiz = uiop->uio_resid;
95668653Smckusick 	if (uiop->uio_offset + tsiz > 0xffffffff && !v3)
95756289Smckusick 		return (EFBIG);
95838414Smckusick 	while (tsiz > 0) {
95938414Smckusick 		nfsstats.rpccnt[NFSPROC_READ]++;
96038414Smckusick 		len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
96168653Smckusick 		nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3);
96268653Smckusick 		nfsm_fhtom(vp, v3);
96368653Smckusick 		nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3);
96468653Smckusick 		if (v3) {
96556289Smckusick 			txdr_hyper(&uiop->uio_offset, tl);
96656289Smckusick 			*(tl + 2) = txdr_unsigned(len);
96756289Smckusick 		} else {
96856289Smckusick 			*tl++ = txdr_unsigned(uiop->uio_offset);
96956289Smckusick 			*tl++ = txdr_unsigned(len);
97056289Smckusick 			*tl = 0;
97156289Smckusick 		}
97252196Smckusick 		nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred);
97368653Smckusick 		if (v3) {
97468653Smckusick 			nfsm_postop_attr(vp, attrflag);
97568653Smckusick 			if (error) {
97668653Smckusick 				m_freem(mrep);
97768653Smckusick 				goto nfsmout;
97868653Smckusick 			}
97968653Smckusick 			nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
98068653Smckusick 			eof = fxdr_unsigned(int, *(tl + 1));
98168653Smckusick 		} else
98268653Smckusick 			nfsm_loadattr(vp, (struct vattr *)0);
98338414Smckusick 		nfsm_strsiz(retlen, nmp->nm_rsize);
98438414Smckusick 		nfsm_mtouio(uiop, retlen);
98538414Smckusick 		m_freem(mrep);
98668653Smckusick 		tsiz -= retlen;
98768653Smckusick 		if (v3) {
98868653Smckusick 			if (eof || retlen == 0)
98968653Smckusick 				tsiz = 0;
99068653Smckusick 		} else if (retlen < len)
99138414Smckusick 			tsiz = 0;
99238414Smckusick 	}
99338414Smckusick nfsmout:
99438414Smckusick 	return (error);
99538414Smckusick }
99638414Smckusick 
99738414Smckusick /*
99838414Smckusick  * nfs write call
99938414Smckusick  */
100052234Sheideman int
nfs_writerpc(vp,uiop,cred,iomode,must_commit)100168653Smckusick nfs_writerpc(vp, uiop, cred, iomode, must_commit)
100239488Smckusick 	register struct vnode *vp;
100368653Smckusick 	register struct uio *uiop;
100438414Smckusick 	struct ucred *cred;
100568653Smckusick 	int *iomode, *must_commit;
100638414Smckusick {
100748054Smckusick 	register u_long *tl;
100839488Smckusick 	register caddr_t cp;
100968653Smckusick 	register int t1, t2, backup;
101052196Smckusick 	caddr_t bpos, dpos, cp2;
101139488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
101268653Smckusick 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
101352196Smckusick 	struct nfsnode *np = VTONFS(vp);
101452196Smckusick 	u_quad_t frev;
101568653Smckusick 	int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit;
101668653Smckusick 	int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC;
101738414Smckusick 
101868653Smckusick #ifndef DIAGNOSTIC
101968653Smckusick 	if (uiop->uio_iovcnt != 1)
102068653Smckusick 		panic("nfs: writerpc iovcnt > 1");
102168653Smckusick #endif
102268653Smckusick 	*must_commit = 0;
102338414Smckusick 	tsiz = uiop->uio_resid;
102468653Smckusick 	if (uiop->uio_offset + tsiz > 0xffffffff && !v3)
102556289Smckusick 		return (EFBIG);
102638414Smckusick 	while (tsiz > 0) {
102738414Smckusick 		nfsstats.rpccnt[NFSPROC_WRITE]++;
102838414Smckusick 		len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
102952196Smckusick 		nfsm_reqhead(vp, NFSPROC_WRITE,
103068653Smckusick 			NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len));
103168653Smckusick 		nfsm_fhtom(vp, v3);
103268653Smckusick 		if (v3) {
103368653Smckusick 			nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
103456289Smckusick 			txdr_hyper(&uiop->uio_offset, tl);
103556289Smckusick 			tl += 2;
103668653Smckusick 			*tl++ = txdr_unsigned(len);
103768653Smckusick 			*tl++ = txdr_unsigned(*iomode);
103856289Smckusick 		} else {
103968653Smckusick 			nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
104056289Smckusick 			*++tl = txdr_unsigned(uiop->uio_offset);
104156289Smckusick 			tl += 2;
104256289Smckusick 		}
104356289Smckusick 		*tl = txdr_unsigned(len);
104438414Smckusick 		nfsm_uiotom(uiop, len);
104552196Smckusick 		nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred);
104668653Smckusick 		if (v3) {
104768653Smckusick 			wccflag = NFSV3_WCCCHK;
104868653Smckusick 			nfsm_wcc_data(vp, wccflag);
104968653Smckusick 			if (!error) {
105068653Smckusick 				nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED +
105168653Smckusick 					NFSX_V3WRITEVERF);
105268653Smckusick 				rlen = fxdr_unsigned(int, *tl++);
105368653Smckusick 				if (rlen == 0) {
105468653Smckusick 					error = NFSERR_IO;
105568653Smckusick 					break;
105668653Smckusick 				} else if (rlen < len) {
105768653Smckusick 					backup = len - rlen;
105868653Smckusick 					uiop->uio_iov->iov_base -= backup;
105968653Smckusick 					uiop->uio_iov->iov_len += backup;
106068653Smckusick 					uiop->uio_offset -= backup;
106168653Smckusick 					uiop->uio_resid += backup;
106268653Smckusick 					len = rlen;
106368653Smckusick 				}
106468653Smckusick 				commit = fxdr_unsigned(int, *tl++);
106568653Smckusick 
106668653Smckusick 				/*
106768653Smckusick 				 * Return the lowest committment level
106868653Smckusick 				 * obtained by any of the RPCs.
106968653Smckusick 				 */
107068653Smckusick 				if (committed == NFSV3WRITE_FILESYNC)
107168653Smckusick 					committed = commit;
107268653Smckusick 				else if (committed == NFSV3WRITE_DATASYNC &&
107368653Smckusick 					commit == NFSV3WRITE_UNSTABLE)
107468653Smckusick 					committed = commit;
107568653Smckusick 				if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) {
107668653Smckusick 				    bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
107768653Smckusick 					NFSX_V3WRITEVERF);
107868653Smckusick 				    nmp->nm_flag |= NFSMNT_HASWRITEVERF;
107968653Smckusick 				} else if (bcmp((caddr_t)tl,
108068653Smckusick 				    (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) {
108168653Smckusick 				    *must_commit = 1;
108268653Smckusick 				    bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
108368653Smckusick 					NFSX_V3WRITEVERF);
108468653Smckusick 				}
108568653Smckusick 			}
108668653Smckusick 		} else
108768653Smckusick 		    nfsm_loadattr(vp, (struct vattr *)0);
108868653Smckusick 		if (wccflag)
108968653Smckusick 		    VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec;
109038414Smckusick 		m_freem(mrep);
109138414Smckusick 		tsiz -= len;
109238414Smckusick 	}
109338414Smckusick nfsmout:
109468653Smckusick 	*iomode = committed;
109552196Smckusick 	if (error)
109652196Smckusick 		uiop->uio_resid = tsiz;
109738414Smckusick 	return (error);
109838414Smckusick }
109938414Smckusick 
110038414Smckusick /*
110168653Smckusick  * nfs mknod rpc
110268653Smckusick  * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
110368653Smckusick  * mode set to specify the file type and the size field for rdev.
110439459Smckusick  */
110552234Sheideman int
nfs_mknodrpc(dvp,vpp,cnp,vap)110668653Smckusick nfs_mknodrpc(dvp, vpp, cnp, vap)
110768653Smckusick 	register struct vnode *dvp;
110868653Smckusick 	register struct vnode **vpp;
110968653Smckusick 	register struct componentname *cnp;
111068653Smckusick 	register struct vattr *vap;
111139459Smckusick {
111242246Smckusick 	register struct nfsv2_sattr *sp;
111368653Smckusick 	register struct nfsv3_sattr *sp3;
111448054Smckusick 	register u_long *tl;
111542246Smckusick 	register caddr_t cp;
111656289Smckusick 	register long t1, t2;
111768653Smckusick 	struct vnode *newvp = (struct vnode *)0;
111868653Smckusick 	struct nfsnode *np;
111959116Smckusick 	struct vattr vattr;
112056289Smckusick 	char *cp2;
112142246Smckusick 	caddr_t bpos, dpos;
112268653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0;
112342246Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
112442246Smckusick 	u_long rdev;
112568653Smckusick 	int v3 = NFS_ISV3(dvp);
112639459Smckusick 
112753806Smckusick 	if (vap->va_type == VCHR || vap->va_type == VBLK)
112853806Smckusick 		rdev = txdr_unsigned(vap->va_rdev);
112968653Smckusick 	else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
113042246Smckusick 		rdev = 0xffffffff;
113142246Smckusick 	else {
113253806Smckusick 		VOP_ABORTOP(dvp, cnp);
113353806Smckusick 		vput(dvp);
113442246Smckusick 		return (EOPNOTSUPP);
113542246Smckusick 	}
113659116Smckusick 	if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
113759116Smckusick 		VOP_ABORTOP(dvp, cnp);
113859116Smckusick 		vput(dvp);
113959116Smckusick 		return (error);
114059116Smckusick 	}
114168653Smckusick 	nfsstats.rpccnt[NFSPROC_MKNOD]++;
114268653Smckusick 	nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED +
114368653Smckusick 		+ nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
114468653Smckusick 	nfsm_fhtom(dvp, v3);
114553806Smckusick 	nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
114668653Smckusick 	if (v3) {
114768653Smckusick 		nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR);
114868653Smckusick 		*tl++ = vtonfsv3_type(vap->va_type);
114968653Smckusick 		sp3 = (struct nfsv3_sattr *)tl;
115068653Smckusick 		nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
115168653Smckusick 		if (vap->va_type == VCHR || vap->va_type == VBLK) {
115268653Smckusick 			nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
115368653Smckusick 			*tl++ = txdr_unsigned(major(vap->va_rdev));
115468653Smckusick 			*tl = txdr_unsigned(minor(vap->va_rdev));
115568653Smckusick 		}
115656289Smckusick 	} else {
115768653Smckusick 		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
115868653Smckusick 		sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
115968653Smckusick 		sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
116068653Smckusick 		sp->sa_gid = txdr_unsigned(vattr.va_gid);
116168653Smckusick 		sp->sa_size = rdev;
116268653Smckusick 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
116368653Smckusick 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
116456289Smckusick 	}
116568653Smckusick 	nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred);
116668653Smckusick 	if (!error) {
116768653Smckusick 		nfsm_mtofh(dvp, newvp, v3, gotvp);
116868653Smckusick 		if (!gotvp) {
116968653Smckusick 			if (newvp) {
117068653Smckusick 				vrele(newvp);
117168653Smckusick 				newvp = (struct vnode *)0;
117268653Smckusick 			}
117368653Smckusick 			error = nfs_lookitup(dvp, cnp->cn_nameptr,
117468653Smckusick 			    cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
117568653Smckusick 			if (!error)
117668653Smckusick 				newvp = NFSTOV(np);
117768653Smckusick 		}
117868653Smckusick 	}
117968653Smckusick 	if (v3)
118068653Smckusick 		nfsm_wcc_data(dvp, wccflag);
118142246Smckusick 	nfsm_reqdone;
118268653Smckusick 	if (error) {
118368653Smckusick 		if (newvp)
118468653Smckusick 			vrele(newvp);
118568653Smckusick 	} else {
118668653Smckusick 		if (cnp->cn_flags & MAKEENTRY)
118768653Smckusick 			cache_enter(dvp, newvp, cnp);
118868653Smckusick 		*vpp = newvp;
118968653Smckusick 	}
119053806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
119153806Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
119268653Smckusick 	if (!wccflag)
119368653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
119453806Smckusick 	vrele(dvp);
119542246Smckusick 	return (error);
119639459Smckusick }
119739459Smckusick 
119839459Smckusick /*
119968653Smckusick  * nfs mknod vop
120068653Smckusick  * just call nfs_mknodrpc() to do the work.
120168653Smckusick  */
120268653Smckusick /* ARGSUSED */
120368653Smckusick int
nfs_mknod(ap)120468653Smckusick nfs_mknod(ap)
120568653Smckusick 	struct vop_mknod_args /* {
120668653Smckusick 		struct vnode *a_dvp;
120768653Smckusick 		struct vnode **a_vpp;
120868653Smckusick 		struct componentname *a_cnp;
120968653Smckusick 		struct vattr *a_vap;
121068653Smckusick 	} */ *ap;
121168653Smckusick {
121268653Smckusick 	struct vnode *newvp;
121368653Smckusick 	int error;
121468653Smckusick 
121568653Smckusick 	error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap);
121668653Smckusick 	if (!error)
121768653Smckusick 		vrele(newvp);
121868653Smckusick 	return (error);
121968653Smckusick }
122068653Smckusick 
122168653Smckusick static u_long create_verf;
122268653Smckusick /*
122338414Smckusick  * nfs file create call
122438414Smckusick  */
122552234Sheideman int
nfs_create(ap)122653806Smckusick nfs_create(ap)
122754668Smckusick 	struct vop_create_args /* {
122854668Smckusick 		struct vnode *a_dvp;
122954668Smckusick 		struct vnode **a_vpp;
123054668Smckusick 		struct componentname *a_cnp;
123154668Smckusick 		struct vattr *a_vap;
123254668Smckusick 	} */ *ap;
123338414Smckusick {
123453806Smckusick 	register struct vnode *dvp = ap->a_dvp;
123553806Smckusick 	register struct vattr *vap = ap->a_vap;
123653806Smckusick 	register struct componentname *cnp = ap->a_cnp;
123738884Smacklem 	register struct nfsv2_sattr *sp;
123868653Smckusick 	register struct nfsv3_sattr *sp3;
123948054Smckusick 	register u_long *tl;
124039488Smckusick 	register caddr_t cp;
124139488Smckusick 	register long t1, t2;
124268653Smckusick 	struct nfsnode *np = (struct nfsnode *)0;
124368653Smckusick 	struct vnode *newvp = (struct vnode *)0;
124439488Smckusick 	caddr_t bpos, dpos, cp2;
124568653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0;
124639488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
124759116Smckusick 	struct vattr vattr;
124868653Smckusick 	int v3 = NFS_ISV3(dvp);
124938414Smckusick 
125068653Smckusick 	/*
125168653Smckusick 	 * Oops, not for me..
125268653Smckusick 	 */
125368653Smckusick 	if (vap->va_type == VSOCK)
125468653Smckusick 		return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
125568653Smckusick 
125659116Smckusick 	if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
125759116Smckusick 		VOP_ABORTOP(dvp, cnp);
125859116Smckusick 		vput(dvp);
125959116Smckusick 		return (error);
126059116Smckusick 	}
126168653Smckusick 	if (vap->va_vaflags & VA_EXCLUSIVE)
126268653Smckusick 		fmode |= O_EXCL;
126368653Smckusick again:
126438414Smckusick 	nfsstats.rpccnt[NFSPROC_CREATE]++;
126568653Smckusick 	nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED +
126668653Smckusick 		nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3));
126768653Smckusick 	nfsm_fhtom(dvp, v3);
126853806Smckusick 	nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
126968653Smckusick 	if (v3) {
127068653Smckusick 		nfsm_build(tl, u_long *, NFSX_UNSIGNED);
127168653Smckusick 		if (fmode & O_EXCL) {
127268653Smckusick 		    *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE);
127368653Smckusick 		    nfsm_build(tl, u_long *, NFSX_V3CREATEVERF);
127468653Smckusick 		    if (in_ifaddr)
127568653Smckusick 			*tl++ = IA_SIN(in_ifaddr)->sin_addr.s_addr;
127668653Smckusick 		    else
127768653Smckusick 			*tl++ = create_verf;
127868653Smckusick 		    *tl = ++create_verf;
127968653Smckusick 		} else {
128068653Smckusick 		    *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);
128168653Smckusick 		    nfsm_build(tl, u_long *, NFSX_V3SRVSATTR);
128268653Smckusick 		    sp3 = (struct nfsv3_sattr *)tl;
128368653Smckusick 		    nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
128468653Smckusick 		}
128556289Smckusick 	} else {
128668653Smckusick 		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
128768653Smckusick 		sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
128868653Smckusick 		sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
128968653Smckusick 		sp->sa_gid = txdr_unsigned(vattr.va_gid);
129068653Smckusick 		sp->sa_size = 0;
129168653Smckusick 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
129268653Smckusick 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
129356289Smckusick 	}
129453806Smckusick 	nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred);
129568653Smckusick 	if (!error) {
129668653Smckusick 		nfsm_mtofh(dvp, newvp, v3, gotvp);
129768653Smckusick 		if (!gotvp) {
129868653Smckusick 			if (newvp) {
129968653Smckusick 				vrele(newvp);
130068653Smckusick 				newvp = (struct vnode *)0;
130168653Smckusick 			}
130268653Smckusick 			error = nfs_lookitup(dvp, cnp->cn_nameptr,
130368653Smckusick 			    cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np);
130468653Smckusick 			if (!error)
130568653Smckusick 				newvp = NFSTOV(np);
130668653Smckusick 		}
130768653Smckusick 	}
130868653Smckusick 	if (v3)
130968653Smckusick 		nfsm_wcc_data(dvp, wccflag);
131038414Smckusick 	nfsm_reqdone;
131168653Smckusick 	if (error) {
131268653Smckusick 		if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) {
131368653Smckusick 			fmode &= ~O_EXCL;
131468653Smckusick 			goto again;
131568653Smckusick 		}
131668653Smckusick 		if (newvp)
131768653Smckusick 			vrele(newvp);
131868653Smckusick 	} else if (v3 && (fmode & O_EXCL))
131968653Smckusick 		error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc);
132068653Smckusick 	if (!error) {
132168653Smckusick 		if (cnp->cn_flags & MAKEENTRY)
132268653Smckusick 			cache_enter(dvp, newvp, cnp);
132368653Smckusick 		*ap->a_vpp = newvp;
132468653Smckusick 	}
132553806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
132653806Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
132768653Smckusick 	if (!wccflag)
132868653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
132953806Smckusick 	vrele(dvp);
133038414Smckusick 	return (error);
133138414Smckusick }
133238414Smckusick 
133338414Smckusick /*
133438414Smckusick  * nfs file remove call
133541905Smckusick  * To try and make nfs semantics closer to ufs semantics, a file that has
133641905Smckusick  * other processes using the vnode is renamed instead of removed and then
133739341Smckusick  * removed later on the last close.
133841905Smckusick  * - If v_usecount > 1
133939341Smckusick  *	  If a rename is not already in the works
134039341Smckusick  *	     call nfs_sillyrename() to set it up
134139341Smckusick  *     else
134239341Smckusick  *	  do the remove rpc
134338414Smckusick  */
134452234Sheideman int
nfs_remove(ap)134553806Smckusick nfs_remove(ap)
134654451Smckusick 	struct vop_remove_args /* {
134754451Smckusick 		struct vnodeop_desc *a_desc;
134854451Smckusick 		struct vnode * a_dvp;
134954451Smckusick 		struct vnode * a_vp;
135054451Smckusick 		struct componentname * a_cnp;
135154451Smckusick 	} */ *ap;
135238414Smckusick {
135353806Smckusick 	register struct vnode *vp = ap->a_vp;
135453806Smckusick 	register struct vnode *dvp = ap->a_dvp;
135553806Smckusick 	register struct componentname *cnp = ap->a_cnp;
135653806Smckusick 	register struct nfsnode *np = VTONFS(vp);
135748054Smckusick 	register u_long *tl;
135839488Smckusick 	register caddr_t cp;
135952196Smckusick 	register long t2;
136039488Smckusick 	caddr_t bpos, dpos;
136139488Smckusick 	int error = 0;
136239488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
136368653Smckusick 	struct vattr vattr;
136468653Smckusick 	int v3 = NFS_ISV3(dvp);
136538414Smckusick 
136668653Smckusick #ifndef DIAGNOSTIC
136768653Smckusick 	if ((cnp->cn_flags & HASBUF) == 0)
136868653Smckusick 		panic("nfs_remove: no name");
136968653Smckusick 	if (vp->v_usecount < 1)
137068653Smckusick 		panic("nfs_remove: bad v_usecount");
137168653Smckusick #endif
137268653Smckusick 	if (vp->v_usecount == 1 || (np->n_sillyrename &&
137368653Smckusick 	    VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 &&
137468653Smckusick 	    vattr.va_nlink > 1)) {
137552196Smckusick 		/*
137652196Smckusick 		 * Purge the name cache so that the chance of a lookup for
137752196Smckusick 		 * the name succeeding while the remove is in progress is
137852196Smckusick 		 * minimized. Without node locking it can still happen, such
137952196Smckusick 		 * that an I/O op returns ESTALE, but since you get this if
138052196Smckusick 		 * another host removes the file..
138152196Smckusick 		 */
138253806Smckusick 		cache_purge(vp);
138352196Smckusick 		/*
138468653Smckusick 		 * throw away biocache buffers, mainly to avoid
138568653Smckusick 		 * unnecessary delayed writes later.
138652196Smckusick 		 */
138757791Smckusick 		error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1);
138852196Smckusick 		/* Do the rpc */
138968653Smckusick 		if (error != EINTR)
139068653Smckusick 			error = nfs_removerpc(dvp, cnp->cn_nameptr,
139168653Smckusick 				cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc);
139239751Smckusick 		/*
139339751Smckusick 		 * Kludge City: If the first reply to the remove rpc is lost..
139439751Smckusick 		 *   the reply to the retransmitted request will be ENOENT
139539751Smckusick 		 *   since the file was in fact removed
139639751Smckusick 		 *   Therefore, we cheat and return success.
139739751Smckusick 		 */
139839751Smckusick 		if (error == ENOENT)
139939751Smckusick 			error = 0;
140068653Smckusick 	} else if (!np->n_sillyrename)
140168653Smckusick 		error = nfs_sillyrename(dvp, vp, cnp);
140268653Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
140340042Smckusick 	np->n_attrstamp = 0;
140453806Smckusick 	vrele(dvp);
140553806Smckusick 	vrele(vp);
140638414Smckusick 	return (error);
140738414Smckusick }
140838414Smckusick 
140938414Smckusick /*
141038414Smckusick  * nfs file remove rpc called from nfs_inactive
141138414Smckusick  */
141252234Sheideman int
nfs_removeit(sp)141354451Smckusick nfs_removeit(sp)
141448364Smckusick 	register struct sillyrename *sp;
141538414Smckusick {
141668653Smckusick 
141768653Smckusick 	return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred,
141868653Smckusick 		(struct proc *)0));
141968653Smckusick }
142068653Smckusick 
142168653Smckusick /*
142268653Smckusick  * Nfs remove rpc, called from nfs_remove() and nfs_removeit().
142368653Smckusick  */
142468653Smckusick int
nfs_removerpc(dvp,name,namelen,cred,proc)142568653Smckusick nfs_removerpc(dvp, name, namelen, cred, proc)
142668653Smckusick 	register struct vnode *dvp;
142768653Smckusick 	char *name;
142868653Smckusick 	int namelen;
142968653Smckusick 	struct ucred *cred;
143068653Smckusick 	struct proc *proc;
143168653Smckusick {
143248054Smckusick 	register u_long *tl;
143339488Smckusick 	register caddr_t cp;
143468653Smckusick 	register long t1, t2;
143568653Smckusick 	caddr_t bpos, dpos, cp2;
143668653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR;
143739488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
143868653Smckusick 	int v3 = NFS_ISV3(dvp);
143938414Smckusick 
144038414Smckusick 	nfsstats.rpccnt[NFSPROC_REMOVE]++;
144168653Smckusick 	nfsm_reqhead(dvp, NFSPROC_REMOVE,
144268653Smckusick 		NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen));
144368653Smckusick 	nfsm_fhtom(dvp, v3);
144468653Smckusick 	nfsm_strtom(name, namelen, NFS_MAXNAMLEN);
144568653Smckusick 	nfsm_request(dvp, NFSPROC_REMOVE, proc, cred);
144668653Smckusick 	if (v3)
144768653Smckusick 		nfsm_wcc_data(dvp, wccflag);
144838414Smckusick 	nfsm_reqdone;
144968653Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
145068653Smckusick 	if (!wccflag)
145168653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
145238414Smckusick 	return (error);
145338414Smckusick }
145438414Smckusick 
145538414Smckusick /*
145638414Smckusick  * nfs file rename call
145738414Smckusick  */
145852234Sheideman int
nfs_rename(ap)145953806Smckusick nfs_rename(ap)
146054668Smckusick 	struct vop_rename_args  /* {
146154668Smckusick 		struct vnode *a_fdvp;
146254668Smckusick 		struct vnode *a_fvp;
146354668Smckusick 		struct componentname *a_fcnp;
146454668Smckusick 		struct vnode *a_tdvp;
146554668Smckusick 		struct vnode *a_tvp;
146654668Smckusick 		struct componentname *a_tcnp;
146754668Smckusick 	} */ *ap;
146838414Smckusick {
146953806Smckusick 	register struct vnode *fvp = ap->a_fvp;
147053806Smckusick 	register struct vnode *tvp = ap->a_tvp;
147153806Smckusick 	register struct vnode *fdvp = ap->a_fdvp;
147253806Smckusick 	register struct vnode *tdvp = ap->a_tdvp;
147353806Smckusick 	register struct componentname *tcnp = ap->a_tcnp;
147453806Smckusick 	register struct componentname *fcnp = ap->a_fcnp;
147568653Smckusick 	int error;
147638414Smckusick 
147768653Smckusick #ifndef DIAGNOSTIC
147868653Smckusick 	if ((tcnp->cn_flags & HASBUF) == 0 ||
147968653Smckusick 	    (fcnp->cn_flags & HASBUF) == 0)
148068653Smckusick 		panic("nfs_rename: no name");
148168653Smckusick #endif
148253804Spendry 	/* Check for cross-device rename */
148353806Smckusick 	if ((fvp->v_mount != tdvp->v_mount) ||
148453806Smckusick 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
148553804Spendry 		error = EXDEV;
148653804Spendry 		goto out;
148753804Spendry 	}
148853804Spendry 
148968653Smckusick 	/*
149068653Smckusick 	 * If the tvp exists and is in use, sillyrename it before doing the
149168653Smckusick 	 * rename of the new file over it.
149268653Smckusick 	 */
149368653Smckusick 	if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename &&
149468653Smckusick 		!nfs_sillyrename(tdvp, tvp, tcnp)) {
149568653Smckusick 		vrele(tvp);
149668653Smckusick 		tvp = NULL;
149768653Smckusick 	}
149853804Spendry 
149968653Smckusick 	error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen,
150068653Smckusick 		tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
150168653Smckusick 		tcnp->cn_proc);
150268653Smckusick 
150353806Smckusick 	if (fvp->v_type == VDIR) {
150453806Smckusick 		if (tvp != NULL && tvp->v_type == VDIR)
150553806Smckusick 			cache_purge(tdvp);
150653806Smckusick 		cache_purge(fdvp);
150738414Smckusick 	}
150853804Spendry out:
150953806Smckusick 	if (tdvp == tvp)
151053806Smckusick 		vrele(tdvp);
151143360Smckusick 	else
151253806Smckusick 		vput(tdvp);
151353806Smckusick 	if (tvp)
151453806Smckusick 		vput(tvp);
151553806Smckusick 	vrele(fdvp);
151653806Smckusick 	vrele(fvp);
151740112Smckusick 	/*
151840112Smckusick 	 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
151940112Smckusick 	 */
152040112Smckusick 	if (error == ENOENT)
152140112Smckusick 		error = 0;
152238414Smckusick 	return (error);
152338414Smckusick }
152438414Smckusick 
152538414Smckusick /*
152641905Smckusick  * nfs file rename rpc called from nfs_remove() above
152738414Smckusick  */
152852234Sheideman int
nfs_renameit(sdvp,scnp,sp)152952234Sheideman nfs_renameit(sdvp, scnp, sp)
153052234Sheideman 	struct vnode *sdvp;
153152234Sheideman 	struct componentname *scnp;
153248364Smckusick 	register struct sillyrename *sp;
153338414Smckusick {
153468653Smckusick 	return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen,
153568653Smckusick 		sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_proc));
153668653Smckusick }
153768653Smckusick 
153868653Smckusick /*
153968653Smckusick  * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit().
154068653Smckusick  */
154168653Smckusick int
nfs_renamerpc(fdvp,fnameptr,fnamelen,tdvp,tnameptr,tnamelen,cred,proc)154268653Smckusick nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc)
154368653Smckusick 	register struct vnode *fdvp;
154468653Smckusick 	char *fnameptr;
154568653Smckusick 	int fnamelen;
154668653Smckusick 	register struct vnode *tdvp;
154768653Smckusick 	char *tnameptr;
154868653Smckusick 	int tnamelen;
154968653Smckusick 	struct ucred *cred;
155068653Smckusick 	struct proc *proc;
155168653Smckusick {
155248054Smckusick 	register u_long *tl;
155339488Smckusick 	register caddr_t cp;
155468653Smckusick 	register long t1, t2;
155568653Smckusick 	caddr_t bpos, dpos, cp2;
155668653Smckusick 	int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR;
155739488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
155868653Smckusick 	int v3 = NFS_ISV3(fdvp);
155938414Smckusick 
156038414Smckusick 	nfsstats.rpccnt[NFSPROC_RENAME]++;
156168653Smckusick 	nfsm_reqhead(fdvp, NFSPROC_RENAME,
156268653Smckusick 		(NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) +
156368653Smckusick 		nfsm_rndup(tnamelen));
156468653Smckusick 	nfsm_fhtom(fdvp, v3);
156568653Smckusick 	nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN);
156668653Smckusick 	nfsm_fhtom(tdvp, v3);
156768653Smckusick 	nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN);
156868653Smckusick 	nfsm_request(fdvp, NFSPROC_RENAME, proc, cred);
156968653Smckusick 	if (v3) {
157068653Smckusick 		nfsm_wcc_data(fdvp, fwccflag);
157168653Smckusick 		nfsm_wcc_data(tdvp, twccflag);
157268653Smckusick 	}
157338414Smckusick 	nfsm_reqdone;
157468653Smckusick 	VTONFS(fdvp)->n_flag |= NMODIFIED;
157568653Smckusick 	VTONFS(tdvp)->n_flag |= NMODIFIED;
157668653Smckusick 	if (!fwccflag)
157768653Smckusick 		VTONFS(fdvp)->n_attrstamp = 0;
157868653Smckusick 	if (!twccflag)
157968653Smckusick 		VTONFS(tdvp)->n_attrstamp = 0;
158038414Smckusick 	return (error);
158138414Smckusick }
158238414Smckusick 
158338414Smckusick /*
158438414Smckusick  * nfs hard link create call
158538414Smckusick  */
158652234Sheideman int
nfs_link(ap)158753806Smckusick nfs_link(ap)
158854668Smckusick 	struct vop_link_args /* {
158954668Smckusick 		struct vnode *a_vp;
159054668Smckusick 		struct vnode *a_tdvp;
159154668Smckusick 		struct componentname *a_cnp;
159254668Smckusick 	} */ *ap;
159338414Smckusick {
159453806Smckusick 	register struct vnode *vp = ap->a_vp;
159553806Smckusick 	register struct vnode *tdvp = ap->a_tdvp;
159653806Smckusick 	register struct componentname *cnp = ap->a_cnp;
159748054Smckusick 	register u_long *tl;
159839488Smckusick 	register caddr_t cp;
159968653Smckusick 	register long t1, t2;
160068653Smckusick 	caddr_t bpos, dpos, cp2;
160168653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0;
160239488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
160368653Smckusick 	int v3 = NFS_ISV3(vp);
160438414Smckusick 
160568653Smckusick 	if (vp->v_mount != tdvp->v_mount) {
160668653Smckusick 		/*VOP_ABORTOP(vp, cnp);*/
160768653Smckusick 		if (tdvp == vp)
160868653Smckusick 			vrele(tdvp);
160968653Smckusick 		else
161068653Smckusick 			vput(tdvp);
161153804Spendry 		return (EXDEV);
161253804Spendry 	}
161353804Spendry 
161467659Smckusick 	/*
161567659Smckusick 	 * Push all writes to the server, so that the attribute cache
161667659Smckusick 	 * doesn't get "out of sync" with the server.
161767659Smckusick 	 * XXX There should be a better way!
161867659Smckusick 	 */
161968539Smckusick 	VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc);
162067659Smckusick 
162138414Smckusick 	nfsstats.rpccnt[NFSPROC_LINK]++;
162268539Smckusick 	nfsm_reqhead(vp, NFSPROC_LINK,
162368653Smckusick 		NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
162468653Smckusick 	nfsm_fhtom(vp, v3);
162568653Smckusick 	nfsm_fhtom(tdvp, v3);
162653806Smckusick 	nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
162768539Smckusick 	nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred);
162868653Smckusick 	if (v3) {
162968653Smckusick 		nfsm_postop_attr(vp, attrflag);
163068653Smckusick 		nfsm_wcc_data(tdvp, wccflag);
163168653Smckusick 	}
163238414Smckusick 	nfsm_reqdone;
163353806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
163468653Smckusick 	VTONFS(tdvp)->n_flag |= NMODIFIED;
163568653Smckusick 	if (!attrflag)
163668653Smckusick 		VTONFS(vp)->n_attrstamp = 0;
163768653Smckusick 	if (!wccflag)
163868653Smckusick 		VTONFS(tdvp)->n_attrstamp = 0;
163968653Smckusick 	vrele(tdvp);
164040112Smckusick 	/*
164140112Smckusick 	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
164240112Smckusick 	 */
164340112Smckusick 	if (error == EEXIST)
164440112Smckusick 		error = 0;
164538414Smckusick 	return (error);
164638414Smckusick }
164738414Smckusick 
164838414Smckusick /*
164938414Smckusick  * nfs symbolic link create call
165038414Smckusick  */
165152234Sheideman int
nfs_symlink(ap)165253806Smckusick nfs_symlink(ap)
165354668Smckusick 	struct vop_symlink_args /* {
165454668Smckusick 		struct vnode *a_dvp;
165554668Smckusick 		struct vnode **a_vpp;
165654668Smckusick 		struct componentname *a_cnp;
165754668Smckusick 		struct vattr *a_vap;
165854668Smckusick 		char *a_target;
165954668Smckusick 	} */ *ap;
166038414Smckusick {
166153806Smckusick 	register struct vnode *dvp = ap->a_dvp;
166253806Smckusick 	register struct vattr *vap = ap->a_vap;
166353806Smckusick 	register struct componentname *cnp = ap->a_cnp;
166438884Smacklem 	register struct nfsv2_sattr *sp;
166568653Smckusick 	register struct nfsv3_sattr *sp3;
166648054Smckusick 	register u_long *tl;
166739488Smckusick 	register caddr_t cp;
166868653Smckusick 	register long t1, t2;
166968653Smckusick 	caddr_t bpos, dpos, cp2;
167068653Smckusick 	int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp;
167139488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
167268653Smckusick 	struct vnode *newvp = (struct vnode *)0;
167368653Smckusick 	int v3 = NFS_ISV3(dvp);
167438414Smckusick 
167538414Smckusick 	nfsstats.rpccnt[NFSPROC_SYMLINK]++;
167653600Sheideman 	slen = strlen(ap->a_target);
167768653Smckusick 	nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED +
167868653Smckusick 	    nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3));
167968653Smckusick 	nfsm_fhtom(dvp, v3);
168053806Smckusick 	nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
168168653Smckusick 	if (v3) {
168268653Smckusick 		nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
168368653Smckusick 		nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid,
168468653Smckusick 			cnp->cn_cred->cr_gid);
168568653Smckusick 	}
168653600Sheideman 	nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN);
168768653Smckusick 	if (!v3) {
168868653Smckusick 		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
168968653Smckusick 		sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode);
169068653Smckusick 		sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
169168653Smckusick 		sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid);
169268653Smckusick 		sp->sa_size = -1;
169368653Smckusick 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
169468653Smckusick 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
169556289Smckusick 	}
169653806Smckusick 	nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred);
169768653Smckusick 	if (v3) {
169868653Smckusick 		if (!error)
169968653Smckusick 			nfsm_mtofh(dvp, newvp, v3, gotvp);
170068653Smckusick 		nfsm_wcc_data(dvp, wccflag);
170168653Smckusick 	}
170238414Smckusick 	nfsm_reqdone;
170368653Smckusick 	if (newvp)
170468653Smckusick 		vrele(newvp);
170553806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
170653806Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
170768653Smckusick 	if (!wccflag)
170868653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
170953806Smckusick 	vrele(dvp);
171040112Smckusick 	/*
171140112Smckusick 	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
171240112Smckusick 	 */
171340112Smckusick 	if (error == EEXIST)
171440112Smckusick 		error = 0;
171538414Smckusick 	return (error);
171638414Smckusick }
171738414Smckusick 
171838414Smckusick /*
171938414Smckusick  * nfs make dir call
172038414Smckusick  */
172152234Sheideman int
nfs_mkdir(ap)172253806Smckusick nfs_mkdir(ap)
172354668Smckusick 	struct vop_mkdir_args /* {
172454668Smckusick 		struct vnode *a_dvp;
172554668Smckusick 		struct vnode **a_vpp;
172654668Smckusick 		struct componentname *a_cnp;
172754668Smckusick 		struct vattr *a_vap;
172854668Smckusick 	} */ *ap;
172938414Smckusick {
173053806Smckusick 	register struct vnode *dvp = ap->a_dvp;
173153806Smckusick 	register struct vattr *vap = ap->a_vap;
173253806Smckusick 	register struct componentname *cnp = ap->a_cnp;
173338884Smacklem 	register struct nfsv2_sattr *sp;
173468653Smckusick 	register struct nfsv3_sattr *sp3;
173548054Smckusick 	register u_long *tl;
173639488Smckusick 	register caddr_t cp;
173739488Smckusick 	register long t1, t2;
173841905Smckusick 	register int len;
173968653Smckusick 	struct nfsnode *np = (struct nfsnode *)0;
174068653Smckusick 	struct vnode *newvp = (struct vnode *)0;
174139488Smckusick 	caddr_t bpos, dpos, cp2;
174268653Smckusick 	nfsfh_t *fhp;
174368653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR, attrflag;
174468653Smckusick 	int fhsize, gotvp = 0;
174539488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
174659116Smckusick 	struct vattr vattr;
174768653Smckusick 	int v3 = NFS_ISV3(dvp);
174838414Smckusick 
174959116Smckusick 	if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
175059116Smckusick 		VOP_ABORTOP(dvp, cnp);
175159116Smckusick 		vput(dvp);
175259116Smckusick 		return (error);
175359116Smckusick 	}
175453806Smckusick 	len = cnp->cn_namelen;
175538414Smckusick 	nfsstats.rpccnt[NFSPROC_MKDIR]++;
175653806Smckusick 	nfsm_reqhead(dvp, NFSPROC_MKDIR,
175768653Smckusick 	  NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3));
175868653Smckusick 	nfsm_fhtom(dvp, v3);
175953806Smckusick 	nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN);
176068653Smckusick 	if (v3) {
176168653Smckusick 		nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR);
176268653Smckusick 		nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid);
176356289Smckusick 	} else {
176468653Smckusick 		nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
176568653Smckusick 		sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode);
176668653Smckusick 		sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid);
176768653Smckusick 		sp->sa_gid = txdr_unsigned(vattr.va_gid);
176868653Smckusick 		sp->sa_size = -1;
176968653Smckusick 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
177068653Smckusick 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
177156289Smckusick 	}
177253806Smckusick 	nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred);
177368653Smckusick 	if (!error)
177468653Smckusick 		nfsm_mtofh(dvp, newvp, v3, gotvp);
177568653Smckusick 	if (v3)
177668653Smckusick 		nfsm_wcc_data(dvp, wccflag);
177738414Smckusick 	nfsm_reqdone;
177853806Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
177968653Smckusick 	if (!wccflag)
178068653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
178140112Smckusick 	/*
178241905Smckusick 	 * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
178341905Smckusick 	 * if we can succeed in looking up the directory.
178440112Smckusick 	 */
178568653Smckusick 	if (error == EEXIST || (!error && !gotvp)) {
178668653Smckusick 		if (newvp) {
178768653Smckusick 			vrele(newvp);
178868653Smckusick 			newvp = (struct vnode *)0;
178941905Smckusick 		}
179068653Smckusick 		error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred,
179168653Smckusick 			cnp->cn_proc, &np);
179268653Smckusick 		if (!error) {
179368653Smckusick 			newvp = NFSTOV(np);
179468653Smckusick 			if (newvp->v_type != VDIR)
179568653Smckusick 				error = EEXIST;
179668653Smckusick 		}
179741905Smckusick 	}
179868653Smckusick 	if (error) {
179968653Smckusick 		if (newvp)
180068653Smckusick 			vrele(newvp);
180168653Smckusick 	} else
180268653Smckusick 		*ap->a_vpp = newvp;
180353806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
180453806Smckusick 	vrele(dvp);
180538414Smckusick 	return (error);
180638414Smckusick }
180738414Smckusick 
180838414Smckusick /*
180938414Smckusick  * nfs remove directory call
181038414Smckusick  */
181152234Sheideman int
nfs_rmdir(ap)181253806Smckusick nfs_rmdir(ap)
181354668Smckusick 	struct vop_rmdir_args /* {
181454668Smckusick 		struct vnode *a_dvp;
181554668Smckusick 		struct vnode *a_vp;
181654668Smckusick 		struct componentname *a_cnp;
181754668Smckusick 	} */ *ap;
181838414Smckusick {
181953806Smckusick 	register struct vnode *vp = ap->a_vp;
182053806Smckusick 	register struct vnode *dvp = ap->a_dvp;
182153806Smckusick 	register struct componentname *cnp = ap->a_cnp;
182248054Smckusick 	register u_long *tl;
182339488Smckusick 	register caddr_t cp;
182468653Smckusick 	register long t1, t2;
182568653Smckusick 	caddr_t bpos, dpos, cp2;
182668653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR;
182739488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
182868653Smckusick 	int v3 = NFS_ISV3(dvp);
182938414Smckusick 
183053806Smckusick 	if (dvp == vp) {
183153806Smckusick 		vrele(dvp);
183253806Smckusick 		vrele(dvp);
183353806Smckusick 		FREE(cnp->cn_pnbuf, M_NAMEI);
183438414Smckusick 		return (EINVAL);
183538414Smckusick 	}
183638414Smckusick 	nfsstats.rpccnt[NFSPROC_RMDIR]++;
183753806Smckusick 	nfsm_reqhead(dvp, NFSPROC_RMDIR,
183868653Smckusick 		NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen));
183968653Smckusick 	nfsm_fhtom(dvp, v3);
184053806Smckusick 	nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN);
184153806Smckusick 	nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred);
184268653Smckusick 	if (v3)
184368653Smckusick 		nfsm_wcc_data(dvp, wccflag);
184438414Smckusick 	nfsm_reqdone;
184553806Smckusick 	FREE(cnp->cn_pnbuf, M_NAMEI);
184653806Smckusick 	VTONFS(dvp)->n_flag |= NMODIFIED;
184768653Smckusick 	if (!wccflag)
184868653Smckusick 		VTONFS(dvp)->n_attrstamp = 0;
184953806Smckusick 	cache_purge(dvp);
185053806Smckusick 	cache_purge(vp);
185153806Smckusick 	vrele(vp);
185253806Smckusick 	vrele(dvp);
185340112Smckusick 	/*
185440112Smckusick 	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
185540112Smckusick 	 */
185640112Smckusick 	if (error == ENOENT)
185740112Smckusick 		error = 0;
185838414Smckusick 	return (error);
185938414Smckusick }
186038414Smckusick 
186138414Smckusick /*
186238414Smckusick  * nfs readdir call
186338414Smckusick  */
186452234Sheideman int
nfs_readdir(ap)186553806Smckusick nfs_readdir(ap)
186654668Smckusick 	struct vop_readdir_args /* {
186754668Smckusick 		struct vnode *a_vp;
186854668Smckusick 		struct uio *a_uio;
186954668Smckusick 		struct ucred *a_cred;
187054668Smckusick 	} */ *ap;
187138414Smckusick {
187253806Smckusick 	register struct vnode *vp = ap->a_vp;
187353806Smckusick 	register struct nfsnode *np = VTONFS(vp);
187453806Smckusick 	register struct uio *uio = ap->a_uio;
187541905Smckusick 	int tresid, error;
187641905Smckusick 	struct vattr vattr;
187741905Smckusick 
187853806Smckusick 	if (vp->v_type != VDIR)
187941905Smckusick 		return (EPERM);
188041905Smckusick 	/*
188141905Smckusick 	 * First, check for hit on the EOF offset cache
188241905Smckusick 	 */
188368653Smckusick 	if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
188452196Smckusick 	    (np->n_flag & NMODIFIED) == 0) {
188553806Smckusick 		if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
188668653Smckusick 			if (NQNFS_CKCACHABLE(vp, ND_READ)) {
188752196Smckusick 				nfsstats.direofcache_hits++;
188852196Smckusick 				return (0);
188952196Smckusick 			}
189053806Smckusick 		} else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 &&
189154106Smckusick 			np->n_mtime == vattr.va_mtime.ts_sec) {
189252196Smckusick 			nfsstats.direofcache_hits++;
189352196Smckusick 			return (0);
189452196Smckusick 		}
189541905Smckusick 	}
189641905Smckusick 
189741905Smckusick 	/*
189841905Smckusick 	 * Call nfs_bioread() to do the real work.
189941905Smckusick 	 */
190053806Smckusick 	tresid = uio->uio_resid;
190153806Smckusick 	error = nfs_bioread(vp, uio, 0, ap->a_cred);
190241905Smckusick 
190354451Smckusick 	if (!error && uio->uio_resid == tresid)
190441905Smckusick 		nfsstats.direofcache_misses++;
190541905Smckusick 	return (error);
190641905Smckusick }
190741905Smckusick 
190841905Smckusick /*
190941905Smckusick  * Readdir rpc call.
191041905Smckusick  * Called from below the buffer cache by nfs_doio().
191141905Smckusick  */
191252234Sheideman int
nfs_readdirrpc(vp,uiop,cred)191348054Smckusick nfs_readdirrpc(vp, uiop, cred)
191468653Smckusick 	struct vnode *vp;
191568653Smckusick 	register struct uio *uiop;
191641905Smckusick 	struct ucred *cred;
191741905Smckusick {
191868653Smckusick 	register int len, left;
191954740Smckusick 	register struct dirent *dp;
192048054Smckusick 	register u_long *tl;
192139488Smckusick 	register caddr_t cp;
192268653Smckusick 	register long t1, t2;
192368653Smckusick 	register nfsuint64 *cookiep;
192439488Smckusick 	caddr_t bpos, dpos, cp2;
192539488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
192668653Smckusick 	nfsuint64 cookie;
192768653Smckusick 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
192868653Smckusick 	struct nfsnode *dnp = VTONFS(vp);
192968653Smckusick 	nfsfh_t *fhp;
193068653Smckusick 	u_quad_t frev, fileno;
193168653Smckusick 	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1, i;
193268653Smckusick 	int cachable, attrflag, fhsize;
193368653Smckusick 	int v3 = NFS_ISV3(vp);
193438414Smckusick 
193568653Smckusick #ifndef nolint
193668653Smckusick 	dp = (struct dirent *)0;
193768653Smckusick #endif
193868653Smckusick #ifndef DIAGNOSTIC
193968653Smckusick 	if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) ||
194068653Smckusick 		(uiop->uio_resid & (NFS_DIRBLKSIZ - 1)))
194168653Smckusick 		panic("nfs readdirrpc bad uio");
194268653Smckusick #endif
194368653Smckusick 
194440296Smckusick 	/*
194568653Smckusick 	 * If there is no cookie, assume end of directory.
194668653Smckusick 	 */
194768653Smckusick 	cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
194868653Smckusick 	if (cookiep)
194968653Smckusick 		cookie = *cookiep;
195068653Smckusick 	else
195168653Smckusick 		return (0);
195268653Smckusick 	/*
195368653Smckusick 	 * Loop around doing readdir rpc's of size nm_readdirsize
195468653Smckusick 	 * truncated to a multiple of DIRBLKSIZ.
195541905Smckusick 	 * The stopping criteria is EOF or buffer full.
195640296Smckusick 	 */
195768653Smckusick 	while (more_dirs && bigenough) {
195840296Smckusick 		nfsstats.rpccnt[NFSPROC_READDIR]++;
195968653Smckusick 		nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) +
196068653Smckusick 			NFSX_READDIR(v3));
196168653Smckusick 		nfsm_fhtom(vp, v3);
196268653Smckusick 		if (v3) {
196368653Smckusick 			nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED);
196468653Smckusick 			*tl++ = cookie.nfsuquad[0];
196568653Smckusick 			*tl++ = cookie.nfsuquad[1];
196668653Smckusick 			*tl++ = dnp->n_cookieverf.nfsuquad[0];
196768653Smckusick 			*tl++ = dnp->n_cookieverf.nfsuquad[1];
196868653Smckusick 		} else {
196968653Smckusick 			nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
197068653Smckusick 			*tl++ = cookie.nfsuquad[0];
197168653Smckusick 		}
197268653Smckusick 		*tl = txdr_unsigned(nmp->nm_readdirsize);
197352196Smckusick 		nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred);
197468653Smckusick 		if (v3) {
197568653Smckusick 			nfsm_postop_attr(vp, attrflag);
197668653Smckusick 			if (!error) {
197768653Smckusick 				nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
197868653Smckusick 				dnp->n_cookieverf.nfsuquad[0] = *tl++;
197968653Smckusick 				dnp->n_cookieverf.nfsuquad[1] = *tl;
198068653Smckusick 			} else {
198168653Smckusick 				m_freem(mrep);
198268653Smckusick 				goto nfsmout;
198368653Smckusick 			}
198468653Smckusick 		}
198552196Smckusick 		nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
198648054Smckusick 		more_dirs = fxdr_unsigned(int, *tl);
198740296Smckusick 
198840296Smckusick 		/* loop thru the dir entries, doctoring them to 4bsd form */
198968653Smckusick 		while (more_dirs && bigenough) {
199068653Smckusick 			if (v3) {
199168653Smckusick 				nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
199268653Smckusick 				fxdr_hyper(tl, &fileno);
199368653Smckusick 				len = fxdr_unsigned(int, *(tl + 2));
199468653Smckusick 			} else {
199568653Smckusick 				nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
199668653Smckusick 				fileno = fxdr_unsigned(u_quad_t, *tl++);
199768653Smckusick 				len = fxdr_unsigned(int, *tl);
199868653Smckusick 			}
199940296Smckusick 			if (len <= 0 || len > NFS_MAXNAMLEN) {
200040296Smckusick 				error = EBADRPC;
200140296Smckusick 				m_freem(mrep);
200240296Smckusick 				goto nfsmout;
200340296Smckusick 			}
200440296Smckusick 			tlen = nfsm_rndup(len);
200568653Smckusick 			if (tlen == len)
200668653Smckusick 				tlen += 4;	/* To ensure null termination */
200768653Smckusick 			left = DIRBLKSIZ - blksiz;
200868653Smckusick 			if ((tlen + DIRHDSIZ) > left) {
200968653Smckusick 				dp->d_reclen += left;
201068653Smckusick 				uiop->uio_iov->iov_base += left;
201168653Smckusick 				uiop->uio_iov->iov_len -= left;
201268653Smckusick 				uiop->uio_offset += left;
201368653Smckusick 				uiop->uio_resid -= left;
201468653Smckusick 				blksiz = 0;
201540296Smckusick 			}
201668653Smckusick 			if ((tlen + DIRHDSIZ) > uiop->uio_resid)
201768653Smckusick 				bigenough = 0;
201868653Smckusick 			if (bigenough) {
201968653Smckusick 				dp = (struct dirent *)uiop->uio_iov->iov_base;
202068653Smckusick 				dp->d_fileno = (int)fileno;
202168653Smckusick 				dp->d_namlen = len;
202268653Smckusick 				dp->d_reclen = tlen + DIRHDSIZ;
202368653Smckusick 				dp->d_type = DT_UNKNOWN;
202468653Smckusick 				blksiz += dp->d_reclen;
202568653Smckusick 				if (blksiz == DIRBLKSIZ)
202668653Smckusick 					blksiz = 0;
202768653Smckusick 				uiop->uio_offset += DIRHDSIZ;
202868653Smckusick 				uiop->uio_resid -= DIRHDSIZ;
202968653Smckusick 				uiop->uio_iov->iov_base += DIRHDSIZ;
203068653Smckusick 				uiop->uio_iov->iov_len -= DIRHDSIZ;
203168653Smckusick 				nfsm_mtouio(uiop, len);
203268653Smckusick 				cp = uiop->uio_iov->iov_base;
203368653Smckusick 				tlen -= len;
203468653Smckusick 				*cp = '\0';	/* null terminate */
203568653Smckusick 				uiop->uio_iov->iov_base += tlen;
203668653Smckusick 				uiop->uio_iov->iov_len -= tlen;
203768653Smckusick 				uiop->uio_offset += tlen;
203868653Smckusick 				uiop->uio_resid -= tlen;
203968653Smckusick 			} else
204068653Smckusick 				nfsm_adv(nfsm_rndup(len));
204168653Smckusick 			if (v3) {
204268653Smckusick 				nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
204368653Smckusick 			} else {
204468653Smckusick 				nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
204568653Smckusick 			}
204668653Smckusick 			if (bigenough) {
204768653Smckusick 				cookie.nfsuquad[0] = *tl++;
204868653Smckusick 				if (v3)
204968653Smckusick 					cookie.nfsuquad[1] = *tl++;
205068653Smckusick 			} else if (v3)
205168653Smckusick 				tl += 2;
205268653Smckusick 			else
205368653Smckusick 				tl++;
205448054Smckusick 			more_dirs = fxdr_unsigned(int, *tl);
205540296Smckusick 		}
205640296Smckusick 		/*
205740296Smckusick 		 * If at end of rpc data, get the eof boolean
205840296Smckusick 		 */
205940296Smckusick 		if (!more_dirs) {
206063483Smckusick 			nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
206148054Smckusick 			more_dirs = (fxdr_unsigned(int, *tl) == 0);
206238414Smckusick 		}
206340296Smckusick 		m_freem(mrep);
206438414Smckusick 	}
206541905Smckusick 	/*
206668653Smckusick 	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
206741905Smckusick 	 * by increasing d_reclen for the last record.
206841905Smckusick 	 */
206968653Smckusick 	if (blksiz > 0) {
207068653Smckusick 		left = DIRBLKSIZ - blksiz;
207168653Smckusick 		dp->d_reclen += left;
207268653Smckusick 		uiop->uio_iov->iov_base += left;
207368653Smckusick 		uiop->uio_iov->iov_len -= left;
207468653Smckusick 		uiop->uio_offset += left;
207568653Smckusick 		uiop->uio_resid -= left;
207641905Smckusick 	}
207768653Smckusick 
207868653Smckusick 	/*
207968653Smckusick 	 * We are now either at the end of the directory or have filled the
208068653Smckusick 	 * block.
208168653Smckusick 	 */
208268653Smckusick 	if (bigenough)
208368653Smckusick 		dnp->n_direofoffset = uiop->uio_offset;
208468653Smckusick 	else {
208568653Smckusick 		if (uiop->uio_resid > 0)
208668653Smckusick 			printf("EEK! readdirrpc resid > 0\n");
208768653Smckusick 		cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
208868653Smckusick 		*cookiep = cookie;
208968653Smckusick 	}
209040296Smckusick nfsmout:
209138414Smckusick 	return (error);
209238414Smckusick }
209338414Smckusick 
209452196Smckusick /*
209568653Smckusick  * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc().
209652196Smckusick  */
209752234Sheideman int
nfs_readdirplusrpc(vp,uiop,cred)209868653Smckusick nfs_readdirplusrpc(vp, uiop, cred)
209952196Smckusick 	struct vnode *vp;
210052196Smckusick 	register struct uio *uiop;
210152196Smckusick 	struct ucred *cred;
210252196Smckusick {
210368653Smckusick 	register int len, left;
210454740Smckusick 	register struct dirent *dp;
210552196Smckusick 	register u_long *tl;
210652196Smckusick 	register caddr_t cp;
210768653Smckusick 	register long t1, t2;
210868653Smckusick 	register struct vnode *newvp;
210968653Smckusick 	register nfsuint64 *cookiep;
211068653Smckusick 	caddr_t bpos, dpos, cp2, dpossav1, dpossav2;
211168653Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2;
211252196Smckusick 	struct nameidata nami, *ndp = &nami;
211352317Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
211468653Smckusick 	nfsuint64 cookie;
211568653Smckusick 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
211668653Smckusick 	struct nfsnode *dnp = VTONFS(vp), *np;
211768653Smckusick 	nfsfh_t *fhp;
211868653Smckusick 	u_quad_t frev, fileno;
211968653Smckusick 	int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i;
212068653Smckusick 	int cachable, attrflag, fhsize;
212152196Smckusick 
212268653Smckusick #ifndef nolint
212368653Smckusick 	dp = (struct dirent *)0;
212468653Smckusick #endif
212568653Smckusick #ifndef DIAGNOSTIC
212668653Smckusick 	if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) ||
212768653Smckusick 		(uiop->uio_resid & (DIRBLKSIZ - 1)))
212868653Smckusick 		panic("nfs readdirplusrpc bad uio");
212968653Smckusick #endif
213052196Smckusick 	ndp->ni_dvp = vp;
213152196Smckusick 	newvp = NULLVP;
213268653Smckusick 
213352196Smckusick 	/*
213468653Smckusick 	 * If there is no cookie, assume end of directory.
213568653Smckusick 	 */
213668653Smckusick 	cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0);
213768653Smckusick 	if (cookiep)
213868653Smckusick 		cookie = *cookiep;
213968653Smckusick 	else
214068653Smckusick 		return (0);
214168653Smckusick 	/*
214268653Smckusick 	 * Loop around doing readdir rpc's of size nm_readdirsize
214368653Smckusick 	 * truncated to a multiple of DIRBLKSIZ.
214452196Smckusick 	 * The stopping criteria is EOF or buffer full.
214552196Smckusick 	 */
214668653Smckusick 	while (more_dirs && bigenough) {
214768653Smckusick 		nfsstats.rpccnt[NFSPROC_READDIRPLUS]++;
214868653Smckusick 		nfsm_reqhead(vp, NFSPROC_READDIRPLUS,
214968653Smckusick 			NFSX_FH(1) + 6 * NFSX_UNSIGNED);
215068653Smckusick 		nfsm_fhtom(vp, 1);
215168653Smckusick  		nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED);
215268653Smckusick 		*tl++ = cookie.nfsuquad[0];
215368653Smckusick 		*tl++ = cookie.nfsuquad[1];
215468653Smckusick 		*tl++ = dnp->n_cookieverf.nfsuquad[0];
215568653Smckusick 		*tl++ = dnp->n_cookieverf.nfsuquad[1];
215668653Smckusick 		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
215768653Smckusick 		*tl = txdr_unsigned(nmp->nm_rsize);
215868653Smckusick 		nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred);
215968653Smckusick 		nfsm_postop_attr(vp, attrflag);
216068653Smckusick 		if (error) {
216168653Smckusick 			m_freem(mrep);
216268653Smckusick 			goto nfsmout;
216368653Smckusick 		}
216468653Smckusick 		nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
216568653Smckusick 		dnp->n_cookieverf.nfsuquad[0] = *tl++;
216668653Smckusick 		dnp->n_cookieverf.nfsuquad[1] = *tl++;
216752196Smckusick 		more_dirs = fxdr_unsigned(int, *tl);
216852196Smckusick 
216952196Smckusick 		/* loop thru the dir entries, doctoring them to 4bsd form */
217052196Smckusick 		while (more_dirs && bigenough) {
217168653Smckusick 			nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
217268653Smckusick 			fxdr_hyper(tl, &fileno);
217368653Smckusick 			len = fxdr_unsigned(int, *(tl + 2));
217452196Smckusick 			if (len <= 0 || len > NFS_MAXNAMLEN) {
217552196Smckusick 				error = EBADRPC;
217652196Smckusick 				m_freem(mrep);
217752196Smckusick 				goto nfsmout;
217852196Smckusick 			}
217968653Smckusick 			tlen = nfsm_rndup(len);
218068653Smckusick 			if (tlen == len)
218168653Smckusick 				tlen += 4;	/* To ensure null termination*/
218268653Smckusick 			left = DIRBLKSIZ - blksiz;
218368653Smckusick 			if ((tlen + DIRHDSIZ) > left) {
218468653Smckusick 				dp->d_reclen += left;
218568653Smckusick 				uiop->uio_iov->iov_base += left;
218668653Smckusick 				uiop->uio_iov->iov_len -= left;
218768653Smckusick 				uiop->uio_offset += left;
218868653Smckusick 				uiop->uio_resid -= left;
218968653Smckusick 				blksiz = 0;
219068653Smckusick 			}
219152196Smckusick 			if ((tlen + DIRHDSIZ) > uiop->uio_resid)
219252196Smckusick 				bigenough = 0;
219368653Smckusick 			if (bigenough) {
219454740Smckusick 				dp = (struct dirent *)uiop->uio_iov->iov_base;
219568653Smckusick 				dp->d_fileno = (int)fileno;
219652196Smckusick 				dp->d_namlen = len;
219752196Smckusick 				dp->d_reclen = tlen + DIRHDSIZ;
219868653Smckusick 				dp->d_type = DT_UNKNOWN;
219968653Smckusick 				blksiz += dp->d_reclen;
220068653Smckusick 				if (blksiz == DIRBLKSIZ)
220168653Smckusick 					blksiz = 0;
220268653Smckusick 				uiop->uio_offset += DIRHDSIZ;
220352196Smckusick 				uiop->uio_resid -= DIRHDSIZ;
220452196Smckusick 				uiop->uio_iov->iov_base += DIRHDSIZ;
220552196Smckusick 				uiop->uio_iov->iov_len -= DIRHDSIZ;
220652317Sheideman 				cnp->cn_nameptr = uiop->uio_iov->iov_base;
220752317Sheideman 				cnp->cn_namelen = len;
220852196Smckusick 				nfsm_mtouio(uiop, len);
220952196Smckusick 				cp = uiop->uio_iov->iov_base;
221052196Smckusick 				tlen -= len;
221168653Smckusick 				*cp = '\0';
221252196Smckusick 				uiop->uio_iov->iov_base += tlen;
221352196Smckusick 				uiop->uio_iov->iov_len -= tlen;
221468653Smckusick 				uiop->uio_offset += tlen;
221552196Smckusick 				uiop->uio_resid -= tlen;
221668653Smckusick 			} else
221768653Smckusick 				nfsm_adv(nfsm_rndup(len));
221868653Smckusick 			nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
221968653Smckusick 			if (bigenough) {
222068653Smckusick 				cookie.nfsuquad[0] = *tl++;
222168653Smckusick 				cookie.nfsuquad[1] = *tl++;
222268653Smckusick 			} else
222368653Smckusick 				tl += 2;
222468653Smckusick 
222568653Smckusick 			/*
222668653Smckusick 			 * Since the attributes are before the file handle
222768653Smckusick 			 * (sigh), we must skip over the attributes and then
222868653Smckusick 			 * come back and get them.
222968653Smckusick 			 */
223068653Smckusick 			attrflag = fxdr_unsigned(int, *tl);
223168653Smckusick 			if (attrflag) {
223268653Smckusick 			    dpossav1 = dpos;
223368653Smckusick 			    mdsav1 = md;
223468653Smckusick 			    nfsm_adv(NFSX_V3FATTR);
223568653Smckusick 			    nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
223668653Smckusick 			    doit = fxdr_unsigned(int, *tl);
223768653Smckusick 			    if (doit) {
223868653Smckusick 				nfsm_getfh(fhp, fhsize, 1);
223968653Smckusick 				if (NFS_CMPFH(dnp, fhp, fhsize)) {
224068653Smckusick 				    VREF(vp);
224168653Smckusick 				    newvp = vp;
224268653Smckusick 				    np = dnp;
224368653Smckusick 				} else {
224468653Smckusick 				    if (error = nfs_nget(vp->v_mount, fhp,
224568653Smckusick 					fhsize, &np))
224668653Smckusick 					doit = 0;
224768653Smckusick 				    else
224868653Smckusick 					newvp = NFSTOV(np);
224968653Smckusick 				}
225068653Smckusick 			    }
225168653Smckusick 			    if (doit) {
225268653Smckusick 				dpossav2 = dpos;
225368653Smckusick 				dpos = dpossav1;
225468653Smckusick 				mdsav2 = md;
225568653Smckusick 				md = mdsav1;
225668653Smckusick 				nfsm_loadattr(newvp, (struct vattr *)0);
225768653Smckusick 				dpos = dpossav2;
225868653Smckusick 				md = mdsav2;
225968653Smckusick 				dp->d_type =
226068653Smckusick 				    IFTODT(VTTOIF(np->n_vattr.va_type));
226168653Smckusick 				ndp->ni_vp = newvp;
226252317Sheideman 				cnp->cn_hash = 0;
226368653Smckusick 				for (cp = cnp->cn_nameptr, i = 1; i <= len;
226468653Smckusick 				    i++, cp++)
226568653Smckusick 				    cnp->cn_hash += (unsigned char)*cp * i;
226656927Smckusick 				if (cnp->cn_namelen <= NCHNAMLEN)
226757791Smckusick 				    cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp);
226868653Smckusick 			    }
226952196Smckusick 			} else {
227068653Smckusick 			    /* Just skip over the file handle */
227168653Smckusick 			    nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
227268653Smckusick 			    i = fxdr_unsigned(int, *tl);
227368653Smckusick 			    nfsm_adv(nfsm_rndup(i));
227452196Smckusick 			}
227552196Smckusick 			if (newvp != NULLVP) {
227668653Smckusick 			    vrele(newvp);
227768653Smckusick 			    newvp = NULLVP;
227852196Smckusick 			}
227968653Smckusick 			nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
228052196Smckusick 			more_dirs = fxdr_unsigned(int, *tl);
228152196Smckusick 		}
228252196Smckusick 		/*
228352196Smckusick 		 * If at end of rpc data, get the eof boolean
228452196Smckusick 		 */
228552196Smckusick 		if (!more_dirs) {
228652196Smckusick 			nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
228752196Smckusick 			more_dirs = (fxdr_unsigned(int, *tl) == 0);
228852196Smckusick 		}
228952196Smckusick 		m_freem(mrep);
229052196Smckusick 	}
229152196Smckusick 	/*
229252196Smckusick 	 * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ
229352196Smckusick 	 * by increasing d_reclen for the last record.
229452196Smckusick 	 */
229568653Smckusick 	if (blksiz > 0) {
229668653Smckusick 		left = DIRBLKSIZ - blksiz;
229768653Smckusick 		dp->d_reclen += left;
229868653Smckusick 		uiop->uio_iov->iov_base += left;
229968653Smckusick 		uiop->uio_iov->iov_len -= left;
230068653Smckusick 		uiop->uio_offset += left;
230168653Smckusick 		uiop->uio_resid -= left;
230252196Smckusick 	}
230368653Smckusick 
230468653Smckusick 	/*
230568653Smckusick 	 * We are now either at the end of the directory or have filled the
230668653Smckusick 	 * block.
230768653Smckusick 	 */
230868653Smckusick 	if (bigenough)
230968653Smckusick 		dnp->n_direofoffset = uiop->uio_offset;
231068653Smckusick 	else {
231168653Smckusick 		if (uiop->uio_resid > 0)
231268653Smckusick 			printf("EEK! readdirplusrpc resid > 0\n");
231368653Smckusick 		cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1);
231468653Smckusick 		*cookiep = cookie;
231568653Smckusick 	}
231652196Smckusick nfsmout:
231752196Smckusick 	if (newvp != NULLVP)
231852196Smckusick 		vrele(newvp);
231952196Smckusick 	return (error);
232052196Smckusick }
232139488Smckusick static char hextoasc[] = "0123456789abcdef";
232238414Smckusick 
232338414Smckusick /*
232438414Smckusick  * Silly rename. To make the NFS filesystem that is stateless look a little
232538414Smckusick  * more like the "ufs" a remove of an active vnode is translated to a rename
232638414Smckusick  * to a funny looking filename that is removed by nfs_inactive on the
232738414Smckusick  * nfsnode. There is the potential for another process on a different client
232838414Smckusick  * to create the same funny name between the nfs_lookitup() fails and the
232938414Smckusick  * nfs_rename() completes, but...
233038414Smckusick  */
233152234Sheideman int
nfs_sillyrename(dvp,vp,cnp)233252317Sheideman nfs_sillyrename(dvp, vp, cnp)
233352234Sheideman 	struct vnode *dvp, *vp;
233452234Sheideman 	struct componentname *cnp;
233538414Smckusick {
233638414Smckusick 	register struct sillyrename *sp;
233768653Smckusick 	struct nfsnode *np;
233838414Smckusick 	int error;
233938414Smckusick 	short pid;
234038414Smckusick 
234152234Sheideman 	cache_purge(dvp);
234252234Sheideman 	np = VTONFS(vp);
234368653Smckusick #ifndef DIAGNOSTIC
234468653Smckusick 	if (vp->v_type == VDIR)
234568653Smckusick 		panic("nfs: sillyrename dir");
234668653Smckusick #endif
234738414Smckusick 	MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
234848364Smckusick 		M_NFSREQ, M_WAITOK);
234952234Sheideman 	sp->s_cred = crdup(cnp->cn_cred);
235052234Sheideman 	sp->s_dvp = dvp;
235152234Sheideman 	VREF(dvp);
235238414Smckusick 
235338414Smckusick 	/* Fudge together a funny name */
235452234Sheideman 	pid = cnp->cn_proc->p_pid;
235548364Smckusick 	bcopy(".nfsAxxxx4.4", sp->s_name, 13);
235648364Smckusick 	sp->s_namlen = 12;
235748364Smckusick 	sp->s_name[8] = hextoasc[pid & 0xf];
235848364Smckusick 	sp->s_name[7] = hextoasc[(pid >> 4) & 0xf];
235948364Smckusick 	sp->s_name[6] = hextoasc[(pid >> 8) & 0xf];
236048364Smckusick 	sp->s_name[5] = hextoasc[(pid >> 12) & 0xf];
236138414Smckusick 
236238414Smckusick 	/* Try lookitups until we get one that isn't there */
236368653Smckusick 	while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
236468653Smckusick 		cnp->cn_proc, (struct nfsnode **)0) == 0) {
236548364Smckusick 		sp->s_name[4]++;
236648364Smckusick 		if (sp->s_name[4] > 'z') {
236738414Smckusick 			error = EINVAL;
236838414Smckusick 			goto bad;
236938414Smckusick 		}
237038414Smckusick 	}
237152234Sheideman 	if (error = nfs_renameit(dvp, cnp, sp))
237238414Smckusick 		goto bad;
237368653Smckusick 	error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
237468653Smckusick 		cnp->cn_proc, &np);
237538414Smckusick 	np->n_sillyrename = sp;
237638414Smckusick 	return (0);
237738414Smckusick bad:
237848364Smckusick 	vrele(sp->s_dvp);
237948364Smckusick 	crfree(sp->s_cred);
238048364Smckusick 	free((caddr_t)sp, M_NFSREQ);
238138414Smckusick 	return (error);
238238414Smckusick }
238338414Smckusick 
238438414Smckusick /*
238568653Smckusick  * Look up a file name and optionally either update the file handle or
238668653Smckusick  * allocate an nfsnode, depending on the value of npp.
238768653Smckusick  * npp == NULL	--> just do the lookup
238868653Smckusick  * *npp == NULL --> allocate a new nfsnode and make sure attributes are
238968653Smckusick  *			handled too
239068653Smckusick  * *npp != NULL --> update the file handle in the vnode
239138414Smckusick  */
239252234Sheideman int
nfs_lookitup(dvp,name,len,cred,procp,npp)239368653Smckusick nfs_lookitup(dvp, name, len, cred, procp, npp)
239468653Smckusick 	register struct vnode *dvp;
239568653Smckusick 	char *name;
239668653Smckusick 	int len;
239768653Smckusick 	struct ucred *cred;
239852196Smckusick 	struct proc *procp;
239968653Smckusick 	struct nfsnode **npp;
240038414Smckusick {
240148054Smckusick 	register u_long *tl;
240239488Smckusick 	register caddr_t cp;
240339488Smckusick 	register long t1, t2;
240468653Smckusick 	struct vnode *newvp = (struct vnode *)0;
240568653Smckusick 	struct nfsnode *np, *dnp = VTONFS(dvp);
240639488Smckusick 	caddr_t bpos, dpos, cp2;
240768653Smckusick 	int error = 0, fhlen, attrflag;
240839488Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
240968653Smckusick 	nfsfh_t *nfhp;
241068653Smckusick 	int v3 = NFS_ISV3(dvp);
241138414Smckusick 
241238414Smckusick 	nfsstats.rpccnt[NFSPROC_LOOKUP]++;
241368653Smckusick 	nfsm_reqhead(dvp, NFSPROC_LOOKUP,
241468653Smckusick 		NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len));
241568653Smckusick 	nfsm_fhtom(dvp, v3);
241668653Smckusick 	nfsm_strtom(name, len, NFS_MAXNAMLEN);
241768653Smckusick 	nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred);
241868653Smckusick 	if (npp && !error) {
241968653Smckusick 		nfsm_getfh(nfhp, fhlen, v3);
242068653Smckusick 		if (*npp) {
242168653Smckusick 		    np = *npp;
242268653Smckusick 		    if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) {
242368653Smckusick 			free((caddr_t)np->n_fhp, M_NFSBIGFH);
242468653Smckusick 			np->n_fhp = &np->n_fh;
242568653Smckusick 		    } else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH)
242668653Smckusick 			np->n_fhp =(nfsfh_t *)malloc(fhlen,M_NFSBIGFH,M_WAITOK);
242768653Smckusick 		    bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen);
242868653Smckusick 		    np->n_fhsize = fhlen;
242968653Smckusick 		    newvp = NFSTOV(np);
243068653Smckusick 		} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
243168653Smckusick 		    VREF(dvp);
243268653Smckusick 		    newvp = dvp;
243368653Smckusick 		} else {
243468653Smckusick 		    error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np);
243568653Smckusick 		    if (error) {
243668653Smckusick 			m_freem(mrep);
243768653Smckusick 			return (error);
243868653Smckusick 		    }
243968653Smckusick 		    newvp = NFSTOV(np);
244068653Smckusick 		}
244168653Smckusick 		if (v3) {
244268653Smckusick 			nfsm_postop_attr(newvp, attrflag);
244368653Smckusick 			if (!attrflag && *npp == NULL) {
244468653Smckusick 				m_freem(mrep);
244568653Smckusick 				vrele(newvp);
244668653Smckusick 				return (ENOENT);
244768653Smckusick 			}
244868653Smckusick 		} else
244968653Smckusick 			nfsm_loadattr(newvp, (struct vattr *)0);
245056289Smckusick 	}
245168653Smckusick 	nfsm_reqdone;
245268653Smckusick 	if (npp && *npp == NULL) {
245368653Smckusick 		if (error) {
245468653Smckusick 			if (newvp)
245568653Smckusick 				vrele(newvp);
245668653Smckusick 		} else
245768653Smckusick 			*npp = np;
245838414Smckusick 	}
245968653Smckusick 	return (error);
246068653Smckusick }
246168653Smckusick 
246268653Smckusick /*
246368653Smckusick  * Nfs Version 3 commit rpc
246468653Smckusick  */
246568653Smckusick int
nfs_commit(vp,offset,cnt,cred,procp)246668653Smckusick nfs_commit(vp, offset, cnt, cred, procp)
246768653Smckusick 	register struct vnode *vp;
246868653Smckusick 	u_quad_t offset;
246968653Smckusick 	int cnt;
247068653Smckusick 	struct ucred *cred;
247168653Smckusick 	struct proc *procp;
247268653Smckusick {
247368653Smckusick 	register caddr_t cp;
247468653Smckusick 	register u_long *tl;
247568653Smckusick 	register int t1, t2;
247668653Smckusick 	register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
247768653Smckusick 	caddr_t bpos, dpos, cp2;
247868653Smckusick 	int error = 0, wccflag = NFSV3_WCCRATTR;
247968653Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
248068653Smckusick 
248168653Smckusick 	if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0)
248268653Smckusick 		return (0);
248368653Smckusick 	nfsstats.rpccnt[NFSPROC_COMMIT]++;
248468653Smckusick 	nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1));
248568653Smckusick 	nfsm_fhtom(vp, 1);
248668653Smckusick 	nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED);
248768653Smckusick 	txdr_hyper(&offset, tl);
248868653Smckusick 	tl += 2;
248968653Smckusick 	*tl = txdr_unsigned(cnt);
249068653Smckusick 	nfsm_request(vp, NFSPROC_COMMIT, procp, cred);
249168653Smckusick 	nfsm_wcc_data(vp, wccflag);
249268653Smckusick 	if (!error) {
249368653Smckusick 		nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF);
249468653Smckusick 		if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl,
249568653Smckusick 			NFSX_V3WRITEVERF)) {
249668653Smckusick 			bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf,
249768653Smckusick 				NFSX_V3WRITEVERF);
249868653Smckusick 			error = NFSERR_STALEWRITEVERF;
249968653Smckusick 		}
250068653Smckusick 	}
250138414Smckusick 	nfsm_reqdone;
250238414Smckusick 	return (error);
250338414Smckusick }
250438414Smckusick 
250538414Smckusick /*
250638414Smckusick  * Kludge City..
250738414Smckusick  * - make nfs_bmap() essentially a no-op that does no translation
250868653Smckusick  * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc
250938414Smckusick  *   (Maybe I could use the process's page mapping, but I was concerned that
251038414Smckusick  *    Kernel Write might not be enabled and also figured copyout() would do
251138414Smckusick  *    a lot more work than bcopy() and also it currently happens in the
251238414Smckusick  *    context of the swapper process (2).
251338414Smckusick  */
251452234Sheideman int
nfs_bmap(ap)251553806Smckusick nfs_bmap(ap)
251654668Smckusick 	struct vop_bmap_args /* {
251754668Smckusick 		struct vnode *a_vp;
251854668Smckusick 		daddr_t  a_bn;
251954668Smckusick 		struct vnode **a_vpp;
252054668Smckusick 		daddr_t *a_bnp;
252156452Smargo 		int *a_runp;
252254668Smckusick 	} */ *ap;
252338414Smckusick {
252453806Smckusick 	register struct vnode *vp = ap->a_vp;
252553806Smckusick 
252653600Sheideman 	if (ap->a_vpp != NULL)
252753806Smckusick 		*ap->a_vpp = vp;
252853600Sheideman 	if (ap->a_bnp != NULL)
252953806Smckusick 		*ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
253038414Smckusick 	return (0);
253138414Smckusick }
253238414Smckusick 
253338414Smckusick /*
253457791Smckusick  * Strategy routine.
253557791Smckusick  * For async requests when nfsiod(s) are running, queue the request by
253657791Smckusick  * calling nfs_asyncio(), otherwise just all nfs_doio() to do the
253757791Smckusick  * request.
253838414Smckusick  */
253952234Sheideman int
nfs_strategy(ap)254053806Smckusick nfs_strategy(ap)
254157791Smckusick 	struct vop_strategy_args *ap;
254238414Smckusick {
254353806Smckusick 	register struct buf *bp = ap->a_bp;
254457791Smckusick 	struct ucred *cr;
254557791Smckusick 	struct proc *p;
254638884Smacklem 	int error = 0;
254738884Smacklem 
254857791Smckusick 	if (bp->b_flags & B_PHYS)
254957791Smckusick 		panic("nfs physio");
255057791Smckusick 	if (bp->b_flags & B_ASYNC)
255157791Smckusick 		p = (struct proc *)0;
255257791Smckusick 	else
255357791Smckusick 		p = curproc;	/* XXX */
255457791Smckusick 	if (bp->b_flags & B_READ)
255557791Smckusick 		cr = bp->b_rcred;
255657791Smckusick 	else
255757791Smckusick 		cr = bp->b_wcred;
255838884Smacklem 	/*
255946450Skarels 	 * If the op is asynchronous and an i/o daemon is waiting
256038884Smacklem 	 * queue the request, wake it up and wait for completion
256146450Skarels 	 * otherwise just do it ourselves.
256238884Smacklem 	 */
256357791Smckusick 	if ((bp->b_flags & B_ASYNC) == 0 ||
256457791Smckusick 		nfs_asyncio(bp, NOCRED))
256557791Smckusick 		error = nfs_doio(bp, cr, p);
256638884Smacklem 	return (error);
256738884Smacklem }
256838884Smacklem 
256938884Smacklem /*
257048054Smckusick  * Mmap a file
257148054Smckusick  *
257248054Smckusick  * NB Currently unsupported.
257348054Smckusick  */
257448054Smckusick /* ARGSUSED */
257552234Sheideman int
nfs_mmap(ap)257653806Smckusick nfs_mmap(ap)
257754668Smckusick 	struct vop_mmap_args /* {
257854668Smckusick 		struct vnode *a_vp;
257954668Smckusick 		int  a_fflags;
258054668Smckusick 		struct ucred *a_cred;
258154668Smckusick 		struct proc *a_p;
258254668Smckusick 	} */ *ap;
258348054Smckusick {
258448054Smckusick 
258548054Smckusick 	return (EINVAL);
258648054Smckusick }
258748054Smckusick 
258848054Smckusick /*
258968653Smckusick  * fsync vnode op. Just call nfs_flush() with commit == 1.
259038884Smacklem  */
259139488Smckusick /* ARGSUSED */
259252234Sheideman int
nfs_fsync(ap)259353806Smckusick nfs_fsync(ap)
259454451Smckusick 	struct vop_fsync_args /* {
259554451Smckusick 		struct vnodeop_desc *a_desc;
259654451Smckusick 		struct vnode * a_vp;
259754451Smckusick 		struct ucred * a_cred;
259854451Smckusick 		int  a_waitfor;
259954451Smckusick 		struct proc * a_p;
260054451Smckusick 	} */ *ap;
260138884Smacklem {
260268653Smckusick 
260368653Smckusick 	return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1));
260468653Smckusick }
260568653Smckusick 
260668653Smckusick /*
260768653Smckusick  * Flush all the blocks associated with a vnode.
260868653Smckusick  * 	Walk through the buffer pool and push any dirty pages
260968653Smckusick  *	associated with the vnode.
261068653Smckusick  */
261168653Smckusick int
nfs_flush(vp,cred,waitfor,p,commit)261268653Smckusick nfs_flush(vp, cred, waitfor, p, commit)
261368653Smckusick 	register struct vnode *vp;
261468653Smckusick 	struct ucred *cred;
261568653Smckusick 	int waitfor;
261668653Smckusick 	struct proc *p;
261768653Smckusick 	int commit;
261868653Smckusick {
261954451Smckusick 	register struct nfsnode *np = VTONFS(vp);
262054451Smckusick 	register struct buf *bp;
262168653Smckusick 	register int i;
262254451Smckusick 	struct buf *nbp;
262368653Smckusick 	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
262468653Smckusick 	int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos;
262568653Smckusick 	int passone = 1;
262668653Smckusick 	u_quad_t off = (u_quad_t)-1, endoff = 0, toff;
262768653Smckusick #ifndef NFS_COMMITBVECSIZ
262868653Smckusick #define NFS_COMMITBVECSIZ	20
262968653Smckusick #endif
263068653Smckusick 	struct buf *bvec[NFS_COMMITBVECSIZ];
263138884Smacklem 
263257791Smckusick 	if (nmp->nm_flag & NFSMNT_INT)
263357791Smckusick 		slpflag = PCATCH;
263468653Smckusick 	if (!commit)
263568653Smckusick 		passone = 0;
263668653Smckusick 	/*
263768653Smckusick 	 * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the
263868653Smckusick 	 * server, but nas not been committed to stable storage on the server
263968653Smckusick 	 * yet. On the first pass, the byte range is worked out and the commit
264068653Smckusick 	 * rpc is done. On the second pass, nfs_writebp() is called to do the
264168653Smckusick 	 * job.
264268653Smckusick 	 */
264368653Smckusick again:
264468653Smckusick 	bvecpos = 0;
264568653Smckusick 	if (NFS_ISV3(vp) && commit) {
264668653Smckusick 		s = splbio();
264768653Smckusick 		for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
264868653Smckusick 			nbp = bp->b_vnbufs.le_next;
264968653Smckusick 			if (bvecpos >= NFS_COMMITBVECSIZ)
265068653Smckusick 				break;
265168653Smckusick 			if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT))
265268653Smckusick 				!= (B_DELWRI | B_NEEDCOMMIT))
265368653Smckusick 				continue;
265468653Smckusick 			bremfree(bp);
265568653Smckusick 			bp->b_flags |= (B_BUSY | B_WRITEINPROG);
265668653Smckusick 			/*
265768653Smckusick 			 * A list of these buffers is kept so that the
265868653Smckusick 			 * second loop knows which buffers have actually
265968653Smckusick 			 * been committed. This is necessary, since there
266068653Smckusick 			 * may be a race between the commit rpc and new
266168653Smckusick 			 * uncommitted writes on the file.
266268653Smckusick 			 */
266368653Smckusick 			bvec[bvecpos++] = bp;
266468653Smckusick 			toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
266568653Smckusick 				bp->b_dirtyoff;
266668653Smckusick 			if (toff < off)
266768653Smckusick 				off = toff;
266868653Smckusick 			toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
266968653Smckusick 			if (toff > endoff)
267068653Smckusick 				endoff = toff;
267168653Smckusick 		}
267268653Smckusick 		splx(s);
267368653Smckusick 	}
267468653Smckusick 	if (bvecpos > 0) {
267568653Smckusick 		/*
267668653Smckusick 		 * Commit data on the server, as required.
267768653Smckusick 		 */
267868653Smckusick 		retv = nfs_commit(vp, off, (int)(endoff - off), cred, p);
267968653Smckusick 		if (retv == NFSERR_STALEWRITEVERF)
268068653Smckusick 			nfs_clearcommit(vp->v_mount);
268168653Smckusick 		/*
268268653Smckusick 		 * Now, either mark the blocks I/O done or mark the
268368653Smckusick 		 * blocks dirty, depending on whether the commit
268468653Smckusick 		 * succeeded.
268568653Smckusick 		 */
268668653Smckusick 		for (i = 0; i < bvecpos; i++) {
268768653Smckusick 			bp = bvec[i];
268868653Smckusick 			bp->b_flags &= ~(B_NEEDCOMMIT | B_WRITEINPROG);
268968653Smckusick 			if (retv) {
269068653Smckusick 			    brelse(bp);
269168653Smckusick 			} else {
269268653Smckusick 			    vp->v_numoutput++;
269368653Smckusick 			    bp->b_flags |= B_ASYNC;
269468653Smckusick 			    bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
269568653Smckusick 			    bp->b_dirtyoff = bp->b_dirtyend = 0;
269668653Smckusick 			    reassignbuf(bp, vp);
269768653Smckusick 			    biodone(bp);
269868653Smckusick 			}
269968653Smckusick 		}
270068653Smckusick 	}
270168653Smckusick 
270268653Smckusick 	/*
270368653Smckusick 	 * Start/do any write(s) that are required.
270468653Smckusick 	 */
270554451Smckusick loop:
270654451Smckusick 	s = splbio();
270765252Smckusick 	for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
270865252Smckusick 		nbp = bp->b_vnbufs.le_next;
270957791Smckusick 		if (bp->b_flags & B_BUSY) {
271068653Smckusick 			if (waitfor != MNT_WAIT || passone)
271157791Smckusick 				continue;
271257791Smckusick 			bp->b_flags |= B_WANTED;
271357791Smckusick 			error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1),
271457791Smckusick 				"nfsfsync", slptimeo);
271557791Smckusick 			splx(s);
271657791Smckusick 			if (error) {
271768653Smckusick 			    if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
271857791Smckusick 				return (EINTR);
271957791Smckusick 			    if (slpflag == PCATCH) {
272057791Smckusick 				slpflag = 0;
272157791Smckusick 				slptimeo = 2 * hz;
272257791Smckusick 			    }
272357791Smckusick 			}
272457791Smckusick 			goto loop;
272557791Smckusick 		}
272654451Smckusick 		if ((bp->b_flags & B_DELWRI) == 0)
272754451Smckusick 			panic("nfs_fsync: not dirty");
272868653Smckusick 		if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT))
272968653Smckusick 			continue;
273054451Smckusick 		bremfree(bp);
273168653Smckusick 		if (passone || !commit)
273268653Smckusick 		    bp->b_flags |= (B_BUSY|B_ASYNC);
273368653Smckusick 		else
273468653Smckusick 		    bp->b_flags |= (B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT);
273554451Smckusick 		splx(s);
273657791Smckusick 		VOP_BWRITE(bp);
273754451Smckusick 		goto loop;
273838884Smacklem 	}
273957791Smckusick 	splx(s);
274068653Smckusick 	if (passone) {
274168653Smckusick 		passone = 0;
274268653Smckusick 		goto again;
274368653Smckusick 	}
274468653Smckusick 	if (waitfor == MNT_WAIT) {
274554451Smckusick 		while (vp->v_numoutput) {
274654451Smckusick 			vp->v_flag |= VBWAIT;
274757791Smckusick 			error = tsleep((caddr_t)&vp->v_numoutput,
274857791Smckusick 				slpflag | (PRIBIO + 1), "nfsfsync", slptimeo);
274957791Smckusick 			if (error) {
275068653Smckusick 			    if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
275157791Smckusick 				return (EINTR);
275257791Smckusick 			    if (slpflag == PCATCH) {
275357791Smckusick 				slpflag = 0;
275457791Smckusick 				slptimeo = 2 * hz;
275557791Smckusick 			    }
275657791Smckusick 			}
275754451Smckusick 		}
275868653Smckusick 		if (vp->v_dirtyblkhd.lh_first && commit) {
275968653Smckusick #ifndef DIAGNOSTIC
276054451Smckusick 			vprint("nfs_fsync: dirty", vp);
276157791Smckusick #endif
276254451Smckusick 			goto loop;
276354451Smckusick 		}
276454451Smckusick 	}
276553629Smckusick 	if (np->n_flag & NWRITEERR) {
276639751Smckusick 		error = np->n_error;
276753629Smckusick 		np->n_flag &= ~NWRITEERR;
276853629Smckusick 	}
276938884Smacklem 	return (error);
277038884Smacklem }
277139672Smckusick 
277239672Smckusick /*
277360400Smckusick  * Return POSIX pathconf information applicable to nfs.
277460400Smckusick  *
277568653Smckusick  * The NFS V2 protocol doesn't support this, so just return EINVAL
277668653Smckusick  * for V2.
277760400Smckusick  */
277860400Smckusick /* ARGSUSED */
277968653Smckusick int
nfs_pathconf(ap)278060400Smckusick nfs_pathconf(ap)
278160400Smckusick 	struct vop_pathconf_args /* {
278260400Smckusick 		struct vnode *a_vp;
278360400Smckusick 		int a_name;
278460400Smckusick 		int *a_retval;
278560400Smckusick 	} */ *ap;
278660400Smckusick {
278760400Smckusick 
278860400Smckusick 	return (EINVAL);
278960400Smckusick }
279060400Smckusick 
279160400Smckusick /*
279246201Smckusick  * NFS advisory byte-level locks.
279346201Smckusick  * Currently unsupported.
279446201Smckusick  */
279552234Sheideman int
nfs_advlock(ap)279653806Smckusick nfs_advlock(ap)
279754668Smckusick 	struct vop_advlock_args /* {
279854668Smckusick 		struct vnode *a_vp;
279954668Smckusick 		caddr_t  a_id;
280054668Smckusick 		int  a_op;
280154668Smckusick 		struct flock *a_fl;
280254668Smckusick 		int  a_flags;
280354668Smckusick 	} */ *ap;
280446201Smckusick {
280546201Smckusick 
280646201Smckusick 	return (EOPNOTSUPP);
280746201Smckusick }
280846201Smckusick 
280946201Smckusick /*
281039672Smckusick  * Print out the contents of an nfsnode.
281139672Smckusick  */
281252234Sheideman int
nfs_print(ap)281353806Smckusick nfs_print(ap)
281454668Smckusick 	struct vop_print_args /* {
281554668Smckusick 		struct vnode *a_vp;
281654668Smckusick 	} */ *ap;
281739672Smckusick {
281853806Smckusick 	register struct vnode *vp = ap->a_vp;
281953806Smckusick 	register struct nfsnode *np = VTONFS(vp);
282039672Smckusick 
282168653Smckusick 	printf("tag VT_NFS, fileid %ld fsid 0x%lx",
282240294Smckusick 		np->n_vattr.va_fileid, np->n_vattr.va_fsid);
282353806Smckusick 	if (vp->v_type == VFIFO)
282453806Smckusick 		fifo_printinfo(vp);
282539914Smckusick 	printf("\n");
282668653Smckusick 	return (0);
282739672Smckusick }
282851573Smckusick 
282951573Smckusick /*
283051573Smckusick  * NFS directory offset lookup.
283151573Smckusick  * Currently unsupported.
283251573Smckusick  */
283352234Sheideman int
nfs_blkatoff(ap)283453806Smckusick nfs_blkatoff(ap)
283554668Smckusick 	struct vop_blkatoff_args /* {
283654668Smckusick 		struct vnode *a_vp;
283754668Smckusick 		off_t a_offset;
283854668Smckusick 		char **a_res;
283954668Smckusick 		struct buf **a_bpp;
284054668Smckusick 	} */ *ap;
284151573Smckusick {
284251573Smckusick 
284351573Smckusick 	return (EOPNOTSUPP);
284451573Smckusick }
284551573Smckusick 
284651573Smckusick /*
284751573Smckusick  * NFS flat namespace allocation.
284851573Smckusick  * Currently unsupported.
284951573Smckusick  */
285052234Sheideman int
nfs_valloc(ap)285153806Smckusick nfs_valloc(ap)
285254668Smckusick 	struct vop_valloc_args /* {
285354668Smckusick 		struct vnode *a_pvp;
285454668Smckusick 		int a_mode;
285554668Smckusick 		struct ucred *a_cred;
285654668Smckusick 		struct vnode **a_vpp;
285754668Smckusick 	} */ *ap;
285851573Smckusick {
285951573Smckusick 
286051573Smckusick 	return (EOPNOTSUPP);
286151573Smckusick }
286251573Smckusick 
286351573Smckusick /*
286451573Smckusick  * NFS flat namespace free.
286551573Smckusick  * Currently unsupported.
286651573Smckusick  */
286753582Sheideman int
nfs_vfree(ap)286853806Smckusick nfs_vfree(ap)
286954668Smckusick 	struct vop_vfree_args /* {
287054668Smckusick 		struct vnode *a_pvp;
287154668Smckusick 		ino_t a_ino;
287254668Smckusick 		int a_mode;
287354668Smckusick 	} */ *ap;
287451573Smckusick {
287551573Smckusick 
287653582Sheideman 	return (EOPNOTSUPP);
287751573Smckusick }
287851573Smckusick 
287951573Smckusick /*
288051573Smckusick  * NFS file truncation.
288151573Smckusick  */
288252234Sheideman int
nfs_truncate(ap)288353806Smckusick nfs_truncate(ap)
288454668Smckusick 	struct vop_truncate_args /* {
288554668Smckusick 		struct vnode *a_vp;
288654668Smckusick 		off_t a_length;
288754668Smckusick 		int a_flags;
288854668Smckusick 		struct ucred *a_cred;
288954668Smckusick 		struct proc *a_p;
289054668Smckusick 	} */ *ap;
289151573Smckusick {
289251573Smckusick 
289351573Smckusick 	/* Use nfs_setattr */
289451573Smckusick 	printf("nfs_truncate: need to implement!!");
289551573Smckusick 	return (EOPNOTSUPP);
289651573Smckusick }
289751573Smckusick 
289851573Smckusick /*
289951573Smckusick  * NFS update.
290051573Smckusick  */
290152234Sheideman int
nfs_update(ap)290253806Smckusick nfs_update(ap)
290354668Smckusick 	struct vop_update_args /* {
290454668Smckusick 		struct vnode *a_vp;
290554668Smckusick 		struct timeval *a_ta;
290654668Smckusick 		struct timeval *a_tm;
290754668Smckusick 		int a_waitfor;
290854668Smckusick 	} */ *ap;
290951573Smckusick {
291051573Smckusick 
291151573Smckusick 	/* Use nfs_setattr */
291251573Smckusick 	printf("nfs_update: need to implement!!");
291351573Smckusick 	return (EOPNOTSUPP);
291451573Smckusick }
291553629Smckusick 
291653629Smckusick /*
291768653Smckusick  * Just call nfs_writebp() with the force argument set to 1.
291868653Smckusick  */
291968653Smckusick int
nfs_bwrite(ap)292068653Smckusick nfs_bwrite(ap)
292168653Smckusick 	struct vop_bwrite_args /* {
292268653Smckusick 		struct vnode *a_bp;
292368653Smckusick 	} */ *ap;
292468653Smckusick {
292568653Smckusick 
292668653Smckusick 	return (nfs_writebp(ap->a_bp, 1));
292768653Smckusick }
292868653Smckusick 
292968653Smckusick /*
293068653Smckusick  * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless
293168653Smckusick  * the force flag is one and it also handles the B_NEEDCOMMIT flag.
293268653Smckusick  */
293368653Smckusick int
nfs_writebp(bp,force)293468653Smckusick nfs_writebp(bp, force)
293568653Smckusick 	register struct buf *bp;
293668653Smckusick 	int force;
293768653Smckusick {
293868653Smckusick 	register int oldflags = bp->b_flags, retv = 1;
293968653Smckusick 	register struct proc *p = curproc;	/* XXX */
294068653Smckusick 	off_t off;
294168653Smckusick 
294268653Smckusick 	if(!(bp->b_flags & B_BUSY))
294368653Smckusick 		panic("bwrite: buffer is not busy???");
294468653Smckusick 
294568653Smckusick 	bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
294668653Smckusick 
294768653Smckusick 	if (oldflags & B_ASYNC) {
294868653Smckusick 		if (oldflags & B_DELWRI) {
294968653Smckusick 			reassignbuf(bp, bp->b_vp);
295068653Smckusick 		} else if (p) {
295168653Smckusick 			++p->p_stats->p_ru.ru_oublock;
295268653Smckusick 		}
295368653Smckusick 	}
295468653Smckusick 	bp->b_vp->v_numoutput++;
295568653Smckusick 
295668653Smckusick 	/*
295768653Smckusick 	 * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not
295868653Smckusick 	 * an actual write will have to be scheduled via. VOP_STRATEGY().
295968653Smckusick 	 * If B_WRITEINPROG is already set, then push it with a write anyhow.
296068653Smckusick 	 */
296168653Smckusick 	if (oldflags & (B_NEEDCOMMIT | B_WRITEINPROG) == B_NEEDCOMMIT) {
296268653Smckusick 		off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
296368653Smckusick 		bp->b_flags |= B_WRITEINPROG;
296468653Smckusick 		retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff,
296568653Smckusick 			bp->b_wcred, bp->b_proc);
296668653Smckusick 		bp->b_flags &= ~B_WRITEINPROG;
296768653Smckusick 		if (!retv) {
296868653Smckusick 			bp->b_dirtyoff = bp->b_dirtyend = 0;
296968653Smckusick 			bp->b_flags &= ~B_NEEDCOMMIT;
297068653Smckusick 			biodone(bp);
297168653Smckusick 		} else if (retv == NFSERR_STALEWRITEVERF)
297268653Smckusick 			nfs_clearcommit(bp->b_vp->v_mount);
297368653Smckusick 	}
297468653Smckusick 	if (retv) {
297568653Smckusick 		if (force)
297668653Smckusick 			bp->b_flags |= B_WRITEINPROG;
297768653Smckusick 		VOP_STRATEGY(bp);
297868653Smckusick 	}
297968653Smckusick 
298068653Smckusick 	if( (oldflags & B_ASYNC) == 0) {
298168653Smckusick 		int rtval = biowait(bp);
298268653Smckusick 		if (oldflags & B_DELWRI) {
298368653Smckusick 			reassignbuf(bp, bp->b_vp);
298468653Smckusick 		} else if (p) {
298568653Smckusick 			++p->p_stats->p_ru.ru_oublock;
298668653Smckusick 		}
298768653Smckusick 		brelse(bp);
298868653Smckusick 		return (rtval);
298968653Smckusick 	}
299068653Smckusick 
299168653Smckusick 	return (0);
299268653Smckusick }
299368653Smckusick 
299468653Smckusick /*
299556364Smckusick  * nfs special file access vnode op.
299656364Smckusick  * Essentially just get vattr and then imitate iaccess() since the device is
299756364Smckusick  * local to the client.
299856364Smckusick  */
299956364Smckusick int
nfsspec_access(ap)300056364Smckusick nfsspec_access(ap)
300156364Smckusick 	struct vop_access_args /* {
300256364Smckusick 		struct vnode *a_vp;
300356364Smckusick 		int  a_mode;
300456364Smckusick 		struct ucred *a_cred;
300556364Smckusick 		struct proc *a_p;
300656364Smckusick 	} */ *ap;
300756364Smckusick {
300856364Smckusick 	register struct vattr *vap;
300956364Smckusick 	register gid_t *gp;
301056364Smckusick 	register struct ucred *cred = ap->a_cred;
3011*69743Smckusick 	struct vnode *vp = ap->a_vp;
301256364Smckusick 	mode_t mode = ap->a_mode;
301356364Smckusick 	struct vattr vattr;
301456364Smckusick 	register int i;
301556364Smckusick 	int error;
301656364Smckusick 
301756364Smckusick 	/*
3018*69743Smckusick 	 * Disallow write attempts on filesystems mounted read-only;
3019*69743Smckusick 	 * unless the file is a socket, fifo, or a block or character
3020*69743Smckusick 	 * device resident on the filesystem.
3021*69743Smckusick 	 */
3022*69743Smckusick 	if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
3023*69743Smckusick 		switch (vp->v_type) {
3024*69743Smckusick 		case VREG: case VDIR: case VLNK:
3025*69743Smckusick 			return (EROFS);
3026*69743Smckusick 		}
3027*69743Smckusick 	}
3028*69743Smckusick 	/*
302956364Smckusick 	 * If you're the super-user,
303056364Smckusick 	 * you always get access.
303156364Smckusick 	 */
303256364Smckusick 	if (cred->cr_uid == 0)
303356364Smckusick 		return (0);
303456364Smckusick 	vap = &vattr;
3035*69743Smckusick 	error = VOP_GETATTR(vp, vap, cred, ap->a_p);
303668653Smckusick 	if (error)
303756364Smckusick 		return (error);
303856364Smckusick 	/*
303956364Smckusick 	 * Access check is based on only one of owner, group, public.
304056364Smckusick 	 * If not owner, then check group. If not a member of the
304156364Smckusick 	 * group, then check public access.
304256364Smckusick 	 */
304356364Smckusick 	if (cred->cr_uid != vap->va_uid) {
304456364Smckusick 		mode >>= 3;
304556364Smckusick 		gp = cred->cr_groups;
304656364Smckusick 		for (i = 0; i < cred->cr_ngroups; i++, gp++)
304756364Smckusick 			if (vap->va_gid == *gp)
304856364Smckusick 				goto found;
304956364Smckusick 		mode >>= 3;
305056364Smckusick found:
305156364Smckusick 		;
305256364Smckusick 	}
305368653Smckusick 	error = (vap->va_mode & mode) == mode ? 0 : EACCES;
305468653Smckusick 	return (error);
305556364Smckusick }
305656364Smckusick 
305756364Smckusick /*
305853629Smckusick  * Read wrapper for special devices.
305953629Smckusick  */
306053629Smckusick int
nfsspec_read(ap)306153629Smckusick nfsspec_read(ap)
306254668Smckusick 	struct vop_read_args /* {
306354668Smckusick 		struct vnode *a_vp;
306454668Smckusick 		struct uio *a_uio;
306554668Smckusick 		int  a_ioflag;
306654668Smckusick 		struct ucred *a_cred;
306754668Smckusick 	} */ *ap;
306853629Smckusick {
306954032Smckusick 	register struct nfsnode *np = VTONFS(ap->a_vp);
307053629Smckusick 
307153629Smckusick 	/*
307253629Smckusick 	 * Set access flag.
307353629Smckusick 	 */
307454032Smckusick 	np->n_flag |= NACC;
307568653Smckusick 	np->n_atim.ts_sec = time.tv_sec;
307668653Smckusick 	np->n_atim.ts_nsec = time.tv_usec * 1000;
307753629Smckusick 	return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap));
307853629Smckusick }
307953629Smckusick 
308053629Smckusick /*
308153629Smckusick  * Write wrapper for special devices.
308253629Smckusick  */
308353629Smckusick int
nfsspec_write(ap)308453629Smckusick nfsspec_write(ap)
308554668Smckusick 	struct vop_write_args /* {
308654668Smckusick 		struct vnode *a_vp;
308754668Smckusick 		struct uio *a_uio;
308854668Smckusick 		int  a_ioflag;
308954668Smckusick 		struct ucred *a_cred;
309054668Smckusick 	} */ *ap;
309153629Smckusick {
309254032Smckusick 	register struct nfsnode *np = VTONFS(ap->a_vp);
309353629Smckusick 
309453629Smckusick 	/*
309554032Smckusick 	 * Set update flag.
309653629Smckusick 	 */
309754032Smckusick 	np->n_flag |= NUPD;
309868653Smckusick 	np->n_mtim.ts_sec = time.tv_sec;
309968653Smckusick 	np->n_mtim.ts_nsec = time.tv_usec * 1000;
310053629Smckusick 	return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap));
310153629Smckusick }
310253629Smckusick 
310353629Smckusick /*
310453629Smckusick  * Close wrapper for special devices.
310553629Smckusick  *
310653629Smckusick  * Update the times on the nfsnode then do device close.
310753629Smckusick  */
310853629Smckusick int
nfsspec_close(ap)310953629Smckusick nfsspec_close(ap)
311054668Smckusick 	struct vop_close_args /* {
311154668Smckusick 		struct vnode *a_vp;
311254668Smckusick 		int  a_fflag;
311354668Smckusick 		struct ucred *a_cred;
311454668Smckusick 		struct proc *a_p;
311554668Smckusick 	} */ *ap;
311653629Smckusick {
311753806Smckusick 	register struct vnode *vp = ap->a_vp;
311853806Smckusick 	register struct nfsnode *np = VTONFS(vp);
311953629Smckusick 	struct vattr vattr;
312053629Smckusick 
312153629Smckusick 	if (np->n_flag & (NACC | NUPD)) {
312253629Smckusick 		np->n_flag |= NCHG;
312353806Smckusick 		if (vp->v_usecount == 1 &&
312453806Smckusick 		    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
312553629Smckusick 			VATTR_NULL(&vattr);
312668653Smckusick 			if (np->n_flag & NACC)
312768653Smckusick 				vattr.va_atime = np->n_atim;
312868653Smckusick 			if (np->n_flag & NUPD)
312968653Smckusick 				vattr.va_mtime = np->n_mtim;
313053806Smckusick 			(void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
313153629Smckusick 		}
313253629Smckusick 	}
313353629Smckusick 	return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap));
313453629Smckusick }
313553629Smckusick 
313653629Smckusick /*
313753629Smckusick  * Read wrapper for fifos.
313853629Smckusick  */
313953629Smckusick int
nfsfifo_read(ap)314053629Smckusick nfsfifo_read(ap)
314154668Smckusick 	struct vop_read_args /* {
314254668Smckusick 		struct vnode *a_vp;
314354668Smckusick 		struct uio *a_uio;
314454668Smckusick 		int  a_ioflag;
314554668Smckusick 		struct ucred *a_cred;
314654668Smckusick 	} */ *ap;
314753629Smckusick {
314853629Smckusick 	extern int (**fifo_vnodeop_p)();
314954032Smckusick 	register struct nfsnode *np = VTONFS(ap->a_vp);
315053629Smckusick 
315153629Smckusick 	/*
315253629Smckusick 	 * Set access flag.
315353629Smckusick 	 */
315454032Smckusick 	np->n_flag |= NACC;
315568653Smckusick 	np->n_atim.ts_sec = time.tv_sec;
315668653Smckusick 	np->n_atim.ts_nsec = time.tv_usec * 1000;
315753629Smckusick 	return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap));
315853629Smckusick }
315953629Smckusick 
316053629Smckusick /*
316153629Smckusick  * Write wrapper for fifos.
316253629Smckusick  */
316353629Smckusick int
nfsfifo_write(ap)316453629Smckusick nfsfifo_write(ap)
316554668Smckusick 	struct vop_write_args /* {
316654668Smckusick 		struct vnode *a_vp;
316754668Smckusick 		struct uio *a_uio;
316854668Smckusick 		int  a_ioflag;
316954668Smckusick 		struct ucred *a_cred;
317054668Smckusick 	} */ *ap;
317153629Smckusick {
317253629Smckusick 	extern int (**fifo_vnodeop_p)();
317354032Smckusick 	register struct nfsnode *np = VTONFS(ap->a_vp);
317453629Smckusick 
317553629Smckusick 	/*
317653629Smckusick 	 * Set update flag.
317753629Smckusick 	 */
317854032Smckusick 	np->n_flag |= NUPD;
317968653Smckusick 	np->n_mtim.ts_sec = time.tv_sec;
318068653Smckusick 	np->n_mtim.ts_nsec = time.tv_usec * 1000;
318153629Smckusick 	return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap));
318253629Smckusick }
318353629Smckusick 
318453629Smckusick /*
318553629Smckusick  * Close wrapper for fifos.
318653629Smckusick  *
318753629Smckusick  * Update the times on the nfsnode then do fifo close.
318853629Smckusick  */
318953629Smckusick int
nfsfifo_close(ap)319053629Smckusick nfsfifo_close(ap)
319154668Smckusick 	struct vop_close_args /* {
319254668Smckusick 		struct vnode *a_vp;
319354668Smckusick 		int  a_fflag;
319454668Smckusick 		struct ucred *a_cred;
319554668Smckusick 		struct proc *a_p;
319654668Smckusick 	} */ *ap;
319753629Smckusick {
319853806Smckusick 	register struct vnode *vp = ap->a_vp;
319953806Smckusick 	register struct nfsnode *np = VTONFS(vp);
320053629Smckusick 	struct vattr vattr;
320153629Smckusick 	extern int (**fifo_vnodeop_p)();
320253629Smckusick 
320353629Smckusick 	if (np->n_flag & (NACC | NUPD)) {
320468653Smckusick 		if (np->n_flag & NACC) {
320568653Smckusick 			np->n_atim.ts_sec = time.tv_sec;
320668653Smckusick 			np->n_atim.ts_nsec = time.tv_usec * 1000;
320768653Smckusick 		}
320868653Smckusick 		if (np->n_flag & NUPD) {
320968653Smckusick 			np->n_mtim.ts_sec = time.tv_sec;
321068653Smckusick 			np->n_mtim.ts_nsec = time.tv_usec * 1000;
321168653Smckusick 		}
321253629Smckusick 		np->n_flag |= NCHG;
321353806Smckusick 		if (vp->v_usecount == 1 &&
321453806Smckusick 		    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
321553629Smckusick 			VATTR_NULL(&vattr);
321668653Smckusick 			if (np->n_flag & NACC)
321768653Smckusick 				vattr.va_atime = np->n_atim;
321868653Smckusick 			if (np->n_flag & NUPD)
321968653Smckusick 				vattr.va_mtime = np->n_mtim;
322053806Smckusick 			(void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p);
322153629Smckusick 		}
322253629Smckusick 	}
322353629Smckusick 	return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
322453629Smckusick }
3225