1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem *	from nfs_vnops.c	8.16 (Berkeley) 5/27/95
33191783Srmacklem */
34191783Srmacklem
35191783Srmacklem#include <sys/cdefs.h>
36191783Srmacklem__FBSDID("$FreeBSD$");
37191783Srmacklem
38191783Srmacklem/*
39191783Srmacklem * vnode op calls for Sun NFS version 2, 3 and 4
40191783Srmacklem */
41191783Srmacklem
42223280Srmacklem#include "opt_kdtrace.h"
43191783Srmacklem#include "opt_inet.h"
44191783Srmacklem
45191783Srmacklem#include <sys/param.h>
46191783Srmacklem#include <sys/kernel.h>
47191783Srmacklem#include <sys/systm.h>
48191783Srmacklem#include <sys/resourcevar.h>
49191783Srmacklem#include <sys/proc.h>
50191783Srmacklem#include <sys/mount.h>
51191783Srmacklem#include <sys/bio.h>
52191783Srmacklem#include <sys/buf.h>
53194368Sbz#include <sys/jail.h>
54191783Srmacklem#include <sys/malloc.h>
55191783Srmacklem#include <sys/mbuf.h>
56191783Srmacklem#include <sys/namei.h>
57191783Srmacklem#include <sys/socket.h>
58191783Srmacklem#include <sys/vnode.h>
59191783Srmacklem#include <sys/dirent.h>
60191783Srmacklem#include <sys/fcntl.h>
61191783Srmacklem#include <sys/lockf.h>
62191783Srmacklem#include <sys/stat.h>
63191783Srmacklem#include <sys/sysctl.h>
64191783Srmacklem#include <sys/signalvar.h>
65191783Srmacklem
66191783Srmacklem#include <vm/vm.h>
67191783Srmacklem#include <vm/vm_extern.h>
68191783Srmacklem#include <vm/vm_object.h>
69191783Srmacklem
70191783Srmacklem#include <fs/nfs/nfsport.h>
71191783Srmacklem#include <fs/nfsclient/nfsnode.h>
72191783Srmacklem#include <fs/nfsclient/nfsmount.h>
73191783Srmacklem#include <fs/nfsclient/nfs.h>
74223280Srmacklem#include <fs/nfsclient/nfs_kdtrace.h>
75191783Srmacklem
76191783Srmacklem#include <net/if.h>
77191783Srmacklem#include <netinet/in.h>
78191783Srmacklem#include <netinet/in_var.h>
79191783Srmacklem
80210455Srmacklem#include <nfs/nfs_lock.h>
81210455Srmacklem
82223280Srmacklem#ifdef KDTRACE_HOOKS
83223280Srmacklem#include <sys/dtrace_bsd.h>
84223280Srmacklem
85223280Srmacklemdtrace_nfsclient_accesscache_flush_probe_func_t
86223280Srmacklem		dtrace_nfscl_accesscache_flush_done_probe;
87223280Srmacklemuint32_t	nfscl_accesscache_flush_done_id;
88223280Srmacklem
89223280Srmacklemdtrace_nfsclient_accesscache_get_probe_func_t
90223280Srmacklem		dtrace_nfscl_accesscache_get_hit_probe,
91223280Srmacklem		dtrace_nfscl_accesscache_get_miss_probe;
92223280Srmacklemuint32_t	nfscl_accesscache_get_hit_id;
93223280Srmacklemuint32_t	nfscl_accesscache_get_miss_id;
94223280Srmacklem
95223280Srmacklemdtrace_nfsclient_accesscache_load_probe_func_t
96223280Srmacklem		dtrace_nfscl_accesscache_load_done_probe;
97223280Srmacklemuint32_t	nfscl_accesscache_load_done_id;
98223280Srmacklem#endif /* !KDTRACE_HOOKS */
99223280Srmacklem
100191783Srmacklem/* Defs */
101191783Srmacklem#define	TRUE	1
102191783Srmacklem#define	FALSE	0
103191783Srmacklem
104191783Srmacklemextern struct nfsstats newnfsstats;
105220611Srmacklemextern int nfsrv_useacl;
106191783SrmacklemMALLOC_DECLARE(M_NEWNFSREQ);
107191783Srmacklem
108191783Srmacklem/*
109191783Srmacklem * Ifdef for FreeBSD-current merged buffer cache. It is unfortunate that these
110191783Srmacklem * calls are not in getblk() and brelse() so that they would not be necessary
111191783Srmacklem * here.
112191783Srmacklem */
113191783Srmacklem#ifndef B_VMIO
114191783Srmacklem#define	vfs_busy_pages(bp, f)
115191783Srmacklem#endif
116191783Srmacklem
117191783Srmacklemstatic vop_read_t	nfsfifo_read;
118191783Srmacklemstatic vop_write_t	nfsfifo_write;
119191783Srmacklemstatic vop_close_t	nfsfifo_close;
120191783Srmacklemstatic int	nfs_setattrrpc(struct vnode *, struct vattr *, struct ucred *,
121191783Srmacklem		    struct thread *);
122191783Srmacklemstatic vop_lookup_t	nfs_lookup;
123191783Srmacklemstatic vop_create_t	nfs_create;
124191783Srmacklemstatic vop_mknod_t	nfs_mknod;
125191783Srmacklemstatic vop_open_t	nfs_open;
126220611Srmacklemstatic vop_pathconf_t	nfs_pathconf;
127191783Srmacklemstatic vop_close_t	nfs_close;
128191783Srmacklemstatic vop_access_t	nfs_access;
129191783Srmacklemstatic vop_getattr_t	nfs_getattr;
130191783Srmacklemstatic vop_setattr_t	nfs_setattr;
131191783Srmacklemstatic vop_read_t	nfs_read;
132191783Srmacklemstatic vop_fsync_t	nfs_fsync;
133191783Srmacklemstatic vop_remove_t	nfs_remove;
134191783Srmacklemstatic vop_link_t	nfs_link;
135191783Srmacklemstatic vop_rename_t	nfs_rename;
136191783Srmacklemstatic vop_mkdir_t	nfs_mkdir;
137191783Srmacklemstatic vop_rmdir_t	nfs_rmdir;
138191783Srmacklemstatic vop_symlink_t	nfs_symlink;
139191783Srmacklemstatic vop_readdir_t	nfs_readdir;
140191783Srmacklemstatic vop_strategy_t	nfs_strategy;
141191783Srmacklemstatic vop_lock1_t	nfs_lock1;
142191783Srmacklemstatic	int	nfs_lookitup(struct vnode *, char *, int,
143191783Srmacklem		    struct ucred *, struct thread *, struct nfsnode **);
144191783Srmacklemstatic	int	nfs_sillyrename(struct vnode *, struct vnode *,
145191783Srmacklem		    struct componentname *);
146191783Srmacklemstatic vop_access_t	nfsspec_access;
147191783Srmacklemstatic vop_readlink_t	nfs_readlink;
148191783Srmacklemstatic vop_print_t	nfs_print;
149191783Srmacklemstatic vop_advlock_t	nfs_advlock;
150191783Srmacklemstatic vop_advlockasync_t nfs_advlockasync;
151191783Srmacklemstatic vop_getacl_t nfs_getacl;
152191783Srmacklemstatic vop_setacl_t nfs_setacl;
153191783Srmacklem
154191783Srmacklem/*
155191783Srmacklem * Global vfs data structures for nfs
156191783Srmacklem */
157191783Srmacklemstruct vop_vector newnfs_vnodeops = {
158191783Srmacklem	.vop_default =		&default_vnodeops,
159191783Srmacklem	.vop_access =		nfs_access,
160191783Srmacklem	.vop_advlock =		nfs_advlock,
161191783Srmacklem	.vop_advlockasync =	nfs_advlockasync,
162191783Srmacklem	.vop_close =		nfs_close,
163191783Srmacklem	.vop_create =		nfs_create,
164191783Srmacklem	.vop_fsync =		nfs_fsync,
165191783Srmacklem	.vop_getattr =		nfs_getattr,
166191783Srmacklem	.vop_getpages =		ncl_getpages,
167191783Srmacklem	.vop_putpages =		ncl_putpages,
168191783Srmacklem	.vop_inactive =		ncl_inactive,
169191783Srmacklem	.vop_link =		nfs_link,
170191783Srmacklem	.vop_lock1 = 		nfs_lock1,
171191783Srmacklem	.vop_lookup =		nfs_lookup,
172191783Srmacklem	.vop_mkdir =		nfs_mkdir,
173191783Srmacklem	.vop_mknod =		nfs_mknod,
174191783Srmacklem	.vop_open =		nfs_open,
175220611Srmacklem	.vop_pathconf =		nfs_pathconf,
176191783Srmacklem	.vop_print =		nfs_print,
177191783Srmacklem	.vop_read =		nfs_read,
178191783Srmacklem	.vop_readdir =		nfs_readdir,
179191783Srmacklem	.vop_readlink =		nfs_readlink,
180191783Srmacklem	.vop_reclaim =		ncl_reclaim,
181191783Srmacklem	.vop_remove =		nfs_remove,
182191783Srmacklem	.vop_rename =		nfs_rename,
183191783Srmacklem	.vop_rmdir =		nfs_rmdir,
184191783Srmacklem	.vop_setattr =		nfs_setattr,
185191783Srmacklem	.vop_strategy =		nfs_strategy,
186191783Srmacklem	.vop_symlink =		nfs_symlink,
187191783Srmacklem	.vop_write =		ncl_write,
188191783Srmacklem	.vop_getacl =		nfs_getacl,
189191783Srmacklem	.vop_setacl =		nfs_setacl,
190191783Srmacklem};
191191783Srmacklem
192191783Srmacklemstruct vop_vector newnfs_fifoops = {
193191783Srmacklem	.vop_default =		&fifo_specops,
194191783Srmacklem	.vop_access =		nfsspec_access,
195191783Srmacklem	.vop_close =		nfsfifo_close,
196191783Srmacklem	.vop_fsync =		nfs_fsync,
197191783Srmacklem	.vop_getattr =		nfs_getattr,
198191783Srmacklem	.vop_inactive =		ncl_inactive,
199191783Srmacklem	.vop_print =		nfs_print,
200191783Srmacklem	.vop_read =		nfsfifo_read,
201191783Srmacklem	.vop_reclaim =		ncl_reclaim,
202191783Srmacklem	.vop_setattr =		nfs_setattr,
203191783Srmacklem	.vop_write =		nfsfifo_write,
204191783Srmacklem};
205191783Srmacklem
206191783Srmacklemstatic int nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp,
207191783Srmacklem    struct componentname *cnp, struct vattr *vap);
208191783Srmacklemstatic int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
209191783Srmacklem    int namelen, struct ucred *cred, struct thread *td);
210191783Srmacklemstatic int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp,
211191783Srmacklem    char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp,
212191783Srmacklem    char *tnameptr, int tnamelen, struct ucred *cred, struct thread *td);
213191783Srmacklemstatic int nfs_renameit(struct vnode *sdvp, struct vnode *svp,
214191783Srmacklem    struct componentname *scnp, struct sillyrename *sp);
215191783Srmacklem
216191783Srmacklem/*
217191783Srmacklem * Global variables
218191783Srmacklem */
219191783Srmacklem#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
220191783Srmacklem
221221973SrmacklemSYSCTL_DECL(_vfs_nfs);
222191783Srmacklem
223191783Srmacklemstatic int	nfsaccess_cache_timeout = NFS_MAXATTRTIMO;
224221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW,
225191783Srmacklem	   &nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
226191783Srmacklem
227191783Srmacklemstatic int	nfs_prime_access_cache = 0;
228221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, prime_access_cache, CTLFLAG_RW,
229191783Srmacklem	   &nfs_prime_access_cache, 0,
230191783Srmacklem	   "Prime NFS ACCESS cache when fetching attributes");
231191783Srmacklem
232191783Srmacklemstatic int	newnfs_commit_on_close = 0;
233221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, commit_on_close, CTLFLAG_RW,
234191783Srmacklem    &newnfs_commit_on_close, 0, "write+commit on close, else only write");
235191783Srmacklem
236191783Srmacklemstatic int	nfs_clean_pages_on_close = 1;
237221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, clean_pages_on_close, CTLFLAG_RW,
238191783Srmacklem	   &nfs_clean_pages_on_close, 0, "NFS clean dirty pages on close");
239191783Srmacklem
240191783Srmacklemint newnfs_directio_enable = 0;
241221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_enable, CTLFLAG_RW,
242191783Srmacklem	   &newnfs_directio_enable, 0, "Enable NFS directio");
243191783Srmacklem
244233730Skibint nfs_keep_dirty_on_error;
245233730SkibSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_keep_dirty_on_error, CTLFLAG_RW,
246233730Skib    &nfs_keep_dirty_on_error, 0, "Retry pageout if error returned");
247233730Skib
248191783Srmacklem/*
249191783Srmacklem * This sysctl allows other processes to mmap a file that has been opened
250191783Srmacklem * O_DIRECT by a process.  In general, having processes mmap the file while
251191783Srmacklem * Direct IO is in progress can lead to Data Inconsistencies.  But, we allow
252191783Srmacklem * this by default to prevent DoS attacks - to prevent a malicious user from
253191783Srmacklem * opening up files O_DIRECT preventing other users from mmap'ing these
254191783Srmacklem * files.  "Protected" environments where stricter consistency guarantees are
255191783Srmacklem * required can disable this knob.  The process that opened the file O_DIRECT
256191783Srmacklem * cannot mmap() the file, because mmap'ed IO on an O_DIRECT open() is not
257191783Srmacklem * meaningful.
258191783Srmacklem */
259191783Srmacklemint newnfs_directio_allow_mmap = 1;
260221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_allow_mmap, CTLFLAG_RW,
261191783Srmacklem	   &newnfs_directio_allow_mmap, 0, "Enable mmaped IO on file with O_DIRECT opens");
262191783Srmacklem
263191783Srmacklem#if 0
264221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_hits, CTLFLAG_RD,
265191783Srmacklem	   &newnfsstats.accesscache_hits, 0, "NFS ACCESS cache hit count");
266191783Srmacklem
267221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_misses, CTLFLAG_RD,
268191783Srmacklem	   &newnfsstats.accesscache_misses, 0, "NFS ACCESS cache miss count");
269191783Srmacklem#endif
270191783Srmacklem
271191783Srmacklem#define	NFSACCESS_ALL (NFSACCESS_READ | NFSACCESS_MODIFY		\
272191783Srmacklem			 | NFSACCESS_EXTEND | NFSACCESS_EXECUTE	\
273191783Srmacklem			 | NFSACCESS_DELETE | NFSACCESS_LOOKUP)
274191783Srmacklem
275191783Srmacklem/*
276191783Srmacklem * SMP Locking Note :
277191783Srmacklem * The list of locks after the description of the lock is the ordering
278191783Srmacklem * of other locks acquired with the lock held.
279191783Srmacklem * np->n_mtx : Protects the fields in the nfsnode.
280191783Srmacklem       VM Object Lock
281191783Srmacklem       VI_MTX (acquired indirectly)
282191783Srmacklem * nmp->nm_mtx : Protects the fields in the nfsmount.
283191783Srmacklem       rep->r_mtx
284191783Srmacklem * ncl_iod_mutex : Global lock, protects shared nfsiod state.
285191783Srmacklem * nfs_reqq_mtx : Global lock, protects the nfs_reqq list.
286191783Srmacklem       nmp->nm_mtx
287191783Srmacklem       rep->r_mtx
288191783Srmacklem * rep->r_mtx : Protects the fields in an nfsreq.
289191783Srmacklem */
290191783Srmacklem
291191783Srmacklemstatic int
292191783Srmacklemnfs34_access_otw(struct vnode *vp, int wmode, struct thread *td,
293191783Srmacklem    struct ucred *cred, u_int32_t *retmode)
294191783Srmacklem{
295191783Srmacklem	int error = 0, attrflag, i, lrupos;
296191783Srmacklem	u_int32_t rmode;
297191783Srmacklem	struct nfsnode *np = VTONFS(vp);
298191783Srmacklem	struct nfsvattr nfsva;
299191783Srmacklem
300191783Srmacklem	error = nfsrpc_accessrpc(vp, wmode, cred, td, &nfsva, &attrflag,
301191783Srmacklem	    &rmode, NULL);
302191783Srmacklem	if (attrflag)
303191783Srmacklem		(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
304191783Srmacklem	if (!error) {
305191783Srmacklem		lrupos = 0;
306191783Srmacklem		mtx_lock(&np->n_mtx);
307191783Srmacklem		for (i = 0; i < NFS_ACCESSCACHESIZE; i++) {
308191783Srmacklem			if (np->n_accesscache[i].uid == cred->cr_uid) {
309191783Srmacklem				np->n_accesscache[i].mode = rmode;
310191783Srmacklem				np->n_accesscache[i].stamp = time_second;
311191783Srmacklem				break;
312191783Srmacklem			}
313191783Srmacklem			if (i > 0 && np->n_accesscache[i].stamp <
314191783Srmacklem			    np->n_accesscache[lrupos].stamp)
315191783Srmacklem				lrupos = i;
316191783Srmacklem		}
317191783Srmacklem		if (i == NFS_ACCESSCACHESIZE) {
318191783Srmacklem			np->n_accesscache[lrupos].uid = cred->cr_uid;
319191783Srmacklem			np->n_accesscache[lrupos].mode = rmode;
320191783Srmacklem			np->n_accesscache[lrupos].stamp = time_second;
321191783Srmacklem		}
322191783Srmacklem		mtx_unlock(&np->n_mtx);
323191783Srmacklem		if (retmode != NULL)
324191783Srmacklem			*retmode = rmode;
325223280Srmacklem		KDTRACE_NFS_ACCESSCACHE_LOAD_DONE(vp, cred->cr_uid, rmode, 0);
326191783Srmacklem	} else if (NFS_ISV4(vp)) {
327191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
328191783Srmacklem	}
329223280Srmacklem#ifdef KDTRACE_HOOKS
330223280Srmacklem	if (error != 0)
331223280Srmacklem		KDTRACE_NFS_ACCESSCACHE_LOAD_DONE(vp, cred->cr_uid, 0,
332223280Srmacklem		    error);
333223280Srmacklem#endif
334191783Srmacklem	return (error);
335191783Srmacklem}
336191783Srmacklem
337191783Srmacklem/*
338191783Srmacklem * nfs access vnode op.
339191783Srmacklem * For nfs version 2, just return ok. File accesses may fail later.
340191783Srmacklem * For nfs version 3, use the access rpc to check accessibility. If file modes
341191783Srmacklem * are changed on the server, accesses might still fail later.
342191783Srmacklem */
343191783Srmacklemstatic int
344191783Srmacklemnfs_access(struct vop_access_args *ap)
345191783Srmacklem{
346191783Srmacklem	struct vnode *vp = ap->a_vp;
347191783Srmacklem	int error = 0, i, gotahit;
348191783Srmacklem	u_int32_t mode, wmode, rmode;
349191783Srmacklem	int v34 = NFS_ISV34(vp);
350191783Srmacklem	struct nfsnode *np = VTONFS(vp);
351191783Srmacklem
352191783Srmacklem	/*
353191783Srmacklem	 * Disallow write attempts on filesystems mounted read-only;
354191783Srmacklem	 * unless the file is a socket, fifo, or a block or character
355191783Srmacklem	 * device resident on the filesystem.
356191783Srmacklem	 */
357200069Strasz	if ((ap->a_accmode & (VWRITE | VAPPEND | VWRITE_NAMED_ATTRS |
358200069Strasz	    VDELETE_CHILD | VWRITE_ATTRIBUTES | VDELETE | VWRITE_ACL |
359200069Strasz	    VWRITE_OWNER)) != 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
360191783Srmacklem		switch (vp->v_type) {
361191783Srmacklem		case VREG:
362191783Srmacklem		case VDIR:
363191783Srmacklem		case VLNK:
364191783Srmacklem			return (EROFS);
365191783Srmacklem		default:
366191783Srmacklem			break;
367191783Srmacklem		}
368191783Srmacklem	}
369191783Srmacklem	/*
370191783Srmacklem	 * For nfs v3 or v4, check to see if we have done this recently, and if
371191783Srmacklem	 * so return our cached result instead of making an ACCESS call.
372191783Srmacklem	 * If not, do an access rpc, otherwise you are stuck emulating
373191783Srmacklem	 * ufs_access() locally using the vattr. This may not be correct,
374191783Srmacklem	 * since the server may apply other access criteria such as
375191783Srmacklem	 * client uid-->server uid mapping that we do not know about.
376191783Srmacklem	 */
377191783Srmacklem	if (v34) {
378191783Srmacklem		if (ap->a_accmode & VREAD)
379191783Srmacklem			mode = NFSACCESS_READ;
380191783Srmacklem		else
381191783Srmacklem			mode = 0;
382191783Srmacklem		if (vp->v_type != VDIR) {
383191783Srmacklem			if (ap->a_accmode & VWRITE)
384191783Srmacklem				mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
385191783Srmacklem			if (ap->a_accmode & VAPPEND)
386191783Srmacklem				mode |= NFSACCESS_EXTEND;
387191783Srmacklem			if (ap->a_accmode & VEXEC)
388191783Srmacklem				mode |= NFSACCESS_EXECUTE;
389191783Srmacklem			if (ap->a_accmode & VDELETE)
390191783Srmacklem				mode |= NFSACCESS_DELETE;
391191783Srmacklem		} else {
392191783Srmacklem			if (ap->a_accmode & VWRITE)
393191783Srmacklem				mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
394191783Srmacklem			if (ap->a_accmode & VAPPEND)
395191783Srmacklem				mode |= NFSACCESS_EXTEND;
396191783Srmacklem			if (ap->a_accmode & VEXEC)
397191783Srmacklem				mode |= NFSACCESS_LOOKUP;
398191783Srmacklem			if (ap->a_accmode & VDELETE)
399191783Srmacklem				mode |= NFSACCESS_DELETE;
400191783Srmacklem			if (ap->a_accmode & VDELETE_CHILD)
401191783Srmacklem				mode |= NFSACCESS_MODIFY;
402191783Srmacklem		}
403191783Srmacklem		/* XXX safety belt, only make blanket request if caching */
404191783Srmacklem		if (nfsaccess_cache_timeout > 0) {
405191783Srmacklem			wmode = NFSACCESS_READ | NFSACCESS_MODIFY |
406191783Srmacklem				NFSACCESS_EXTEND | NFSACCESS_EXECUTE |
407191783Srmacklem				NFSACCESS_DELETE | NFSACCESS_LOOKUP;
408191783Srmacklem		} else {
409191783Srmacklem			wmode = mode;
410191783Srmacklem		}
411191783Srmacklem
412191783Srmacklem		/*
413191783Srmacklem		 * Does our cached result allow us to give a definite yes to
414191783Srmacklem		 * this request?
415191783Srmacklem		 */
416191783Srmacklem		gotahit = 0;
417191783Srmacklem		mtx_lock(&np->n_mtx);
418191783Srmacklem		for (i = 0; i < NFS_ACCESSCACHESIZE; i++) {
419191783Srmacklem			if (ap->a_cred->cr_uid == np->n_accesscache[i].uid) {
420191783Srmacklem			    if (time_second < (np->n_accesscache[i].stamp
421191783Srmacklem				+ nfsaccess_cache_timeout) &&
422191783Srmacklem				(np->n_accesscache[i].mode & mode) == mode) {
423191783Srmacklem				NFSINCRGLOBAL(newnfsstats.accesscache_hits);
424191783Srmacklem				gotahit = 1;
425191783Srmacklem			    }
426191783Srmacklem			    break;
427191783Srmacklem			}
428191783Srmacklem		}
429191783Srmacklem		mtx_unlock(&np->n_mtx);
430223280Srmacklem#ifdef KDTRACE_HOOKS
431223280Srmacklem		if (gotahit != 0)
432223280Srmacklem			KDTRACE_NFS_ACCESSCACHE_GET_HIT(vp,
433223280Srmacklem			    ap->a_cred->cr_uid, mode);
434223280Srmacklem		else
435223280Srmacklem			KDTRACE_NFS_ACCESSCACHE_GET_MISS(vp,
436223280Srmacklem			    ap->a_cred->cr_uid, mode);
437223280Srmacklem#endif
438191783Srmacklem		if (gotahit == 0) {
439191783Srmacklem			/*
440191783Srmacklem			 * Either a no, or a don't know.  Go to the wire.
441191783Srmacklem			 */
442191783Srmacklem			NFSINCRGLOBAL(newnfsstats.accesscache_misses);
443191783Srmacklem		        error = nfs34_access_otw(vp, wmode, ap->a_td,
444191783Srmacklem			    ap->a_cred, &rmode);
445191783Srmacklem			if (!error &&
446191783Srmacklem			    (rmode & mode) != mode)
447191783Srmacklem				error = EACCES;
448191783Srmacklem		}
449191783Srmacklem		return (error);
450191783Srmacklem	} else {
451191783Srmacklem		if ((error = nfsspec_access(ap)) != 0) {
452191783Srmacklem			return (error);
453191783Srmacklem		}
454191783Srmacklem		/*
455191783Srmacklem		 * Attempt to prevent a mapped root from accessing a file
456191783Srmacklem		 * which it shouldn't.  We try to read a byte from the file
457191783Srmacklem		 * if the user is root and the file is not zero length.
458191783Srmacklem		 * After calling nfsspec_access, we should have the correct
459191783Srmacklem		 * file size cached.
460191783Srmacklem		 */
461191783Srmacklem		mtx_lock(&np->n_mtx);
462191783Srmacklem		if (ap->a_cred->cr_uid == 0 && (ap->a_accmode & VREAD)
463191783Srmacklem		    && VTONFS(vp)->n_size > 0) {
464191783Srmacklem			struct iovec aiov;
465191783Srmacklem			struct uio auio;
466191783Srmacklem			char buf[1];
467191783Srmacklem
468191783Srmacklem			mtx_unlock(&np->n_mtx);
469191783Srmacklem			aiov.iov_base = buf;
470191783Srmacklem			aiov.iov_len = 1;
471191783Srmacklem			auio.uio_iov = &aiov;
472191783Srmacklem			auio.uio_iovcnt = 1;
473191783Srmacklem			auio.uio_offset = 0;
474191783Srmacklem			auio.uio_resid = 1;
475191783Srmacklem			auio.uio_segflg = UIO_SYSSPACE;
476191783Srmacklem			auio.uio_rw = UIO_READ;
477191783Srmacklem			auio.uio_td = ap->a_td;
478191783Srmacklem
479191783Srmacklem			if (vp->v_type == VREG)
480191783Srmacklem				error = ncl_readrpc(vp, &auio, ap->a_cred);
481191783Srmacklem			else if (vp->v_type == VDIR) {
482191783Srmacklem				char* bp;
483191783Srmacklem				bp = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK);
484191783Srmacklem				aiov.iov_base = bp;
485191783Srmacklem				aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ;
486191783Srmacklem				error = ncl_readdirrpc(vp, &auio, ap->a_cred,
487191783Srmacklem				    ap->a_td);
488191783Srmacklem				free(bp, M_TEMP);
489191783Srmacklem			} else if (vp->v_type == VLNK)
490191783Srmacklem				error = ncl_readlinkrpc(vp, &auio, ap->a_cred);
491191783Srmacklem			else
492191783Srmacklem				error = EACCES;
493191783Srmacklem		} else
494191783Srmacklem			mtx_unlock(&np->n_mtx);
495191783Srmacklem		return (error);
496191783Srmacklem	}
497191783Srmacklem}
498191783Srmacklem
499191783Srmacklem
500191783Srmacklem/*
501191783Srmacklem * nfs open vnode op
502191783Srmacklem * Check to see if the type is ok
503191783Srmacklem * and that deletion is not in progress.
504191783Srmacklem * For paged in text files, you will need to flush the page cache
505191783Srmacklem * if consistency is lost.
506191783Srmacklem */
507191783Srmacklem/* ARGSUSED */
508191783Srmacklemstatic int
509191783Srmacklemnfs_open(struct vop_open_args *ap)
510191783Srmacklem{
511191783Srmacklem	struct vnode *vp = ap->a_vp;
512191783Srmacklem	struct nfsnode *np = VTONFS(vp);
513191783Srmacklem	struct vattr vattr;
514191783Srmacklem	int error;
515191783Srmacklem	int fmode = ap->a_mode;
516236096Srmacklem	struct ucred *cred;
517191783Srmacklem
518191783Srmacklem	if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
519191783Srmacklem		return (EOPNOTSUPP);
520191783Srmacklem
521191783Srmacklem	/*
522191783Srmacklem	 * For NFSv4, we need to do the Open Op before cache validation,
523191783Srmacklem	 * so that we conform to RFC3530 Sec. 9.3.1.
524191783Srmacklem	 */
525191783Srmacklem	if (NFS_ISV4(vp)) {
526191783Srmacklem		error = nfsrpc_open(vp, fmode, ap->a_cred, ap->a_td);
527191783Srmacklem		if (error) {
528191783Srmacklem			error = nfscl_maperr(ap->a_td, error, (uid_t)0,
529191783Srmacklem			    (gid_t)0);
530191783Srmacklem			return (error);
531191783Srmacklem		}
532191783Srmacklem	}
533191783Srmacklem
534191783Srmacklem	/*
535191783Srmacklem	 * Now, if this Open will be doing reading, re-validate/flush the
536191783Srmacklem	 * cache, so that Close/Open coherency is maintained.
537191783Srmacklem	 */
538214513Srmacklem	mtx_lock(&np->n_mtx);
539214513Srmacklem	if (np->n_flag & NMODIFIED) {
540214513Srmacklem		mtx_unlock(&np->n_mtx);
541214513Srmacklem		error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
542214513Srmacklem		if (error == EINTR || error == EIO) {
543214513Srmacklem			if (NFS_ISV4(vp))
544214513Srmacklem				(void) nfsrpc_close(vp, 0, ap->a_td);
545214513Srmacklem			return (error);
546214513Srmacklem		}
547191783Srmacklem		mtx_lock(&np->n_mtx);
548214513Srmacklem		np->n_attrstamp = 0;
549223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
550214513Srmacklem		if (vp->v_type == VDIR)
551214513Srmacklem			np->n_direofoffset = 0;
552214513Srmacklem		mtx_unlock(&np->n_mtx);
553214513Srmacklem		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
554214513Srmacklem		if (error) {
555214513Srmacklem			if (NFS_ISV4(vp))
556214513Srmacklem				(void) nfsrpc_close(vp, 0, ap->a_td);
557214513Srmacklem			return (error);
558214513Srmacklem		}
559214513Srmacklem		mtx_lock(&np->n_mtx);
560214513Srmacklem		np->n_mtime = vattr.va_mtime;
561214513Srmacklem		if (NFS_ISV4(vp))
562214513Srmacklem			np->n_change = vattr.va_filerev;
563214513Srmacklem	} else {
564214513Srmacklem		mtx_unlock(&np->n_mtx);
565214513Srmacklem		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
566214513Srmacklem		if (error) {
567214513Srmacklem			if (NFS_ISV4(vp))
568214513Srmacklem				(void) nfsrpc_close(vp, 0, ap->a_td);
569214513Srmacklem			return (error);
570214513Srmacklem		}
571214513Srmacklem		mtx_lock(&np->n_mtx);
572214513Srmacklem		if ((NFS_ISV4(vp) && np->n_change != vattr.va_filerev) ||
573214513Srmacklem		    NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) {
574191783Srmacklem			if (vp->v_type == VDIR)
575191783Srmacklem				np->n_direofoffset = 0;
576210135Sjhb			mtx_unlock(&np->n_mtx);
577214513Srmacklem			error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
578214513Srmacklem			if (error == EINTR || error == EIO) {
579191783Srmacklem				if (NFS_ISV4(vp))
580192337Srmacklem					(void) nfsrpc_close(vp, 0, ap->a_td);
581191783Srmacklem				return (error);
582191783Srmacklem			}
583191783Srmacklem			mtx_lock(&np->n_mtx);
584191783Srmacklem			np->n_mtime = vattr.va_mtime;
585191783Srmacklem			if (NFS_ISV4(vp))
586191783Srmacklem				np->n_change = vattr.va_filerev;
587191783Srmacklem		}
588191783Srmacklem	}
589191783Srmacklem
590191783Srmacklem	/*
591191783Srmacklem	 * If the object has >= 1 O_DIRECT active opens, we disable caching.
592191783Srmacklem	 */
593214513Srmacklem	if (newnfs_directio_enable && (fmode & O_DIRECT) &&
594214513Srmacklem	    (vp->v_type == VREG)) {
595191783Srmacklem		if (np->n_directio_opens == 0) {
596214513Srmacklem			mtx_unlock(&np->n_mtx);
597191783Srmacklem			error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
598191783Srmacklem			if (error) {
599191783Srmacklem				if (NFS_ISV4(vp))
600192337Srmacklem					(void) nfsrpc_close(vp, 0, ap->a_td);
601191783Srmacklem				return (error);
602191783Srmacklem			}
603191783Srmacklem			mtx_lock(&np->n_mtx);
604191783Srmacklem			np->n_flag |= NNONCACHE;
605191783Srmacklem		}
606191783Srmacklem		np->n_directio_opens++;
607191783Srmacklem	}
608236096Srmacklem
609236096Srmacklem	/*
610236096Srmacklem	 * If this is an open for writing, capture a reference to the
611236096Srmacklem	 * credentials, so they can be used by ncl_putpages(). Using
612236096Srmacklem	 * these write credentials is preferable to the credentials of
613236096Srmacklem	 * whatever thread happens to be doing the VOP_PUTPAGES() since
614236096Srmacklem	 * the write RPCs are less likely to fail with EACCES.
615236096Srmacklem	 */
616236096Srmacklem	if ((fmode & FWRITE) != 0) {
617236096Srmacklem		cred = np->n_writecred;
618236096Srmacklem		np->n_writecred = crhold(ap->a_cred);
619236096Srmacklem	} else
620236096Srmacklem		cred = NULL;
621214513Srmacklem	mtx_unlock(&np->n_mtx);
622236096Srmacklem	if (cred != NULL)
623236096Srmacklem		crfree(cred);
624191783Srmacklem	vnode_create_vobject(vp, vattr.va_size, ap->a_td);
625191783Srmacklem	return (0);
626191783Srmacklem}
627191783Srmacklem
628191783Srmacklem/*
629191783Srmacklem * nfs close vnode op
630191783Srmacklem * What an NFS client should do upon close after writing is a debatable issue.
631191783Srmacklem * Most NFS clients push delayed writes to the server upon close, basically for
632191783Srmacklem * two reasons:
633191783Srmacklem * 1 - So that any write errors may be reported back to the client process
634191783Srmacklem *     doing the close system call. By far the two most likely errors are
635191783Srmacklem *     NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure.
636191783Srmacklem * 2 - To put a worst case upper bound on cache inconsistency between
637191783Srmacklem *     multiple clients for the file.
638191783Srmacklem * There is also a consistency problem for Version 2 of the protocol w.r.t.
639191783Srmacklem * not being able to tell if other clients are writing a file concurrently,
640191783Srmacklem * since there is no way of knowing if the changed modify time in the reply
641191783Srmacklem * is only due to the write for this client.
642191783Srmacklem * (NFS Version 3 provides weak cache consistency data in the reply that
643191783Srmacklem *  should be sufficient to detect and handle this case.)
644191783Srmacklem *
645191783Srmacklem * The current code does the following:
646191783Srmacklem * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers
647191783Srmacklem * for NFS Version 3 - flush dirty buffers to the server but don't invalidate
648191783Srmacklem *                     or commit them (this satisfies 1 and 2 except for the
649191783Srmacklem *                     case where the server crashes after this close but
650191783Srmacklem *                     before the commit RPC, which is felt to be "good
651191783Srmacklem *                     enough". Changing the last argument to ncl_flush() to
652191783Srmacklem *                     a 1 would force a commit operation, if it is felt a
653191783Srmacklem *                     commit is necessary now.
654191783Srmacklem * for NFS Version 4 - flush the dirty buffers and commit them, if
655191783Srmacklem *		       nfscl_mustflush() says this is necessary.
656191783Srmacklem *                     It is necessary if there is no write delegation held,
657191783Srmacklem *                     in order to satisfy open/close coherency.
658191783Srmacklem *                     If the file isn't cached on local stable storage,
659191783Srmacklem *                     it may be necessary in order to detect "out of space"
660191783Srmacklem *                     errors from the server, if the write delegation
661191783Srmacklem *                     issued by the server doesn't allow the file to grow.
662191783Srmacklem */
663191783Srmacklem/* ARGSUSED */
664191783Srmacklemstatic int
665191783Srmacklemnfs_close(struct vop_close_args *ap)
666191783Srmacklem{
667191783Srmacklem	struct vnode *vp = ap->a_vp;
668191783Srmacklem	struct nfsnode *np = VTONFS(vp);
669191783Srmacklem	struct nfsvattr nfsva;
670191783Srmacklem	struct ucred *cred;
671191783Srmacklem	int error = 0, ret, localcred = 0;
672191783Srmacklem	int fmode = ap->a_fflag;
673191783Srmacklem
674191783Srmacklem	if ((vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF))
675191783Srmacklem		return (0);
676191783Srmacklem	/*
677191783Srmacklem	 * During shutdown, a_cred isn't valid, so just use root.
678191783Srmacklem	 */
679191783Srmacklem	if (ap->a_cred == NOCRED) {
680191783Srmacklem		cred = newnfs_getcred();
681191783Srmacklem		localcred = 1;
682191783Srmacklem	} else {
683191783Srmacklem		cred = ap->a_cred;
684191783Srmacklem	}
685191783Srmacklem	if (vp->v_type == VREG) {
686191783Srmacklem	    /*
687191783Srmacklem	     * Examine and clean dirty pages, regardless of NMODIFIED.
688191783Srmacklem	     * This closes a major hole in close-to-open consistency.
689191783Srmacklem	     * We want to push out all dirty pages (and buffers) on
690191783Srmacklem	     * close, regardless of whether they were dirtied by
691191783Srmacklem	     * mmap'ed writes or via write().
692191783Srmacklem	     */
693191783Srmacklem	    if (nfs_clean_pages_on_close && vp->v_object) {
694191783Srmacklem		VM_OBJECT_LOCK(vp->v_object);
695191783Srmacklem		vm_object_page_clean(vp->v_object, 0, 0, 0);
696191783Srmacklem		VM_OBJECT_UNLOCK(vp->v_object);
697191783Srmacklem	    }
698191783Srmacklem	    mtx_lock(&np->n_mtx);
699191783Srmacklem	    if (np->n_flag & NMODIFIED) {
700191783Srmacklem		mtx_unlock(&np->n_mtx);
701191783Srmacklem		if (NFS_ISV3(vp)) {
702191783Srmacklem		    /*
703191783Srmacklem		     * Under NFSv3 we have dirty buffers to dispose of.  We
704191783Srmacklem		     * must flush them to the NFS server.  We have the option
705191783Srmacklem		     * of waiting all the way through the commit rpc or just
706191783Srmacklem		     * waiting for the initial write.  The default is to only
707191783Srmacklem		     * wait through the initial write so the data is in the
708191783Srmacklem		     * server's cache, which is roughly similar to the state
709191783Srmacklem		     * a standard disk subsystem leaves the file in on close().
710191783Srmacklem		     *
711191783Srmacklem		     * We cannot clear the NMODIFIED bit in np->n_flag due to
712191783Srmacklem		     * potential races with other processes, and certainly
713191783Srmacklem		     * cannot clear it if we don't commit.
714191783Srmacklem		     * These races occur when there is no longer the old
715191783Srmacklem		     * traditional vnode locking implemented for Vnode Ops.
716191783Srmacklem		     */
717191783Srmacklem		    int cm = newnfs_commit_on_close ? 1 : 0;
718207082Srmacklem		    error = ncl_flush(vp, MNT_WAIT, cred, ap->a_td, cm, 0);
719191783Srmacklem		    /* np->n_flag &= ~NMODIFIED; */
720195943Srmacklem		} else if (NFS_ISV4(vp)) {
721210786Srmacklem			if (nfscl_mustflush(vp) != 0) {
722195943Srmacklem				int cm = newnfs_commit_on_close ? 1 : 0;
723195943Srmacklem				error = ncl_flush(vp, MNT_WAIT, cred, ap->a_td,
724207082Srmacklem				    cm, 0);
725195943Srmacklem				/*
726195943Srmacklem				 * as above w.r.t races when clearing
727195943Srmacklem				 * NMODIFIED.
728195943Srmacklem				 * np->n_flag &= ~NMODIFIED;
729195943Srmacklem				 */
730195943Srmacklem			}
731191783Srmacklem		} else
732191783Srmacklem		    error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
733191783Srmacklem		mtx_lock(&np->n_mtx);
734191783Srmacklem	    }
735191783Srmacklem 	    /*
736191783Srmacklem 	     * Invalidate the attribute cache in all cases.
737191783Srmacklem 	     * An open is going to fetch fresh attrs any way, other procs
738191783Srmacklem 	     * on this node that have file open will be forced to do an
739191783Srmacklem 	     * otw attr fetch, but this is safe.
740191783Srmacklem	     * --> A user found that their RPC count dropped by 20% when
741191783Srmacklem	     *     this was commented out and I can't see any requirement
742191783Srmacklem	     *     for it, so I've disabled it when negative lookups are
743191783Srmacklem	     *     enabled. (What does this have to do with negative lookup
744191783Srmacklem	     *     caching? Well nothing, except it was reported by the
745191783Srmacklem	     *     same user that needed negative lookup caching and I wanted
746203303Srmacklem	     *     there to be a way to disable it to see if it
747191783Srmacklem	     *     is the cause of some caching/coherency issue that might
748191783Srmacklem	     *     crop up.)
749191783Srmacklem 	     */
750223280Srmacklem	    if (VFSTONFS(vp->v_mount)->nm_negnametimeo == 0) {
751191783Srmacklem		    np->n_attrstamp = 0;
752223280Srmacklem		    KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
753223280Srmacklem	    }
754191783Srmacklem	    if (np->n_flag & NWRITEERR) {
755191783Srmacklem		np->n_flag &= ~NWRITEERR;
756191783Srmacklem		error = np->n_error;
757191783Srmacklem	    }
758191783Srmacklem	    mtx_unlock(&np->n_mtx);
759191783Srmacklem	}
760191783Srmacklem
761191783Srmacklem	if (NFS_ISV4(vp)) {
762191783Srmacklem		/*
763191783Srmacklem		 * Get attributes so "change" is up to date.
764191783Srmacklem		 */
765265390Srmacklem		if (error == 0 && nfscl_mustflush(vp) != 0 &&
766265390Srmacklem		    vp->v_type == VREG &&
767265390Srmacklem		    (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) == 0) {
768191783Srmacklem			ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva,
769191783Srmacklem			    NULL);
770191783Srmacklem			if (!ret) {
771191783Srmacklem				np->n_change = nfsva.na_filerev;
772191783Srmacklem				(void) nfscl_loadattrcache(&vp, &nfsva, NULL,
773191783Srmacklem				    NULL, 0, 0);
774191783Srmacklem			}
775191783Srmacklem		}
776191783Srmacklem
777191783Srmacklem		/*
778191783Srmacklem		 * and do the close.
779191783Srmacklem		 */
780192337Srmacklem		ret = nfsrpc_close(vp, 0, ap->a_td);
781191783Srmacklem		if (!error && ret)
782191783Srmacklem			error = ret;
783191783Srmacklem		if (error)
784191783Srmacklem			error = nfscl_maperr(ap->a_td, error, (uid_t)0,
785191783Srmacklem			    (gid_t)0);
786191783Srmacklem	}
787191783Srmacklem	if (newnfs_directio_enable)
788191783Srmacklem		KASSERT((np->n_directio_asyncwr == 0),
789191783Srmacklem			("nfs_close: dirty unflushed (%d) directio buffers\n",
790191783Srmacklem			 np->n_directio_asyncwr));
791191783Srmacklem	if (newnfs_directio_enable && (fmode & O_DIRECT) && (vp->v_type == VREG)) {
792191783Srmacklem		mtx_lock(&np->n_mtx);
793191783Srmacklem		KASSERT((np->n_directio_opens > 0),
794191783Srmacklem			("nfs_close: unexpectedly value (0) of n_directio_opens\n"));
795191783Srmacklem		np->n_directio_opens--;
796191783Srmacklem		if (np->n_directio_opens == 0)
797191783Srmacklem			np->n_flag &= ~NNONCACHE;
798191783Srmacklem		mtx_unlock(&np->n_mtx);
799191783Srmacklem	}
800191783Srmacklem	if (localcred)
801191783Srmacklem		NFSFREECRED(cred);
802191783Srmacklem	return (error);
803191783Srmacklem}
804191783Srmacklem
805191783Srmacklem/*
806191783Srmacklem * nfs getattr call from vfs.
807191783Srmacklem */
808191783Srmacklemstatic int
809191783Srmacklemnfs_getattr(struct vop_getattr_args *ap)
810191783Srmacklem{
811191783Srmacklem	struct vnode *vp = ap->a_vp;
812191783Srmacklem	struct thread *td = curthread;	/* XXX */
813191783Srmacklem	struct nfsnode *np = VTONFS(vp);
814191783Srmacklem	int error = 0;
815191783Srmacklem	struct nfsvattr nfsva;
816191783Srmacklem	struct vattr *vap = ap->a_vap;
817191783Srmacklem	struct vattr vattr;
818191783Srmacklem
819191783Srmacklem	/*
820191783Srmacklem	 * Update local times for special files.
821191783Srmacklem	 */
822191783Srmacklem	mtx_lock(&np->n_mtx);
823191783Srmacklem	if (np->n_flag & (NACC | NUPD))
824191783Srmacklem		np->n_flag |= NCHG;
825191783Srmacklem	mtx_unlock(&np->n_mtx);
826191783Srmacklem	/*
827191783Srmacklem	 * First look in the cache.
828191783Srmacklem	 */
829191783Srmacklem	if (ncl_getattrcache(vp, &vattr) == 0) {
830191783Srmacklem		vap->va_type = vattr.va_type;
831191783Srmacklem		vap->va_mode = vattr.va_mode;
832191783Srmacklem		vap->va_nlink = vattr.va_nlink;
833191783Srmacklem		vap->va_uid = vattr.va_uid;
834191783Srmacklem		vap->va_gid = vattr.va_gid;
835191783Srmacklem		vap->va_fsid = vattr.va_fsid;
836191783Srmacklem		vap->va_fileid = vattr.va_fileid;
837191783Srmacklem		vap->va_size = vattr.va_size;
838191783Srmacklem		vap->va_blocksize = vattr.va_blocksize;
839191783Srmacklem		vap->va_atime = vattr.va_atime;
840191783Srmacklem		vap->va_mtime = vattr.va_mtime;
841191783Srmacklem		vap->va_ctime = vattr.va_ctime;
842191783Srmacklem		vap->va_gen = vattr.va_gen;
843191783Srmacklem		vap->va_flags = vattr.va_flags;
844191783Srmacklem		vap->va_rdev = vattr.va_rdev;
845191783Srmacklem		vap->va_bytes = vattr.va_bytes;
846191783Srmacklem		vap->va_filerev = vattr.va_filerev;
847191783Srmacklem		/*
848191783Srmacklem		 * Get the local modify time for the case of a write
849191783Srmacklem		 * delegation.
850191783Srmacklem		 */
851191783Srmacklem		nfscl_deleggetmodtime(vp, &vap->va_mtime);
852191783Srmacklem		return (0);
853191783Srmacklem	}
854191783Srmacklem
855191783Srmacklem	if (NFS_ISV34(vp) && nfs_prime_access_cache &&
856191783Srmacklem	    nfsaccess_cache_timeout > 0) {
857191783Srmacklem		NFSINCRGLOBAL(newnfsstats.accesscache_misses);
858191783Srmacklem		nfs34_access_otw(vp, NFSACCESS_ALL, td, ap->a_cred, NULL);
859191783Srmacklem		if (ncl_getattrcache(vp, ap->a_vap) == 0) {
860191783Srmacklem			nfscl_deleggetmodtime(vp, &ap->a_vap->va_mtime);
861191783Srmacklem			return (0);
862191783Srmacklem		}
863191783Srmacklem	}
864191783Srmacklem	error = nfsrpc_getattr(vp, ap->a_cred, td, &nfsva, NULL);
865191783Srmacklem	if (!error)
866191783Srmacklem		error = nfscl_loadattrcache(&vp, &nfsva, vap, NULL, 0, 0);
867191783Srmacklem	if (!error) {
868191783Srmacklem		/*
869191783Srmacklem		 * Get the local modify time for the case of a write
870191783Srmacklem		 * delegation.
871191783Srmacklem		 */
872191783Srmacklem		nfscl_deleggetmodtime(vp, &vap->va_mtime);
873191783Srmacklem	} else if (NFS_ISV4(vp)) {
874191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
875191783Srmacklem	}
876191783Srmacklem	return (error);
877191783Srmacklem}
878191783Srmacklem
879191783Srmacklem/*
880191783Srmacklem * nfs setattr call.
881191783Srmacklem */
882191783Srmacklemstatic int
883191783Srmacklemnfs_setattr(struct vop_setattr_args *ap)
884191783Srmacklem{
885191783Srmacklem	struct vnode *vp = ap->a_vp;
886191783Srmacklem	struct nfsnode *np = VTONFS(vp);
887191783Srmacklem	struct thread *td = curthread;	/* XXX */
888191783Srmacklem	struct vattr *vap = ap->a_vap;
889191783Srmacklem	int error = 0;
890191783Srmacklem	u_quad_t tsize;
891191783Srmacklem
892191783Srmacklem#ifndef nolint
893191783Srmacklem	tsize = (u_quad_t)0;
894191783Srmacklem#endif
895191783Srmacklem
896191783Srmacklem	/*
897191783Srmacklem	 * Setting of flags and marking of atimes are not supported.
898191783Srmacklem	 */
899191783Srmacklem	if (vap->va_flags != VNOVAL)
900191783Srmacklem		return (EOPNOTSUPP);
901191783Srmacklem
902191783Srmacklem	/*
903191783Srmacklem	 * Disallow write attempts if the filesystem is mounted read-only.
904191783Srmacklem	 */
905191783Srmacklem  	if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
906191783Srmacklem	    vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
907191783Srmacklem	    vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
908191783Srmacklem	    (vp->v_mount->mnt_flag & MNT_RDONLY))
909191783Srmacklem		return (EROFS);
910191783Srmacklem	if (vap->va_size != VNOVAL) {
911191783Srmacklem 		switch (vp->v_type) {
912191783Srmacklem 		case VDIR:
913191783Srmacklem 			return (EISDIR);
914191783Srmacklem 		case VCHR:
915191783Srmacklem 		case VBLK:
916191783Srmacklem 		case VSOCK:
917191783Srmacklem 		case VFIFO:
918191783Srmacklem			if (vap->va_mtime.tv_sec == VNOVAL &&
919191783Srmacklem			    vap->va_atime.tv_sec == VNOVAL &&
920191783Srmacklem			    vap->va_mode == (mode_t)VNOVAL &&
921191783Srmacklem			    vap->va_uid == (uid_t)VNOVAL &&
922191783Srmacklem			    vap->va_gid == (gid_t)VNOVAL)
923191783Srmacklem				return (0);
924191783Srmacklem 			vap->va_size = VNOVAL;
925191783Srmacklem 			break;
926191783Srmacklem 		default:
927191783Srmacklem			/*
928191783Srmacklem			 * Disallow write attempts if the filesystem is
929191783Srmacklem			 * mounted read-only.
930191783Srmacklem			 */
931191783Srmacklem			if (vp->v_mount->mnt_flag & MNT_RDONLY)
932191783Srmacklem				return (EROFS);
933191783Srmacklem			/*
934191783Srmacklem			 *  We run vnode_pager_setsize() early (why?),
935191783Srmacklem			 * we must set np->n_size now to avoid vinvalbuf
936191783Srmacklem			 * V_SAVE races that might setsize a lower
937191783Srmacklem			 * value.
938191783Srmacklem			 */
939191783Srmacklem			mtx_lock(&np->n_mtx);
940191783Srmacklem			tsize = np->n_size;
941191783Srmacklem			mtx_unlock(&np->n_mtx);
942191783Srmacklem			error = ncl_meta_setsize(vp, ap->a_cred, td,
943191783Srmacklem			    vap->va_size);
944191783Srmacklem			mtx_lock(&np->n_mtx);
945191783Srmacklem 			if (np->n_flag & NMODIFIED) {
946191783Srmacklem			    tsize = np->n_size;
947191783Srmacklem			    mtx_unlock(&np->n_mtx);
948191783Srmacklem 			    if (vap->va_size == 0)
949191783Srmacklem 				error = ncl_vinvalbuf(vp, 0, td, 1);
950191783Srmacklem 			    else
951191783Srmacklem 				error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
952191783Srmacklem 			    if (error) {
953191783Srmacklem				vnode_pager_setsize(vp, tsize);
954191783Srmacklem				return (error);
955191783Srmacklem			    }
956191783Srmacklem			    /*
957191783Srmacklem			     * Call nfscl_delegmodtime() to set the modify time
958191783Srmacklem			     * locally, as required.
959191783Srmacklem			     */
960191783Srmacklem			    nfscl_delegmodtime(vp);
961191783Srmacklem 			} else
962191783Srmacklem			    mtx_unlock(&np->n_mtx);
963191783Srmacklem			/*
964191783Srmacklem			 * np->n_size has already been set to vap->va_size
965191783Srmacklem			 * in ncl_meta_setsize(). We must set it again since
966191783Srmacklem			 * nfs_loadattrcache() could be called through
967191783Srmacklem			 * ncl_meta_setsize() and could modify np->n_size.
968191783Srmacklem			 */
969191783Srmacklem			mtx_lock(&np->n_mtx);
970191783Srmacklem 			np->n_vattr.na_size = np->n_size = vap->va_size;
971191783Srmacklem			mtx_unlock(&np->n_mtx);
972191783Srmacklem  		};
973191783Srmacklem  	} else {
974191783Srmacklem		mtx_lock(&np->n_mtx);
975191783Srmacklem		if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) &&
976191783Srmacklem		    (np->n_flag & NMODIFIED) && vp->v_type == VREG) {
977191783Srmacklem			mtx_unlock(&np->n_mtx);
978191783Srmacklem			if ((error = ncl_vinvalbuf(vp, V_SAVE, td, 1)) != 0 &&
979191783Srmacklem			    (error == EINTR || error == EIO))
980191783Srmacklem				return (error);
981191783Srmacklem		} else
982191783Srmacklem			mtx_unlock(&np->n_mtx);
983191783Srmacklem	}
984191783Srmacklem	error = nfs_setattrrpc(vp, vap, ap->a_cred, td);
985191783Srmacklem	if (error && vap->va_size != VNOVAL) {
986191783Srmacklem		mtx_lock(&np->n_mtx);
987191783Srmacklem		np->n_size = np->n_vattr.na_size = tsize;
988191783Srmacklem		vnode_pager_setsize(vp, tsize);
989191783Srmacklem		mtx_unlock(&np->n_mtx);
990191783Srmacklem	}
991191783Srmacklem	return (error);
992191783Srmacklem}
993191783Srmacklem
994191783Srmacklem/*
995191783Srmacklem * Do an nfs setattr rpc.
996191783Srmacklem */
997191783Srmacklemstatic int
998191783Srmacklemnfs_setattrrpc(struct vnode *vp, struct vattr *vap, struct ucred *cred,
999191783Srmacklem    struct thread *td)
1000191783Srmacklem{
1001191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1002191783Srmacklem	int error, ret, attrflag, i;
1003191783Srmacklem	struct nfsvattr nfsva;
1004191783Srmacklem
1005191783Srmacklem	if (NFS_ISV34(vp)) {
1006191783Srmacklem		mtx_lock(&np->n_mtx);
1007191783Srmacklem		for (i = 0; i < NFS_ACCESSCACHESIZE; i++)
1008191783Srmacklem			np->n_accesscache[i].stamp = 0;
1009191783Srmacklem		np->n_flag |= NDELEGMOD;
1010191783Srmacklem		mtx_unlock(&np->n_mtx);
1011223280Srmacklem		KDTRACE_NFS_ACCESSCACHE_FLUSH_DONE(vp);
1012191783Srmacklem	}
1013191783Srmacklem	error = nfsrpc_setattr(vp, vap, NULL, cred, td, &nfsva, &attrflag,
1014191783Srmacklem	    NULL);
1015191783Srmacklem	if (attrflag) {
1016191783Srmacklem		ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
1017191783Srmacklem		if (ret && !error)
1018191783Srmacklem			error = ret;
1019191783Srmacklem	}
1020191783Srmacklem	if (error && NFS_ISV4(vp))
1021191783Srmacklem		error = nfscl_maperr(td, error, vap->va_uid, vap->va_gid);
1022191783Srmacklem	return (error);
1023191783Srmacklem}
1024191783Srmacklem
1025191783Srmacklem/*
1026191783Srmacklem * nfs lookup call, one step at a time...
1027191783Srmacklem * First look in cache
1028191783Srmacklem * If not found, unlock the directory nfsnode and do the rpc
1029191783Srmacklem */
1030191783Srmacklemstatic int
1031191783Srmacklemnfs_lookup(struct vop_lookup_args *ap)
1032191783Srmacklem{
1033191783Srmacklem	struct componentname *cnp = ap->a_cnp;
1034191783Srmacklem	struct vnode *dvp = ap->a_dvp;
1035191783Srmacklem	struct vnode **vpp = ap->a_vpp;
1036194363Srmacklem	struct mount *mp = dvp->v_mount;
1037191783Srmacklem	int flags = cnp->cn_flags;
1038191783Srmacklem	struct vnode *newvp;
1039191783Srmacklem	struct nfsmount *nmp;
1040210135Sjhb	struct nfsnode *np, *newnp;
1041233285Sjhb	int error = 0, attrflag, dattrflag, ltype, ncticks;
1042191783Srmacklem	struct thread *td = cnp->cn_thread;
1043191783Srmacklem	struct nfsfh *nfhp;
1044191783Srmacklem	struct nfsvattr dnfsva, nfsva;
1045203303Srmacklem	struct vattr vattr;
1046233285Sjhb	struct timespec nctime;
1047191783Srmacklem
1048191783Srmacklem	*vpp = NULLVP;
1049194363Srmacklem	if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) &&
1050191783Srmacklem	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
1051191783Srmacklem		return (EROFS);
1052191783Srmacklem	if (dvp->v_type != VDIR)
1053191783Srmacklem		return (ENOTDIR);
1054194363Srmacklem	nmp = VFSTONFS(mp);
1055191783Srmacklem	np = VTONFS(dvp);
1056191783Srmacklem
1057191783Srmacklem	/* For NFSv4, wait until any remove is done. */
1058191783Srmacklem	mtx_lock(&np->n_mtx);
1059191783Srmacklem	while (NFSHASNFSV4(nmp) && (np->n_flag & NREMOVEINPROG)) {
1060191783Srmacklem		np->n_flag |= NREMOVEWANT;
1061191783Srmacklem		(void) msleep((caddr_t)np, &np->n_mtx, PZERO, "nfslkup", 0);
1062191783Srmacklem	}
1063191783Srmacklem	mtx_unlock(&np->n_mtx);
1064191783Srmacklem
1065191783Srmacklem	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1066191783Srmacklem		return (error);
1067233285Sjhb	error = cache_lookup_times(dvp, vpp, cnp, &nctime, &ncticks);
1068203303Srmacklem	if (error > 0 && error != ENOENT)
1069203303Srmacklem		return (error);
1070203303Srmacklem	if (error == -1) {
1071203303Srmacklem		/*
1072233285Sjhb		 * Lookups of "." are special and always return the
1073233285Sjhb		 * current directory.  cache_lookup() already handles
1074233285Sjhb		 * associated locking bookkeeping, etc.
1075233285Sjhb		 */
1076233285Sjhb		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
1077233285Sjhb			/* XXX: Is this really correct? */
1078233285Sjhb			if (cnp->cn_nameiop != LOOKUP &&
1079233285Sjhb			    (flags & ISLASTCN))
1080233285Sjhb				cnp->cn_flags |= SAVENAME;
1081233285Sjhb			return (0);
1082233285Sjhb		}
1083233285Sjhb
1084233285Sjhb		/*
1085203303Srmacklem		 * We only accept a positive hit in the cache if the
1086203303Srmacklem		 * change time of the file matches our cached copy.
1087203303Srmacklem		 * Otherwise, we discard the cache entry and fallback
1088233326Sjhb		 * to doing a lookup RPC.  We also only trust cache
1089233326Sjhb		 * entries for less than nm_nametimeo seconds.
1090210135Sjhb		 *
1091210135Sjhb		 * To better handle stale file handles and attributes,
1092210135Sjhb		 * clear the attribute cache of this node if it is a
1093210135Sjhb		 * leaf component, part of an open() call, and not
1094210135Sjhb		 * locally modified before fetching the attributes.
1095210135Sjhb		 * This should allow stale file handles to be detected
1096210135Sjhb		 * here where we can fall back to a LOOKUP RPC to
1097210135Sjhb		 * recover rather than having nfs_open() detect the
1098210135Sjhb		 * stale file handle and failing open(2) with ESTALE.
1099203303Srmacklem		 */
1100203303Srmacklem		newvp = *vpp;
1101210135Sjhb		newnp = VTONFS(newvp);
1102221436Sru		if (!(nmp->nm_flag & NFSMNT_NOCTO) &&
1103221436Sru		    (flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) &&
1104210135Sjhb		    !(newnp->n_flag & NMODIFIED)) {
1105210135Sjhb			mtx_lock(&newnp->n_mtx);
1106210135Sjhb			newnp->n_attrstamp = 0;
1107223280Srmacklem			KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(newvp);
1108210135Sjhb			mtx_unlock(&newnp->n_mtx);
1109210135Sjhb		}
1110203303Srmacklem		if (nfscl_nodeleg(newvp, 0) == 0 ||
1111233326Sjhb		    ((u_int)(ticks - ncticks) < (nmp->nm_nametimeo * hz) &&
1112233326Sjhb		    VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 &&
1113233285Sjhb		    timespeccmp(&vattr.va_ctime, &nctime, ==))) {
1114203303Srmacklem			NFSINCRGLOBAL(newnfsstats.lookupcache_hits);
1115203303Srmacklem			if (cnp->cn_nameiop != LOOKUP &&
1116203303Srmacklem			    (flags & ISLASTCN))
1117203303Srmacklem				cnp->cn_flags |= SAVENAME;
1118203303Srmacklem			return (0);
1119191783Srmacklem		}
1120203303Srmacklem		cache_purge(newvp);
1121203303Srmacklem		if (dvp != newvp)
1122203303Srmacklem			vput(newvp);
1123203303Srmacklem		else
1124203303Srmacklem			vrele(newvp);
1125203303Srmacklem		*vpp = NULLVP;
1126203303Srmacklem	} else if (error == ENOENT) {
1127203303Srmacklem		if (dvp->v_iflag & VI_DOOMED)
1128203303Srmacklem			return (ENOENT);
1129203303Srmacklem		/*
1130203303Srmacklem		 * We only accept a negative hit in the cache if the
1131203303Srmacklem		 * modification time of the parent directory matches
1132233285Sjhb		 * the cached copy in the name cache entry.
1133233285Sjhb		 * Otherwise, we discard all of the negative cache
1134233285Sjhb		 * entries for this directory.  We also only trust
1135233285Sjhb		 * negative cache entries for up to nm_negnametimeo
1136233285Sjhb		 * seconds.
1137203303Srmacklem		 */
1138233285Sjhb		if ((u_int)(ticks - ncticks) < (nmp->nm_negnametimeo * hz) &&
1139203303Srmacklem		    VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 &&
1140233285Sjhb		    timespeccmp(&vattr.va_mtime, &nctime, ==)) {
1141203303Srmacklem			NFSINCRGLOBAL(newnfsstats.lookupcache_hits);
1142203303Srmacklem			return (ENOENT);
1143203303Srmacklem		}
1144203303Srmacklem		cache_purge_negative(dvp);
1145191783Srmacklem	}
1146203303Srmacklem
1147191783Srmacklem	error = 0;
1148191783Srmacklem	newvp = NULLVP;
1149191783Srmacklem	NFSINCRGLOBAL(newnfsstats.lookupcache_misses);
1150191783Srmacklem	error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
1151191783Srmacklem	    cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
1152191783Srmacklem	    NULL);
1153191783Srmacklem	if (dattrflag)
1154191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
1155191783Srmacklem	if (error) {
1156191783Srmacklem		if (newvp != NULLVP) {
1157191783Srmacklem			vput(newvp);
1158191783Srmacklem			*vpp = NULLVP;
1159191783Srmacklem		}
1160203303Srmacklem
1161203303Srmacklem		if (error != ENOENT) {
1162203303Srmacklem			if (NFS_ISV4(dvp))
1163203303Srmacklem				error = nfscl_maperr(td, error, (uid_t)0,
1164203303Srmacklem				    (gid_t)0);
1165203303Srmacklem			return (error);
1166203303Srmacklem		}
1167203303Srmacklem
1168203303Srmacklem		/* The requested file was not found. */
1169191783Srmacklem		if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
1170203303Srmacklem		    (flags & ISLASTCN)) {
1171203303Srmacklem			/*
1172203303Srmacklem			 * XXX: UFS does a full VOP_ACCESS(dvp,
1173203303Srmacklem			 * VWRITE) here instead of just checking
1174203303Srmacklem			 * MNT_RDONLY.
1175203303Srmacklem			 */
1176194363Srmacklem			if (mp->mnt_flag & MNT_RDONLY)
1177203303Srmacklem				return (EROFS);
1178203303Srmacklem			cnp->cn_flags |= SAVENAME;
1179203303Srmacklem			return (EJUSTRETURN);
1180191783Srmacklem		}
1181203303Srmacklem
1182233285Sjhb		if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE &&
1183233285Sjhb		    dattrflag) {
1184203303Srmacklem			/*
1185233285Sjhb			 * Cache the modification time of the parent
1186233285Sjhb			 * directory from the post-op attributes in
1187233285Sjhb			 * the name cache entry.  The negative cache
1188233285Sjhb			 * entry will be ignored once the directory
1189233285Sjhb			 * has changed.  Don't bother adding the entry
1190233285Sjhb			 * if the directory has already changed.
1191203303Srmacklem			 */
1192203303Srmacklem			mtx_lock(&np->n_mtx);
1193233285Sjhb			if (timespeccmp(&np->n_vattr.na_mtime,
1194233285Sjhb			    &dnfsva.na_mtime, ==)) {
1195203303Srmacklem				mtx_unlock(&np->n_mtx);
1196233285Sjhb				cache_enter_time(dvp, NULL, cnp,
1197233285Sjhb				    &dnfsva.na_mtime, NULL);
1198203303Srmacklem			} else
1199203303Srmacklem				mtx_unlock(&np->n_mtx);
1200203303Srmacklem		}
1201203303Srmacklem		return (ENOENT);
1202191783Srmacklem	}
1203191783Srmacklem
1204191783Srmacklem	/*
1205191783Srmacklem	 * Handle RENAME case...
1206191783Srmacklem	 */
1207191783Srmacklem	if (cnp->cn_nameiop == RENAME && (flags & ISLASTCN)) {
1208191783Srmacklem		if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) {
1209191783Srmacklem			FREE((caddr_t)nfhp, M_NFSFH);
1210191783Srmacklem			return (EISDIR);
1211191783Srmacklem		}
1212220732Srmacklem		error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL,
1213220732Srmacklem		    LK_EXCLUSIVE);
1214191783Srmacklem		if (error)
1215191783Srmacklem			return (error);
1216191783Srmacklem		newvp = NFSTOV(np);
1217191783Srmacklem		if (attrflag)
1218191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1219191783Srmacklem			    0, 1);
1220191783Srmacklem		*vpp = newvp;
1221191783Srmacklem		cnp->cn_flags |= SAVENAME;
1222191783Srmacklem		return (0);
1223191783Srmacklem	}
1224191783Srmacklem
1225194363Srmacklem	if (flags & ISDOTDOT) {
1226224083Szack		ltype = NFSVOPISLOCKED(dvp);
1227194363Srmacklem		error = vfs_busy(mp, MBF_NOWAIT);
1228194363Srmacklem		if (error != 0) {
1229195641Srmacklem			vfs_ref(mp);
1230224082Szack			NFSVOPUNLOCK(dvp, 0);
1231194363Srmacklem			error = vfs_busy(mp, 0);
1232224081Szack			NFSVOPLOCK(dvp, ltype | LK_RETRY);
1233195641Srmacklem			vfs_rel(mp);
1234194363Srmacklem			if (error == 0 && (dvp->v_iflag & VI_DOOMED)) {
1235194363Srmacklem				vfs_unbusy(mp);
1236194363Srmacklem				error = ENOENT;
1237194363Srmacklem			}
1238194363Srmacklem			if (error != 0)
1239194363Srmacklem				return (error);
1240194363Srmacklem		}
1241224082Szack		NFSVOPUNLOCK(dvp, 0);
1242220732Srmacklem		error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL,
1243220732Srmacklem		    cnp->cn_lkflags);
1244194363Srmacklem		if (error == 0)
1245194363Srmacklem			newvp = NFSTOV(np);
1246194363Srmacklem		vfs_unbusy(mp);
1247195704Srmacklem		if (newvp != dvp)
1248224081Szack			NFSVOPLOCK(dvp, ltype | LK_RETRY);
1249194363Srmacklem		if (dvp->v_iflag & VI_DOOMED) {
1250194363Srmacklem			if (error == 0) {
1251194363Srmacklem				if (newvp == dvp)
1252194363Srmacklem					vrele(newvp);
1253194363Srmacklem				else
1254194363Srmacklem					vput(newvp);
1255194363Srmacklem			}
1256194363Srmacklem			error = ENOENT;
1257194363Srmacklem		}
1258194363Srmacklem		if (error != 0)
1259191783Srmacklem			return (error);
1260191783Srmacklem		if (attrflag)
1261191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1262191783Srmacklem			    0, 1);
1263191783Srmacklem	} else if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) {
1264191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
1265191783Srmacklem		VREF(dvp);
1266191783Srmacklem		newvp = dvp;
1267191783Srmacklem		if (attrflag)
1268191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1269191783Srmacklem			    0, 1);
1270191783Srmacklem	} else {
1271220732Srmacklem		error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL,
1272220732Srmacklem		    cnp->cn_lkflags);
1273191783Srmacklem		if (error)
1274191783Srmacklem			return (error);
1275191783Srmacklem		newvp = NFSTOV(np);
1276191783Srmacklem		if (attrflag)
1277191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1278191783Srmacklem			    0, 1);
1279210135Sjhb		else if ((flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) &&
1280210135Sjhb		    !(np->n_flag & NMODIFIED)) {
1281210135Sjhb			/*
1282210135Sjhb			 * Flush the attribute cache when opening a
1283210135Sjhb			 * leaf node to ensure that fresh attributes
1284210135Sjhb			 * are fetched in nfs_open() since we did not
1285210135Sjhb			 * fetch attributes from the LOOKUP reply.
1286210135Sjhb			 */
1287210135Sjhb			mtx_lock(&np->n_mtx);
1288210135Sjhb			np->n_attrstamp = 0;
1289223280Srmacklem			KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(newvp);
1290210135Sjhb			mtx_unlock(&np->n_mtx);
1291210135Sjhb		}
1292191783Srmacklem	}
1293191783Srmacklem	if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
1294191783Srmacklem		cnp->cn_flags |= SAVENAME;
1295191783Srmacklem	if ((cnp->cn_flags & MAKEENTRY) &&
1296233285Sjhb	    (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) &&
1297233285Sjhb	    attrflag != 0 && (newvp->v_type != VDIR || dattrflag != 0))
1298233285Sjhb		cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime,
1299233285Sjhb		    newvp->v_type != VDIR ? NULL : &dnfsva.na_ctime);
1300191783Srmacklem	*vpp = newvp;
1301191783Srmacklem	return (0);
1302191783Srmacklem}
1303191783Srmacklem
1304191783Srmacklem/*
1305191783Srmacklem * nfs read call.
1306191783Srmacklem * Just call ncl_bioread() to do the work.
1307191783Srmacklem */
1308191783Srmacklemstatic int
1309191783Srmacklemnfs_read(struct vop_read_args *ap)
1310191783Srmacklem{
1311191783Srmacklem	struct vnode *vp = ap->a_vp;
1312191783Srmacklem
1313191783Srmacklem	switch (vp->v_type) {
1314191783Srmacklem	case VREG:
1315191783Srmacklem		return (ncl_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred));
1316191783Srmacklem	case VDIR:
1317191783Srmacklem		return (EISDIR);
1318191783Srmacklem	default:
1319191783Srmacklem		return (EOPNOTSUPP);
1320191783Srmacklem	}
1321191783Srmacklem}
1322191783Srmacklem
1323191783Srmacklem/*
1324191783Srmacklem * nfs readlink call
1325191783Srmacklem */
1326191783Srmacklemstatic int
1327191783Srmacklemnfs_readlink(struct vop_readlink_args *ap)
1328191783Srmacklem{
1329191783Srmacklem	struct vnode *vp = ap->a_vp;
1330191783Srmacklem
1331191783Srmacklem	if (vp->v_type != VLNK)
1332191783Srmacklem		return (EINVAL);
1333191783Srmacklem	return (ncl_bioread(vp, ap->a_uio, 0, ap->a_cred));
1334191783Srmacklem}
1335191783Srmacklem
1336191783Srmacklem/*
1337191783Srmacklem * Do a readlink rpc.
1338191783Srmacklem * Called by ncl_doio() from below the buffer cache.
1339191783Srmacklem */
1340191783Srmacklemint
1341191783Srmacklemncl_readlinkrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred)
1342191783Srmacklem{
1343191783Srmacklem	int error, ret, attrflag;
1344191783Srmacklem	struct nfsvattr nfsva;
1345191783Srmacklem
1346191783Srmacklem	error = nfsrpc_readlink(vp, uiop, cred, uiop->uio_td, &nfsva,
1347191783Srmacklem	    &attrflag, NULL);
1348191783Srmacklem	if (attrflag) {
1349191783Srmacklem		ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
1350191783Srmacklem		if (ret && !error)
1351191783Srmacklem			error = ret;
1352191783Srmacklem	}
1353191783Srmacklem	if (error && NFS_ISV4(vp))
1354191783Srmacklem		error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
1355191783Srmacklem	return (error);
1356191783Srmacklem}
1357191783Srmacklem
1358191783Srmacklem/*
1359191783Srmacklem * nfs read rpc call
1360191783Srmacklem * Ditto above
1361191783Srmacklem */
1362191783Srmacklemint
1363191783Srmacklemncl_readrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred)
1364191783Srmacklem{
1365191783Srmacklem	int error, ret, attrflag;
1366191783Srmacklem	struct nfsvattr nfsva;
1367191783Srmacklem
1368191783Srmacklem	error = nfsrpc_read(vp, uiop, cred, uiop->uio_td, &nfsva, &attrflag,
1369191783Srmacklem	    NULL);
1370191783Srmacklem	if (attrflag) {
1371191783Srmacklem		ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
1372191783Srmacklem		if (ret && !error)
1373191783Srmacklem			error = ret;
1374191783Srmacklem	}
1375191783Srmacklem	if (error && NFS_ISV4(vp))
1376191783Srmacklem		error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
1377191783Srmacklem	return (error);
1378191783Srmacklem}
1379191783Srmacklem
1380191783Srmacklem/*
1381191783Srmacklem * nfs write call
1382191783Srmacklem */
1383191783Srmacklemint
1384191783Srmacklemncl_writerpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
1385207082Srmacklem    int *iomode, int *must_commit, int called_from_strategy)
1386191783Srmacklem{
1387191783Srmacklem	struct nfsvattr nfsva;
1388191783Srmacklem	int error = 0, attrflag, ret;
1389191783Srmacklem
1390222289Srmacklem	error = nfsrpc_write(vp, uiop, iomode, must_commit, cred,
1391207082Srmacklem	    uiop->uio_td, &nfsva, &attrflag, NULL, called_from_strategy);
1392191783Srmacklem	if (attrflag) {
1393191783Srmacklem		if (VTONFS(vp)->n_flag & ND_NFSV4)
1394191783Srmacklem			ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 1,
1395191783Srmacklem			    1);
1396191783Srmacklem		else
1397191783Srmacklem			ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
1398191783Srmacklem			    1);
1399191783Srmacklem		if (ret && !error)
1400191783Srmacklem			error = ret;
1401191783Srmacklem	}
1402231936Skib	if (DOINGASYNC(vp))
1403191783Srmacklem		*iomode = NFSWRITE_FILESYNC;
1404191783Srmacklem	if (error && NFS_ISV4(vp))
1405191783Srmacklem		error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0);
1406191783Srmacklem	return (error);
1407191783Srmacklem}
1408191783Srmacklem
1409191783Srmacklem/*
1410191783Srmacklem * nfs mknod rpc
1411191783Srmacklem * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1412191783Srmacklem * mode set to specify the file type and the size field for rdev.
1413191783Srmacklem */
1414191783Srmacklemstatic int
1415191783Srmacklemnfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
1416191783Srmacklem    struct vattr *vap)
1417191783Srmacklem{
1418191783Srmacklem	struct nfsvattr nfsva, dnfsva;
1419191783Srmacklem	struct vnode *newvp = NULL;
1420191783Srmacklem	struct nfsnode *np = NULL, *dnp;
1421191783Srmacklem	struct nfsfh *nfhp;
1422191783Srmacklem	struct vattr vattr;
1423191783Srmacklem	int error = 0, attrflag, dattrflag;
1424191783Srmacklem	u_int32_t rdev;
1425191783Srmacklem
1426191783Srmacklem	if (vap->va_type == VCHR || vap->va_type == VBLK)
1427191783Srmacklem		rdev = vap->va_rdev;
1428191783Srmacklem	else if (vap->va_type == VFIFO || vap->va_type == VSOCK)
1429191783Srmacklem		rdev = 0xffffffff;
1430191783Srmacklem	else
1431191783Srmacklem		return (EOPNOTSUPP);
1432191783Srmacklem	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
1433191783Srmacklem		return (error);
1434191783Srmacklem	error = nfsrpc_mknod(dvp, cnp->cn_nameptr, cnp->cn_namelen, vap,
1435191783Srmacklem	    rdev, vap->va_type, cnp->cn_cred, cnp->cn_thread, &dnfsva,
1436191783Srmacklem	    &nfsva, &nfhp, &attrflag, &dattrflag, NULL);
1437191783Srmacklem	if (!error) {
1438191783Srmacklem		if (!nfhp)
1439191783Srmacklem			(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
1440191783Srmacklem			    cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
1441191783Srmacklem			    &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
1442191783Srmacklem			    NULL);
1443191783Srmacklem		if (nfhp)
1444191783Srmacklem			error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
1445220732Srmacklem			    cnp->cn_thread, &np, NULL, LK_EXCLUSIVE);
1446191783Srmacklem	}
1447191783Srmacklem	if (dattrflag)
1448191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
1449191783Srmacklem	if (!error) {
1450191783Srmacklem		newvp = NFSTOV(np);
1451220763Srmacklem		if (attrflag != 0) {
1452191783Srmacklem			error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1453191783Srmacklem			    0, 1);
1454220763Srmacklem			if (error != 0)
1455220763Srmacklem				vput(newvp);
1456220763Srmacklem		}
1457191783Srmacklem	}
1458191783Srmacklem	if (!error) {
1459191783Srmacklem		*vpp = newvp;
1460191783Srmacklem	} else if (NFS_ISV4(dvp)) {
1461191783Srmacklem		error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid,
1462191783Srmacklem		    vap->va_gid);
1463191783Srmacklem	}
1464191783Srmacklem	dnp = VTONFS(dvp);
1465191783Srmacklem	mtx_lock(&dnp->n_mtx);
1466191783Srmacklem	dnp->n_flag |= NMODIFIED;
1467223280Srmacklem	if (!dattrflag) {
1468191783Srmacklem		dnp->n_attrstamp = 0;
1469223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
1470223280Srmacklem	}
1471191783Srmacklem	mtx_unlock(&dnp->n_mtx);
1472191783Srmacklem	return (error);
1473191783Srmacklem}
1474191783Srmacklem
1475191783Srmacklem/*
1476191783Srmacklem * nfs mknod vop
1477191783Srmacklem * just call nfs_mknodrpc() to do the work.
1478191783Srmacklem */
1479191783Srmacklem/* ARGSUSED */
1480191783Srmacklemstatic int
1481191783Srmacklemnfs_mknod(struct vop_mknod_args *ap)
1482191783Srmacklem{
1483191783Srmacklem	return (nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap));
1484191783Srmacklem}
1485191783Srmacklem
1486199189Sjhstatic struct mtx nfs_cverf_mtx;
1487199189SjhMTX_SYSINIT(nfs_cverf_mtx, &nfs_cverf_mtx, "NFS create verifier mutex",
1488199189Sjh    MTX_DEF);
1489199189Sjh
1490199189Sjhstatic nfsquad_t
1491199189Sjhnfs_get_cverf(void)
1492199189Sjh{
1493199189Sjh	static nfsquad_t cverf;
1494199189Sjh	nfsquad_t ret;
1495199189Sjh	static int cverf_initialized = 0;
1496199189Sjh
1497199189Sjh	mtx_lock(&nfs_cverf_mtx);
1498199189Sjh	if (cverf_initialized == 0) {
1499199189Sjh		cverf.lval[0] = arc4random();
1500199189Sjh		cverf.lval[1] = arc4random();
1501199189Sjh		cverf_initialized = 1;
1502199189Sjh	} else
1503199189Sjh		cverf.qval++;
1504199189Sjh	ret = cverf;
1505199189Sjh	mtx_unlock(&nfs_cverf_mtx);
1506199189Sjh
1507199189Sjh	return (ret);
1508199189Sjh}
1509199189Sjh
1510191783Srmacklem/*
1511191783Srmacklem * nfs file create call
1512191783Srmacklem */
1513191783Srmacklemstatic int
1514191783Srmacklemnfs_create(struct vop_create_args *ap)
1515191783Srmacklem{
1516191783Srmacklem	struct vnode *dvp = ap->a_dvp;
1517191783Srmacklem	struct vattr *vap = ap->a_vap;
1518191783Srmacklem	struct componentname *cnp = ap->a_cnp;
1519191783Srmacklem	struct nfsnode *np = NULL, *dnp;
1520191783Srmacklem	struct vnode *newvp = NULL;
1521191783Srmacklem	struct nfsmount *nmp;
1522191783Srmacklem	struct nfsvattr dnfsva, nfsva;
1523191783Srmacklem	struct nfsfh *nfhp;
1524191783Srmacklem	nfsquad_t cverf;
1525191783Srmacklem	int error = 0, attrflag, dattrflag, fmode = 0;
1526191783Srmacklem	struct vattr vattr;
1527191783Srmacklem
1528191783Srmacklem	/*
1529191783Srmacklem	 * Oops, not for me..
1530191783Srmacklem	 */
1531191783Srmacklem	if (vap->va_type == VSOCK)
1532191783Srmacklem		return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
1533191783Srmacklem
1534191783Srmacklem	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
1535191783Srmacklem		return (error);
1536191783Srmacklem	if (vap->va_vaflags & VA_EXCLUSIVE)
1537191783Srmacklem		fmode |= O_EXCL;
1538191783Srmacklem	dnp = VTONFS(dvp);
1539191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
1540191783Srmacklemagain:
1541191783Srmacklem	/* For NFSv4, wait until any remove is done. */
1542191783Srmacklem	mtx_lock(&dnp->n_mtx);
1543191783Srmacklem	while (NFSHASNFSV4(nmp) && (dnp->n_flag & NREMOVEINPROG)) {
1544191783Srmacklem		dnp->n_flag |= NREMOVEWANT;
1545191783Srmacklem		(void) msleep((caddr_t)dnp, &dnp->n_mtx, PZERO, "nfscrt", 0);
1546191783Srmacklem	}
1547191783Srmacklem	mtx_unlock(&dnp->n_mtx);
1548191783Srmacklem
1549199189Sjh	cverf = nfs_get_cverf();
1550191783Srmacklem	error = nfsrpc_create(dvp, cnp->cn_nameptr, cnp->cn_namelen,
1551191783Srmacklem	    vap, cverf, fmode, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva,
1552191783Srmacklem	    &nfhp, &attrflag, &dattrflag, NULL);
1553191783Srmacklem	if (!error) {
1554191783Srmacklem		if (nfhp == NULL)
1555191783Srmacklem			(void) nfsrpc_lookup(dvp, cnp->cn_nameptr,
1556191783Srmacklem			    cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread,
1557191783Srmacklem			    &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag,
1558191783Srmacklem			    NULL);
1559191783Srmacklem		if (nfhp != NULL)
1560191783Srmacklem			error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp,
1561220732Srmacklem			    cnp->cn_thread, &np, NULL, LK_EXCLUSIVE);
1562191783Srmacklem	}
1563191783Srmacklem	if (dattrflag)
1564191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
1565191783Srmacklem	if (!error) {
1566191783Srmacklem		newvp = NFSTOV(np);
1567235397Srmacklem		if (attrflag == 0)
1568235397Srmacklem			error = nfsrpc_getattr(newvp, cnp->cn_cred,
1569235397Srmacklem			    cnp->cn_thread, &nfsva, NULL);
1570235397Srmacklem		if (error == 0)
1571191783Srmacklem			error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
1572191783Srmacklem			    0, 1);
1573191783Srmacklem	}
1574191783Srmacklem	if (error) {
1575191783Srmacklem		if (newvp != NULL) {
1576220763Srmacklem			vput(newvp);
1577191783Srmacklem			newvp = NULL;
1578191783Srmacklem		}
1579191783Srmacklem		if (NFS_ISV34(dvp) && (fmode & O_EXCL) &&
1580191783Srmacklem		    error == NFSERR_NOTSUPP) {
1581191783Srmacklem			fmode &= ~O_EXCL;
1582191783Srmacklem			goto again;
1583191783Srmacklem		}
1584191783Srmacklem	} else if (NFS_ISV34(dvp) && (fmode & O_EXCL)) {
1585191783Srmacklem		if (nfscl_checksattr(vap, &nfsva)) {
1586220762Srmacklem			/*
1587220762Srmacklem			 * We are normally called with only a partially
1588220762Srmacklem			 * initialized VAP. Since the NFSv3 spec says that
1589220762Srmacklem			 * the server may use the file attributes to
1590220762Srmacklem			 * store the verifier, the spec requires us to do a
1591220762Srmacklem			 * SETATTR RPC. FreeBSD servers store the verifier in
1592220762Srmacklem			 * atime, but we can't really assume that all servers
1593220762Srmacklem			 * will so we ensure that our SETATTR sets both atime
1594220762Srmacklem			 * and mtime.
1595220762Srmacklem			 */
1596220762Srmacklem			if (vap->va_mtime.tv_sec == VNOVAL)
1597220762Srmacklem				vfs_timestamp(&vap->va_mtime);
1598220762Srmacklem			if (vap->va_atime.tv_sec == VNOVAL)
1599220762Srmacklem				vap->va_atime = vap->va_mtime;
1600191783Srmacklem			error = nfsrpc_setattr(newvp, vap, NULL, cnp->cn_cred,
1601191783Srmacklem			    cnp->cn_thread, &nfsva, &attrflag, NULL);
1602191783Srmacklem			if (error && (vap->va_uid != (uid_t)VNOVAL ||
1603191783Srmacklem			    vap->va_gid != (gid_t)VNOVAL)) {
1604191783Srmacklem				/* try again without setting uid/gid */
1605191783Srmacklem				vap->va_uid = (uid_t)VNOVAL;
1606191783Srmacklem				vap->va_gid = (uid_t)VNOVAL;
1607191783Srmacklem				error = nfsrpc_setattr(newvp, vap, NULL,
1608191783Srmacklem				    cnp->cn_cred, cnp->cn_thread, &nfsva,
1609191783Srmacklem				    &attrflag, NULL);
1610191783Srmacklem			}
1611191783Srmacklem			if (attrflag)
1612191783Srmacklem				(void) nfscl_loadattrcache(&newvp, &nfsva, NULL,
1613191783Srmacklem				    NULL, 0, 1);
1614224532Srmacklem			if (error != 0)
1615224532Srmacklem				vput(newvp);
1616191783Srmacklem		}
1617191783Srmacklem	}
1618191783Srmacklem	if (!error) {
1619233285Sjhb		if ((cnp->cn_flags & MAKEENTRY) && attrflag)
1620233285Sjhb			cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime,
1621233285Sjhb			    NULL);
1622191783Srmacklem		*ap->a_vpp = newvp;
1623191783Srmacklem	} else if (NFS_ISV4(dvp)) {
1624191783Srmacklem		error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid,
1625191783Srmacklem		    vap->va_gid);
1626191783Srmacklem	}
1627191783Srmacklem	mtx_lock(&dnp->n_mtx);
1628191783Srmacklem	dnp->n_flag |= NMODIFIED;
1629223280Srmacklem	if (!dattrflag) {
1630191783Srmacklem		dnp->n_attrstamp = 0;
1631223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
1632223280Srmacklem	}
1633191783Srmacklem	mtx_unlock(&dnp->n_mtx);
1634191783Srmacklem	return (error);
1635191783Srmacklem}
1636191783Srmacklem
1637191783Srmacklem/*
1638191783Srmacklem * nfs file remove call
1639191783Srmacklem * To try and make nfs semantics closer to ufs semantics, a file that has
1640191783Srmacklem * other processes using the vnode is renamed instead of removed and then
1641191783Srmacklem * removed later on the last close.
1642191783Srmacklem * - If v_usecount > 1
1643191783Srmacklem *	  If a rename is not already in the works
1644191783Srmacklem *	     call nfs_sillyrename() to set it up
1645191783Srmacklem *     else
1646191783Srmacklem *	  do the remove rpc
1647191783Srmacklem */
1648191783Srmacklemstatic int
1649191783Srmacklemnfs_remove(struct vop_remove_args *ap)
1650191783Srmacklem{
1651191783Srmacklem	struct vnode *vp = ap->a_vp;
1652191783Srmacklem	struct vnode *dvp = ap->a_dvp;
1653191783Srmacklem	struct componentname *cnp = ap->a_cnp;
1654191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1655191783Srmacklem	int error = 0;
1656191783Srmacklem	struct vattr vattr;
1657191783Srmacklem
1658209120Skib	KASSERT((cnp->cn_flags & HASBUF) != 0, ("nfs_remove: no name"));
1659209120Skib	KASSERT(vrefcnt(vp) > 0, ("nfs_remove: bad v_usecount"));
1660191783Srmacklem	if (vp->v_type == VDIR)
1661191783Srmacklem		error = EPERM;
1662191783Srmacklem	else if (vrefcnt(vp) == 1 || (np->n_sillyrename &&
1663191783Srmacklem	    VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 &&
1664191783Srmacklem	    vattr.va_nlink > 1)) {
1665191783Srmacklem		/*
1666191783Srmacklem		 * Purge the name cache so that the chance of a lookup for
1667191783Srmacklem		 * the name succeeding while the remove is in progress is
1668191783Srmacklem		 * minimized. Without node locking it can still happen, such
1669191783Srmacklem		 * that an I/O op returns ESTALE, but since you get this if
1670191783Srmacklem		 * another host removes the file..
1671191783Srmacklem		 */
1672191783Srmacklem		cache_purge(vp);
1673191783Srmacklem		/*
1674191783Srmacklem		 * throw away biocache buffers, mainly to avoid
1675191783Srmacklem		 * unnecessary delayed writes later.
1676191783Srmacklem		 */
1677191783Srmacklem		error = ncl_vinvalbuf(vp, 0, cnp->cn_thread, 1);
1678191783Srmacklem		/* Do the rpc */
1679191783Srmacklem		if (error != EINTR && error != EIO)
1680191783Srmacklem			error = nfs_removerpc(dvp, vp, cnp->cn_nameptr,
1681191783Srmacklem			    cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread);
1682191783Srmacklem		/*
1683191783Srmacklem		 * Kludge City: If the first reply to the remove rpc is lost..
1684191783Srmacklem		 *   the reply to the retransmitted request will be ENOENT
1685191783Srmacklem		 *   since the file was in fact removed
1686191783Srmacklem		 *   Therefore, we cheat and return success.
1687191783Srmacklem		 */
1688191783Srmacklem		if (error == ENOENT)
1689191783Srmacklem			error = 0;
1690191783Srmacklem	} else if (!np->n_sillyrename)
1691191783Srmacklem		error = nfs_sillyrename(dvp, vp, cnp);
1692220762Srmacklem	mtx_lock(&np->n_mtx);
1693191783Srmacklem	np->n_attrstamp = 0;
1694220762Srmacklem	mtx_unlock(&np->n_mtx);
1695223280Srmacklem	KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
1696191783Srmacklem	return (error);
1697191783Srmacklem}
1698191783Srmacklem
1699191783Srmacklem/*
1700191783Srmacklem * nfs file remove rpc called from nfs_inactive
1701191783Srmacklem */
1702191783Srmacklemint
1703191783Srmacklemncl_removeit(struct sillyrename *sp, struct vnode *vp)
1704191783Srmacklem{
1705191783Srmacklem	/*
1706191783Srmacklem	 * Make sure that the directory vnode is still valid.
1707191783Srmacklem	 * XXX we should lock sp->s_dvp here.
1708191783Srmacklem	 */
1709191783Srmacklem	if (sp->s_dvp->v_type == VBAD)
1710191783Srmacklem		return (0);
1711191783Srmacklem	return (nfs_removerpc(sp->s_dvp, vp, sp->s_name, sp->s_namlen,
1712191783Srmacklem	    sp->s_cred, NULL));
1713191783Srmacklem}
1714191783Srmacklem
1715191783Srmacklem/*
1716191783Srmacklem * Nfs remove rpc, called from nfs_remove() and ncl_removeit().
1717191783Srmacklem */
1718191783Srmacklemstatic int
1719191783Srmacklemnfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
1720191783Srmacklem    int namelen, struct ucred *cred, struct thread *td)
1721191783Srmacklem{
1722191783Srmacklem	struct nfsvattr dnfsva;
1723191783Srmacklem	struct nfsnode *dnp = VTONFS(dvp);
1724191783Srmacklem	int error = 0, dattrflag;
1725191783Srmacklem
1726191783Srmacklem	mtx_lock(&dnp->n_mtx);
1727191783Srmacklem	dnp->n_flag |= NREMOVEINPROG;
1728191783Srmacklem	mtx_unlock(&dnp->n_mtx);
1729191783Srmacklem	error = nfsrpc_remove(dvp, name, namelen, vp, cred, td, &dnfsva,
1730191783Srmacklem	    &dattrflag, NULL);
1731191783Srmacklem	mtx_lock(&dnp->n_mtx);
1732191783Srmacklem	if ((dnp->n_flag & NREMOVEWANT)) {
1733191783Srmacklem		dnp->n_flag &= ~(NREMOVEWANT | NREMOVEINPROG);
1734191783Srmacklem		mtx_unlock(&dnp->n_mtx);
1735191783Srmacklem		wakeup((caddr_t)dnp);
1736191783Srmacklem	} else {
1737191783Srmacklem		dnp->n_flag &= ~NREMOVEINPROG;
1738191783Srmacklem		mtx_unlock(&dnp->n_mtx);
1739191783Srmacklem	}
1740191783Srmacklem	if (dattrflag)
1741191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
1742191783Srmacklem	mtx_lock(&dnp->n_mtx);
1743191783Srmacklem	dnp->n_flag |= NMODIFIED;
1744223280Srmacklem	if (!dattrflag) {
1745191783Srmacklem		dnp->n_attrstamp = 0;
1746223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
1747223280Srmacklem	}
1748191783Srmacklem	mtx_unlock(&dnp->n_mtx);
1749191783Srmacklem	if (error && NFS_ISV4(dvp))
1750191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1751191783Srmacklem	return (error);
1752191783Srmacklem}
1753191783Srmacklem
1754191783Srmacklem/*
1755191783Srmacklem * nfs file rename call
1756191783Srmacklem */
1757191783Srmacklemstatic int
1758191783Srmacklemnfs_rename(struct vop_rename_args *ap)
1759191783Srmacklem{
1760191783Srmacklem	struct vnode *fvp = ap->a_fvp;
1761191783Srmacklem	struct vnode *tvp = ap->a_tvp;
1762191783Srmacklem	struct vnode *fdvp = ap->a_fdvp;
1763191783Srmacklem	struct vnode *tdvp = ap->a_tdvp;
1764191783Srmacklem	struct componentname *tcnp = ap->a_tcnp;
1765191783Srmacklem	struct componentname *fcnp = ap->a_fcnp;
1766191783Srmacklem	struct nfsnode *fnp = VTONFS(ap->a_fvp);
1767191783Srmacklem	struct nfsnode *tdnp = VTONFS(ap->a_tdvp);
1768191783Srmacklem	struct nfsv4node *newv4 = NULL;
1769191783Srmacklem	int error;
1770191783Srmacklem
1771209120Skib	KASSERT((tcnp->cn_flags & HASBUF) != 0 &&
1772209120Skib	    (fcnp->cn_flags & HASBUF) != 0, ("nfs_rename: no name"));
1773191783Srmacklem	/* Check for cross-device rename */
1774191783Srmacklem	if ((fvp->v_mount != tdvp->v_mount) ||
1775191783Srmacklem	    (tvp && (fvp->v_mount != tvp->v_mount))) {
1776191783Srmacklem		error = EXDEV;
1777191783Srmacklem		goto out;
1778191783Srmacklem	}
1779191783Srmacklem
1780191783Srmacklem	if (fvp == tvp) {
1781191783Srmacklem		ncl_printf("nfs_rename: fvp == tvp (can't happen)\n");
1782191783Srmacklem		error = 0;
1783191783Srmacklem		goto out;
1784191783Srmacklem	}
1785224081Szack	if ((error = NFSVOPLOCK(fvp, LK_EXCLUSIVE)) != 0)
1786191783Srmacklem		goto out;
1787191783Srmacklem
1788191783Srmacklem	/*
1789191783Srmacklem	 * We have to flush B_DELWRI data prior to renaming
1790191783Srmacklem	 * the file.  If we don't, the delayed-write buffers
1791191783Srmacklem	 * can be flushed out later after the file has gone stale
1792191783Srmacklem	 * under NFSV3.  NFSV2 does not have this problem because
1793191783Srmacklem	 * ( as far as I can tell ) it flushes dirty buffers more
1794191783Srmacklem	 * often.
1795191783Srmacklem	 *
1796191783Srmacklem	 * Skip the rename operation if the fsync fails, this can happen
1797191783Srmacklem	 * due to the server's volume being full, when we pushed out data
1798191783Srmacklem	 * that was written back to our cache earlier. Not checking for
1799191783Srmacklem	 * this condition can result in potential (silent) data loss.
1800191783Srmacklem	 */
1801191783Srmacklem	error = VOP_FSYNC(fvp, MNT_WAIT, fcnp->cn_thread);
1802224082Szack	NFSVOPUNLOCK(fvp, 0);
1803191783Srmacklem	if (!error && tvp)
1804191783Srmacklem		error = VOP_FSYNC(tvp, MNT_WAIT, tcnp->cn_thread);
1805191783Srmacklem	if (error)
1806191783Srmacklem		goto out;
1807191783Srmacklem
1808191783Srmacklem	/*
1809191783Srmacklem	 * If the tvp exists and is in use, sillyrename it before doing the
1810191783Srmacklem	 * rename of the new file over it.
1811191783Srmacklem	 * XXX Can't sillyrename a directory.
1812191783Srmacklem	 */
1813191783Srmacklem	if (tvp && vrefcnt(tvp) > 1 && !VTONFS(tvp)->n_sillyrename &&
1814191783Srmacklem		tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) {
1815191783Srmacklem		vput(tvp);
1816191783Srmacklem		tvp = NULL;
1817191783Srmacklem	}
1818191783Srmacklem
1819191783Srmacklem	error = nfs_renamerpc(fdvp, fvp, fcnp->cn_nameptr, fcnp->cn_namelen,
1820191783Srmacklem	    tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
1821191783Srmacklem	    tcnp->cn_thread);
1822191783Srmacklem
1823220762Srmacklem	if (error == 0 && NFS_ISV4(tdvp)) {
1824191783Srmacklem		/*
1825191783Srmacklem		 * For NFSv4, check to see if it is the same name and
1826191783Srmacklem		 * replace the name, if it is different.
1827191783Srmacklem		 */
1828191783Srmacklem		MALLOC(newv4, struct nfsv4node *,
1829191783Srmacklem		    sizeof (struct nfsv4node) +
1830191783Srmacklem		    tdnp->n_fhp->nfh_len + tcnp->cn_namelen - 1,
1831191783Srmacklem		    M_NFSV4NODE, M_WAITOK);
1832191783Srmacklem		mtx_lock(&tdnp->n_mtx);
1833191783Srmacklem		mtx_lock(&fnp->n_mtx);
1834191783Srmacklem		if (fnp->n_v4 != NULL && fvp->v_type == VREG &&
1835191783Srmacklem		    (fnp->n_v4->n4_namelen != tcnp->cn_namelen ||
1836191783Srmacklem		      NFSBCMP(tcnp->cn_nameptr, NFS4NODENAME(fnp->n_v4),
1837191783Srmacklem		      tcnp->cn_namelen) ||
1838191783Srmacklem		      tdnp->n_fhp->nfh_len != fnp->n_v4->n4_fhlen ||
1839191783Srmacklem		      NFSBCMP(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data,
1840191783Srmacklem			tdnp->n_fhp->nfh_len))) {
1841191783Srmacklem#ifdef notdef
1842191783Srmacklem{ char nnn[100]; int nnnl;
1843191783Srmacklemnnnl = (tcnp->cn_namelen < 100) ? tcnp->cn_namelen : 99;
1844191783Srmacklembcopy(tcnp->cn_nameptr, nnn, nnnl);
1845191783Srmacklemnnn[nnnl] = '\0';
1846191783Srmacklemprintf("ren replace=%s\n",nnn);
1847191783Srmacklem}
1848191783Srmacklem#endif
1849191783Srmacklem			FREE((caddr_t)fnp->n_v4, M_NFSV4NODE);
1850191783Srmacklem			fnp->n_v4 = newv4;
1851191783Srmacklem			newv4 = NULL;
1852191783Srmacklem			fnp->n_v4->n4_fhlen = tdnp->n_fhp->nfh_len;
1853191783Srmacklem			fnp->n_v4->n4_namelen = tcnp->cn_namelen;
1854191783Srmacklem			NFSBCOPY(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data,
1855191783Srmacklem			    tdnp->n_fhp->nfh_len);
1856191783Srmacklem			NFSBCOPY(tcnp->cn_nameptr,
1857191783Srmacklem			    NFS4NODENAME(fnp->n_v4), tcnp->cn_namelen);
1858191783Srmacklem		}
1859191783Srmacklem		mtx_unlock(&tdnp->n_mtx);
1860191783Srmacklem		mtx_unlock(&fnp->n_mtx);
1861191783Srmacklem		if (newv4 != NULL)
1862191783Srmacklem			FREE((caddr_t)newv4, M_NFSV4NODE);
1863191783Srmacklem	}
1864191783Srmacklem
1865191783Srmacklem	if (fvp->v_type == VDIR) {
1866191783Srmacklem		if (tvp != NULL && tvp->v_type == VDIR)
1867191783Srmacklem			cache_purge(tdvp);
1868191783Srmacklem		cache_purge(fdvp);
1869191783Srmacklem	}
1870191783Srmacklem
1871191783Srmacklemout:
1872191783Srmacklem	if (tdvp == tvp)
1873191783Srmacklem		vrele(tdvp);
1874191783Srmacklem	else
1875191783Srmacklem		vput(tdvp);
1876191783Srmacklem	if (tvp)
1877191783Srmacklem		vput(tvp);
1878191783Srmacklem	vrele(fdvp);
1879191783Srmacklem	vrele(fvp);
1880191783Srmacklem	/*
1881191783Srmacklem	 * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
1882191783Srmacklem	 */
1883191783Srmacklem	if (error == ENOENT)
1884191783Srmacklem		error = 0;
1885191783Srmacklem	return (error);
1886191783Srmacklem}
1887191783Srmacklem
1888191783Srmacklem/*
1889191783Srmacklem * nfs file rename rpc called from nfs_remove() above
1890191783Srmacklem */
1891191783Srmacklemstatic int
1892191783Srmacklemnfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp,
1893191783Srmacklem    struct sillyrename *sp)
1894191783Srmacklem{
1895191783Srmacklem
1896191783Srmacklem	return (nfs_renamerpc(sdvp, svp, scnp->cn_nameptr, scnp->cn_namelen,
1897191783Srmacklem	    sdvp, NULL, sp->s_name, sp->s_namlen, scnp->cn_cred,
1898191783Srmacklem	    scnp->cn_thread));
1899191783Srmacklem}
1900191783Srmacklem
1901191783Srmacklem/*
1902191783Srmacklem * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit().
1903191783Srmacklem */
1904191783Srmacklemstatic int
1905191783Srmacklemnfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr,
1906191783Srmacklem    int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr,
1907191783Srmacklem    int tnamelen, struct ucred *cred, struct thread *td)
1908191783Srmacklem{
1909191783Srmacklem	struct nfsvattr fnfsva, tnfsva;
1910191783Srmacklem	struct nfsnode *fdnp = VTONFS(fdvp);
1911191783Srmacklem	struct nfsnode *tdnp = VTONFS(tdvp);
1912191783Srmacklem	int error = 0, fattrflag, tattrflag;
1913191783Srmacklem
1914191783Srmacklem	error = nfsrpc_rename(fdvp, fvp, fnameptr, fnamelen, tdvp, tvp,
1915191783Srmacklem	    tnameptr, tnamelen, cred, td, &fnfsva, &tnfsva, &fattrflag,
1916191783Srmacklem	    &tattrflag, NULL, NULL);
1917191783Srmacklem	mtx_lock(&fdnp->n_mtx);
1918191783Srmacklem	fdnp->n_flag |= NMODIFIED;
1919220762Srmacklem	if (fattrflag != 0) {
1920220762Srmacklem		mtx_unlock(&fdnp->n_mtx);
1921220762Srmacklem		(void) nfscl_loadattrcache(&fdvp, &fnfsva, NULL, NULL, 0, 1);
1922220762Srmacklem	} else {
1923220762Srmacklem		fdnp->n_attrstamp = 0;
1924220762Srmacklem		mtx_unlock(&fdnp->n_mtx);
1925223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(fdvp);
1926220762Srmacklem	}
1927191783Srmacklem	mtx_lock(&tdnp->n_mtx);
1928191783Srmacklem	tdnp->n_flag |= NMODIFIED;
1929220762Srmacklem	if (tattrflag != 0) {
1930220762Srmacklem		mtx_unlock(&tdnp->n_mtx);
1931191783Srmacklem		(void) nfscl_loadattrcache(&tdvp, &tnfsva, NULL, NULL, 0, 1);
1932220762Srmacklem	} else {
1933191783Srmacklem		tdnp->n_attrstamp = 0;
1934220762Srmacklem		mtx_unlock(&tdnp->n_mtx);
1935223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp);
1936220762Srmacklem	}
1937191783Srmacklem	if (error && NFS_ISV4(fdvp))
1938191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1939191783Srmacklem	return (error);
1940191783Srmacklem}
1941191783Srmacklem
1942191783Srmacklem/*
1943191783Srmacklem * nfs hard link create call
1944191783Srmacklem */
1945191783Srmacklemstatic int
1946191783Srmacklemnfs_link(struct vop_link_args *ap)
1947191783Srmacklem{
1948191783Srmacklem	struct vnode *vp = ap->a_vp;
1949191783Srmacklem	struct vnode *tdvp = ap->a_tdvp;
1950191783Srmacklem	struct componentname *cnp = ap->a_cnp;
1951220762Srmacklem	struct nfsnode *np, *tdnp;
1952191783Srmacklem	struct nfsvattr nfsva, dnfsva;
1953191783Srmacklem	int error = 0, attrflag, dattrflag;
1954191783Srmacklem
1955191783Srmacklem	if (vp->v_mount != tdvp->v_mount) {
1956191783Srmacklem		return (EXDEV);
1957191783Srmacklem	}
1958191783Srmacklem
1959191783Srmacklem	/*
1960191783Srmacklem	 * Push all writes to the server, so that the attribute cache
1961191783Srmacklem	 * doesn't get "out of sync" with the server.
1962191783Srmacklem	 * XXX There should be a better way!
1963191783Srmacklem	 */
1964191783Srmacklem	VOP_FSYNC(vp, MNT_WAIT, cnp->cn_thread);
1965191783Srmacklem
1966191783Srmacklem	error = nfsrpc_link(tdvp, vp, cnp->cn_nameptr, cnp->cn_namelen,
1967191783Srmacklem	    cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &attrflag,
1968191783Srmacklem	    &dattrflag, NULL);
1969191783Srmacklem	tdnp = VTONFS(tdvp);
1970191783Srmacklem	mtx_lock(&tdnp->n_mtx);
1971191783Srmacklem	tdnp->n_flag |= NMODIFIED;
1972220762Srmacklem	if (dattrflag != 0) {
1973220762Srmacklem		mtx_unlock(&tdnp->n_mtx);
1974220762Srmacklem		(void) nfscl_loadattrcache(&tdvp, &dnfsva, NULL, NULL, 0, 1);
1975220762Srmacklem	} else {
1976220762Srmacklem		tdnp->n_attrstamp = 0;
1977220762Srmacklem		mtx_unlock(&tdnp->n_mtx);
1978223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp);
1979220762Srmacklem	}
1980191783Srmacklem	if (attrflag)
1981191783Srmacklem		(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
1982220762Srmacklem	else {
1983220762Srmacklem		np = VTONFS(vp);
1984220762Srmacklem		mtx_lock(&np->n_mtx);
1985220762Srmacklem		np->n_attrstamp = 0;
1986220762Srmacklem		mtx_unlock(&np->n_mtx);
1987223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
1988220762Srmacklem	}
1989191783Srmacklem	/*
1990191783Srmacklem	 * If negative lookup caching is enabled, I might as well
1991191783Srmacklem	 * add an entry for this node. Not necessary for correctness,
1992191783Srmacklem	 * but if negative caching is enabled, then the system
1993191783Srmacklem	 * must care about lookup caching hit rate, so...
1994191783Srmacklem	 */
1995203303Srmacklem	if (VFSTONFS(vp->v_mount)->nm_negnametimeo != 0 &&
1996233285Sjhb	    (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) {
1997233285Sjhb		cache_enter_time(tdvp, vp, cnp, &nfsva.na_ctime, NULL);
1998233285Sjhb	}
1999191783Srmacklem	if (error && NFS_ISV4(vp))
2000191783Srmacklem		error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0,
2001191783Srmacklem		    (gid_t)0);
2002191783Srmacklem	return (error);
2003191783Srmacklem}
2004191783Srmacklem
2005191783Srmacklem/*
2006191783Srmacklem * nfs symbolic link create call
2007191783Srmacklem */
2008191783Srmacklemstatic int
2009191783Srmacklemnfs_symlink(struct vop_symlink_args *ap)
2010191783Srmacklem{
2011191783Srmacklem	struct vnode *dvp = ap->a_dvp;
2012191783Srmacklem	struct vattr *vap = ap->a_vap;
2013191783Srmacklem	struct componentname *cnp = ap->a_cnp;
2014191783Srmacklem	struct nfsvattr nfsva, dnfsva;
2015191783Srmacklem	struct nfsfh *nfhp;
2016191783Srmacklem	struct nfsnode *np = NULL, *dnp;
2017191783Srmacklem	struct vnode *newvp = NULL;
2018191783Srmacklem	int error = 0, attrflag, dattrflag, ret;
2019191783Srmacklem
2020191783Srmacklem	vap->va_type = VLNK;
2021191783Srmacklem	error = nfsrpc_symlink(dvp, cnp->cn_nameptr, cnp->cn_namelen,
2022191783Srmacklem	    ap->a_target, vap, cnp->cn_cred, cnp->cn_thread, &dnfsva,
2023191783Srmacklem	    &nfsva, &nfhp, &attrflag, &dattrflag, NULL);
2024191783Srmacklem	if (nfhp) {
2025191783Srmacklem		ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread,
2026220732Srmacklem		    &np, NULL, LK_EXCLUSIVE);
2027191783Srmacklem		if (!ret)
2028191783Srmacklem			newvp = NFSTOV(np);
2029191783Srmacklem		else if (!error)
2030191783Srmacklem			error = ret;
2031191783Srmacklem	}
2032191783Srmacklem	if (newvp != NULL) {
2033191783Srmacklem		if (attrflag)
2034191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
2035191783Srmacklem			    0, 1);
2036191783Srmacklem	} else if (!error) {
2037191783Srmacklem		/*
2038191783Srmacklem		 * If we do not have an error and we could not extract the
2039191783Srmacklem		 * newvp from the response due to the request being NFSv2, we
2040191783Srmacklem		 * have to do a lookup in order to obtain a newvp to return.
2041191783Srmacklem		 */
2042191783Srmacklem		error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
2043191783Srmacklem		    cnp->cn_cred, cnp->cn_thread, &np);
2044191783Srmacklem		if (!error)
2045191783Srmacklem			newvp = NFSTOV(np);
2046191783Srmacklem	}
2047191783Srmacklem	if (error) {
2048191783Srmacklem		if (newvp)
2049191783Srmacklem			vput(newvp);
2050191783Srmacklem		if (NFS_ISV4(dvp))
2051191783Srmacklem			error = nfscl_maperr(cnp->cn_thread, error,
2052191783Srmacklem			    vap->va_uid, vap->va_gid);
2053191783Srmacklem	} else {
2054191783Srmacklem		*ap->a_vpp = newvp;
2055191783Srmacklem	}
2056191783Srmacklem
2057191783Srmacklem	dnp = VTONFS(dvp);
2058191783Srmacklem	mtx_lock(&dnp->n_mtx);
2059191783Srmacklem	dnp->n_flag |= NMODIFIED;
2060220762Srmacklem	if (dattrflag != 0) {
2061220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2062191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
2063220762Srmacklem	} else {
2064191783Srmacklem		dnp->n_attrstamp = 0;
2065220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2066223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
2067220762Srmacklem	}
2068233285Sjhb	/*
2069233285Sjhb	 * If negative lookup caching is enabled, I might as well
2070233285Sjhb	 * add an entry for this node. Not necessary for correctness,
2071233285Sjhb	 * but if negative caching is enabled, then the system
2072233285Sjhb	 * must care about lookup caching hit rate, so...
2073233285Sjhb	 */
2074233285Sjhb	if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 &&
2075233285Sjhb	    (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) {
2076233285Sjhb		cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, NULL);
2077233285Sjhb	}
2078191783Srmacklem	return (error);
2079191783Srmacklem}
2080191783Srmacklem
2081191783Srmacklem/*
2082191783Srmacklem * nfs make dir call
2083191783Srmacklem */
2084191783Srmacklemstatic int
2085191783Srmacklemnfs_mkdir(struct vop_mkdir_args *ap)
2086191783Srmacklem{
2087191783Srmacklem	struct vnode *dvp = ap->a_dvp;
2088191783Srmacklem	struct vattr *vap = ap->a_vap;
2089191783Srmacklem	struct componentname *cnp = ap->a_cnp;
2090191783Srmacklem	struct nfsnode *np = NULL, *dnp;
2091191783Srmacklem	struct vnode *newvp = NULL;
2092191783Srmacklem	struct vattr vattr;
2093191783Srmacklem	struct nfsfh *nfhp;
2094191783Srmacklem	struct nfsvattr nfsva, dnfsva;
2095191783Srmacklem	int error = 0, attrflag, dattrflag, ret;
2096191783Srmacklem
2097220762Srmacklem	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)) != 0)
2098191783Srmacklem		return (error);
2099191783Srmacklem	vap->va_type = VDIR;
2100191783Srmacklem	error = nfsrpc_mkdir(dvp, cnp->cn_nameptr, cnp->cn_namelen,
2101191783Srmacklem	    vap, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &nfhp,
2102191783Srmacklem	    &attrflag, &dattrflag, NULL);
2103191783Srmacklem	dnp = VTONFS(dvp);
2104191783Srmacklem	mtx_lock(&dnp->n_mtx);
2105191783Srmacklem	dnp->n_flag |= NMODIFIED;
2106220762Srmacklem	if (dattrflag != 0) {
2107220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2108191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
2109220762Srmacklem	} else {
2110191783Srmacklem		dnp->n_attrstamp = 0;
2111220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2112223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
2113220762Srmacklem	}
2114191783Srmacklem	if (nfhp) {
2115191783Srmacklem		ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread,
2116220732Srmacklem		    &np, NULL, LK_EXCLUSIVE);
2117191783Srmacklem		if (!ret) {
2118191783Srmacklem			newvp = NFSTOV(np);
2119191783Srmacklem			if (attrflag)
2120191783Srmacklem			   (void) nfscl_loadattrcache(&newvp, &nfsva, NULL,
2121191783Srmacklem				NULL, 0, 1);
2122191783Srmacklem		} else if (!error)
2123191783Srmacklem			error = ret;
2124191783Srmacklem	}
2125191783Srmacklem	if (!error && newvp == NULL) {
2126191783Srmacklem		error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
2127191783Srmacklem		    cnp->cn_cred, cnp->cn_thread, &np);
2128191783Srmacklem		if (!error) {
2129191783Srmacklem			newvp = NFSTOV(np);
2130191783Srmacklem			if (newvp->v_type != VDIR)
2131191783Srmacklem				error = EEXIST;
2132191783Srmacklem		}
2133191783Srmacklem	}
2134191783Srmacklem	if (error) {
2135191783Srmacklem		if (newvp)
2136191783Srmacklem			vput(newvp);
2137191783Srmacklem		if (NFS_ISV4(dvp))
2138191783Srmacklem			error = nfscl_maperr(cnp->cn_thread, error,
2139191783Srmacklem			    vap->va_uid, vap->va_gid);
2140191783Srmacklem	} else {
2141191783Srmacklem		/*
2142191783Srmacklem		 * If negative lookup caching is enabled, I might as well
2143191783Srmacklem		 * add an entry for this node. Not necessary for correctness,
2144191783Srmacklem		 * but if negative caching is enabled, then the system
2145191783Srmacklem		 * must care about lookup caching hit rate, so...
2146191783Srmacklem		 */
2147203303Srmacklem		if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 &&
2148233285Sjhb		    (cnp->cn_flags & MAKEENTRY) &&
2149233285Sjhb		    attrflag != 0 && dattrflag != 0)
2150233285Sjhb			cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime,
2151233285Sjhb			    &dnfsva.na_ctime);
2152191783Srmacklem		*ap->a_vpp = newvp;
2153191783Srmacklem	}
2154191783Srmacklem	return (error);
2155191783Srmacklem}
2156191783Srmacklem
2157191783Srmacklem/*
2158191783Srmacklem * nfs remove directory call
2159191783Srmacklem */
2160191783Srmacklemstatic int
2161191783Srmacklemnfs_rmdir(struct vop_rmdir_args *ap)
2162191783Srmacklem{
2163191783Srmacklem	struct vnode *vp = ap->a_vp;
2164191783Srmacklem	struct vnode *dvp = ap->a_dvp;
2165191783Srmacklem	struct componentname *cnp = ap->a_cnp;
2166191783Srmacklem	struct nfsnode *dnp;
2167191783Srmacklem	struct nfsvattr dnfsva;
2168191783Srmacklem	int error, dattrflag;
2169191783Srmacklem
2170191783Srmacklem	if (dvp == vp)
2171191783Srmacklem		return (EINVAL);
2172191783Srmacklem	error = nfsrpc_rmdir(dvp, cnp->cn_nameptr, cnp->cn_namelen,
2173191783Srmacklem	    cnp->cn_cred, cnp->cn_thread, &dnfsva, &dattrflag, NULL);
2174191783Srmacklem	dnp = VTONFS(dvp);
2175191783Srmacklem	mtx_lock(&dnp->n_mtx);
2176191783Srmacklem	dnp->n_flag |= NMODIFIED;
2177220762Srmacklem	if (dattrflag != 0) {
2178220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2179191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
2180220762Srmacklem	} else {
2181191783Srmacklem		dnp->n_attrstamp = 0;
2182220762Srmacklem		mtx_unlock(&dnp->n_mtx);
2183223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
2184220762Srmacklem	}
2185191783Srmacklem
2186191783Srmacklem	cache_purge(dvp);
2187191783Srmacklem	cache_purge(vp);
2188191783Srmacklem	if (error && NFS_ISV4(dvp))
2189191783Srmacklem		error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0,
2190191783Srmacklem		    (gid_t)0);
2191191783Srmacklem	/*
2192191783Srmacklem	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2193191783Srmacklem	 */
2194191783Srmacklem	if (error == ENOENT)
2195191783Srmacklem		error = 0;
2196191783Srmacklem	return (error);
2197191783Srmacklem}
2198191783Srmacklem
2199191783Srmacklem/*
2200191783Srmacklem * nfs readdir call
2201191783Srmacklem */
2202191783Srmacklemstatic int
2203191783Srmacklemnfs_readdir(struct vop_readdir_args *ap)
2204191783Srmacklem{
2205191783Srmacklem	struct vnode *vp = ap->a_vp;
2206191783Srmacklem	struct nfsnode *np = VTONFS(vp);
2207191783Srmacklem	struct uio *uio = ap->a_uio;
2208233353Skib	ssize_t tresid;
2209233353Skib	int error = 0;
2210191783Srmacklem	struct vattr vattr;
2211191783Srmacklem
2212250996Srmacklem	if (ap->a_eofflag != NULL)
2213250996Srmacklem		*ap->a_eofflag = 0;
2214191783Srmacklem	if (vp->v_type != VDIR)
2215191783Srmacklem		return(EPERM);
2216191783Srmacklem
2217191783Srmacklem	/*
2218191783Srmacklem	 * First, check for hit on the EOF offset cache
2219191783Srmacklem	 */
2220191783Srmacklem	if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset &&
2221191783Srmacklem	    (np->n_flag & NMODIFIED) == 0) {
2222191783Srmacklem		if (VOP_GETATTR(vp, &vattr, ap->a_cred) == 0) {
2223191783Srmacklem			mtx_lock(&np->n_mtx);
2224191783Srmacklem			if ((NFS_ISV4(vp) && np->n_change == vattr.va_filerev) ||
2225191783Srmacklem			    !NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) {
2226191783Srmacklem				mtx_unlock(&np->n_mtx);
2227191783Srmacklem				NFSINCRGLOBAL(newnfsstats.direofcache_hits);
2228250996Srmacklem				if (ap->a_eofflag != NULL)
2229250996Srmacklem					*ap->a_eofflag = 1;
2230191783Srmacklem				return (0);
2231191783Srmacklem			} else
2232191783Srmacklem				mtx_unlock(&np->n_mtx);
2233191783Srmacklem		}
2234191783Srmacklem	}
2235191783Srmacklem
2236191783Srmacklem	/*
2237191783Srmacklem	 * Call ncl_bioread() to do the real work.
2238191783Srmacklem	 */
2239191783Srmacklem	tresid = uio->uio_resid;
2240191783Srmacklem	error = ncl_bioread(vp, uio, 0, ap->a_cred);
2241191783Srmacklem
2242250996Srmacklem	if (!error && uio->uio_resid == tresid) {
2243191783Srmacklem		NFSINCRGLOBAL(newnfsstats.direofcache_misses);
2244250996Srmacklem		if (ap->a_eofflag != NULL)
2245250996Srmacklem			*ap->a_eofflag = 1;
2246250996Srmacklem	}
2247191783Srmacklem	return (error);
2248191783Srmacklem}
2249191783Srmacklem
2250191783Srmacklem/*
2251191783Srmacklem * Readdir rpc call.
2252191783Srmacklem * Called from below the buffer cache by ncl_doio().
2253191783Srmacklem */
2254191783Srmacklemint
2255191783Srmacklemncl_readdirrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
2256191783Srmacklem    struct thread *td)
2257191783Srmacklem{
2258191783Srmacklem	struct nfsvattr nfsva;
2259191783Srmacklem	nfsuint64 *cookiep, cookie;
2260191783Srmacklem	struct nfsnode *dnp = VTONFS(vp);
2261191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
2262191783Srmacklem	int error = 0, eof, attrflag;
2263191783Srmacklem
2264209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
2265209120Skib	    (uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 &&
2266209120Skib	    (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
2267209120Skib	    ("nfs readdirrpc bad uio"));
2268191783Srmacklem
2269191783Srmacklem	/*
2270191783Srmacklem	 * If there is no cookie, assume directory was stale.
2271191783Srmacklem	 */
2272191783Srmacklem	ncl_dircookie_lock(dnp);
2273191783Srmacklem	cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0);
2274191783Srmacklem	if (cookiep) {
2275191783Srmacklem		cookie = *cookiep;
2276191783Srmacklem		ncl_dircookie_unlock(dnp);
2277191783Srmacklem	} else {
2278191783Srmacklem		ncl_dircookie_unlock(dnp);
2279191783Srmacklem		return (NFSERR_BAD_COOKIE);
2280191783Srmacklem	}
2281191783Srmacklem
2282191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp))
2283191783Srmacklem		(void)ncl_fsinfo(nmp, vp, cred, td);
2284191783Srmacklem
2285191783Srmacklem	error = nfsrpc_readdir(vp, uiop, &cookie, cred, td, &nfsva,
2286191783Srmacklem	    &attrflag, &eof, NULL);
2287191783Srmacklem	if (attrflag)
2288191783Srmacklem		(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
2289191783Srmacklem
2290191783Srmacklem	if (!error) {
2291191783Srmacklem		/*
2292191783Srmacklem		 * We are now either at the end of the directory or have filled
2293191783Srmacklem		 * the block.
2294191783Srmacklem		 */
2295191783Srmacklem		if (eof)
2296191783Srmacklem			dnp->n_direofoffset = uiop->uio_offset;
2297191783Srmacklem		else {
2298191783Srmacklem			if (uiop->uio_resid > 0)
2299191783Srmacklem				ncl_printf("EEK! readdirrpc resid > 0\n");
2300191783Srmacklem			ncl_dircookie_lock(dnp);
2301191783Srmacklem			cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1);
2302191783Srmacklem			*cookiep = cookie;
2303191783Srmacklem			ncl_dircookie_unlock(dnp);
2304191783Srmacklem		}
2305191783Srmacklem	} else if (NFS_ISV4(vp)) {
2306191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
2307191783Srmacklem	}
2308191783Srmacklem	return (error);
2309191783Srmacklem}
2310191783Srmacklem
2311191783Srmacklem/*
2312191783Srmacklem * NFS V3 readdir plus RPC. Used in place of ncl_readdirrpc().
2313191783Srmacklem */
2314191783Srmacklemint
2315191783Srmacklemncl_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
2316191783Srmacklem    struct thread *td)
2317191783Srmacklem{
2318191783Srmacklem	struct nfsvattr nfsva;
2319191783Srmacklem	nfsuint64 *cookiep, cookie;
2320191783Srmacklem	struct nfsnode *dnp = VTONFS(vp);
2321191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
2322191783Srmacklem	int error = 0, attrflag, eof;
2323191783Srmacklem
2324209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
2325209120Skib	    (uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 &&
2326209120Skib	    (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
2327209120Skib	    ("nfs readdirplusrpc bad uio"));
2328191783Srmacklem
2329191783Srmacklem	/*
2330191783Srmacklem	 * If there is no cookie, assume directory was stale.
2331191783Srmacklem	 */
2332191783Srmacklem	ncl_dircookie_lock(dnp);
2333191783Srmacklem	cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0);
2334191783Srmacklem	if (cookiep) {
2335191783Srmacklem		cookie = *cookiep;
2336191783Srmacklem		ncl_dircookie_unlock(dnp);
2337191783Srmacklem	} else {
2338191783Srmacklem		ncl_dircookie_unlock(dnp);
2339191783Srmacklem		return (NFSERR_BAD_COOKIE);
2340191783Srmacklem	}
2341191783Srmacklem
2342191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp))
2343191783Srmacklem		(void)ncl_fsinfo(nmp, vp, cred, td);
2344191783Srmacklem	error = nfsrpc_readdirplus(vp, uiop, &cookie, cred, td, &nfsva,
2345191783Srmacklem	    &attrflag, &eof, NULL);
2346191783Srmacklem	if (attrflag)
2347191783Srmacklem		(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
2348191783Srmacklem
2349191783Srmacklem	if (!error) {
2350191783Srmacklem		/*
2351191783Srmacklem		 * We are now either at end of the directory or have filled the
2352191783Srmacklem		 * the block.
2353191783Srmacklem		 */
2354191783Srmacklem		if (eof)
2355191783Srmacklem			dnp->n_direofoffset = uiop->uio_offset;
2356191783Srmacklem		else {
2357191783Srmacklem			if (uiop->uio_resid > 0)
2358191783Srmacklem				ncl_printf("EEK! readdirplusrpc resid > 0\n");
2359191783Srmacklem			ncl_dircookie_lock(dnp);
2360191783Srmacklem			cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1);
2361191783Srmacklem			*cookiep = cookie;
2362191783Srmacklem			ncl_dircookie_unlock(dnp);
2363191783Srmacklem		}
2364191783Srmacklem	} else if (NFS_ISV4(vp)) {
2365191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
2366191783Srmacklem	}
2367191783Srmacklem	return (error);
2368191783Srmacklem}
2369191783Srmacklem
2370191783Srmacklem/*
2371191783Srmacklem * Silly rename. To make the NFS filesystem that is stateless look a little
2372191783Srmacklem * more like the "ufs" a remove of an active vnode is translated to a rename
2373191783Srmacklem * to a funny looking filename that is removed by nfs_inactive on the
2374191783Srmacklem * nfsnode. There is the potential for another process on a different client
2375191783Srmacklem * to create the same funny name between the nfs_lookitup() fails and the
2376191783Srmacklem * nfs_rename() completes, but...
2377191783Srmacklem */
2378191783Srmacklemstatic int
2379191783Srmacklemnfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
2380191783Srmacklem{
2381191783Srmacklem	struct sillyrename *sp;
2382191783Srmacklem	struct nfsnode *np;
2383191783Srmacklem	int error;
2384191783Srmacklem	short pid;
2385191783Srmacklem	unsigned int lticks;
2386191783Srmacklem
2387191783Srmacklem	cache_purge(dvp);
2388191783Srmacklem	np = VTONFS(vp);
2389209120Skib	KASSERT(vp->v_type != VDIR, ("nfs: sillyrename dir"));
2390191783Srmacklem	MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename),
2391191783Srmacklem	    M_NEWNFSREQ, M_WAITOK);
2392191783Srmacklem	sp->s_cred = crhold(cnp->cn_cred);
2393191783Srmacklem	sp->s_dvp = dvp;
2394191783Srmacklem	VREF(dvp);
2395191783Srmacklem
2396191783Srmacklem	/*
2397191783Srmacklem	 * Fudge together a funny name.
2398191783Srmacklem	 * Changing the format of the funny name to accomodate more
2399191783Srmacklem	 * sillynames per directory.
2400191783Srmacklem	 * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is
2401191783Srmacklem	 * CPU ticks since boot.
2402191783Srmacklem	 */
2403191783Srmacklem	pid = cnp->cn_thread->td_proc->p_pid;
2404191783Srmacklem	lticks = (unsigned int)ticks;
2405191783Srmacklem	for ( ; ; ) {
2406191783Srmacklem		sp->s_namlen = sprintf(sp->s_name,
2407191783Srmacklem				       ".nfs.%08x.%04x4.4", lticks,
2408191783Srmacklem				       pid);
2409191783Srmacklem		if (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
2410191783Srmacklem				 cnp->cn_thread, NULL))
2411191783Srmacklem			break;
2412191783Srmacklem		lticks++;
2413191783Srmacklem	}
2414191783Srmacklem	error = nfs_renameit(dvp, vp, cnp, sp);
2415191783Srmacklem	if (error)
2416191783Srmacklem		goto bad;
2417191783Srmacklem	error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred,
2418191783Srmacklem		cnp->cn_thread, &np);
2419191783Srmacklem	np->n_sillyrename = sp;
2420191783Srmacklem	return (0);
2421191783Srmacklembad:
2422191783Srmacklem	vrele(sp->s_dvp);
2423191783Srmacklem	crfree(sp->s_cred);
2424191783Srmacklem	free((caddr_t)sp, M_NEWNFSREQ);
2425191783Srmacklem	return (error);
2426191783Srmacklem}
2427191783Srmacklem
2428191783Srmacklem/*
2429191783Srmacklem * Look up a file name and optionally either update the file handle or
2430191783Srmacklem * allocate an nfsnode, depending on the value of npp.
2431191783Srmacklem * npp == NULL	--> just do the lookup
2432191783Srmacklem * *npp == NULL --> allocate a new nfsnode and make sure attributes are
2433191783Srmacklem *			handled too
2434191783Srmacklem * *npp != NULL --> update the file handle in the vnode
2435191783Srmacklem */
2436191783Srmacklemstatic int
2437191783Srmacklemnfs_lookitup(struct vnode *dvp, char *name, int len, struct ucred *cred,
2438191783Srmacklem    struct thread *td, struct nfsnode **npp)
2439191783Srmacklem{
2440191783Srmacklem	struct vnode *newvp = NULL, *vp;
2441191783Srmacklem	struct nfsnode *np, *dnp = VTONFS(dvp);
2442191783Srmacklem	struct nfsfh *nfhp, *onfhp;
2443191783Srmacklem	struct nfsvattr nfsva, dnfsva;
2444191783Srmacklem	struct componentname cn;
2445191783Srmacklem	int error = 0, attrflag, dattrflag;
2446191783Srmacklem	u_int hash;
2447191783Srmacklem
2448191783Srmacklem	error = nfsrpc_lookup(dvp, name, len, cred, td, &dnfsva, &nfsva,
2449191783Srmacklem	    &nfhp, &attrflag, &dattrflag, NULL);
2450191783Srmacklem	if (dattrflag)
2451191783Srmacklem		(void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1);
2452191783Srmacklem	if (npp && !error) {
2453191783Srmacklem		if (*npp != NULL) {
2454191783Srmacklem		    np = *npp;
2455191783Srmacklem		    vp = NFSTOV(np);
2456191783Srmacklem		    /*
2457191783Srmacklem		     * For NFSv4, check to see if it is the same name and
2458191783Srmacklem		     * replace the name, if it is different.
2459191783Srmacklem		     */
2460191783Srmacklem		    if (np->n_v4 != NULL && nfsva.na_type == VREG &&
2461191783Srmacklem			(np->n_v4->n4_namelen != len ||
2462191783Srmacklem			 NFSBCMP(name, NFS4NODENAME(np->n_v4), len) ||
2463191783Srmacklem			 dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen ||
2464191783Srmacklem			 NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
2465191783Srmacklem			 dnp->n_fhp->nfh_len))) {
2466191783Srmacklem#ifdef notdef
2467191783Srmacklem{ char nnn[100]; int nnnl;
2468191783Srmacklemnnnl = (len < 100) ? len : 99;
2469191783Srmacklembcopy(name, nnn, nnnl);
2470191783Srmacklemnnn[nnnl] = '\0';
2471191783Srmacklemprintf("replace=%s\n",nnn);
2472191783Srmacklem}
2473191783Srmacklem#endif
2474191783Srmacklem			    FREE((caddr_t)np->n_v4, M_NFSV4NODE);
2475191783Srmacklem			    MALLOC(np->n_v4, struct nfsv4node *,
2476191783Srmacklem				sizeof (struct nfsv4node) +
2477191783Srmacklem				dnp->n_fhp->nfh_len + len - 1,
2478191783Srmacklem				M_NFSV4NODE, M_WAITOK);
2479191783Srmacklem			    np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
2480191783Srmacklem			    np->n_v4->n4_namelen = len;
2481191783Srmacklem			    NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
2482191783Srmacklem				dnp->n_fhp->nfh_len);
2483191783Srmacklem			    NFSBCOPY(name, NFS4NODENAME(np->n_v4), len);
2484191783Srmacklem		    }
2485191783Srmacklem		    hash = fnv_32_buf(nfhp->nfh_fh, nfhp->nfh_len,
2486191783Srmacklem			FNV1_32_INIT);
2487191783Srmacklem		    onfhp = np->n_fhp;
2488191783Srmacklem		    /*
2489191783Srmacklem		     * Rehash node for new file handle.
2490191783Srmacklem		     */
2491191783Srmacklem		    vfs_hash_rehash(vp, hash);
2492191783Srmacklem		    np->n_fhp = nfhp;
2493191783Srmacklem		    if (onfhp != NULL)
2494191783Srmacklem			FREE((caddr_t)onfhp, M_NFSFH);
2495191783Srmacklem		    newvp = NFSTOV(np);
2496191783Srmacklem		} else if (NFS_CMPFH(dnp, nfhp->nfh_fh, nfhp->nfh_len)) {
2497191783Srmacklem		    FREE((caddr_t)nfhp, M_NFSFH);
2498191783Srmacklem		    VREF(dvp);
2499191783Srmacklem		    newvp = dvp;
2500191783Srmacklem		} else {
2501191783Srmacklem		    cn.cn_nameptr = name;
2502191783Srmacklem		    cn.cn_namelen = len;
2503191783Srmacklem		    error = nfscl_nget(dvp->v_mount, dvp, nfhp, &cn, td,
2504220732Srmacklem			&np, NULL, LK_EXCLUSIVE);
2505191783Srmacklem		    if (error)
2506191783Srmacklem			return (error);
2507191783Srmacklem		    newvp = NFSTOV(np);
2508191783Srmacklem		}
2509191783Srmacklem		if (!attrflag && *npp == NULL) {
2510220764Srmacklem			if (newvp == dvp)
2511220764Srmacklem				vrele(newvp);
2512220764Srmacklem			else
2513220764Srmacklem				vput(newvp);
2514191783Srmacklem			return (ENOENT);
2515191783Srmacklem		}
2516191783Srmacklem		if (attrflag)
2517191783Srmacklem			(void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL,
2518191783Srmacklem			    0, 1);
2519191783Srmacklem	}
2520191783Srmacklem	if (npp && *npp == NULL) {
2521191783Srmacklem		if (error) {
2522191783Srmacklem			if (newvp) {
2523191783Srmacklem				if (newvp == dvp)
2524191783Srmacklem					vrele(newvp);
2525191783Srmacklem				else
2526191783Srmacklem					vput(newvp);
2527191783Srmacklem			}
2528191783Srmacklem		} else
2529191783Srmacklem			*npp = np;
2530191783Srmacklem	}
2531191783Srmacklem	if (error && NFS_ISV4(dvp))
2532191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
2533191783Srmacklem	return (error);
2534191783Srmacklem}
2535191783Srmacklem
2536191783Srmacklem/*
2537191783Srmacklem * Nfs Version 3 and 4 commit rpc
2538191783Srmacklem */
2539191783Srmacklemint
2540191783Srmacklemncl_commit(struct vnode *vp, u_quad_t offset, int cnt, struct ucred *cred,
2541191783Srmacklem   struct thread *td)
2542191783Srmacklem{
2543191783Srmacklem	struct nfsvattr nfsva;
2544191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
2545191783Srmacklem	int error, attrflag;
2546191783Srmacklem	u_char verf[NFSX_VERF];
2547191783Srmacklem
2548191783Srmacklem	mtx_lock(&nmp->nm_mtx);
2549191783Srmacklem	if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) {
2550191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
2551191783Srmacklem		return (0);
2552191783Srmacklem	}
2553191783Srmacklem	mtx_unlock(&nmp->nm_mtx);
2554191783Srmacklem	error = nfsrpc_commit(vp, offset, cnt, cred, td, verf, &nfsva,
2555191783Srmacklem	    &attrflag, NULL);
2556191783Srmacklem	if (!error) {
2557222291Srmacklem		mtx_lock(&nmp->nm_mtx);
2558191783Srmacklem		if (NFSBCMP((caddr_t)nmp->nm_verf, verf, NFSX_VERF)) {
2559191783Srmacklem			NFSBCOPY(verf, (caddr_t)nmp->nm_verf, NFSX_VERF);
2560191783Srmacklem			error = NFSERR_STALEWRITEVERF;
2561191783Srmacklem		}
2562222291Srmacklem		mtx_unlock(&nmp->nm_mtx);
2563191783Srmacklem		if (!error && attrflag)
2564191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL,
2565191783Srmacklem			    0, 1);
2566191783Srmacklem	} else if (NFS_ISV4(vp)) {
2567191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
2568191783Srmacklem	}
2569191783Srmacklem	return (error);
2570191783Srmacklem}
2571191783Srmacklem
2572191783Srmacklem/*
2573191783Srmacklem * Strategy routine.
2574191783Srmacklem * For async requests when nfsiod(s) are running, queue the request by
2575191783Srmacklem * calling ncl_asyncio(), otherwise just all ncl_doio() to do the
2576191783Srmacklem * request.
2577191783Srmacklem */
2578191783Srmacklemstatic int
2579191783Srmacklemnfs_strategy(struct vop_strategy_args *ap)
2580191783Srmacklem{
2581191783Srmacklem	struct buf *bp = ap->a_bp;
2582191783Srmacklem	struct ucred *cr;
2583191783Srmacklem
2584191783Srmacklem	KASSERT(!(bp->b_flags & B_DONE),
2585191783Srmacklem	    ("nfs_strategy: buffer %p unexpectedly marked B_DONE", bp));
2586191783Srmacklem	BUF_ASSERT_HELD(bp);
2587191783Srmacklem
2588191783Srmacklem	if (bp->b_iocmd == BIO_READ)
2589191783Srmacklem		cr = bp->b_rcred;
2590191783Srmacklem	else
2591191783Srmacklem		cr = bp->b_wcred;
2592191783Srmacklem
2593191783Srmacklem	/*
2594191783Srmacklem	 * If the op is asynchronous and an i/o daemon is waiting
2595191783Srmacklem	 * queue the request, wake it up and wait for completion
2596191783Srmacklem	 * otherwise just do it ourselves.
2597191783Srmacklem	 */
2598191783Srmacklem	if ((bp->b_flags & B_ASYNC) == 0 ||
2599191783Srmacklem	    ncl_asyncio(VFSTONFS(ap->a_vp->v_mount), bp, NOCRED, curthread))
2600207082Srmacklem		(void) ncl_doio(ap->a_vp, bp, cr, curthread, 1);
2601191783Srmacklem	return (0);
2602191783Srmacklem}
2603191783Srmacklem
2604191783Srmacklem/*
2605191783Srmacklem * fsync vnode op. Just call ncl_flush() with commit == 1.
2606191783Srmacklem */
2607191783Srmacklem/* ARGSUSED */
2608191783Srmacklemstatic int
2609191783Srmacklemnfs_fsync(struct vop_fsync_args *ap)
2610191783Srmacklem{
2611229287Srmacklem
2612229287Srmacklem	if (ap->a_vp->v_type != VREG) {
2613229287Srmacklem		/*
2614229287Srmacklem		 * For NFS, metadata is changed synchronously on the server,
2615229287Srmacklem		 * so there is nothing to flush. Also, ncl_flush() clears
2616229287Srmacklem		 * the NMODIFIED flag and that shouldn't be done here for
2617229287Srmacklem		 * directories.
2618229287Srmacklem		 */
2619229287Srmacklem		return (0);
2620229287Srmacklem	}
2621207082Srmacklem	return (ncl_flush(ap->a_vp, ap->a_waitfor, NULL, ap->a_td, 1, 0));
2622191783Srmacklem}
2623191783Srmacklem
2624191783Srmacklem/*
2625191783Srmacklem * Flush all the blocks associated with a vnode.
2626191783Srmacklem * 	Walk through the buffer pool and push any dirty pages
2627191783Srmacklem *	associated with the vnode.
2628207082Srmacklem * If the called_from_renewthread argument is TRUE, it has been called
2629207082Srmacklem * from the NFSv4 renew thread and, as such, cannot block indefinitely
2630207082Srmacklem * waiting for a buffer write to complete.
2631191783Srmacklem */
2632191783Srmacklemint
2633191783Srmacklemncl_flush(struct vnode *vp, int waitfor, struct ucred *cred, struct thread *td,
2634207082Srmacklem    int commit, int called_from_renewthread)
2635191783Srmacklem{
2636191783Srmacklem	struct nfsnode *np = VTONFS(vp);
2637191783Srmacklem	struct buf *bp;
2638191783Srmacklem	int i;
2639191783Srmacklem	struct buf *nbp;
2640191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
2641191783Srmacklem	int error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos;
2642191783Srmacklem	int passone = 1, trycnt = 0;
2643191783Srmacklem	u_quad_t off, endoff, toff;
2644191783Srmacklem	struct ucred* wcred = NULL;
2645191783Srmacklem	struct buf **bvec = NULL;
2646191783Srmacklem	struct bufobj *bo;
2647191783Srmacklem#ifndef NFS_COMMITBVECSIZ
2648191783Srmacklem#define	NFS_COMMITBVECSIZ	20
2649191783Srmacklem#endif
2650191783Srmacklem	struct buf *bvec_on_stack[NFS_COMMITBVECSIZ];
2651191783Srmacklem	int bvecsize = 0, bveccount;
2652191783Srmacklem
2653207082Srmacklem	if (called_from_renewthread != 0)
2654207082Srmacklem		slptimeo = hz;
2655191783Srmacklem	if (nmp->nm_flag & NFSMNT_INT)
2656195821Srmacklem		slpflag = NFS_PCATCH;
2657191783Srmacklem	if (!commit)
2658191783Srmacklem		passone = 0;
2659191783Srmacklem	bo = &vp->v_bufobj;
2660191783Srmacklem	/*
2661191783Srmacklem	 * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the
2662191783Srmacklem	 * server, but has not been committed to stable storage on the server
2663191783Srmacklem	 * yet. On the first pass, the byte range is worked out and the commit
2664191783Srmacklem	 * rpc is done. On the second pass, ncl_writebp() is called to do the
2665191783Srmacklem	 * job.
2666191783Srmacklem	 */
2667191783Srmacklemagain:
2668191783Srmacklem	off = (u_quad_t)-1;
2669191783Srmacklem	endoff = 0;
2670191783Srmacklem	bvecpos = 0;
2671191783Srmacklem	if (NFS_ISV34(vp) && commit) {
2672191783Srmacklem		if (bvec != NULL && bvec != bvec_on_stack)
2673191783Srmacklem			free(bvec, M_TEMP);
2674191783Srmacklem		/*
2675191783Srmacklem		 * Count up how many buffers waiting for a commit.
2676191783Srmacklem		 */
2677191783Srmacklem		bveccount = 0;
2678191783Srmacklem		BO_LOCK(bo);
2679191783Srmacklem		TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
2680191783Srmacklem			if (!BUF_ISLOCKED(bp) &&
2681191783Srmacklem			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
2682191783Srmacklem				== (B_DELWRI | B_NEEDCOMMIT))
2683191783Srmacklem				bveccount++;
2684191783Srmacklem		}
2685191783Srmacklem		/*
2686191783Srmacklem		 * Allocate space to remember the list of bufs to commit.  It is
2687191783Srmacklem		 * important to use M_NOWAIT here to avoid a race with nfs_write.
2688191783Srmacklem		 * If we can't get memory (for whatever reason), we will end up
2689191783Srmacklem		 * committing the buffers one-by-one in the loop below.
2690191783Srmacklem		 */
2691191783Srmacklem		if (bveccount > NFS_COMMITBVECSIZ) {
2692191783Srmacklem			/*
2693191783Srmacklem			 * Release the vnode interlock to avoid a lock
2694191783Srmacklem			 * order reversal.
2695191783Srmacklem			 */
2696191783Srmacklem			BO_UNLOCK(bo);
2697191783Srmacklem			bvec = (struct buf **)
2698191783Srmacklem				malloc(bveccount * sizeof(struct buf *),
2699191783Srmacklem				       M_TEMP, M_NOWAIT);
2700191783Srmacklem			BO_LOCK(bo);
2701191783Srmacklem			if (bvec == NULL) {
2702191783Srmacklem				bvec = bvec_on_stack;
2703191783Srmacklem				bvecsize = NFS_COMMITBVECSIZ;
2704191783Srmacklem			} else
2705191783Srmacklem				bvecsize = bveccount;
2706191783Srmacklem		} else {
2707191783Srmacklem			bvec = bvec_on_stack;
2708191783Srmacklem			bvecsize = NFS_COMMITBVECSIZ;
2709191783Srmacklem		}
2710191783Srmacklem		TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
2711191783Srmacklem			if (bvecpos >= bvecsize)
2712191783Srmacklem				break;
2713191783Srmacklem			if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
2714191783Srmacklem				nbp = TAILQ_NEXT(bp, b_bobufs);
2715191783Srmacklem				continue;
2716191783Srmacklem			}
2717191783Srmacklem			if ((bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) !=
2718191783Srmacklem			    (B_DELWRI | B_NEEDCOMMIT)) {
2719191783Srmacklem				BUF_UNLOCK(bp);
2720191783Srmacklem				nbp = TAILQ_NEXT(bp, b_bobufs);
2721191783Srmacklem				continue;
2722191783Srmacklem			}
2723191783Srmacklem			BO_UNLOCK(bo);
2724191783Srmacklem			bremfree(bp);
2725191783Srmacklem			/*
2726191783Srmacklem			 * Work out if all buffers are using the same cred
2727191783Srmacklem			 * so we can deal with them all with one commit.
2728191783Srmacklem			 *
2729191783Srmacklem			 * NOTE: we are not clearing B_DONE here, so we have
2730191783Srmacklem			 * to do it later on in this routine if we intend to
2731191783Srmacklem			 * initiate I/O on the bp.
2732191783Srmacklem			 *
2733191783Srmacklem			 * Note: to avoid loopback deadlocks, we do not
2734191783Srmacklem			 * assign b_runningbufspace.
2735191783Srmacklem			 */
2736191783Srmacklem			if (wcred == NULL)
2737191783Srmacklem				wcred = bp->b_wcred;
2738191783Srmacklem			else if (wcred != bp->b_wcred)
2739191783Srmacklem				wcred = NOCRED;
2740191783Srmacklem			vfs_busy_pages(bp, 1);
2741191783Srmacklem
2742191783Srmacklem			BO_LOCK(bo);
2743191783Srmacklem			/*
2744191783Srmacklem			 * bp is protected by being locked, but nbp is not
2745191783Srmacklem			 * and vfs_busy_pages() may sleep.  We have to
2746191783Srmacklem			 * recalculate nbp.
2747191783Srmacklem			 */
2748191783Srmacklem			nbp = TAILQ_NEXT(bp, b_bobufs);
2749191783Srmacklem
2750191783Srmacklem			/*
2751191783Srmacklem			 * A list of these buffers is kept so that the
2752191783Srmacklem			 * second loop knows which buffers have actually
2753191783Srmacklem			 * been committed. This is necessary, since there
2754191783Srmacklem			 * may be a race between the commit rpc and new
2755191783Srmacklem			 * uncommitted writes on the file.
2756191783Srmacklem			 */
2757191783Srmacklem			bvec[bvecpos++] = bp;
2758191783Srmacklem			toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
2759191783Srmacklem				bp->b_dirtyoff;
2760191783Srmacklem			if (toff < off)
2761191783Srmacklem				off = toff;
2762191783Srmacklem			toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff);
2763191783Srmacklem			if (toff > endoff)
2764191783Srmacklem				endoff = toff;
2765191783Srmacklem		}
2766191783Srmacklem		BO_UNLOCK(bo);
2767191783Srmacklem	}
2768191783Srmacklem	if (bvecpos > 0) {
2769191783Srmacklem		/*
2770191783Srmacklem		 * Commit data on the server, as required.
2771191783Srmacklem		 * If all bufs are using the same wcred, then use that with
2772191783Srmacklem		 * one call for all of them, otherwise commit each one
2773191783Srmacklem		 * separately.
2774191783Srmacklem		 */
2775191783Srmacklem		if (wcred != NOCRED)
2776191783Srmacklem			retv = ncl_commit(vp, off, (int)(endoff - off),
2777191783Srmacklem					  wcred, td);
2778191783Srmacklem		else {
2779191783Srmacklem			retv = 0;
2780191783Srmacklem			for (i = 0; i < bvecpos; i++) {
2781191783Srmacklem				off_t off, size;
2782191783Srmacklem				bp = bvec[i];
2783191783Srmacklem				off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE +
2784191783Srmacklem					bp->b_dirtyoff;
2785191783Srmacklem				size = (u_quad_t)(bp->b_dirtyend
2786191783Srmacklem						  - bp->b_dirtyoff);
2787191783Srmacklem				retv = ncl_commit(vp, off, (int)size,
2788191783Srmacklem						  bp->b_wcred, td);
2789191783Srmacklem				if (retv) break;
2790191783Srmacklem			}
2791191783Srmacklem		}
2792191783Srmacklem
2793191783Srmacklem		if (retv == NFSERR_STALEWRITEVERF)
2794191783Srmacklem			ncl_clearcommit(vp->v_mount);
2795191783Srmacklem
2796191783Srmacklem		/*
2797191783Srmacklem		 * Now, either mark the blocks I/O done or mark the
2798191783Srmacklem		 * blocks dirty, depending on whether the commit
2799191783Srmacklem		 * succeeded.
2800191783Srmacklem		 */
2801191783Srmacklem		for (i = 0; i < bvecpos; i++) {
2802191783Srmacklem			bp = bvec[i];
2803191783Srmacklem			bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
2804191783Srmacklem			if (retv) {
2805191783Srmacklem				/*
2806191783Srmacklem				 * Error, leave B_DELWRI intact
2807191783Srmacklem				 */
2808191783Srmacklem				vfs_unbusy_pages(bp);
2809191783Srmacklem				brelse(bp);
2810191783Srmacklem			} else {
2811191783Srmacklem				/*
2812191783Srmacklem				 * Success, remove B_DELWRI ( bundirty() ).
2813191783Srmacklem				 *
2814191783Srmacklem				 * b_dirtyoff/b_dirtyend seem to be NFS
2815191783Srmacklem				 * specific.  We should probably move that
2816191783Srmacklem				 * into bundirty(). XXX
2817191783Srmacklem				 */
2818191783Srmacklem				bufobj_wref(bo);
2819191783Srmacklem				bp->b_flags |= B_ASYNC;
2820191783Srmacklem				bundirty(bp);
2821191783Srmacklem				bp->b_flags &= ~B_DONE;
2822191783Srmacklem				bp->b_ioflags &= ~BIO_ERROR;
2823191783Srmacklem				bp->b_dirtyoff = bp->b_dirtyend = 0;
2824191783Srmacklem				bufdone(bp);
2825191783Srmacklem			}
2826191783Srmacklem		}
2827191783Srmacklem	}
2828191783Srmacklem
2829191783Srmacklem	/*
2830191783Srmacklem	 * Start/do any write(s) that are required.
2831191783Srmacklem	 */
2832191783Srmacklemloop:
2833191783Srmacklem	BO_LOCK(bo);
2834191783Srmacklem	TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
2835191783Srmacklem		if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) {
2836191783Srmacklem			if (waitfor != MNT_WAIT || passone)
2837191783Srmacklem				continue;
2838191783Srmacklem
2839191783Srmacklem			error = BUF_TIMELOCK(bp,
2840191783Srmacklem			    LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK,
2841191783Srmacklem			    BO_MTX(bo), "nfsfsync", slpflag, slptimeo);
2842191783Srmacklem			if (error == 0) {
2843191783Srmacklem				BUF_UNLOCK(bp);
2844191783Srmacklem				goto loop;
2845191783Srmacklem			}
2846191783Srmacklem			if (error == ENOLCK) {
2847191783Srmacklem				error = 0;
2848191783Srmacklem				goto loop;
2849191783Srmacklem			}
2850207082Srmacklem			if (called_from_renewthread != 0) {
2851207082Srmacklem				/*
2852207082Srmacklem				 * Return EIO so the flush will be retried
2853207082Srmacklem				 * later.
2854207082Srmacklem				 */
2855207082Srmacklem				error = EIO;
2856207082Srmacklem				goto done;
2857207082Srmacklem			}
2858191783Srmacklem			if (newnfs_sigintr(nmp, td)) {
2859191783Srmacklem				error = EINTR;
2860191783Srmacklem				goto done;
2861191783Srmacklem			}
2862195821Srmacklem			if (slpflag & PCATCH) {
2863191783Srmacklem				slpflag = 0;
2864191783Srmacklem				slptimeo = 2 * hz;
2865191783Srmacklem			}
2866191783Srmacklem			goto loop;
2867191783Srmacklem		}
2868191783Srmacklem		if ((bp->b_flags & B_DELWRI) == 0)
2869191783Srmacklem			panic("nfs_fsync: not dirty");
2870191783Srmacklem		if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) {
2871191783Srmacklem			BUF_UNLOCK(bp);
2872191783Srmacklem			continue;
2873191783Srmacklem		}
2874191783Srmacklem		BO_UNLOCK(bo);
2875191783Srmacklem		bremfree(bp);
2876191783Srmacklem		if (passone || !commit)
2877191783Srmacklem		    bp->b_flags |= B_ASYNC;
2878191783Srmacklem		else
2879191783Srmacklem		    bp->b_flags |= B_ASYNC;
2880191783Srmacklem		bwrite(bp);
2881191783Srmacklem		if (newnfs_sigintr(nmp, td)) {
2882191783Srmacklem			error = EINTR;
2883191783Srmacklem			goto done;
2884191783Srmacklem		}
2885191783Srmacklem		goto loop;
2886191783Srmacklem	}
2887191783Srmacklem	if (passone) {
2888191783Srmacklem		passone = 0;
2889191783Srmacklem		BO_UNLOCK(bo);
2890191783Srmacklem		goto again;
2891191783Srmacklem	}
2892191783Srmacklem	if (waitfor == MNT_WAIT) {
2893191783Srmacklem		while (bo->bo_numoutput) {
2894191783Srmacklem			error = bufobj_wwait(bo, slpflag, slptimeo);
2895191783Srmacklem			if (error) {
2896191783Srmacklem			    BO_UNLOCK(bo);
2897207082Srmacklem			    if (called_from_renewthread != 0) {
2898207082Srmacklem				/*
2899207082Srmacklem				 * Return EIO so that the flush will be
2900207082Srmacklem				 * retried later.
2901207082Srmacklem				 */
2902207082Srmacklem				error = EIO;
2903207082Srmacklem				goto done;
2904207082Srmacklem			    }
2905191783Srmacklem			    error = newnfs_sigintr(nmp, td);
2906191783Srmacklem			    if (error)
2907191783Srmacklem				goto done;
2908195821Srmacklem			    if (slpflag & PCATCH) {
2909191783Srmacklem				slpflag = 0;
2910191783Srmacklem				slptimeo = 2 * hz;
2911191783Srmacklem			    }
2912191783Srmacklem			    BO_LOCK(bo);
2913191783Srmacklem			}
2914191783Srmacklem		}
2915191783Srmacklem		if (bo->bo_dirty.bv_cnt != 0 && commit) {
2916191783Srmacklem			BO_UNLOCK(bo);
2917191783Srmacklem			goto loop;
2918191783Srmacklem		}
2919191783Srmacklem		/*
2920191783Srmacklem		 * Wait for all the async IO requests to drain
2921191783Srmacklem		 */
2922191783Srmacklem		BO_UNLOCK(bo);
2923191783Srmacklem		mtx_lock(&np->n_mtx);
2924191783Srmacklem		while (np->n_directio_asyncwr > 0) {
2925191783Srmacklem			np->n_flag |= NFSYNCWAIT;
2926201029Srmacklem			error = newnfs_msleep(td, &np->n_directio_asyncwr,
2927201029Srmacklem			    &np->n_mtx, slpflag | (PRIBIO + 1),
2928201029Srmacklem			    "nfsfsync", 0);
2929191783Srmacklem			if (error) {
2930191783Srmacklem				if (newnfs_sigintr(nmp, td)) {
2931191783Srmacklem					mtx_unlock(&np->n_mtx);
2932191783Srmacklem					error = EINTR;
2933191783Srmacklem					goto done;
2934191783Srmacklem				}
2935191783Srmacklem			}
2936191783Srmacklem		}
2937191783Srmacklem		mtx_unlock(&np->n_mtx);
2938191783Srmacklem	} else
2939191783Srmacklem		BO_UNLOCK(bo);
2940191783Srmacklem	mtx_lock(&np->n_mtx);
2941191783Srmacklem	if (np->n_flag & NWRITEERR) {
2942191783Srmacklem		error = np->n_error;
2943191783Srmacklem		np->n_flag &= ~NWRITEERR;
2944191783Srmacklem	}
2945191783Srmacklem  	if (commit && bo->bo_dirty.bv_cnt == 0 &&
2946191783Srmacklem	    bo->bo_numoutput == 0 && np->n_directio_asyncwr == 0)
2947191783Srmacklem  		np->n_flag &= ~NMODIFIED;
2948191783Srmacklem	mtx_unlock(&np->n_mtx);
2949191783Srmacklemdone:
2950191783Srmacklem	if (bvec != NULL && bvec != bvec_on_stack)
2951191783Srmacklem		free(bvec, M_TEMP);
2952191783Srmacklem	if (error == 0 && commit != 0 && waitfor == MNT_WAIT &&
2953191783Srmacklem	    (bo->bo_dirty.bv_cnt != 0 || bo->bo_numoutput != 0 ||
2954191783Srmacklem	     np->n_directio_asyncwr != 0) && trycnt++ < 5) {
2955191783Srmacklem		/* try, try again... */
2956191783Srmacklem		passone = 1;
2957191783Srmacklem		wcred = NULL;
2958191783Srmacklem		bvec = NULL;
2959191783Srmacklem		bvecsize = 0;
2960191783Srmacklemprintf("try%d\n", trycnt);
2961191783Srmacklem		goto again;
2962191783Srmacklem	}
2963191783Srmacklem	return (error);
2964191783Srmacklem}
2965191783Srmacklem
2966191783Srmacklem/*
2967191783Srmacklem * NFS advisory byte-level locks.
2968191783Srmacklem */
2969191783Srmacklemstatic int
2970191783Srmacklemnfs_advlock(struct vop_advlock_args *ap)
2971191783Srmacklem{
2972191783Srmacklem	struct vnode *vp = ap->a_vp;
2973191783Srmacklem	struct ucred *cred;
2974191783Srmacklem	struct nfsnode *np = VTONFS(ap->a_vp);
2975191783Srmacklem	struct proc *p = (struct proc *)ap->a_id;
2976191783Srmacklem	struct thread *td = curthread;	/* XXX */
2977191783Srmacklem	struct vattr va;
2978193837Srmacklem	int ret, error = EOPNOTSUPP;
2979191783Srmacklem	u_quad_t size;
2980191783Srmacklem
2981222722Srmacklem	if (NFS_ISV4(vp) && (ap->a_flags & (F_POSIX | F_FLOCK)) != 0) {
2982229258Srmacklem		if (vp->v_type != VREG)
2983229258Srmacklem			return (EINVAL);
2984222722Srmacklem		if ((ap->a_flags & F_POSIX) != 0)
2985222722Srmacklem			cred = p->p_ucred;
2986222722Srmacklem		else
2987222722Srmacklem			cred = td->td_ucred;
2988224081Szack		NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
2989193837Srmacklem		if (vp->v_iflag & VI_DOOMED) {
2990224082Szack			NFSVOPUNLOCK(vp, 0);
2991193837Srmacklem			return (EBADF);
2992193837Srmacklem		}
2993191783Srmacklem
2994191783Srmacklem		/*
2995191783Srmacklem		 * If this is unlocking a write locked region, flush and
2996191783Srmacklem		 * commit them before unlocking. This is required by
2997191783Srmacklem		 * RFC3530 Sec. 9.3.2.
2998191783Srmacklem		 */
2999191783Srmacklem		if (ap->a_op == F_UNLCK &&
3000222719Srmacklem		    nfscl_checkwritelocked(vp, ap->a_fl, cred, td, ap->a_id,
3001222719Srmacklem		    ap->a_flags))
3002207082Srmacklem			(void) ncl_flush(vp, MNT_WAIT, cred, td, 1, 0);
3003191783Srmacklem
3004191783Srmacklem		/*
3005191783Srmacklem		 * Loop around doing the lock op, while a blocking lock
3006191783Srmacklem		 * must wait for the lock op to succeed.
3007191783Srmacklem		 */
3008191783Srmacklem		do {
3009191783Srmacklem			ret = nfsrpc_advlock(vp, np->n_size, ap->a_op,
3010222719Srmacklem			    ap->a_fl, 0, cred, td, ap->a_id, ap->a_flags);
3011191783Srmacklem			if (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) &&
3012191783Srmacklem			    ap->a_op == F_SETLK) {
3013224082Szack				NFSVOPUNLOCK(vp, 0);
3014207170Srmacklem				error = nfs_catnap(PZERO | PCATCH, ret,
3015207170Srmacklem				    "ncladvl");
3016191783Srmacklem				if (error)
3017191783Srmacklem					return (EINTR);
3018224081Szack				NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
3019193837Srmacklem				if (vp->v_iflag & VI_DOOMED) {
3020224082Szack					NFSVOPUNLOCK(vp, 0);
3021193837Srmacklem					return (EBADF);
3022193837Srmacklem				}
3023191783Srmacklem			}
3024191783Srmacklem		} while (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) &&
3025191783Srmacklem		     ap->a_op == F_SETLK);
3026191783Srmacklem		if (ret == NFSERR_DENIED) {
3027224082Szack			NFSVOPUNLOCK(vp, 0);
3028191783Srmacklem			return (EAGAIN);
3029191783Srmacklem		} else if (ret == EINVAL || ret == EBADF || ret == EINTR) {
3030224082Szack			NFSVOPUNLOCK(vp, 0);
3031191783Srmacklem			return (ret);
3032191783Srmacklem		} else if (ret != 0) {
3033224082Szack			NFSVOPUNLOCK(vp, 0);
3034191783Srmacklem			return (EACCES);
3035191783Srmacklem		}
3036191783Srmacklem
3037191783Srmacklem		/*
3038191783Srmacklem		 * Now, if we just got a lock, invalidate data in the buffer
3039191783Srmacklem		 * cache, as required, so that the coherency conforms with
3040191783Srmacklem		 * RFC3530 Sec. 9.3.2.
3041191783Srmacklem		 */
3042191783Srmacklem		if (ap->a_op == F_SETLK) {
3043191783Srmacklem			if ((np->n_flag & NMODIFIED) == 0) {
3044191783Srmacklem				np->n_attrstamp = 0;
3045223280Srmacklem				KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
3046191783Srmacklem				ret = VOP_GETATTR(vp, &va, cred);
3047191783Srmacklem			}
3048191783Srmacklem			if ((np->n_flag & NMODIFIED) || ret ||
3049191783Srmacklem			    np->n_change != va.va_filerev) {
3050191783Srmacklem				(void) ncl_vinvalbuf(vp, V_SAVE, td, 1);
3051191783Srmacklem				np->n_attrstamp = 0;
3052223280Srmacklem				KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
3053191783Srmacklem				ret = VOP_GETATTR(vp, &va, cred);
3054191783Srmacklem				if (!ret) {
3055191783Srmacklem					np->n_mtime = va.va_mtime;
3056191783Srmacklem					np->n_change = va.va_filerev;
3057191783Srmacklem				}
3058191783Srmacklem			}
3059260170Srmacklem			/* Mark that a file lock has been acquired. */
3060260170Srmacklem			mtx_lock(&np->n_mtx);
3061260170Srmacklem			np->n_flag |= NHASBEENLOCKED;
3062260170Srmacklem			mtx_unlock(&np->n_mtx);
3063191783Srmacklem		}
3064224082Szack		NFSVOPUNLOCK(vp, 0);
3065191783Srmacklem		return (0);
3066191783Srmacklem	} else if (!NFS_ISV4(vp)) {
3067224081Szack		error = NFSVOPLOCK(vp, LK_SHARED);
3068191783Srmacklem		if (error)
3069191783Srmacklem			return (error);
3070191783Srmacklem		if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) {
3071191783Srmacklem			size = VTONFS(vp)->n_size;
3072224082Szack			NFSVOPUNLOCK(vp, 0);
3073191783Srmacklem			error = lf_advlock(ap, &(vp->v_lockf), size);
3074191783Srmacklem		} else {
3075214048Srmacklem			if (nfs_advlock_p != NULL)
3076214048Srmacklem				error = nfs_advlock_p(ap);
3077212362Srmacklem			else {
3078224082Szack				NFSVOPUNLOCK(vp, 0);
3079191783Srmacklem				error = ENOLCK;
3080212362Srmacklem			}
3081191783Srmacklem		}
3082260170Srmacklem		if (error == 0 && ap->a_op == F_SETLK) {
3083260170Srmacklem			/* Mark that a file lock has been acquired. */
3084260170Srmacklem			mtx_lock(&np->n_mtx);
3085260170Srmacklem			np->n_flag |= NHASBEENLOCKED;
3086260170Srmacklem			mtx_unlock(&np->n_mtx);
3087260170Srmacklem		}
3088191783Srmacklem	}
3089191783Srmacklem	return (error);
3090191783Srmacklem}
3091191783Srmacklem
3092191783Srmacklem/*
3093191783Srmacklem * NFS advisory byte-level locks.
3094191783Srmacklem */
3095191783Srmacklemstatic int
3096191783Srmacklemnfs_advlockasync(struct vop_advlockasync_args *ap)
3097191783Srmacklem{
3098191783Srmacklem	struct vnode *vp = ap->a_vp;
3099191783Srmacklem	u_quad_t size;
3100191783Srmacklem	int error;
3101191783Srmacklem
3102191783Srmacklem	if (NFS_ISV4(vp))
3103191783Srmacklem		return (EOPNOTSUPP);
3104224081Szack	error = NFSVOPLOCK(vp, LK_SHARED);
3105191783Srmacklem	if (error)
3106191783Srmacklem		return (error);
3107191783Srmacklem	if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) {
3108191783Srmacklem		size = VTONFS(vp)->n_size;
3109224082Szack		NFSVOPUNLOCK(vp, 0);
3110191783Srmacklem		error = lf_advlockasync(ap, &(vp->v_lockf), size);
3111191783Srmacklem	} else {
3112224082Szack		NFSVOPUNLOCK(vp, 0);
3113191783Srmacklem		error = EOPNOTSUPP;
3114191783Srmacklem	}
3115191783Srmacklem	return (error);
3116191783Srmacklem}
3117191783Srmacklem
3118191783Srmacklem/*
3119191783Srmacklem * Print out the contents of an nfsnode.
3120191783Srmacklem */
3121191783Srmacklemstatic int
3122191783Srmacklemnfs_print(struct vop_print_args *ap)
3123191783Srmacklem{
3124191783Srmacklem	struct vnode *vp = ap->a_vp;
3125191783Srmacklem	struct nfsnode *np = VTONFS(vp);
3126191783Srmacklem
3127191783Srmacklem	ncl_printf("\tfileid %ld fsid 0x%x",
3128191783Srmacklem	   np->n_vattr.na_fileid, np->n_vattr.na_fsid);
3129191783Srmacklem	if (vp->v_type == VFIFO)
3130191783Srmacklem		fifo_printinfo(vp);
3131191783Srmacklem	printf("\n");
3132191783Srmacklem	return (0);
3133191783Srmacklem}
3134191783Srmacklem
3135191783Srmacklem/*
3136191783Srmacklem * This is the "real" nfs::bwrite(struct buf*).
3137191783Srmacklem * We set B_CACHE if this is a VMIO buffer.
3138191783Srmacklem */
3139191783Srmacklemint
3140191783Srmacklemncl_writebp(struct buf *bp, int force __unused, struct thread *td)
3141191783Srmacklem{
3142191783Srmacklem	int s;
3143191783Srmacklem	int oldflags = bp->b_flags;
3144191783Srmacklem#if 0
3145191783Srmacklem	int retv = 1;
3146191783Srmacklem	off_t off;
3147191783Srmacklem#endif
3148191783Srmacklem
3149191783Srmacklem	BUF_ASSERT_HELD(bp);
3150191783Srmacklem
3151191783Srmacklem	if (bp->b_flags & B_INVAL) {
3152191783Srmacklem		brelse(bp);
3153191783Srmacklem		return(0);
3154191783Srmacklem	}
3155191783Srmacklem
3156191783Srmacklem	bp->b_flags |= B_CACHE;
3157191783Srmacklem
3158191783Srmacklem	/*
3159191783Srmacklem	 * Undirty the bp.  We will redirty it later if the I/O fails.
3160191783Srmacklem	 */
3161191783Srmacklem
3162191783Srmacklem	s = splbio();
3163191783Srmacklem	bundirty(bp);
3164191783Srmacklem	bp->b_flags &= ~B_DONE;
3165191783Srmacklem	bp->b_ioflags &= ~BIO_ERROR;
3166191783Srmacklem	bp->b_iocmd = BIO_WRITE;
3167191783Srmacklem
3168191783Srmacklem	bufobj_wref(bp->b_bufobj);
3169191783Srmacklem	curthread->td_ru.ru_oublock++;
3170191783Srmacklem	splx(s);
3171191783Srmacklem
3172191783Srmacklem	/*
3173191783Srmacklem	 * Note: to avoid loopback deadlocks, we do not
3174191783Srmacklem	 * assign b_runningbufspace.
3175191783Srmacklem	 */
3176191783Srmacklem	vfs_busy_pages(bp, 1);
3177191783Srmacklem
3178191783Srmacklem	BUF_KERNPROC(bp);
3179191783Srmacklem	bp->b_iooffset = dbtob(bp->b_blkno);
3180191783Srmacklem	bstrategy(bp);
3181191783Srmacklem
3182191783Srmacklem	if( (oldflags & B_ASYNC) == 0) {
3183191783Srmacklem		int rtval = bufwait(bp);
3184191783Srmacklem
3185191783Srmacklem		if (oldflags & B_DELWRI) {
3186191783Srmacklem			s = splbio();
3187191783Srmacklem			reassignbuf(bp);
3188191783Srmacklem			splx(s);
3189191783Srmacklem		}
3190191783Srmacklem		brelse(bp);
3191191783Srmacklem		return (rtval);
3192191783Srmacklem	}
3193191783Srmacklem
3194191783Srmacklem	return (0);
3195191783Srmacklem}
3196191783Srmacklem
3197191783Srmacklem/*
3198191783Srmacklem * nfs special file access vnode op.
3199191783Srmacklem * Essentially just get vattr and then imitate iaccess() since the device is
3200191783Srmacklem * local to the client.
3201191783Srmacklem */
3202191783Srmacklemstatic int
3203191783Srmacklemnfsspec_access(struct vop_access_args *ap)
3204191783Srmacklem{
3205191783Srmacklem	struct vattr *vap;
3206191783Srmacklem	struct ucred *cred = ap->a_cred;
3207191783Srmacklem	struct vnode *vp = ap->a_vp;
3208191783Srmacklem	accmode_t accmode = ap->a_accmode;
3209191783Srmacklem	struct vattr vattr;
3210191783Srmacklem	int error;
3211191783Srmacklem
3212191783Srmacklem	/*
3213191783Srmacklem	 * Disallow write attempts on filesystems mounted read-only;
3214191783Srmacklem	 * unless the file is a socket, fifo, or a block or character
3215191783Srmacklem	 * device resident on the filesystem.
3216191783Srmacklem	 */
3217191783Srmacklem	if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
3218191783Srmacklem		switch (vp->v_type) {
3219191783Srmacklem		case VREG:
3220191783Srmacklem		case VDIR:
3221191783Srmacklem		case VLNK:
3222191783Srmacklem			return (EROFS);
3223191783Srmacklem		default:
3224191783Srmacklem			break;
3225191783Srmacklem		}
3226191783Srmacklem	}
3227191783Srmacklem	vap = &vattr;
3228191783Srmacklem	error = VOP_GETATTR(vp, vap, cred);
3229191783Srmacklem	if (error)
3230191783Srmacklem		goto out;
3231191783Srmacklem	error  = vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid,
3232191783Srmacklem	    accmode, cred, NULL);
3233191783Srmacklemout:
3234191783Srmacklem	return error;
3235191783Srmacklem}
3236191783Srmacklem
3237191783Srmacklem/*
3238191783Srmacklem * Read wrapper for fifos.
3239191783Srmacklem */
3240191783Srmacklemstatic int
3241191783Srmacklemnfsfifo_read(struct vop_read_args *ap)
3242191783Srmacklem{
3243191783Srmacklem	struct nfsnode *np = VTONFS(ap->a_vp);
3244191783Srmacklem	int error;
3245191783Srmacklem
3246191783Srmacklem	/*
3247191783Srmacklem	 * Set access flag.
3248191783Srmacklem	 */
3249191783Srmacklem	mtx_lock(&np->n_mtx);
3250191783Srmacklem	np->n_flag |= NACC;
3251247502Sjhb	vfs_timestamp(&np->n_atim);
3252191783Srmacklem	mtx_unlock(&np->n_mtx);
3253191783Srmacklem	error = fifo_specops.vop_read(ap);
3254191783Srmacklem	return error;
3255191783Srmacklem}
3256191783Srmacklem
3257191783Srmacklem/*
3258191783Srmacklem * Write wrapper for fifos.
3259191783Srmacklem */
3260191783Srmacklemstatic int
3261191783Srmacklemnfsfifo_write(struct vop_write_args *ap)
3262191783Srmacklem{
3263191783Srmacklem	struct nfsnode *np = VTONFS(ap->a_vp);
3264191783Srmacklem
3265191783Srmacklem	/*
3266191783Srmacklem	 * Set update flag.
3267191783Srmacklem	 */
3268191783Srmacklem	mtx_lock(&np->n_mtx);
3269191783Srmacklem	np->n_flag |= NUPD;
3270247502Sjhb	vfs_timestamp(&np->n_mtim);
3271191783Srmacklem	mtx_unlock(&np->n_mtx);
3272191783Srmacklem	return(fifo_specops.vop_write(ap));
3273191783Srmacklem}
3274191783Srmacklem
3275191783Srmacklem/*
3276191783Srmacklem * Close wrapper for fifos.
3277191783Srmacklem *
3278191783Srmacklem * Update the times on the nfsnode then do fifo close.
3279191783Srmacklem */
3280191783Srmacklemstatic int
3281191783Srmacklemnfsfifo_close(struct vop_close_args *ap)
3282191783Srmacklem{
3283191783Srmacklem	struct vnode *vp = ap->a_vp;
3284191783Srmacklem	struct nfsnode *np = VTONFS(vp);
3285191783Srmacklem	struct vattr vattr;
3286191783Srmacklem	struct timespec ts;
3287191783Srmacklem
3288191783Srmacklem	mtx_lock(&np->n_mtx);
3289191783Srmacklem	if (np->n_flag & (NACC | NUPD)) {
3290247502Sjhb		vfs_timestamp(&ts);
3291191783Srmacklem		if (np->n_flag & NACC)
3292191783Srmacklem			np->n_atim = ts;
3293191783Srmacklem		if (np->n_flag & NUPD)
3294191783Srmacklem			np->n_mtim = ts;
3295191783Srmacklem		np->n_flag |= NCHG;
3296191783Srmacklem		if (vrefcnt(vp) == 1 &&
3297191783Srmacklem		    (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
3298191783Srmacklem			VATTR_NULL(&vattr);
3299191783Srmacklem			if (np->n_flag & NACC)
3300191783Srmacklem				vattr.va_atime = np->n_atim;
3301191783Srmacklem			if (np->n_flag & NUPD)
3302191783Srmacklem				vattr.va_mtime = np->n_mtim;
3303191783Srmacklem			mtx_unlock(&np->n_mtx);
3304191783Srmacklem			(void)VOP_SETATTR(vp, &vattr, ap->a_cred);
3305191783Srmacklem			goto out;
3306191783Srmacklem		}
3307191783Srmacklem	}
3308191783Srmacklem	mtx_unlock(&np->n_mtx);
3309191783Srmacklemout:
3310191783Srmacklem	return (fifo_specops.vop_close(ap));
3311191783Srmacklem}
3312191783Srmacklem
3313191783Srmacklem/*
3314191783Srmacklem * Just call ncl_writebp() with the force argument set to 1.
3315191783Srmacklem *
3316191783Srmacklem * NOTE: B_DONE may or may not be set in a_bp on call.
3317191783Srmacklem */
3318191783Srmacklemstatic int
3319191783Srmacklemnfs_bwrite(struct buf *bp)
3320191783Srmacklem{
3321191783Srmacklem
3322191783Srmacklem	return (ncl_writebp(bp, 1, curthread));
3323191783Srmacklem}
3324191783Srmacklem
3325191783Srmacklemstruct buf_ops buf_ops_newnfs = {
3326191783Srmacklem	.bop_name	=	"buf_ops_nfs",
3327191783Srmacklem	.bop_write	=	nfs_bwrite,
3328191783Srmacklem	.bop_strategy	=	bufstrategy,
3329191783Srmacklem	.bop_sync	=	bufsync,
3330191783Srmacklem	.bop_bdflush	=	bufbdflush,
3331191783Srmacklem};
3332191783Srmacklem
3333191783Srmacklem/*
3334191783Srmacklem * Cloned from vop_stdlock(), and then the ugly hack added.
3335191783Srmacklem */
3336191783Srmacklemstatic int
3337191783Srmacklemnfs_lock1(struct vop_lock1_args *ap)
3338191783Srmacklem{
3339191783Srmacklem	struct vnode *vp = ap->a_vp;
3340191783Srmacklem	int error = 0;
3341191783Srmacklem
3342191783Srmacklem	/*
3343191783Srmacklem	 * Since vfs_hash_get() calls vget() and it will no longer work
3344191783Srmacklem	 * for FreeBSD8 with flags == 0, I can only think of this horrible
3345191783Srmacklem	 * hack to work around it. I call vfs_hash_get() with LK_EXCLOTHER
3346191783Srmacklem	 * and then handle it here. All I want for this case is a v_usecount
3347191783Srmacklem	 * on the vnode to use for recovery, while another thread might
3348191783Srmacklem	 * hold a lock on the vnode. I have the other threads blocked, so
3349191783Srmacklem	 * there isn't any race problem.
3350191783Srmacklem	 */
3351191783Srmacklem	if ((ap->a_flags & LK_TYPE_MASK) == LK_EXCLOTHER) {
3352191783Srmacklem		if ((ap->a_flags & LK_INTERLOCK) == 0)
3353191783Srmacklem			panic("ncllock1");
3354191783Srmacklem		if ((vp->v_iflag & VI_DOOMED))
3355191783Srmacklem			error = ENOENT;
3356191783Srmacklem		VI_UNLOCK(vp);
3357191783Srmacklem		return (error);
3358191783Srmacklem	}
3359191783Srmacklem	return (_lockmgr_args(vp->v_vnlock, ap->a_flags, VI_MTX(vp),
3360191783Srmacklem	    LK_WMESG_DEFAULT, LK_PRIO_DEFAULT, LK_TIMO_DEFAULT, ap->a_file,
3361191783Srmacklem	    ap->a_line));
3362191783Srmacklem}
3363191783Srmacklem
3364191783Srmacklemstatic int
3365191783Srmacklemnfs_getacl(struct vop_getacl_args *ap)
3366191783Srmacklem{
3367191783Srmacklem	int error;
3368191783Srmacklem
3369191783Srmacklem	if (ap->a_type != ACL_TYPE_NFS4)
3370191783Srmacklem		return (EOPNOTSUPP);
3371191783Srmacklem	error = nfsrpc_getacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp,
3372191783Srmacklem	    NULL);
3373191783Srmacklem	if (error > NFSERR_STALE) {
3374191783Srmacklem		(void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0);
3375191783Srmacklem		error = EPERM;
3376191783Srmacklem	}
3377191783Srmacklem	return (error);
3378191783Srmacklem}
3379191783Srmacklem
3380191783Srmacklemstatic int
3381191783Srmacklemnfs_setacl(struct vop_setacl_args *ap)
3382191783Srmacklem{
3383191783Srmacklem	int error;
3384191783Srmacklem
3385191783Srmacklem	if (ap->a_type != ACL_TYPE_NFS4)
3386191783Srmacklem		return (EOPNOTSUPP);
3387191783Srmacklem	error = nfsrpc_setacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp,
3388191783Srmacklem	    NULL);
3389191783Srmacklem	if (error > NFSERR_STALE) {
3390191783Srmacklem		(void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0);
3391191783Srmacklem		error = EPERM;
3392191783Srmacklem	}
3393191783Srmacklem	return (error);
3394191783Srmacklem}
3395220611Srmacklem
3396220611Srmacklem/*
3397220611Srmacklem * Return POSIX pathconf information applicable to nfs filesystems.
3398220611Srmacklem */
3399220611Srmacklemstatic int
3400220611Srmacklemnfs_pathconf(struct vop_pathconf_args *ap)
3401220611Srmacklem{
3402220611Srmacklem	struct nfsv3_pathconf pc;
3403220611Srmacklem	struct nfsvattr nfsva;
3404220611Srmacklem	struct vnode *vp = ap->a_vp;
3405220611Srmacklem	struct thread *td = curthread;
3406220611Srmacklem	int attrflag, error;
3407220611Srmacklem
3408265721Srmacklem	if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX ||
3409222540Srmacklem	    ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED ||
3410265721Srmacklem	    ap->a_name == _PC_NO_TRUNC)) ||
3411265721Srmacklem	    (NFS_ISV4(vp) && ap->a_name == _PC_ACL_NFS4)) {
3412222540Srmacklem		/*
3413222540Srmacklem		 * Since only the above 4 a_names are returned by the NFSv3
3414222540Srmacklem		 * Pathconf RPC, there is no point in doing it for others.
3415265721Srmacklem		 * For NFSv4, the Pathconf RPC (actually a Getattr Op.) can
3416265721Srmacklem		 * be used for _PC_NFS4_ACL as well.
3417222540Srmacklem		 */
3418220611Srmacklem		error = nfsrpc_pathconf(vp, &pc, td->td_ucred, td, &nfsva,
3419220611Srmacklem		    &attrflag, NULL);
3420220611Srmacklem		if (attrflag != 0)
3421220611Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
3422220611Srmacklem			    1);
3423220611Srmacklem		if (error != 0)
3424220611Srmacklem			return (error);
3425220611Srmacklem	} else {
3426222540Srmacklem		/*
3427222540Srmacklem		 * For NFSv2 (or NFSv3 when not one of the above 4 a_names),
3428222540Srmacklem		 * just fake them.
3429222540Srmacklem		 */
3430220611Srmacklem		pc.pc_linkmax = LINK_MAX;
3431220611Srmacklem		pc.pc_namemax = NFS_MAXNAMLEN;
3432220611Srmacklem		pc.pc_notrunc = 1;
3433220611Srmacklem		pc.pc_chownrestricted = 1;
3434220611Srmacklem		pc.pc_caseinsensitive = 0;
3435220611Srmacklem		pc.pc_casepreserving = 1;
3436220611Srmacklem		error = 0;
3437220611Srmacklem	}
3438220611Srmacklem	switch (ap->a_name) {
3439220611Srmacklem	case _PC_LINK_MAX:
3440220611Srmacklem		*ap->a_retval = pc.pc_linkmax;
3441220611Srmacklem		break;
3442220611Srmacklem	case _PC_NAME_MAX:
3443220611Srmacklem		*ap->a_retval = pc.pc_namemax;
3444220611Srmacklem		break;
3445220611Srmacklem	case _PC_PATH_MAX:
3446220611Srmacklem		*ap->a_retval = PATH_MAX;
3447220611Srmacklem		break;
3448220611Srmacklem	case _PC_PIPE_BUF:
3449220611Srmacklem		*ap->a_retval = PIPE_BUF;
3450220611Srmacklem		break;
3451220611Srmacklem	case _PC_CHOWN_RESTRICTED:
3452220611Srmacklem		*ap->a_retval = pc.pc_chownrestricted;
3453220611Srmacklem		break;
3454220611Srmacklem	case _PC_NO_TRUNC:
3455220611Srmacklem		*ap->a_retval = pc.pc_notrunc;
3456220611Srmacklem		break;
3457220611Srmacklem	case _PC_ACL_EXTENDED:
3458220611Srmacklem		*ap->a_retval = 0;
3459220611Srmacklem		break;
3460220611Srmacklem	case _PC_ACL_NFS4:
3461220611Srmacklem		if (NFS_ISV4(vp) && nfsrv_useacl != 0 && attrflag != 0 &&
3462220611Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL))
3463220611Srmacklem			*ap->a_retval = 1;
3464220611Srmacklem		else
3465220611Srmacklem			*ap->a_retval = 0;
3466220611Srmacklem		break;
3467220611Srmacklem	case _PC_ACL_PATH_MAX:
3468220611Srmacklem		if (NFS_ISV4(vp))
3469220611Srmacklem			*ap->a_retval = ACL_MAX_ENTRIES;
3470220611Srmacklem		else
3471220611Srmacklem			*ap->a_retval = 3;
3472220611Srmacklem		break;
3473220611Srmacklem	case _PC_MAC_PRESENT:
3474220611Srmacklem		*ap->a_retval = 0;
3475220611Srmacklem		break;
3476220611Srmacklem	case _PC_ASYNC_IO:
3477220611Srmacklem		/* _PC_ASYNC_IO should have been handled by upper layers. */
3478220611Srmacklem		KASSERT(0, ("_PC_ASYNC_IO should not get here"));
3479220611Srmacklem		error = EINVAL;
3480220611Srmacklem		break;
3481220611Srmacklem	case _PC_PRIO_IO:
3482220611Srmacklem		*ap->a_retval = 0;
3483220611Srmacklem		break;
3484220611Srmacklem	case _PC_SYNC_IO:
3485220611Srmacklem		*ap->a_retval = 0;
3486220611Srmacklem		break;
3487220611Srmacklem	case _PC_ALLOC_SIZE_MIN:
3488220611Srmacklem		*ap->a_retval = vp->v_mount->mnt_stat.f_bsize;
3489220611Srmacklem		break;
3490220611Srmacklem	case _PC_FILESIZEBITS:
3491220611Srmacklem		if (NFS_ISV34(vp))
3492220611Srmacklem			*ap->a_retval = 64;
3493220611Srmacklem		else
3494220611Srmacklem			*ap->a_retval = 32;
3495220611Srmacklem		break;
3496220611Srmacklem	case _PC_REC_INCR_XFER_SIZE:
3497220611Srmacklem		*ap->a_retval = vp->v_mount->mnt_stat.f_iosize;
3498220611Srmacklem		break;
3499220611Srmacklem	case _PC_REC_MAX_XFER_SIZE:
3500220611Srmacklem		*ap->a_retval = -1; /* means ``unlimited'' */
3501220611Srmacklem		break;
3502220611Srmacklem	case _PC_REC_MIN_XFER_SIZE:
3503220611Srmacklem		*ap->a_retval = vp->v_mount->mnt_stat.f_iosize;
3504220611Srmacklem		break;
3505220611Srmacklem	case _PC_REC_XFER_ALIGN:
3506220611Srmacklem		*ap->a_retval = PAGE_SIZE;
3507220611Srmacklem		break;
3508220611Srmacklem	case _PC_SYMLINK_MAX:
3509220611Srmacklem		*ap->a_retval = NFS_MAXPATHLEN;
3510220611Srmacklem		break;
3511220611Srmacklem
3512220611Srmacklem	default:
3513220611Srmacklem		error = EINVAL;
3514220611Srmacklem		break;
3515220611Srmacklem	}
3516220611Srmacklem	return (error);
3517220611Srmacklem}
3518220611Srmacklem
3519