1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * SPDX-License-Identifier: BSD-3-Clause |
---|
5 | * |
---|
6 | * Copyright (c) 1989, 1993 |
---|
7 | * The Regents of the University of California. All rights reserved. |
---|
8 | * |
---|
9 | * This code is derived from software contributed to Berkeley by |
---|
10 | * Rick Macklem at The University of Guelph. |
---|
11 | * |
---|
12 | * Redistribution and use in source and binary forms, with or without |
---|
13 | * modification, are permitted provided that the following conditions |
---|
14 | * are met: |
---|
15 | * 1. Redistributions of source code must retain the above copyright |
---|
16 | * notice, this list of conditions and the following disclaimer. |
---|
17 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
18 | * notice, this list of conditions and the following disclaimer in the |
---|
19 | * documentation and/or other materials provided with the distribution. |
---|
20 | * 3. Neither the name of the University nor the names of its contributors |
---|
21 | * may be used to endorse or promote products derived from this software |
---|
22 | * without specific prior written permission. |
---|
23 | * |
---|
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
---|
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
---|
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
34 | * SUCH DAMAGE. |
---|
35 | * |
---|
36 | * from nfs_vnops.c 8.16 (Berkeley) 5/27/95 |
---|
37 | */ |
---|
38 | |
---|
39 | #include <sys/cdefs.h> |
---|
40 | __FBSDID("$FreeBSD$"); |
---|
41 | |
---|
42 | /* |
---|
43 | * vnode op calls for Sun NFS version 2, 3 and 4 |
---|
44 | */ |
---|
45 | |
---|
46 | #include <rtems/bsd/local/opt_inet.h> |
---|
47 | |
---|
48 | #include <sys/param.h> |
---|
49 | #include <sys/kernel.h> |
---|
50 | #include <sys/systm.h> |
---|
51 | #include <sys/resourcevar.h> |
---|
52 | #include <sys/proc.h> |
---|
53 | #include <sys/mount.h> |
---|
54 | #include <sys/bio.h> |
---|
55 | #include <sys/buf.h> |
---|
56 | #include <sys/jail.h> |
---|
57 | #include <sys/malloc.h> |
---|
58 | #include <sys/mbuf.h> |
---|
59 | #include <sys/namei.h> |
---|
60 | #include <sys/socket.h> |
---|
61 | #include <sys/vnode.h> |
---|
62 | #include <sys/dirent.h> |
---|
63 | #include <sys/fcntl.h> |
---|
64 | #ifndef __rtems__ |
---|
65 | #include <sys/lockf.h> |
---|
66 | #endif /* __rtems__ */ |
---|
67 | #include <sys/stat.h> |
---|
68 | #include <sys/sysctl.h> |
---|
69 | #include <sys/signalvar.h> |
---|
70 | |
---|
71 | #include <vm/vm.h> |
---|
72 | #include <vm/vm_extern.h> |
---|
73 | #include <vm/vm_object.h> |
---|
74 | |
---|
75 | #include <fs/nfs/nfsport.h> |
---|
76 | #include <fs/nfsclient/nfsnode.h> |
---|
77 | #include <fs/nfsclient/nfsmount.h> |
---|
78 | #include <fs/nfsclient/nfs.h> |
---|
79 | #include <fs/nfsclient/nfs_kdtrace.h> |
---|
80 | |
---|
81 | #include <net/if.h> |
---|
82 | #include <netinet/in.h> |
---|
83 | #include <netinet/in_var.h> |
---|
84 | |
---|
85 | #include <nfs/nfs_lock.h> |
---|
86 | |
---|
87 | #ifdef KDTRACE_HOOKS |
---|
88 | #include <sys/dtrace_bsd.h> |
---|
89 | |
---|
90 | dtrace_nfsclient_accesscache_flush_probe_func_t |
---|
91 | dtrace_nfscl_accesscache_flush_done_probe; |
---|
92 | uint32_t nfscl_accesscache_flush_done_id; |
---|
93 | |
---|
94 | dtrace_nfsclient_accesscache_get_probe_func_t |
---|
95 | dtrace_nfscl_accesscache_get_hit_probe, |
---|
96 | dtrace_nfscl_accesscache_get_miss_probe; |
---|
97 | uint32_t nfscl_accesscache_get_hit_id; |
---|
98 | uint32_t nfscl_accesscache_get_miss_id; |
---|
99 | |
---|
100 | dtrace_nfsclient_accesscache_load_probe_func_t |
---|
101 | dtrace_nfscl_accesscache_load_done_probe; |
---|
102 | uint32_t nfscl_accesscache_load_done_id; |
---|
103 | #endif /* !KDTRACE_HOOKS */ |
---|
104 | |
---|
105 | /* Defs */ |
---|
106 | #define TRUE 1 |
---|
107 | #define FALSE 0 |
---|
108 | |
---|
109 | extern struct nfsstatsv1 nfsstatsv1; |
---|
110 | extern int nfsrv_useacl; |
---|
111 | extern int nfscl_debuglevel; |
---|
112 | MALLOC_DECLARE(M_NEWNFSREQ); |
---|
113 | |
---|
114 | static vop_read_t nfsfifo_read; |
---|
115 | static vop_write_t nfsfifo_write; |
---|
116 | static vop_close_t nfsfifo_close; |
---|
117 | static int nfs_setattrrpc(struct vnode *, struct vattr *, struct ucred *, |
---|
118 | struct thread *); |
---|
119 | static vop_lookup_t nfs_lookup; |
---|
120 | static vop_create_t nfs_create; |
---|
121 | static vop_mknod_t nfs_mknod; |
---|
122 | static vop_open_t nfs_open; |
---|
123 | static vop_pathconf_t nfs_pathconf; |
---|
124 | static vop_close_t nfs_close; |
---|
125 | static vop_access_t nfs_access; |
---|
126 | static vop_getattr_t nfs_getattr; |
---|
127 | static vop_setattr_t nfs_setattr; |
---|
128 | static vop_read_t nfs_read; |
---|
129 | static vop_fsync_t nfs_fsync; |
---|
130 | static vop_remove_t nfs_remove; |
---|
131 | static vop_link_t nfs_link; |
---|
132 | static vop_rename_t nfs_rename; |
---|
133 | static vop_mkdir_t nfs_mkdir; |
---|
134 | static vop_rmdir_t nfs_rmdir; |
---|
135 | static vop_symlink_t nfs_symlink; |
---|
136 | static vop_readdir_t nfs_readdir; |
---|
137 | static vop_strategy_t nfs_strategy; |
---|
138 | static int nfs_lookitup(struct vnode *, char *, int, |
---|
139 | struct ucred *, struct thread *, struct nfsnode **); |
---|
140 | static int nfs_sillyrename(struct vnode *, struct vnode *, |
---|
141 | struct componentname *); |
---|
142 | static vop_access_t nfsspec_access; |
---|
143 | static vop_readlink_t nfs_readlink; |
---|
144 | static vop_print_t nfs_print; |
---|
145 | static vop_advlock_t nfs_advlock; |
---|
146 | static vop_advlockasync_t nfs_advlockasync; |
---|
147 | static vop_getacl_t nfs_getacl; |
---|
148 | static vop_setacl_t nfs_setacl; |
---|
149 | static vop_lock1_t nfs_lock; |
---|
150 | |
---|
151 | /* |
---|
152 | * Global vfs data structures for nfs |
---|
153 | */ |
---|
154 | struct vop_vector newnfs_vnodeops = { |
---|
155 | .vop_default = &default_vnodeops, |
---|
156 | .vop_access = nfs_access, |
---|
157 | .vop_advlock = nfs_advlock, |
---|
158 | .vop_advlockasync = nfs_advlockasync, |
---|
159 | .vop_close = nfs_close, |
---|
160 | .vop_create = nfs_create, |
---|
161 | .vop_fsync = nfs_fsync, |
---|
162 | .vop_getattr = nfs_getattr, |
---|
163 | .vop_getpages = ncl_getpages, |
---|
164 | .vop_putpages = ncl_putpages, |
---|
165 | .vop_inactive = ncl_inactive, |
---|
166 | .vop_link = nfs_link, |
---|
167 | .vop_lock1 = nfs_lock, |
---|
168 | .vop_lookup = nfs_lookup, |
---|
169 | .vop_mkdir = nfs_mkdir, |
---|
170 | .vop_mknod = nfs_mknod, |
---|
171 | .vop_open = nfs_open, |
---|
172 | .vop_pathconf = nfs_pathconf, |
---|
173 | .vop_print = nfs_print, |
---|
174 | .vop_read = nfs_read, |
---|
175 | .vop_readdir = nfs_readdir, |
---|
176 | .vop_readlink = nfs_readlink, |
---|
177 | .vop_reclaim = ncl_reclaim, |
---|
178 | .vop_remove = nfs_remove, |
---|
179 | .vop_rename = nfs_rename, |
---|
180 | .vop_rmdir = nfs_rmdir, |
---|
181 | .vop_setattr = nfs_setattr, |
---|
182 | .vop_strategy = nfs_strategy, |
---|
183 | .vop_symlink = nfs_symlink, |
---|
184 | .vop_write = ncl_write, |
---|
185 | .vop_getacl = nfs_getacl, |
---|
186 | .vop_setacl = nfs_setacl, |
---|
187 | }; |
---|
188 | |
---|
189 | struct vop_vector newnfs_fifoops = { |
---|
190 | .vop_default = &fifo_specops, |
---|
191 | .vop_access = nfsspec_access, |
---|
192 | .vop_close = nfsfifo_close, |
---|
193 | .vop_fsync = nfs_fsync, |
---|
194 | .vop_getattr = nfs_getattr, |
---|
195 | .vop_inactive = ncl_inactive, |
---|
196 | .vop_pathconf = nfs_pathconf, |
---|
197 | .vop_print = nfs_print, |
---|
198 | .vop_read = nfsfifo_read, |
---|
199 | .vop_reclaim = ncl_reclaim, |
---|
200 | .vop_setattr = nfs_setattr, |
---|
201 | .vop_write = nfsfifo_write, |
---|
202 | }; |
---|
203 | |
---|
204 | static int nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, |
---|
205 | struct componentname *cnp, struct vattr *vap); |
---|
206 | static int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name, |
---|
207 | int namelen, struct ucred *cred, struct thread *td); |
---|
208 | static int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, |
---|
209 | char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp, |
---|
210 | char *tnameptr, int tnamelen, struct ucred *cred, struct thread *td); |
---|
211 | static int nfs_renameit(struct vnode *sdvp, struct vnode *svp, |
---|
212 | struct componentname *scnp, struct sillyrename *sp); |
---|
213 | |
---|
214 | /* |
---|
215 | * Global variables |
---|
216 | */ |
---|
217 | SYSCTL_DECL(_vfs_nfs); |
---|
218 | |
---|
219 | static int nfsaccess_cache_timeout = NFS_MAXATTRTIMO; |
---|
220 | SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW, |
---|
221 | &nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout"); |
---|
222 | |
---|
223 | static int nfs_prime_access_cache = 0; |
---|
224 | SYSCTL_INT(_vfs_nfs, OID_AUTO, prime_access_cache, CTLFLAG_RW, |
---|
225 | &nfs_prime_access_cache, 0, |
---|
226 | "Prime NFS ACCESS cache when fetching attributes"); |
---|
227 | |
---|
228 | static int newnfs_commit_on_close = 0; |
---|
229 | SYSCTL_INT(_vfs_nfs, OID_AUTO, commit_on_close, CTLFLAG_RW, |
---|
230 | &newnfs_commit_on_close, 0, "write+commit on close, else only write"); |
---|
231 | |
---|
232 | static int nfs_clean_pages_on_close = 1; |
---|
233 | SYSCTL_INT(_vfs_nfs, OID_AUTO, clean_pages_on_close, CTLFLAG_RW, |
---|
234 | &nfs_clean_pages_on_close, 0, "NFS clean dirty pages on close"); |
---|
235 | |
---|
236 | int newnfs_directio_enable = 0; |
---|
237 | SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_enable, CTLFLAG_RW, |
---|
238 | &newnfs_directio_enable, 0, "Enable NFS directio"); |
---|
239 | |
---|
240 | int nfs_keep_dirty_on_error; |
---|
241 | SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_keep_dirty_on_error, CTLFLAG_RW, |
---|
242 | &nfs_keep_dirty_on_error, 0, "Retry pageout if error returned"); |
---|
243 | |
---|
244 | /* |
---|
245 | * This sysctl allows other processes to mmap a file that has been opened |
---|
246 | * O_DIRECT by a process. In general, having processes mmap the file while |
---|
247 | * Direct IO is in progress can lead to Data Inconsistencies. But, we allow |
---|
248 | * this by default to prevent DoS attacks - to prevent a malicious user from |
---|
249 | * opening up files O_DIRECT preventing other users from mmap'ing these |
---|
250 | * files. "Protected" environments where stricter consistency guarantees are |
---|
251 | * required can disable this knob. The process that opened the file O_DIRECT |
---|
252 | * cannot mmap() the file, because mmap'ed IO on an O_DIRECT open() is not |
---|
253 | * meaningful. |
---|
254 | */ |
---|
255 | int newnfs_directio_allow_mmap = 1; |
---|
256 | SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_allow_mmap, CTLFLAG_RW, |
---|
257 | &newnfs_directio_allow_mmap, 0, "Enable mmaped IO on file with O_DIRECT opens"); |
---|
258 | |
---|
259 | #define NFSACCESS_ALL (NFSACCESS_READ | NFSACCESS_MODIFY \ |
---|
260 | | NFSACCESS_EXTEND | NFSACCESS_EXECUTE \ |
---|
261 | | NFSACCESS_DELETE | NFSACCESS_LOOKUP) |
---|
262 | |
---|
263 | /* |
---|
264 | * SMP Locking Note : |
---|
265 | * The list of locks after the description of the lock is the ordering |
---|
266 | * of other locks acquired with the lock held. |
---|
267 | * np->n_mtx : Protects the fields in the nfsnode. |
---|
268 | VM Object Lock |
---|
269 | VI_MTX (acquired indirectly) |
---|
270 | * nmp->nm_mtx : Protects the fields in the nfsmount. |
---|
271 | rep->r_mtx |
---|
272 | * ncl_iod_mutex : Global lock, protects shared nfsiod state. |
---|
273 | * nfs_reqq_mtx : Global lock, protects the nfs_reqq list. |
---|
274 | nmp->nm_mtx |
---|
275 | rep->r_mtx |
---|
276 | * rep->r_mtx : Protects the fields in an nfsreq. |
---|
277 | */ |
---|
278 | |
---|
279 | static int |
---|
280 | nfs_lock(struct vop_lock1_args *ap) |
---|
281 | { |
---|
282 | struct vnode *vp; |
---|
283 | struct nfsnode *np; |
---|
284 | u_quad_t nsize; |
---|
285 | int error, lktype; |
---|
286 | bool onfault; |
---|
287 | |
---|
288 | vp = ap->a_vp; |
---|
289 | lktype = ap->a_flags & LK_TYPE_MASK; |
---|
290 | error = VOP_LOCK1_APV(&default_vnodeops, ap); |
---|
291 | if (error != 0 || vp->v_op != &newnfs_vnodeops) |
---|
292 | return (error); |
---|
293 | np = VTONFS(vp); |
---|
294 | if (np == NULL) |
---|
295 | return (0); |
---|
296 | NFSLOCKNODE(np); |
---|
297 | if ((np->n_flag & NVNSETSZSKIP) == 0 || (lktype != LK_SHARED && |
---|
298 | lktype != LK_EXCLUSIVE && lktype != LK_UPGRADE && |
---|
299 | lktype != LK_TRYUPGRADE)) { |
---|
300 | NFSUNLOCKNODE(np); |
---|
301 | return (0); |
---|
302 | } |
---|
303 | onfault = (ap->a_flags & LK_EATTR_MASK) == LK_NOWAIT && |
---|
304 | (ap->a_flags & LK_INIT_MASK) == LK_CANRECURSE && |
---|
305 | (lktype == LK_SHARED || lktype == LK_EXCLUSIVE); |
---|
306 | if (onfault && vp->v_vnlock->lk_recurse == 0) { |
---|
307 | /* |
---|
308 | * Force retry in vm_fault(), to make the lock request |
---|
309 | * sleepable, which allows us to piggy-back the |
---|
310 | * sleepable call to vnode_pager_setsize(). |
---|
311 | */ |
---|
312 | NFSUNLOCKNODE(np); |
---|
313 | VOP_UNLOCK(vp, 0); |
---|
314 | return (EBUSY); |
---|
315 | } |
---|
316 | if ((ap->a_flags & LK_NOWAIT) != 0 || |
---|
317 | (lktype == LK_SHARED && vp->v_vnlock->lk_recurse > 0)) { |
---|
318 | NFSUNLOCKNODE(np); |
---|
319 | return (0); |
---|
320 | } |
---|
321 | if (lktype == LK_SHARED) { |
---|
322 | NFSUNLOCKNODE(np); |
---|
323 | VOP_UNLOCK(vp, 0); |
---|
324 | ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK); |
---|
325 | ap->a_flags |= LK_EXCLUSIVE; |
---|
326 | error = VOP_LOCK1_APV(&default_vnodeops, ap); |
---|
327 | if (error != 0 || vp->v_op != &newnfs_vnodeops) |
---|
328 | return (error); |
---|
329 | if (vp->v_data == NULL) |
---|
330 | goto downgrade; |
---|
331 | MPASS(vp->v_data == np); |
---|
332 | NFSLOCKNODE(np); |
---|
333 | if ((np->n_flag & NVNSETSZSKIP) == 0) { |
---|
334 | NFSUNLOCKNODE(np); |
---|
335 | goto downgrade; |
---|
336 | } |
---|
337 | } |
---|
338 | np->n_flag &= ~NVNSETSZSKIP; |
---|
339 | nsize = np->n_size; |
---|
340 | NFSUNLOCKNODE(np); |
---|
341 | #ifndef __rtems__ |
---|
342 | vnode_pager_setsize(vp, nsize); |
---|
343 | #endif /* __rtems__ */ |
---|
344 | downgrade: |
---|
345 | if (lktype == LK_SHARED) { |
---|
346 | ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK); |
---|
347 | ap->a_flags |= LK_DOWNGRADE; |
---|
348 | (void)VOP_LOCK1_APV(&default_vnodeops, ap); |
---|
349 | } |
---|
350 | return (0); |
---|
351 | } |
---|
352 | |
---|
353 | static int |
---|
354 | nfs34_access_otw(struct vnode *vp, int wmode, struct thread *td, |
---|
355 | struct ucred *cred, u_int32_t *retmode) |
---|
356 | { |
---|
357 | int error = 0, attrflag, i, lrupos; |
---|
358 | u_int32_t rmode; |
---|
359 | struct nfsnode *np = VTONFS(vp); |
---|
360 | struct nfsvattr nfsva; |
---|
361 | |
---|
362 | error = nfsrpc_accessrpc(vp, wmode, cred, td, &nfsva, &attrflag, |
---|
363 | &rmode, NULL); |
---|
364 | if (attrflag) |
---|
365 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
366 | if (!error) { |
---|
367 | lrupos = 0; |
---|
368 | NFSLOCKNODE(np); |
---|
369 | for (i = 0; i < NFS_ACCESSCACHESIZE; i++) { |
---|
370 | if (np->n_accesscache[i].uid == cred->cr_uid) { |
---|
371 | np->n_accesscache[i].mode = rmode; |
---|
372 | np->n_accesscache[i].stamp = time_second; |
---|
373 | break; |
---|
374 | } |
---|
375 | if (i > 0 && np->n_accesscache[i].stamp < |
---|
376 | np->n_accesscache[lrupos].stamp) |
---|
377 | lrupos = i; |
---|
378 | } |
---|
379 | if (i == NFS_ACCESSCACHESIZE) { |
---|
380 | np->n_accesscache[lrupos].uid = cred->cr_uid; |
---|
381 | np->n_accesscache[lrupos].mode = rmode; |
---|
382 | np->n_accesscache[lrupos].stamp = time_second; |
---|
383 | } |
---|
384 | NFSUNLOCKNODE(np); |
---|
385 | if (retmode != NULL) |
---|
386 | *retmode = rmode; |
---|
387 | KDTRACE_NFS_ACCESSCACHE_LOAD_DONE(vp, cred->cr_uid, rmode, 0); |
---|
388 | } else if (NFS_ISV4(vp)) { |
---|
389 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
390 | } |
---|
391 | #ifdef KDTRACE_HOOKS |
---|
392 | if (error != 0) |
---|
393 | KDTRACE_NFS_ACCESSCACHE_LOAD_DONE(vp, cred->cr_uid, 0, |
---|
394 | error); |
---|
395 | #endif |
---|
396 | return (error); |
---|
397 | } |
---|
398 | |
---|
399 | /* |
---|
400 | * nfs access vnode op. |
---|
401 | * For nfs version 2, just return ok. File accesses may fail later. |
---|
402 | * For nfs version 3, use the access rpc to check accessibility. If file modes |
---|
403 | * are changed on the server, accesses might still fail later. |
---|
404 | */ |
---|
405 | static int |
---|
406 | nfs_access(struct vop_access_args *ap) |
---|
407 | { |
---|
408 | struct vnode *vp = ap->a_vp; |
---|
409 | int error = 0, i, gotahit; |
---|
410 | u_int32_t mode, wmode, rmode; |
---|
411 | int v34 = NFS_ISV34(vp); |
---|
412 | struct nfsnode *np = VTONFS(vp); |
---|
413 | |
---|
414 | /* |
---|
415 | * Disallow write attempts on filesystems mounted read-only; |
---|
416 | * unless the file is a socket, fifo, or a block or character |
---|
417 | * device resident on the filesystem. |
---|
418 | */ |
---|
419 | if ((ap->a_accmode & (VWRITE | VAPPEND | VWRITE_NAMED_ATTRS | |
---|
420 | VDELETE_CHILD | VWRITE_ATTRIBUTES | VDELETE | VWRITE_ACL | |
---|
421 | VWRITE_OWNER)) != 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) { |
---|
422 | switch (vp->v_type) { |
---|
423 | case VREG: |
---|
424 | case VDIR: |
---|
425 | case VLNK: |
---|
426 | return (EROFS); |
---|
427 | default: |
---|
428 | break; |
---|
429 | } |
---|
430 | } |
---|
431 | /* |
---|
432 | * For nfs v3 or v4, check to see if we have done this recently, and if |
---|
433 | * so return our cached result instead of making an ACCESS call. |
---|
434 | * If not, do an access rpc, otherwise you are stuck emulating |
---|
435 | * ufs_access() locally using the vattr. This may not be correct, |
---|
436 | * since the server may apply other access criteria such as |
---|
437 | * client uid-->server uid mapping that we do not know about. |
---|
438 | */ |
---|
439 | if (v34) { |
---|
440 | if (ap->a_accmode & VREAD) |
---|
441 | mode = NFSACCESS_READ; |
---|
442 | else |
---|
443 | mode = 0; |
---|
444 | if (vp->v_type != VDIR) { |
---|
445 | if (ap->a_accmode & VWRITE) |
---|
446 | mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND); |
---|
447 | if (ap->a_accmode & VAPPEND) |
---|
448 | mode |= NFSACCESS_EXTEND; |
---|
449 | if (ap->a_accmode & VEXEC) |
---|
450 | mode |= NFSACCESS_EXECUTE; |
---|
451 | if (ap->a_accmode & VDELETE) |
---|
452 | mode |= NFSACCESS_DELETE; |
---|
453 | } else { |
---|
454 | if (ap->a_accmode & VWRITE) |
---|
455 | mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND); |
---|
456 | if (ap->a_accmode & VAPPEND) |
---|
457 | mode |= NFSACCESS_EXTEND; |
---|
458 | if (ap->a_accmode & VEXEC) |
---|
459 | mode |= NFSACCESS_LOOKUP; |
---|
460 | if (ap->a_accmode & VDELETE) |
---|
461 | mode |= NFSACCESS_DELETE; |
---|
462 | if (ap->a_accmode & VDELETE_CHILD) |
---|
463 | mode |= NFSACCESS_MODIFY; |
---|
464 | } |
---|
465 | /* XXX safety belt, only make blanket request if caching */ |
---|
466 | if (nfsaccess_cache_timeout > 0) { |
---|
467 | wmode = NFSACCESS_READ | NFSACCESS_MODIFY | |
---|
468 | NFSACCESS_EXTEND | NFSACCESS_EXECUTE | |
---|
469 | NFSACCESS_DELETE | NFSACCESS_LOOKUP; |
---|
470 | } else { |
---|
471 | wmode = mode; |
---|
472 | } |
---|
473 | |
---|
474 | /* |
---|
475 | * Does our cached result allow us to give a definite yes to |
---|
476 | * this request? |
---|
477 | */ |
---|
478 | gotahit = 0; |
---|
479 | NFSLOCKNODE(np); |
---|
480 | for (i = 0; i < NFS_ACCESSCACHESIZE; i++) { |
---|
481 | if (ap->a_cred->cr_uid == np->n_accesscache[i].uid) { |
---|
482 | if (time_second < (np->n_accesscache[i].stamp |
---|
483 | + nfsaccess_cache_timeout) && |
---|
484 | (np->n_accesscache[i].mode & mode) == mode) { |
---|
485 | NFSINCRGLOBAL(nfsstatsv1.accesscache_hits); |
---|
486 | gotahit = 1; |
---|
487 | } |
---|
488 | break; |
---|
489 | } |
---|
490 | } |
---|
491 | NFSUNLOCKNODE(np); |
---|
492 | #ifdef KDTRACE_HOOKS |
---|
493 | if (gotahit != 0) |
---|
494 | KDTRACE_NFS_ACCESSCACHE_GET_HIT(vp, |
---|
495 | ap->a_cred->cr_uid, mode); |
---|
496 | else |
---|
497 | KDTRACE_NFS_ACCESSCACHE_GET_MISS(vp, |
---|
498 | ap->a_cred->cr_uid, mode); |
---|
499 | #endif |
---|
500 | if (gotahit == 0) { |
---|
501 | /* |
---|
502 | * Either a no, or a don't know. Go to the wire. |
---|
503 | */ |
---|
504 | NFSINCRGLOBAL(nfsstatsv1.accesscache_misses); |
---|
505 | error = nfs34_access_otw(vp, wmode, ap->a_td, |
---|
506 | ap->a_cred, &rmode); |
---|
507 | if (!error && |
---|
508 | (rmode & mode) != mode) |
---|
509 | error = EACCES; |
---|
510 | } |
---|
511 | return (error); |
---|
512 | } else { |
---|
513 | if ((error = nfsspec_access(ap)) != 0) { |
---|
514 | return (error); |
---|
515 | } |
---|
516 | /* |
---|
517 | * Attempt to prevent a mapped root from accessing a file |
---|
518 | * which it shouldn't. We try to read a byte from the file |
---|
519 | * if the user is root and the file is not zero length. |
---|
520 | * After calling nfsspec_access, we should have the correct |
---|
521 | * file size cached. |
---|
522 | */ |
---|
523 | NFSLOCKNODE(np); |
---|
524 | if (ap->a_cred->cr_uid == 0 && (ap->a_accmode & VREAD) |
---|
525 | && VTONFS(vp)->n_size > 0) { |
---|
526 | struct iovec aiov; |
---|
527 | struct uio auio; |
---|
528 | char buf[1]; |
---|
529 | |
---|
530 | NFSUNLOCKNODE(np); |
---|
531 | aiov.iov_base = buf; |
---|
532 | aiov.iov_len = 1; |
---|
533 | auio.uio_iov = &aiov; |
---|
534 | auio.uio_iovcnt = 1; |
---|
535 | auio.uio_offset = 0; |
---|
536 | auio.uio_resid = 1; |
---|
537 | auio.uio_segflg = UIO_SYSSPACE; |
---|
538 | auio.uio_rw = UIO_READ; |
---|
539 | auio.uio_td = ap->a_td; |
---|
540 | |
---|
541 | if (vp->v_type == VREG) |
---|
542 | error = ncl_readrpc(vp, &auio, ap->a_cred); |
---|
543 | else if (vp->v_type == VDIR) { |
---|
544 | char* bp; |
---|
545 | bp = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK); |
---|
546 | aiov.iov_base = bp; |
---|
547 | aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ; |
---|
548 | error = ncl_readdirrpc(vp, &auio, ap->a_cred, |
---|
549 | ap->a_td); |
---|
550 | free(bp, M_TEMP); |
---|
551 | } else if (vp->v_type == VLNK) |
---|
552 | error = ncl_readlinkrpc(vp, &auio, ap->a_cred); |
---|
553 | else |
---|
554 | error = EACCES; |
---|
555 | } else |
---|
556 | NFSUNLOCKNODE(np); |
---|
557 | return (error); |
---|
558 | } |
---|
559 | } |
---|
560 | |
---|
561 | |
---|
562 | /* |
---|
563 | * nfs open vnode op |
---|
564 | * Check to see if the type is ok |
---|
565 | * and that deletion is not in progress. |
---|
566 | * For paged in text files, you will need to flush the page cache |
---|
567 | * if consistency is lost. |
---|
568 | */ |
---|
569 | /* ARGSUSED */ |
---|
570 | static int |
---|
571 | nfs_open(struct vop_open_args *ap) |
---|
572 | { |
---|
573 | struct vnode *vp = ap->a_vp; |
---|
574 | struct nfsnode *np = VTONFS(vp); |
---|
575 | struct vattr vattr; |
---|
576 | int error; |
---|
577 | int fmode = ap->a_mode; |
---|
578 | struct ucred *cred; |
---|
579 | vm_object_t obj; |
---|
580 | |
---|
581 | if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) |
---|
582 | return (EOPNOTSUPP); |
---|
583 | |
---|
584 | /* |
---|
585 | * For NFSv4, we need to do the Open Op before cache validation, |
---|
586 | * so that we conform to RFC3530 Sec. 9.3.1. |
---|
587 | */ |
---|
588 | if (NFS_ISV4(vp)) { |
---|
589 | error = nfsrpc_open(vp, fmode, ap->a_cred, ap->a_td); |
---|
590 | if (error) { |
---|
591 | error = nfscl_maperr(ap->a_td, error, (uid_t)0, |
---|
592 | (gid_t)0); |
---|
593 | return (error); |
---|
594 | } |
---|
595 | } |
---|
596 | |
---|
597 | /* |
---|
598 | * Now, if this Open will be doing reading, re-validate/flush the |
---|
599 | * cache, so that Close/Open coherency is maintained. |
---|
600 | */ |
---|
601 | NFSLOCKNODE(np); |
---|
602 | if (np->n_flag & NMODIFIED) { |
---|
603 | NFSUNLOCKNODE(np); |
---|
604 | error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1); |
---|
605 | if (error == EINTR || error == EIO) { |
---|
606 | if (NFS_ISV4(vp)) |
---|
607 | (void) nfsrpc_close(vp, 0, ap->a_td); |
---|
608 | return (error); |
---|
609 | } |
---|
610 | NFSLOCKNODE(np); |
---|
611 | np->n_attrstamp = 0; |
---|
612 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
613 | if (vp->v_type == VDIR) |
---|
614 | np->n_direofoffset = 0; |
---|
615 | NFSUNLOCKNODE(np); |
---|
616 | error = VOP_GETATTR(vp, &vattr, ap->a_cred); |
---|
617 | if (error) { |
---|
618 | if (NFS_ISV4(vp)) |
---|
619 | (void) nfsrpc_close(vp, 0, ap->a_td); |
---|
620 | return (error); |
---|
621 | } |
---|
622 | NFSLOCKNODE(np); |
---|
623 | np->n_mtime = vattr.va_mtime; |
---|
624 | if (NFS_ISV4(vp)) |
---|
625 | np->n_change = vattr.va_filerev; |
---|
626 | } else { |
---|
627 | NFSUNLOCKNODE(np); |
---|
628 | error = VOP_GETATTR(vp, &vattr, ap->a_cred); |
---|
629 | if (error) { |
---|
630 | if (NFS_ISV4(vp)) |
---|
631 | (void) nfsrpc_close(vp, 0, ap->a_td); |
---|
632 | return (error); |
---|
633 | } |
---|
634 | NFSLOCKNODE(np); |
---|
635 | if ((NFS_ISV4(vp) && np->n_change != vattr.va_filerev) || |
---|
636 | NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) { |
---|
637 | if (vp->v_type == VDIR) |
---|
638 | np->n_direofoffset = 0; |
---|
639 | NFSUNLOCKNODE(np); |
---|
640 | error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1); |
---|
641 | if (error == EINTR || error == EIO) { |
---|
642 | if (NFS_ISV4(vp)) |
---|
643 | (void) nfsrpc_close(vp, 0, ap->a_td); |
---|
644 | return (error); |
---|
645 | } |
---|
646 | NFSLOCKNODE(np); |
---|
647 | np->n_mtime = vattr.va_mtime; |
---|
648 | if (NFS_ISV4(vp)) |
---|
649 | np->n_change = vattr.va_filerev; |
---|
650 | } |
---|
651 | } |
---|
652 | |
---|
653 | /* |
---|
654 | * If the object has >= 1 O_DIRECT active opens, we disable caching. |
---|
655 | */ |
---|
656 | if (newnfs_directio_enable && (fmode & O_DIRECT) && |
---|
657 | (vp->v_type == VREG)) { |
---|
658 | if (np->n_directio_opens == 0) { |
---|
659 | NFSUNLOCKNODE(np); |
---|
660 | error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1); |
---|
661 | if (error) { |
---|
662 | if (NFS_ISV4(vp)) |
---|
663 | (void) nfsrpc_close(vp, 0, ap->a_td); |
---|
664 | return (error); |
---|
665 | } |
---|
666 | NFSLOCKNODE(np); |
---|
667 | np->n_flag |= NNONCACHE; |
---|
668 | } |
---|
669 | np->n_directio_opens++; |
---|
670 | } |
---|
671 | |
---|
672 | /* If opened for writing via NFSv4.1 or later, mark that for pNFS. */ |
---|
673 | if (NFSHASPNFS(VFSTONFS(vp->v_mount)) && (fmode & FWRITE) != 0) |
---|
674 | np->n_flag |= NWRITEOPENED; |
---|
675 | |
---|
676 | /* |
---|
677 | * If this is an open for writing, capture a reference to the |
---|
678 | * credentials, so they can be used by ncl_putpages(). Using |
---|
679 | * these write credentials is preferable to the credentials of |
---|
680 | * whatever thread happens to be doing the VOP_PUTPAGES() since |
---|
681 | * the write RPCs are less likely to fail with EACCES. |
---|
682 | */ |
---|
683 | if ((fmode & FWRITE) != 0) { |
---|
684 | cred = np->n_writecred; |
---|
685 | np->n_writecred = crhold(ap->a_cred); |
---|
686 | } else |
---|
687 | cred = NULL; |
---|
688 | NFSUNLOCKNODE(np); |
---|
689 | |
---|
690 | if (cred != NULL) |
---|
691 | crfree(cred); |
---|
692 | #ifndef __rtems__ |
---|
693 | vnode_create_vobject(vp, vattr.va_size, ap->a_td); |
---|
694 | #endif /* __rtems__ */ |
---|
695 | |
---|
696 | /* |
---|
697 | * If the text file has been mmap'd, flush any dirty pages to the |
---|
698 | * buffer cache and then... |
---|
699 | * Make sure all writes are pushed to the NFS server. If this is not |
---|
700 | * done, the modify time of the file can change while the text |
---|
701 | * file is being executed. This will cause the process that is |
---|
702 | * executing the text file to be terminated. |
---|
703 | */ |
---|
704 | if (vp->v_writecount <= -1) { |
---|
705 | #ifndef __rtems__ |
---|
706 | if ((obj = vp->v_object) != NULL && |
---|
707 | (obj->flags & OBJ_MIGHTBEDIRTY) != 0) { |
---|
708 | VM_OBJECT_WLOCK(obj); |
---|
709 | vm_object_page_clean(obj, 0, 0, OBJPC_SYNC); |
---|
710 | VM_OBJECT_WUNLOCK(obj); |
---|
711 | } |
---|
712 | #endif /* __rtems__ */ |
---|
713 | |
---|
714 | /* Now, flush the buffer cache. */ |
---|
715 | ncl_flush(vp, MNT_WAIT, curthread, 0, 0); |
---|
716 | |
---|
717 | /* And, finally, make sure that n_mtime is up to date. */ |
---|
718 | np = VTONFS(vp); |
---|
719 | NFSLOCKNODE(np); |
---|
720 | np->n_mtime = np->n_vattr.na_mtime; |
---|
721 | NFSUNLOCKNODE(np); |
---|
722 | } |
---|
723 | return (0); |
---|
724 | } |
---|
725 | |
---|
726 | /* |
---|
727 | * nfs close vnode op |
---|
728 | * What an NFS client should do upon close after writing is a debatable issue. |
---|
729 | * Most NFS clients push delayed writes to the server upon close, basically for |
---|
730 | * two reasons: |
---|
731 | * 1 - So that any write errors may be reported back to the client process |
---|
732 | * doing the close system call. By far the two most likely errors are |
---|
733 | * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure. |
---|
734 | * 2 - To put a worst case upper bound on cache inconsistency between |
---|
735 | * multiple clients for the file. |
---|
736 | * There is also a consistency problem for Version 2 of the protocol w.r.t. |
---|
737 | * not being able to tell if other clients are writing a file concurrently, |
---|
738 | * since there is no way of knowing if the changed modify time in the reply |
---|
739 | * is only due to the write for this client. |
---|
740 | * (NFS Version 3 provides weak cache consistency data in the reply that |
---|
741 | * should be sufficient to detect and handle this case.) |
---|
742 | * |
---|
743 | * The current code does the following: |
---|
744 | * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers |
---|
745 | * for NFS Version 3 - flush dirty buffers to the server but don't invalidate |
---|
746 | * or commit them (this satisfies 1 and 2 except for the |
---|
747 | * case where the server crashes after this close but |
---|
748 | * before the commit RPC, which is felt to be "good |
---|
749 | * enough". Changing the last argument to ncl_flush() to |
---|
750 | * a 1 would force a commit operation, if it is felt a |
---|
751 | * commit is necessary now. |
---|
752 | * for NFS Version 4 - flush the dirty buffers and commit them, if |
---|
753 | * nfscl_mustflush() says this is necessary. |
---|
754 | * It is necessary if there is no write delegation held, |
---|
755 | * in order to satisfy open/close coherency. |
---|
756 | * If the file isn't cached on local stable storage, |
---|
757 | * it may be necessary in order to detect "out of space" |
---|
758 | * errors from the server, if the write delegation |
---|
759 | * issued by the server doesn't allow the file to grow. |
---|
760 | */ |
---|
761 | /* ARGSUSED */ |
---|
762 | static int |
---|
763 | nfs_close(struct vop_close_args *ap) |
---|
764 | { |
---|
765 | struct vnode *vp = ap->a_vp; |
---|
766 | struct nfsnode *np = VTONFS(vp); |
---|
767 | struct nfsvattr nfsva; |
---|
768 | struct ucred *cred; |
---|
769 | int error = 0, ret, localcred = 0; |
---|
770 | int fmode = ap->a_fflag; |
---|
771 | |
---|
772 | if (NFSCL_FORCEDISM(vp->v_mount)) |
---|
773 | return (0); |
---|
774 | /* |
---|
775 | * During shutdown, a_cred isn't valid, so just use root. |
---|
776 | */ |
---|
777 | if (ap->a_cred == NOCRED) { |
---|
778 | cred = newnfs_getcred(); |
---|
779 | localcred = 1; |
---|
780 | } else { |
---|
781 | cred = ap->a_cred; |
---|
782 | } |
---|
783 | if (vp->v_type == VREG) { |
---|
784 | /* |
---|
785 | * Examine and clean dirty pages, regardless of NMODIFIED. |
---|
786 | * This closes a major hole in close-to-open consistency. |
---|
787 | * We want to push out all dirty pages (and buffers) on |
---|
788 | * close, regardless of whether they were dirtied by |
---|
789 | * mmap'ed writes or via write(). |
---|
790 | */ |
---|
791 | #ifndef __rtems__ |
---|
792 | if (nfs_clean_pages_on_close && vp->v_object) { |
---|
793 | VM_OBJECT_WLOCK(vp->v_object); |
---|
794 | vm_object_page_clean(vp->v_object, 0, 0, 0); |
---|
795 | VM_OBJECT_WUNLOCK(vp->v_object); |
---|
796 | } |
---|
797 | #endif /* __rtems__ */ |
---|
798 | NFSLOCKNODE(np); |
---|
799 | if (np->n_flag & NMODIFIED) { |
---|
800 | NFSUNLOCKNODE(np); |
---|
801 | if (NFS_ISV3(vp)) { |
---|
802 | /* |
---|
803 | * Under NFSv3 we have dirty buffers to dispose of. We |
---|
804 | * must flush them to the NFS server. We have the option |
---|
805 | * of waiting all the way through the commit rpc or just |
---|
806 | * waiting for the initial write. The default is to only |
---|
807 | * wait through the initial write so the data is in the |
---|
808 | * server's cache, which is roughly similar to the state |
---|
809 | * a standard disk subsystem leaves the file in on close(). |
---|
810 | * |
---|
811 | * We cannot clear the NMODIFIED bit in np->n_flag due to |
---|
812 | * potential races with other processes, and certainly |
---|
813 | * cannot clear it if we don't commit. |
---|
814 | * These races occur when there is no longer the old |
---|
815 | * traditional vnode locking implemented for Vnode Ops. |
---|
816 | */ |
---|
817 | int cm = newnfs_commit_on_close ? 1 : 0; |
---|
818 | error = ncl_flush(vp, MNT_WAIT, ap->a_td, cm, 0); |
---|
819 | /* np->n_flag &= ~NMODIFIED; */ |
---|
820 | } else if (NFS_ISV4(vp)) { |
---|
821 | if (nfscl_mustflush(vp) != 0) { |
---|
822 | int cm = newnfs_commit_on_close ? 1 : 0; |
---|
823 | error = ncl_flush(vp, MNT_WAIT, ap->a_td, |
---|
824 | cm, 0); |
---|
825 | /* |
---|
826 | * as above w.r.t races when clearing |
---|
827 | * NMODIFIED. |
---|
828 | * np->n_flag &= ~NMODIFIED; |
---|
829 | */ |
---|
830 | } |
---|
831 | } else { |
---|
832 | error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1); |
---|
833 | } |
---|
834 | NFSLOCKNODE(np); |
---|
835 | } |
---|
836 | /* |
---|
837 | * Invalidate the attribute cache in all cases. |
---|
838 | * An open is going to fetch fresh attrs any way, other procs |
---|
839 | * on this node that have file open will be forced to do an |
---|
840 | * otw attr fetch, but this is safe. |
---|
841 | * --> A user found that their RPC count dropped by 20% when |
---|
842 | * this was commented out and I can't see any requirement |
---|
843 | * for it, so I've disabled it when negative lookups are |
---|
844 | * enabled. (What does this have to do with negative lookup |
---|
845 | * caching? Well nothing, except it was reported by the |
---|
846 | * same user that needed negative lookup caching and I wanted |
---|
847 | * there to be a way to disable it to see if it |
---|
848 | * is the cause of some caching/coherency issue that might |
---|
849 | * crop up.) |
---|
850 | */ |
---|
851 | if (VFSTONFS(vp->v_mount)->nm_negnametimeo == 0) { |
---|
852 | np->n_attrstamp = 0; |
---|
853 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
854 | } |
---|
855 | if (np->n_flag & NWRITEERR) { |
---|
856 | np->n_flag &= ~NWRITEERR; |
---|
857 | error = np->n_error; |
---|
858 | } |
---|
859 | NFSUNLOCKNODE(np); |
---|
860 | } |
---|
861 | |
---|
862 | if (NFS_ISV4(vp)) { |
---|
863 | /* |
---|
864 | * Get attributes so "change" is up to date. |
---|
865 | */ |
---|
866 | if (error == 0 && nfscl_mustflush(vp) != 0 && |
---|
867 | vp->v_type == VREG && |
---|
868 | (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) == 0) { |
---|
869 | ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva, |
---|
870 | NULL); |
---|
871 | if (!ret) { |
---|
872 | np->n_change = nfsva.na_filerev; |
---|
873 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, |
---|
874 | NULL, 0, 0); |
---|
875 | } |
---|
876 | } |
---|
877 | |
---|
878 | /* |
---|
879 | * and do the close. |
---|
880 | */ |
---|
881 | ret = nfsrpc_close(vp, 0, ap->a_td); |
---|
882 | if (!error && ret) |
---|
883 | error = ret; |
---|
884 | if (error) |
---|
885 | error = nfscl_maperr(ap->a_td, error, (uid_t)0, |
---|
886 | (gid_t)0); |
---|
887 | } |
---|
888 | if (newnfs_directio_enable) |
---|
889 | KASSERT((np->n_directio_asyncwr == 0), |
---|
890 | ("nfs_close: dirty unflushed (%d) directio buffers\n", |
---|
891 | np->n_directio_asyncwr)); |
---|
892 | if (newnfs_directio_enable && (fmode & O_DIRECT) && (vp->v_type == VREG)) { |
---|
893 | NFSLOCKNODE(np); |
---|
894 | KASSERT((np->n_directio_opens > 0), |
---|
895 | ("nfs_close: unexpectedly value (0) of n_directio_opens\n")); |
---|
896 | np->n_directio_opens--; |
---|
897 | if (np->n_directio_opens == 0) |
---|
898 | np->n_flag &= ~NNONCACHE; |
---|
899 | NFSUNLOCKNODE(np); |
---|
900 | } |
---|
901 | if (localcred) |
---|
902 | NFSFREECRED(cred); |
---|
903 | return (error); |
---|
904 | } |
---|
905 | |
---|
906 | /* |
---|
907 | * nfs getattr call from vfs. |
---|
908 | */ |
---|
909 | static int |
---|
910 | nfs_getattr(struct vop_getattr_args *ap) |
---|
911 | { |
---|
912 | struct vnode *vp = ap->a_vp; |
---|
913 | struct thread *td = curthread; /* XXX */ |
---|
914 | struct nfsnode *np = VTONFS(vp); |
---|
915 | int error = 0; |
---|
916 | struct nfsvattr nfsva; |
---|
917 | struct vattr *vap = ap->a_vap; |
---|
918 | struct vattr vattr; |
---|
919 | |
---|
920 | /* |
---|
921 | * Update local times for special files. |
---|
922 | */ |
---|
923 | NFSLOCKNODE(np); |
---|
924 | if (np->n_flag & (NACC | NUPD)) |
---|
925 | np->n_flag |= NCHG; |
---|
926 | NFSUNLOCKNODE(np); |
---|
927 | /* |
---|
928 | * First look in the cache. |
---|
929 | */ |
---|
930 | if (ncl_getattrcache(vp, &vattr) == 0) { |
---|
931 | vap->va_type = vattr.va_type; |
---|
932 | vap->va_mode = vattr.va_mode; |
---|
933 | vap->va_nlink = vattr.va_nlink; |
---|
934 | vap->va_uid = vattr.va_uid; |
---|
935 | vap->va_gid = vattr.va_gid; |
---|
936 | vap->va_fsid = vattr.va_fsid; |
---|
937 | vap->va_fileid = vattr.va_fileid; |
---|
938 | vap->va_size = vattr.va_size; |
---|
939 | vap->va_blocksize = vattr.va_blocksize; |
---|
940 | vap->va_atime = vattr.va_atime; |
---|
941 | vap->va_mtime = vattr.va_mtime; |
---|
942 | vap->va_ctime = vattr.va_ctime; |
---|
943 | vap->va_gen = vattr.va_gen; |
---|
944 | vap->va_flags = vattr.va_flags; |
---|
945 | vap->va_rdev = vattr.va_rdev; |
---|
946 | vap->va_bytes = vattr.va_bytes; |
---|
947 | vap->va_filerev = vattr.va_filerev; |
---|
948 | /* |
---|
949 | * Get the local modify time for the case of a write |
---|
950 | * delegation. |
---|
951 | */ |
---|
952 | nfscl_deleggetmodtime(vp, &vap->va_mtime); |
---|
953 | return (0); |
---|
954 | } |
---|
955 | |
---|
956 | if (NFS_ISV34(vp) && nfs_prime_access_cache && |
---|
957 | nfsaccess_cache_timeout > 0) { |
---|
958 | NFSINCRGLOBAL(nfsstatsv1.accesscache_misses); |
---|
959 | nfs34_access_otw(vp, NFSACCESS_ALL, td, ap->a_cred, NULL); |
---|
960 | if (ncl_getattrcache(vp, ap->a_vap) == 0) { |
---|
961 | nfscl_deleggetmodtime(vp, &ap->a_vap->va_mtime); |
---|
962 | return (0); |
---|
963 | } |
---|
964 | } |
---|
965 | error = nfsrpc_getattr(vp, ap->a_cred, td, &nfsva, NULL); |
---|
966 | if (!error) |
---|
967 | error = nfscl_loadattrcache(&vp, &nfsva, vap, NULL, 0, 0); |
---|
968 | if (!error) { |
---|
969 | /* |
---|
970 | * Get the local modify time for the case of a write |
---|
971 | * delegation. |
---|
972 | */ |
---|
973 | nfscl_deleggetmodtime(vp, &vap->va_mtime); |
---|
974 | } else if (NFS_ISV4(vp)) { |
---|
975 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
976 | } |
---|
977 | return (error); |
---|
978 | } |
---|
979 | |
---|
980 | /* |
---|
981 | * nfs setattr call. |
---|
982 | */ |
---|
983 | static int |
---|
984 | nfs_setattr(struct vop_setattr_args *ap) |
---|
985 | { |
---|
986 | struct vnode *vp = ap->a_vp; |
---|
987 | struct nfsnode *np = VTONFS(vp); |
---|
988 | struct thread *td = curthread; /* XXX */ |
---|
989 | struct vattr *vap = ap->a_vap; |
---|
990 | int error = 0; |
---|
991 | u_quad_t tsize; |
---|
992 | |
---|
993 | #ifndef nolint |
---|
994 | tsize = (u_quad_t)0; |
---|
995 | #endif |
---|
996 | |
---|
997 | /* |
---|
998 | * Setting of flags and marking of atimes are not supported. |
---|
999 | */ |
---|
1000 | if (vap->va_flags != VNOVAL) |
---|
1001 | return (EOPNOTSUPP); |
---|
1002 | |
---|
1003 | /* |
---|
1004 | * Disallow write attempts if the filesystem is mounted read-only. |
---|
1005 | */ |
---|
1006 | if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || |
---|
1007 | vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || |
---|
1008 | vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && |
---|
1009 | (vp->v_mount->mnt_flag & MNT_RDONLY)) |
---|
1010 | return (EROFS); |
---|
1011 | if (vap->va_size != VNOVAL) { |
---|
1012 | switch (vp->v_type) { |
---|
1013 | case VDIR: |
---|
1014 | return (EISDIR); |
---|
1015 | case VCHR: |
---|
1016 | case VBLK: |
---|
1017 | case VSOCK: |
---|
1018 | case VFIFO: |
---|
1019 | if (vap->va_mtime.tv_sec == VNOVAL && |
---|
1020 | vap->va_atime.tv_sec == VNOVAL && |
---|
1021 | vap->va_mode == (mode_t)VNOVAL && |
---|
1022 | vap->va_uid == (uid_t)VNOVAL && |
---|
1023 | vap->va_gid == (gid_t)VNOVAL) |
---|
1024 | return (0); |
---|
1025 | vap->va_size = VNOVAL; |
---|
1026 | break; |
---|
1027 | default: |
---|
1028 | /* |
---|
1029 | * Disallow write attempts if the filesystem is |
---|
1030 | * mounted read-only. |
---|
1031 | */ |
---|
1032 | if (vp->v_mount->mnt_flag & MNT_RDONLY) |
---|
1033 | return (EROFS); |
---|
1034 | /* |
---|
1035 | * We run vnode_pager_setsize() early (why?), |
---|
1036 | * we must set np->n_size now to avoid vinvalbuf |
---|
1037 | * V_SAVE races that might setsize a lower |
---|
1038 | * value. |
---|
1039 | */ |
---|
1040 | NFSLOCKNODE(np); |
---|
1041 | tsize = np->n_size; |
---|
1042 | NFSUNLOCKNODE(np); |
---|
1043 | error = ncl_meta_setsize(vp, td, vap->va_size); |
---|
1044 | NFSLOCKNODE(np); |
---|
1045 | if (np->n_flag & NMODIFIED) { |
---|
1046 | tsize = np->n_size; |
---|
1047 | NFSUNLOCKNODE(np); |
---|
1048 | error = ncl_vinvalbuf(vp, vap->va_size == 0 ? |
---|
1049 | 0 : V_SAVE, td, 1); |
---|
1050 | if (error != 0) { |
---|
1051 | #ifndef __rtems__ |
---|
1052 | vnode_pager_setsize(vp, tsize); |
---|
1053 | #endif /* __rtems__ */ |
---|
1054 | return (error); |
---|
1055 | } |
---|
1056 | /* |
---|
1057 | * Call nfscl_delegmodtime() to set the modify time |
---|
1058 | * locally, as required. |
---|
1059 | */ |
---|
1060 | nfscl_delegmodtime(vp); |
---|
1061 | } else |
---|
1062 | NFSUNLOCKNODE(np); |
---|
1063 | /* |
---|
1064 | * np->n_size has already been set to vap->va_size |
---|
1065 | * in ncl_meta_setsize(). We must set it again since |
---|
1066 | * nfs_loadattrcache() could be called through |
---|
1067 | * ncl_meta_setsize() and could modify np->n_size. |
---|
1068 | */ |
---|
1069 | NFSLOCKNODE(np); |
---|
1070 | np->n_vattr.na_size = np->n_size = vap->va_size; |
---|
1071 | NFSUNLOCKNODE(np); |
---|
1072 | } |
---|
1073 | } else { |
---|
1074 | NFSLOCKNODE(np); |
---|
1075 | if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) && |
---|
1076 | (np->n_flag & NMODIFIED) && vp->v_type == VREG) { |
---|
1077 | NFSUNLOCKNODE(np); |
---|
1078 | error = ncl_vinvalbuf(vp, V_SAVE, td, 1); |
---|
1079 | if (error == EINTR || error == EIO) |
---|
1080 | return (error); |
---|
1081 | } else |
---|
1082 | NFSUNLOCKNODE(np); |
---|
1083 | } |
---|
1084 | error = nfs_setattrrpc(vp, vap, ap->a_cred, td); |
---|
1085 | if (error && vap->va_size != VNOVAL) { |
---|
1086 | NFSLOCKNODE(np); |
---|
1087 | np->n_size = np->n_vattr.na_size = tsize; |
---|
1088 | #ifndef __rtems__ |
---|
1089 | vnode_pager_setsize(vp, tsize); |
---|
1090 | #endif /* __rtems__ */ |
---|
1091 | NFSUNLOCKNODE(np); |
---|
1092 | } |
---|
1093 | return (error); |
---|
1094 | } |
---|
1095 | |
---|
1096 | /* |
---|
1097 | * Do an nfs setattr rpc. |
---|
1098 | */ |
---|
1099 | static int |
---|
1100 | nfs_setattrrpc(struct vnode *vp, struct vattr *vap, struct ucred *cred, |
---|
1101 | struct thread *td) |
---|
1102 | { |
---|
1103 | struct nfsnode *np = VTONFS(vp); |
---|
1104 | int error, ret, attrflag, i; |
---|
1105 | struct nfsvattr nfsva; |
---|
1106 | |
---|
1107 | if (NFS_ISV34(vp)) { |
---|
1108 | NFSLOCKNODE(np); |
---|
1109 | for (i = 0; i < NFS_ACCESSCACHESIZE; i++) |
---|
1110 | np->n_accesscache[i].stamp = 0; |
---|
1111 | np->n_flag |= NDELEGMOD; |
---|
1112 | NFSUNLOCKNODE(np); |
---|
1113 | KDTRACE_NFS_ACCESSCACHE_FLUSH_DONE(vp); |
---|
1114 | } |
---|
1115 | error = nfsrpc_setattr(vp, vap, NULL, cred, td, &nfsva, &attrflag, |
---|
1116 | NULL); |
---|
1117 | if (attrflag) { |
---|
1118 | ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
1119 | if (ret && !error) |
---|
1120 | error = ret; |
---|
1121 | } |
---|
1122 | if (error && NFS_ISV4(vp)) |
---|
1123 | error = nfscl_maperr(td, error, vap->va_uid, vap->va_gid); |
---|
1124 | return (error); |
---|
1125 | } |
---|
1126 | |
---|
1127 | /* |
---|
1128 | * nfs lookup call, one step at a time... |
---|
1129 | * First look in cache |
---|
1130 | * If not found, unlock the directory nfsnode and do the rpc |
---|
1131 | */ |
---|
1132 | static int |
---|
1133 | nfs_lookup(struct vop_lookup_args *ap) |
---|
1134 | { |
---|
1135 | struct componentname *cnp = ap->a_cnp; |
---|
1136 | struct vnode *dvp = ap->a_dvp; |
---|
1137 | struct vnode **vpp = ap->a_vpp; |
---|
1138 | struct mount *mp = dvp->v_mount; |
---|
1139 | int flags = cnp->cn_flags; |
---|
1140 | struct vnode *newvp; |
---|
1141 | struct nfsmount *nmp; |
---|
1142 | struct nfsnode *np, *newnp; |
---|
1143 | int error = 0, attrflag, dattrflag, ltype, ncticks; |
---|
1144 | struct thread *td = cnp->cn_thread; |
---|
1145 | struct nfsfh *nfhp; |
---|
1146 | struct nfsvattr dnfsva, nfsva; |
---|
1147 | struct vattr vattr; |
---|
1148 | struct timespec nctime; |
---|
1149 | |
---|
1150 | *vpp = NULLVP; |
---|
1151 | if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) && |
---|
1152 | (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) |
---|
1153 | return (EROFS); |
---|
1154 | if (dvp->v_type != VDIR) |
---|
1155 | return (ENOTDIR); |
---|
1156 | nmp = VFSTONFS(mp); |
---|
1157 | np = VTONFS(dvp); |
---|
1158 | |
---|
1159 | /* For NFSv4, wait until any remove is done. */ |
---|
1160 | NFSLOCKNODE(np); |
---|
1161 | while (NFSHASNFSV4(nmp) && (np->n_flag & NREMOVEINPROG)) { |
---|
1162 | np->n_flag |= NREMOVEWANT; |
---|
1163 | (void) msleep((caddr_t)np, &np->n_mtx, PZERO, "nfslkup", 0); |
---|
1164 | } |
---|
1165 | NFSUNLOCKNODE(np); |
---|
1166 | |
---|
1167 | error = vn_dir_check_exec(dvp, cnp); |
---|
1168 | if (error != 0) |
---|
1169 | return (error); |
---|
1170 | error = cache_lookup(dvp, vpp, cnp, &nctime, &ncticks); |
---|
1171 | if (error > 0 && error != ENOENT) |
---|
1172 | return (error); |
---|
1173 | if (error == -1) { |
---|
1174 | /* |
---|
1175 | * Lookups of "." are special and always return the |
---|
1176 | * current directory. cache_lookup() already handles |
---|
1177 | * associated locking bookkeeping, etc. |
---|
1178 | */ |
---|
1179 | if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { |
---|
1180 | /* XXX: Is this really correct? */ |
---|
1181 | if (cnp->cn_nameiop != LOOKUP && |
---|
1182 | (flags & ISLASTCN)) |
---|
1183 | cnp->cn_flags |= SAVENAME; |
---|
1184 | return (0); |
---|
1185 | } |
---|
1186 | |
---|
1187 | /* |
---|
1188 | * We only accept a positive hit in the cache if the |
---|
1189 | * change time of the file matches our cached copy. |
---|
1190 | * Otherwise, we discard the cache entry and fallback |
---|
1191 | * to doing a lookup RPC. We also only trust cache |
---|
1192 | * entries for less than nm_nametimeo seconds. |
---|
1193 | * |
---|
1194 | * To better handle stale file handles and attributes, |
---|
1195 | * clear the attribute cache of this node if it is a |
---|
1196 | * leaf component, part of an open() call, and not |
---|
1197 | * locally modified before fetching the attributes. |
---|
1198 | * This should allow stale file handles to be detected |
---|
1199 | * here where we can fall back to a LOOKUP RPC to |
---|
1200 | * recover rather than having nfs_open() detect the |
---|
1201 | * stale file handle and failing open(2) with ESTALE. |
---|
1202 | */ |
---|
1203 | newvp = *vpp; |
---|
1204 | newnp = VTONFS(newvp); |
---|
1205 | if (!(nmp->nm_flag & NFSMNT_NOCTO) && |
---|
1206 | (flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) && |
---|
1207 | !(newnp->n_flag & NMODIFIED)) { |
---|
1208 | NFSLOCKNODE(newnp); |
---|
1209 | newnp->n_attrstamp = 0; |
---|
1210 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(newvp); |
---|
1211 | NFSUNLOCKNODE(newnp); |
---|
1212 | } |
---|
1213 | if (nfscl_nodeleg(newvp, 0) == 0 || |
---|
1214 | ((u_int)(ticks - ncticks) < (nmp->nm_nametimeo * hz) && |
---|
1215 | VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && |
---|
1216 | timespeccmp(&vattr.va_ctime, &nctime, ==))) { |
---|
1217 | NFSINCRGLOBAL(nfsstatsv1.lookupcache_hits); |
---|
1218 | if (cnp->cn_nameiop != LOOKUP && |
---|
1219 | (flags & ISLASTCN)) |
---|
1220 | cnp->cn_flags |= SAVENAME; |
---|
1221 | return (0); |
---|
1222 | } |
---|
1223 | cache_purge(newvp); |
---|
1224 | if (dvp != newvp) |
---|
1225 | vput(newvp); |
---|
1226 | else |
---|
1227 | vrele(newvp); |
---|
1228 | *vpp = NULLVP; |
---|
1229 | } else if (error == ENOENT) { |
---|
1230 | if (dvp->v_iflag & VI_DOOMED) |
---|
1231 | return (ENOENT); |
---|
1232 | /* |
---|
1233 | * We only accept a negative hit in the cache if the |
---|
1234 | * modification time of the parent directory matches |
---|
1235 | * the cached copy in the name cache entry. |
---|
1236 | * Otherwise, we discard all of the negative cache |
---|
1237 | * entries for this directory. We also only trust |
---|
1238 | * negative cache entries for up to nm_negnametimeo |
---|
1239 | * seconds. |
---|
1240 | */ |
---|
1241 | if ((u_int)(ticks - ncticks) < (nmp->nm_negnametimeo * hz) && |
---|
1242 | VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 && |
---|
1243 | timespeccmp(&vattr.va_mtime, &nctime, ==)) { |
---|
1244 | NFSINCRGLOBAL(nfsstatsv1.lookupcache_hits); |
---|
1245 | return (ENOENT); |
---|
1246 | } |
---|
1247 | cache_purge_negative(dvp); |
---|
1248 | } |
---|
1249 | |
---|
1250 | error = 0; |
---|
1251 | newvp = NULLVP; |
---|
1252 | NFSINCRGLOBAL(nfsstatsv1.lookupcache_misses); |
---|
1253 | error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
1254 | cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, |
---|
1255 | NULL); |
---|
1256 | if (dattrflag) |
---|
1257 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
1258 | if (error) { |
---|
1259 | if (newvp != NULLVP) { |
---|
1260 | vput(newvp); |
---|
1261 | *vpp = NULLVP; |
---|
1262 | } |
---|
1263 | |
---|
1264 | if (error != ENOENT) { |
---|
1265 | if (NFS_ISV4(dvp)) |
---|
1266 | error = nfscl_maperr(td, error, (uid_t)0, |
---|
1267 | (gid_t)0); |
---|
1268 | return (error); |
---|
1269 | } |
---|
1270 | |
---|
1271 | /* The requested file was not found. */ |
---|
1272 | if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && |
---|
1273 | (flags & ISLASTCN)) { |
---|
1274 | /* |
---|
1275 | * XXX: UFS does a full VOP_ACCESS(dvp, |
---|
1276 | * VWRITE) here instead of just checking |
---|
1277 | * MNT_RDONLY. |
---|
1278 | */ |
---|
1279 | if (mp->mnt_flag & MNT_RDONLY) |
---|
1280 | return (EROFS); |
---|
1281 | cnp->cn_flags |= SAVENAME; |
---|
1282 | return (EJUSTRETURN); |
---|
1283 | } |
---|
1284 | |
---|
1285 | if ((cnp->cn_flags & MAKEENTRY) != 0 && dattrflag) { |
---|
1286 | /* |
---|
1287 | * Cache the modification time of the parent |
---|
1288 | * directory from the post-op attributes in |
---|
1289 | * the name cache entry. The negative cache |
---|
1290 | * entry will be ignored once the directory |
---|
1291 | * has changed. Don't bother adding the entry |
---|
1292 | * if the directory has already changed. |
---|
1293 | */ |
---|
1294 | NFSLOCKNODE(np); |
---|
1295 | if (timespeccmp(&np->n_vattr.na_mtime, |
---|
1296 | &dnfsva.na_mtime, ==)) { |
---|
1297 | NFSUNLOCKNODE(np); |
---|
1298 | cache_enter_time(dvp, NULL, cnp, |
---|
1299 | &dnfsva.na_mtime, NULL); |
---|
1300 | } else |
---|
1301 | NFSUNLOCKNODE(np); |
---|
1302 | } |
---|
1303 | return (ENOENT); |
---|
1304 | } |
---|
1305 | |
---|
1306 | /* |
---|
1307 | * Handle RENAME case... |
---|
1308 | */ |
---|
1309 | if (cnp->cn_nameiop == RENAME && (flags & ISLASTCN)) { |
---|
1310 | if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) { |
---|
1311 | free(nfhp, M_NFSFH); |
---|
1312 | return (EISDIR); |
---|
1313 | } |
---|
1314 | error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL, |
---|
1315 | LK_EXCLUSIVE); |
---|
1316 | if (error) |
---|
1317 | return (error); |
---|
1318 | newvp = NFSTOV(np); |
---|
1319 | if (attrflag) |
---|
1320 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1321 | 0, 1); |
---|
1322 | *vpp = newvp; |
---|
1323 | cnp->cn_flags |= SAVENAME; |
---|
1324 | return (0); |
---|
1325 | } |
---|
1326 | |
---|
1327 | if (flags & ISDOTDOT) { |
---|
1328 | ltype = NFSVOPISLOCKED(dvp); |
---|
1329 | error = vfs_busy(mp, MBF_NOWAIT); |
---|
1330 | if (error != 0) { |
---|
1331 | vfs_ref(mp); |
---|
1332 | NFSVOPUNLOCK(dvp, 0); |
---|
1333 | error = vfs_busy(mp, 0); |
---|
1334 | NFSVOPLOCK(dvp, ltype | LK_RETRY); |
---|
1335 | vfs_rel(mp); |
---|
1336 | if (error == 0 && (dvp->v_iflag & VI_DOOMED)) { |
---|
1337 | vfs_unbusy(mp); |
---|
1338 | error = ENOENT; |
---|
1339 | } |
---|
1340 | if (error != 0) |
---|
1341 | return (error); |
---|
1342 | } |
---|
1343 | NFSVOPUNLOCK(dvp, 0); |
---|
1344 | error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL, |
---|
1345 | cnp->cn_lkflags); |
---|
1346 | if (error == 0) |
---|
1347 | newvp = NFSTOV(np); |
---|
1348 | vfs_unbusy(mp); |
---|
1349 | if (newvp != dvp) |
---|
1350 | NFSVOPLOCK(dvp, ltype | LK_RETRY); |
---|
1351 | if (dvp->v_iflag & VI_DOOMED) { |
---|
1352 | if (error == 0) { |
---|
1353 | if (newvp == dvp) |
---|
1354 | vrele(newvp); |
---|
1355 | else |
---|
1356 | vput(newvp); |
---|
1357 | } |
---|
1358 | error = ENOENT; |
---|
1359 | } |
---|
1360 | if (error != 0) |
---|
1361 | return (error); |
---|
1362 | if (attrflag) |
---|
1363 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1364 | 0, 1); |
---|
1365 | } else if (NFS_CMPFH(np, nfhp->nfh_fh, nfhp->nfh_len)) { |
---|
1366 | free(nfhp, M_NFSFH); |
---|
1367 | VREF(dvp); |
---|
1368 | newvp = dvp; |
---|
1369 | if (attrflag) |
---|
1370 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1371 | 0, 1); |
---|
1372 | } else { |
---|
1373 | error = nfscl_nget(mp, dvp, nfhp, cnp, td, &np, NULL, |
---|
1374 | cnp->cn_lkflags); |
---|
1375 | if (error) |
---|
1376 | return (error); |
---|
1377 | newvp = NFSTOV(np); |
---|
1378 | if (attrflag) |
---|
1379 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1380 | 0, 1); |
---|
1381 | else if ((flags & (ISLASTCN | ISOPEN)) == (ISLASTCN | ISOPEN) && |
---|
1382 | !(np->n_flag & NMODIFIED)) { |
---|
1383 | /* |
---|
1384 | * Flush the attribute cache when opening a |
---|
1385 | * leaf node to ensure that fresh attributes |
---|
1386 | * are fetched in nfs_open() since we did not |
---|
1387 | * fetch attributes from the LOOKUP reply. |
---|
1388 | */ |
---|
1389 | NFSLOCKNODE(np); |
---|
1390 | np->n_attrstamp = 0; |
---|
1391 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(newvp); |
---|
1392 | NFSUNLOCKNODE(np); |
---|
1393 | } |
---|
1394 | } |
---|
1395 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) |
---|
1396 | cnp->cn_flags |= SAVENAME; |
---|
1397 | if ((cnp->cn_flags & MAKEENTRY) && |
---|
1398 | (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && |
---|
1399 | attrflag != 0 && (newvp->v_type != VDIR || dattrflag != 0)) |
---|
1400 | cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, |
---|
1401 | newvp->v_type != VDIR ? NULL : &dnfsva.na_ctime); |
---|
1402 | *vpp = newvp; |
---|
1403 | return (0); |
---|
1404 | } |
---|
1405 | |
---|
1406 | /* |
---|
1407 | * nfs read call. |
---|
1408 | * Just call ncl_bioread() to do the work. |
---|
1409 | */ |
---|
1410 | static int |
---|
1411 | nfs_read(struct vop_read_args *ap) |
---|
1412 | { |
---|
1413 | struct vnode *vp = ap->a_vp; |
---|
1414 | |
---|
1415 | switch (vp->v_type) { |
---|
1416 | case VREG: |
---|
1417 | return (ncl_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); |
---|
1418 | case VDIR: |
---|
1419 | return (EISDIR); |
---|
1420 | default: |
---|
1421 | return (EOPNOTSUPP); |
---|
1422 | } |
---|
1423 | } |
---|
1424 | |
---|
1425 | /* |
---|
1426 | * nfs readlink call |
---|
1427 | */ |
---|
1428 | static int |
---|
1429 | nfs_readlink(struct vop_readlink_args *ap) |
---|
1430 | { |
---|
1431 | struct vnode *vp = ap->a_vp; |
---|
1432 | |
---|
1433 | if (vp->v_type != VLNK) |
---|
1434 | return (EINVAL); |
---|
1435 | return (ncl_bioread(vp, ap->a_uio, 0, ap->a_cred)); |
---|
1436 | } |
---|
1437 | |
---|
1438 | /* |
---|
1439 | * Do a readlink rpc. |
---|
1440 | * Called by ncl_doio() from below the buffer cache. |
---|
1441 | */ |
---|
1442 | int |
---|
1443 | ncl_readlinkrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred) |
---|
1444 | { |
---|
1445 | int error, ret, attrflag; |
---|
1446 | struct nfsvattr nfsva; |
---|
1447 | |
---|
1448 | error = nfsrpc_readlink(vp, uiop, cred, uiop->uio_td, &nfsva, |
---|
1449 | &attrflag, NULL); |
---|
1450 | if (attrflag) { |
---|
1451 | ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
1452 | if (ret && !error) |
---|
1453 | error = ret; |
---|
1454 | } |
---|
1455 | if (error && NFS_ISV4(vp)) |
---|
1456 | error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0); |
---|
1457 | return (error); |
---|
1458 | } |
---|
1459 | |
---|
1460 | /* |
---|
1461 | * nfs read rpc call |
---|
1462 | * Ditto above |
---|
1463 | */ |
---|
1464 | int |
---|
1465 | ncl_readrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred) |
---|
1466 | { |
---|
1467 | int error, ret, attrflag; |
---|
1468 | struct nfsvattr nfsva; |
---|
1469 | struct nfsmount *nmp; |
---|
1470 | |
---|
1471 | nmp = VFSTONFS(vnode_mount(vp)); |
---|
1472 | error = EIO; |
---|
1473 | attrflag = 0; |
---|
1474 | if (NFSHASPNFS(nmp)) |
---|
1475 | error = nfscl_doiods(vp, uiop, NULL, NULL, |
---|
1476 | NFSV4OPEN_ACCESSREAD, 0, cred, uiop->uio_td); |
---|
1477 | NFSCL_DEBUG(4, "readrpc: aft doiods=%d\n", error); |
---|
1478 | if (error != 0) |
---|
1479 | error = nfsrpc_read(vp, uiop, cred, uiop->uio_td, &nfsva, |
---|
1480 | &attrflag, NULL); |
---|
1481 | if (attrflag) { |
---|
1482 | ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
1483 | if (ret && !error) |
---|
1484 | error = ret; |
---|
1485 | } |
---|
1486 | if (error && NFS_ISV4(vp)) |
---|
1487 | error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0); |
---|
1488 | return (error); |
---|
1489 | } |
---|
1490 | |
---|
1491 | /* |
---|
1492 | * nfs write call |
---|
1493 | */ |
---|
1494 | int |
---|
1495 | ncl_writerpc(struct vnode *vp, struct uio *uiop, struct ucred *cred, |
---|
1496 | int *iomode, int *must_commit, int called_from_strategy) |
---|
1497 | { |
---|
1498 | struct nfsvattr nfsva; |
---|
1499 | int error, attrflag, ret; |
---|
1500 | struct nfsmount *nmp; |
---|
1501 | |
---|
1502 | nmp = VFSTONFS(vnode_mount(vp)); |
---|
1503 | error = EIO; |
---|
1504 | attrflag = 0; |
---|
1505 | if (NFSHASPNFS(nmp)) |
---|
1506 | error = nfscl_doiods(vp, uiop, iomode, must_commit, |
---|
1507 | NFSV4OPEN_ACCESSWRITE, 0, cred, uiop->uio_td); |
---|
1508 | NFSCL_DEBUG(4, "writerpc: aft doiods=%d\n", error); |
---|
1509 | if (error != 0) |
---|
1510 | error = nfsrpc_write(vp, uiop, iomode, must_commit, cred, |
---|
1511 | uiop->uio_td, &nfsva, &attrflag, NULL, |
---|
1512 | called_from_strategy); |
---|
1513 | if (attrflag) { |
---|
1514 | if (VTONFS(vp)->n_flag & ND_NFSV4) |
---|
1515 | ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 1, |
---|
1516 | 1); |
---|
1517 | else |
---|
1518 | ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, |
---|
1519 | 1); |
---|
1520 | if (ret && !error) |
---|
1521 | error = ret; |
---|
1522 | } |
---|
1523 | if (DOINGASYNC(vp)) |
---|
1524 | *iomode = NFSWRITE_FILESYNC; |
---|
1525 | if (error && NFS_ISV4(vp)) |
---|
1526 | error = nfscl_maperr(uiop->uio_td, error, (uid_t)0, (gid_t)0); |
---|
1527 | return (error); |
---|
1528 | } |
---|
1529 | |
---|
1530 | /* |
---|
1531 | * nfs mknod rpc |
---|
1532 | * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the |
---|
1533 | * mode set to specify the file type and the size field for rdev. |
---|
1534 | */ |
---|
1535 | static int |
---|
1536 | nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, |
---|
1537 | struct vattr *vap) |
---|
1538 | { |
---|
1539 | struct nfsvattr nfsva, dnfsva; |
---|
1540 | struct vnode *newvp = NULL; |
---|
1541 | struct nfsnode *np = NULL, *dnp; |
---|
1542 | struct nfsfh *nfhp; |
---|
1543 | struct vattr vattr; |
---|
1544 | int error = 0, attrflag, dattrflag; |
---|
1545 | u_int32_t rdev; |
---|
1546 | |
---|
1547 | if (vap->va_type == VCHR || vap->va_type == VBLK) |
---|
1548 | rdev = vap->va_rdev; |
---|
1549 | else if (vap->va_type == VFIFO || vap->va_type == VSOCK) |
---|
1550 | rdev = 0xffffffff; |
---|
1551 | else |
---|
1552 | return (EOPNOTSUPP); |
---|
1553 | if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred))) |
---|
1554 | return (error); |
---|
1555 | error = nfsrpc_mknod(dvp, cnp->cn_nameptr, cnp->cn_namelen, vap, |
---|
1556 | rdev, vap->va_type, cnp->cn_cred, cnp->cn_thread, &dnfsva, |
---|
1557 | &nfsva, &nfhp, &attrflag, &dattrflag, NULL); |
---|
1558 | if (!error) { |
---|
1559 | if (!nfhp) |
---|
1560 | (void) nfsrpc_lookup(dvp, cnp->cn_nameptr, |
---|
1561 | cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread, |
---|
1562 | &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, |
---|
1563 | NULL); |
---|
1564 | if (nfhp) |
---|
1565 | error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, |
---|
1566 | cnp->cn_thread, &np, NULL, LK_EXCLUSIVE); |
---|
1567 | } |
---|
1568 | if (dattrflag) |
---|
1569 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
1570 | if (!error) { |
---|
1571 | newvp = NFSTOV(np); |
---|
1572 | if (attrflag != 0) { |
---|
1573 | error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1574 | 0, 1); |
---|
1575 | if (error != 0) |
---|
1576 | vput(newvp); |
---|
1577 | } |
---|
1578 | } |
---|
1579 | if (!error) { |
---|
1580 | *vpp = newvp; |
---|
1581 | } else if (NFS_ISV4(dvp)) { |
---|
1582 | error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid, |
---|
1583 | vap->va_gid); |
---|
1584 | } |
---|
1585 | dnp = VTONFS(dvp); |
---|
1586 | NFSLOCKNODE(dnp); |
---|
1587 | dnp->n_flag |= NMODIFIED; |
---|
1588 | if (!dattrflag) { |
---|
1589 | dnp->n_attrstamp = 0; |
---|
1590 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
1591 | } |
---|
1592 | NFSUNLOCKNODE(dnp); |
---|
1593 | return (error); |
---|
1594 | } |
---|
1595 | |
---|
1596 | /* |
---|
1597 | * nfs mknod vop |
---|
1598 | * just call nfs_mknodrpc() to do the work. |
---|
1599 | */ |
---|
1600 | /* ARGSUSED */ |
---|
1601 | static int |
---|
1602 | nfs_mknod(struct vop_mknod_args *ap) |
---|
1603 | { |
---|
1604 | return (nfs_mknodrpc(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap)); |
---|
1605 | } |
---|
1606 | |
---|
1607 | static struct mtx nfs_cverf_mtx; |
---|
1608 | MTX_SYSINIT(nfs_cverf_mtx, &nfs_cverf_mtx, "NFS create verifier mutex", |
---|
1609 | MTX_DEF); |
---|
1610 | |
---|
1611 | static nfsquad_t |
---|
1612 | nfs_get_cverf(void) |
---|
1613 | { |
---|
1614 | static nfsquad_t cverf; |
---|
1615 | nfsquad_t ret; |
---|
1616 | static int cverf_initialized = 0; |
---|
1617 | |
---|
1618 | mtx_lock(&nfs_cverf_mtx); |
---|
1619 | if (cverf_initialized == 0) { |
---|
1620 | cverf.lval[0] = arc4random(); |
---|
1621 | cverf.lval[1] = arc4random(); |
---|
1622 | cverf_initialized = 1; |
---|
1623 | } else |
---|
1624 | cverf.qval++; |
---|
1625 | ret = cverf; |
---|
1626 | mtx_unlock(&nfs_cverf_mtx); |
---|
1627 | |
---|
1628 | return (ret); |
---|
1629 | } |
---|
1630 | |
---|
1631 | /* |
---|
1632 | * nfs file create call |
---|
1633 | */ |
---|
1634 | static int |
---|
1635 | nfs_create(struct vop_create_args *ap) |
---|
1636 | { |
---|
1637 | struct vnode *dvp = ap->a_dvp; |
---|
1638 | struct vattr *vap = ap->a_vap; |
---|
1639 | struct componentname *cnp = ap->a_cnp; |
---|
1640 | struct nfsnode *np = NULL, *dnp; |
---|
1641 | struct vnode *newvp = NULL; |
---|
1642 | struct nfsmount *nmp; |
---|
1643 | struct nfsvattr dnfsva, nfsva; |
---|
1644 | struct nfsfh *nfhp; |
---|
1645 | nfsquad_t cverf; |
---|
1646 | int error = 0, attrflag, dattrflag, fmode = 0; |
---|
1647 | struct vattr vattr; |
---|
1648 | |
---|
1649 | /* |
---|
1650 | * Oops, not for me.. |
---|
1651 | */ |
---|
1652 | if (vap->va_type == VSOCK) |
---|
1653 | return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap)); |
---|
1654 | |
---|
1655 | if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred))) |
---|
1656 | return (error); |
---|
1657 | if (vap->va_vaflags & VA_EXCLUSIVE) |
---|
1658 | fmode |= O_EXCL; |
---|
1659 | dnp = VTONFS(dvp); |
---|
1660 | nmp = VFSTONFS(vnode_mount(dvp)); |
---|
1661 | again: |
---|
1662 | /* For NFSv4, wait until any remove is done. */ |
---|
1663 | NFSLOCKNODE(dnp); |
---|
1664 | while (NFSHASNFSV4(nmp) && (dnp->n_flag & NREMOVEINPROG)) { |
---|
1665 | dnp->n_flag |= NREMOVEWANT; |
---|
1666 | (void) msleep((caddr_t)dnp, &dnp->n_mtx, PZERO, "nfscrt", 0); |
---|
1667 | } |
---|
1668 | NFSUNLOCKNODE(dnp); |
---|
1669 | |
---|
1670 | cverf = nfs_get_cverf(); |
---|
1671 | error = nfsrpc_create(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
1672 | vap, cverf, fmode, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, |
---|
1673 | &nfhp, &attrflag, &dattrflag, NULL); |
---|
1674 | if (!error) { |
---|
1675 | if (nfhp == NULL) |
---|
1676 | (void) nfsrpc_lookup(dvp, cnp->cn_nameptr, |
---|
1677 | cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread, |
---|
1678 | &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, |
---|
1679 | NULL); |
---|
1680 | if (nfhp != NULL) |
---|
1681 | error = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, |
---|
1682 | cnp->cn_thread, &np, NULL, LK_EXCLUSIVE); |
---|
1683 | } |
---|
1684 | if (dattrflag) |
---|
1685 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
1686 | if (!error) { |
---|
1687 | newvp = NFSTOV(np); |
---|
1688 | if (attrflag == 0) |
---|
1689 | error = nfsrpc_getattr(newvp, cnp->cn_cred, |
---|
1690 | cnp->cn_thread, &nfsva, NULL); |
---|
1691 | if (error == 0) |
---|
1692 | error = nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
1693 | 0, 1); |
---|
1694 | } |
---|
1695 | if (error) { |
---|
1696 | if (newvp != NULL) { |
---|
1697 | vput(newvp); |
---|
1698 | newvp = NULL; |
---|
1699 | } |
---|
1700 | if (NFS_ISV34(dvp) && (fmode & O_EXCL) && |
---|
1701 | error == NFSERR_NOTSUPP) { |
---|
1702 | fmode &= ~O_EXCL; |
---|
1703 | goto again; |
---|
1704 | } |
---|
1705 | } else if (NFS_ISV34(dvp) && (fmode & O_EXCL)) { |
---|
1706 | if (nfscl_checksattr(vap, &nfsva)) { |
---|
1707 | error = nfsrpc_setattr(newvp, vap, NULL, cnp->cn_cred, |
---|
1708 | cnp->cn_thread, &nfsva, &attrflag, NULL); |
---|
1709 | if (error && (vap->va_uid != (uid_t)VNOVAL || |
---|
1710 | vap->va_gid != (gid_t)VNOVAL)) { |
---|
1711 | /* try again without setting uid/gid */ |
---|
1712 | vap->va_uid = (uid_t)VNOVAL; |
---|
1713 | vap->va_gid = (uid_t)VNOVAL; |
---|
1714 | error = nfsrpc_setattr(newvp, vap, NULL, |
---|
1715 | cnp->cn_cred, cnp->cn_thread, &nfsva, |
---|
1716 | &attrflag, NULL); |
---|
1717 | } |
---|
1718 | if (attrflag) |
---|
1719 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, |
---|
1720 | NULL, 0, 1); |
---|
1721 | if (error != 0) |
---|
1722 | vput(newvp); |
---|
1723 | } |
---|
1724 | } |
---|
1725 | if (!error) { |
---|
1726 | if ((cnp->cn_flags & MAKEENTRY) && attrflag) |
---|
1727 | cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, |
---|
1728 | NULL); |
---|
1729 | *ap->a_vpp = newvp; |
---|
1730 | } else if (NFS_ISV4(dvp)) { |
---|
1731 | error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid, |
---|
1732 | vap->va_gid); |
---|
1733 | } |
---|
1734 | NFSLOCKNODE(dnp); |
---|
1735 | dnp->n_flag |= NMODIFIED; |
---|
1736 | if (!dattrflag) { |
---|
1737 | dnp->n_attrstamp = 0; |
---|
1738 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
1739 | } |
---|
1740 | NFSUNLOCKNODE(dnp); |
---|
1741 | return (error); |
---|
1742 | } |
---|
1743 | |
---|
1744 | /* |
---|
1745 | * nfs file remove call |
---|
1746 | * To try and make nfs semantics closer to ufs semantics, a file that has |
---|
1747 | * other processes using the vnode is renamed instead of removed and then |
---|
1748 | * removed later on the last close. |
---|
1749 | * - If v_usecount > 1 |
---|
1750 | * If a rename is not already in the works |
---|
1751 | * call nfs_sillyrename() to set it up |
---|
1752 | * else |
---|
1753 | * do the remove rpc |
---|
1754 | */ |
---|
1755 | static int |
---|
1756 | nfs_remove(struct vop_remove_args *ap) |
---|
1757 | { |
---|
1758 | struct vnode *vp = ap->a_vp; |
---|
1759 | struct vnode *dvp = ap->a_dvp; |
---|
1760 | struct componentname *cnp = ap->a_cnp; |
---|
1761 | struct nfsnode *np = VTONFS(vp); |
---|
1762 | int error = 0; |
---|
1763 | struct vattr vattr; |
---|
1764 | |
---|
1765 | KASSERT((cnp->cn_flags & HASBUF) != 0, ("nfs_remove: no name")); |
---|
1766 | KASSERT(vrefcnt(vp) > 0, ("nfs_remove: bad v_usecount")); |
---|
1767 | if (vp->v_type == VDIR) |
---|
1768 | error = EPERM; |
---|
1769 | else if (vrefcnt(vp) == 1 || (np->n_sillyrename && |
---|
1770 | VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 && |
---|
1771 | vattr.va_nlink > 1)) { |
---|
1772 | /* |
---|
1773 | * Purge the name cache so that the chance of a lookup for |
---|
1774 | * the name succeeding while the remove is in progress is |
---|
1775 | * minimized. Without node locking it can still happen, such |
---|
1776 | * that an I/O op returns ESTALE, but since you get this if |
---|
1777 | * another host removes the file.. |
---|
1778 | */ |
---|
1779 | cache_purge(vp); |
---|
1780 | /* |
---|
1781 | * throw away biocache buffers, mainly to avoid |
---|
1782 | * unnecessary delayed writes later. |
---|
1783 | */ |
---|
1784 | error = ncl_vinvalbuf(vp, 0, cnp->cn_thread, 1); |
---|
1785 | if (error != EINTR && error != EIO) |
---|
1786 | /* Do the rpc */ |
---|
1787 | error = nfs_removerpc(dvp, vp, cnp->cn_nameptr, |
---|
1788 | cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread); |
---|
1789 | /* |
---|
1790 | * Kludge City: If the first reply to the remove rpc is lost.. |
---|
1791 | * the reply to the retransmitted request will be ENOENT |
---|
1792 | * since the file was in fact removed |
---|
1793 | * Therefore, we cheat and return success. |
---|
1794 | */ |
---|
1795 | if (error == ENOENT) |
---|
1796 | error = 0; |
---|
1797 | } else if (!np->n_sillyrename) |
---|
1798 | error = nfs_sillyrename(dvp, vp, cnp); |
---|
1799 | NFSLOCKNODE(np); |
---|
1800 | np->n_attrstamp = 0; |
---|
1801 | NFSUNLOCKNODE(np); |
---|
1802 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
1803 | return (error); |
---|
1804 | } |
---|
1805 | |
---|
1806 | /* |
---|
1807 | * nfs file remove rpc called from nfs_inactive |
---|
1808 | */ |
---|
1809 | int |
---|
1810 | ncl_removeit(struct sillyrename *sp, struct vnode *vp) |
---|
1811 | { |
---|
1812 | /* |
---|
1813 | * Make sure that the directory vnode is still valid. |
---|
1814 | * XXX we should lock sp->s_dvp here. |
---|
1815 | */ |
---|
1816 | if (sp->s_dvp->v_type == VBAD) |
---|
1817 | return (0); |
---|
1818 | return (nfs_removerpc(sp->s_dvp, vp, sp->s_name, sp->s_namlen, |
---|
1819 | sp->s_cred, NULL)); |
---|
1820 | } |
---|
1821 | |
---|
1822 | /* |
---|
1823 | * Nfs remove rpc, called from nfs_remove() and ncl_removeit(). |
---|
1824 | */ |
---|
1825 | static int |
---|
1826 | nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name, |
---|
1827 | int namelen, struct ucred *cred, struct thread *td) |
---|
1828 | { |
---|
1829 | struct nfsvattr dnfsva; |
---|
1830 | struct nfsnode *dnp = VTONFS(dvp); |
---|
1831 | int error = 0, dattrflag; |
---|
1832 | |
---|
1833 | NFSLOCKNODE(dnp); |
---|
1834 | dnp->n_flag |= NREMOVEINPROG; |
---|
1835 | NFSUNLOCKNODE(dnp); |
---|
1836 | error = nfsrpc_remove(dvp, name, namelen, vp, cred, td, &dnfsva, |
---|
1837 | &dattrflag, NULL); |
---|
1838 | NFSLOCKNODE(dnp); |
---|
1839 | if ((dnp->n_flag & NREMOVEWANT)) { |
---|
1840 | dnp->n_flag &= ~(NREMOVEWANT | NREMOVEINPROG); |
---|
1841 | NFSUNLOCKNODE(dnp); |
---|
1842 | wakeup((caddr_t)dnp); |
---|
1843 | } else { |
---|
1844 | dnp->n_flag &= ~NREMOVEINPROG; |
---|
1845 | NFSUNLOCKNODE(dnp); |
---|
1846 | } |
---|
1847 | if (dattrflag) |
---|
1848 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
1849 | NFSLOCKNODE(dnp); |
---|
1850 | dnp->n_flag |= NMODIFIED; |
---|
1851 | if (!dattrflag) { |
---|
1852 | dnp->n_attrstamp = 0; |
---|
1853 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
1854 | } |
---|
1855 | NFSUNLOCKNODE(dnp); |
---|
1856 | if (error && NFS_ISV4(dvp)) |
---|
1857 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
1858 | return (error); |
---|
1859 | } |
---|
1860 | |
---|
1861 | /* |
---|
1862 | * nfs file rename call |
---|
1863 | */ |
---|
1864 | static int |
---|
1865 | nfs_rename(struct vop_rename_args *ap) |
---|
1866 | { |
---|
1867 | struct vnode *fvp = ap->a_fvp; |
---|
1868 | struct vnode *tvp = ap->a_tvp; |
---|
1869 | struct vnode *fdvp = ap->a_fdvp; |
---|
1870 | struct vnode *tdvp = ap->a_tdvp; |
---|
1871 | struct componentname *tcnp = ap->a_tcnp; |
---|
1872 | struct componentname *fcnp = ap->a_fcnp; |
---|
1873 | struct nfsnode *fnp = VTONFS(ap->a_fvp); |
---|
1874 | struct nfsnode *tdnp = VTONFS(ap->a_tdvp); |
---|
1875 | struct nfsv4node *newv4 = NULL; |
---|
1876 | int error; |
---|
1877 | |
---|
1878 | KASSERT((tcnp->cn_flags & HASBUF) != 0 && |
---|
1879 | (fcnp->cn_flags & HASBUF) != 0, ("nfs_rename: no name")); |
---|
1880 | /* Check for cross-device rename */ |
---|
1881 | if ((fvp->v_mount != tdvp->v_mount) || |
---|
1882 | (tvp && (fvp->v_mount != tvp->v_mount))) { |
---|
1883 | error = EXDEV; |
---|
1884 | goto out; |
---|
1885 | } |
---|
1886 | |
---|
1887 | if (fvp == tvp) { |
---|
1888 | printf("nfs_rename: fvp == tvp (can't happen)\n"); |
---|
1889 | error = 0; |
---|
1890 | goto out; |
---|
1891 | } |
---|
1892 | if ((error = NFSVOPLOCK(fvp, LK_EXCLUSIVE)) != 0) |
---|
1893 | goto out; |
---|
1894 | |
---|
1895 | /* |
---|
1896 | * We have to flush B_DELWRI data prior to renaming |
---|
1897 | * the file. If we don't, the delayed-write buffers |
---|
1898 | * can be flushed out later after the file has gone stale |
---|
1899 | * under NFSV3. NFSV2 does not have this problem because |
---|
1900 | * ( as far as I can tell ) it flushes dirty buffers more |
---|
1901 | * often. |
---|
1902 | * |
---|
1903 | * Skip the rename operation if the fsync fails, this can happen |
---|
1904 | * due to the server's volume being full, when we pushed out data |
---|
1905 | * that was written back to our cache earlier. Not checking for |
---|
1906 | * this condition can result in potential (silent) data loss. |
---|
1907 | */ |
---|
1908 | error = VOP_FSYNC(fvp, MNT_WAIT, fcnp->cn_thread); |
---|
1909 | NFSVOPUNLOCK(fvp, 0); |
---|
1910 | if (!error && tvp) |
---|
1911 | error = VOP_FSYNC(tvp, MNT_WAIT, tcnp->cn_thread); |
---|
1912 | if (error) |
---|
1913 | goto out; |
---|
1914 | |
---|
1915 | /* |
---|
1916 | * If the tvp exists and is in use, sillyrename it before doing the |
---|
1917 | * rename of the new file over it. |
---|
1918 | * XXX Can't sillyrename a directory. |
---|
1919 | */ |
---|
1920 | if (tvp && vrefcnt(tvp) > 1 && !VTONFS(tvp)->n_sillyrename && |
---|
1921 | tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) { |
---|
1922 | vput(tvp); |
---|
1923 | tvp = NULL; |
---|
1924 | } |
---|
1925 | |
---|
1926 | error = nfs_renamerpc(fdvp, fvp, fcnp->cn_nameptr, fcnp->cn_namelen, |
---|
1927 | tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, |
---|
1928 | tcnp->cn_thread); |
---|
1929 | |
---|
1930 | if (error == 0 && NFS_ISV4(tdvp)) { |
---|
1931 | /* |
---|
1932 | * For NFSv4, check to see if it is the same name and |
---|
1933 | * replace the name, if it is different. |
---|
1934 | */ |
---|
1935 | newv4 = malloc( |
---|
1936 | sizeof (struct nfsv4node) + |
---|
1937 | tdnp->n_fhp->nfh_len + tcnp->cn_namelen - 1, |
---|
1938 | M_NFSV4NODE, M_WAITOK); |
---|
1939 | NFSLOCKNODE(tdnp); |
---|
1940 | NFSLOCKNODE(fnp); |
---|
1941 | if (fnp->n_v4 != NULL && fvp->v_type == VREG && |
---|
1942 | (fnp->n_v4->n4_namelen != tcnp->cn_namelen || |
---|
1943 | NFSBCMP(tcnp->cn_nameptr, NFS4NODENAME(fnp->n_v4), |
---|
1944 | tcnp->cn_namelen) || |
---|
1945 | tdnp->n_fhp->nfh_len != fnp->n_v4->n4_fhlen || |
---|
1946 | NFSBCMP(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data, |
---|
1947 | tdnp->n_fhp->nfh_len))) { |
---|
1948 | #ifdef notdef |
---|
1949 | { char nnn[100]; int nnnl; |
---|
1950 | nnnl = (tcnp->cn_namelen < 100) ? tcnp->cn_namelen : 99; |
---|
1951 | bcopy(tcnp->cn_nameptr, nnn, nnnl); |
---|
1952 | nnn[nnnl] = '\0'; |
---|
1953 | printf("ren replace=%s\n",nnn); |
---|
1954 | } |
---|
1955 | #endif |
---|
1956 | free(fnp->n_v4, M_NFSV4NODE); |
---|
1957 | fnp->n_v4 = newv4; |
---|
1958 | newv4 = NULL; |
---|
1959 | fnp->n_v4->n4_fhlen = tdnp->n_fhp->nfh_len; |
---|
1960 | fnp->n_v4->n4_namelen = tcnp->cn_namelen; |
---|
1961 | NFSBCOPY(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data, |
---|
1962 | tdnp->n_fhp->nfh_len); |
---|
1963 | NFSBCOPY(tcnp->cn_nameptr, |
---|
1964 | NFS4NODENAME(fnp->n_v4), tcnp->cn_namelen); |
---|
1965 | } |
---|
1966 | NFSUNLOCKNODE(tdnp); |
---|
1967 | NFSUNLOCKNODE(fnp); |
---|
1968 | if (newv4 != NULL) |
---|
1969 | free(newv4, M_NFSV4NODE); |
---|
1970 | } |
---|
1971 | |
---|
1972 | if (fvp->v_type == VDIR) { |
---|
1973 | if (tvp != NULL && tvp->v_type == VDIR) |
---|
1974 | cache_purge(tdvp); |
---|
1975 | cache_purge(fdvp); |
---|
1976 | } |
---|
1977 | |
---|
1978 | out: |
---|
1979 | if (tdvp == tvp) |
---|
1980 | vrele(tdvp); |
---|
1981 | else |
---|
1982 | vput(tdvp); |
---|
1983 | if (tvp) |
---|
1984 | vput(tvp); |
---|
1985 | vrele(fdvp); |
---|
1986 | vrele(fvp); |
---|
1987 | /* |
---|
1988 | * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. |
---|
1989 | */ |
---|
1990 | if (error == ENOENT) |
---|
1991 | error = 0; |
---|
1992 | return (error); |
---|
1993 | } |
---|
1994 | |
---|
1995 | /* |
---|
1996 | * nfs file rename rpc called from nfs_remove() above |
---|
1997 | */ |
---|
1998 | static int |
---|
1999 | nfs_renameit(struct vnode *sdvp, struct vnode *svp, struct componentname *scnp, |
---|
2000 | struct sillyrename *sp) |
---|
2001 | { |
---|
2002 | |
---|
2003 | return (nfs_renamerpc(sdvp, svp, scnp->cn_nameptr, scnp->cn_namelen, |
---|
2004 | sdvp, NULL, sp->s_name, sp->s_namlen, scnp->cn_cred, |
---|
2005 | scnp->cn_thread)); |
---|
2006 | } |
---|
2007 | |
---|
2008 | /* |
---|
2009 | * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit(). |
---|
2010 | */ |
---|
2011 | static int |
---|
2012 | nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr, |
---|
2013 | int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr, |
---|
2014 | int tnamelen, struct ucred *cred, struct thread *td) |
---|
2015 | { |
---|
2016 | struct nfsvattr fnfsva, tnfsva; |
---|
2017 | struct nfsnode *fdnp = VTONFS(fdvp); |
---|
2018 | struct nfsnode *tdnp = VTONFS(tdvp); |
---|
2019 | int error = 0, fattrflag, tattrflag; |
---|
2020 | |
---|
2021 | error = nfsrpc_rename(fdvp, fvp, fnameptr, fnamelen, tdvp, tvp, |
---|
2022 | tnameptr, tnamelen, cred, td, &fnfsva, &tnfsva, &fattrflag, |
---|
2023 | &tattrflag, NULL, NULL); |
---|
2024 | NFSLOCKNODE(fdnp); |
---|
2025 | fdnp->n_flag |= NMODIFIED; |
---|
2026 | if (fattrflag != 0) { |
---|
2027 | NFSUNLOCKNODE(fdnp); |
---|
2028 | (void) nfscl_loadattrcache(&fdvp, &fnfsva, NULL, NULL, 0, 1); |
---|
2029 | } else { |
---|
2030 | fdnp->n_attrstamp = 0; |
---|
2031 | NFSUNLOCKNODE(fdnp); |
---|
2032 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(fdvp); |
---|
2033 | } |
---|
2034 | NFSLOCKNODE(tdnp); |
---|
2035 | tdnp->n_flag |= NMODIFIED; |
---|
2036 | if (tattrflag != 0) { |
---|
2037 | NFSUNLOCKNODE(tdnp); |
---|
2038 | (void) nfscl_loadattrcache(&tdvp, &tnfsva, NULL, NULL, 0, 1); |
---|
2039 | } else { |
---|
2040 | tdnp->n_attrstamp = 0; |
---|
2041 | NFSUNLOCKNODE(tdnp); |
---|
2042 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp); |
---|
2043 | } |
---|
2044 | if (error && NFS_ISV4(fdvp)) |
---|
2045 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
2046 | return (error); |
---|
2047 | } |
---|
2048 | |
---|
2049 | /* |
---|
2050 | * nfs hard link create call |
---|
2051 | */ |
---|
2052 | static int |
---|
2053 | nfs_link(struct vop_link_args *ap) |
---|
2054 | { |
---|
2055 | struct vnode *vp = ap->a_vp; |
---|
2056 | struct vnode *tdvp = ap->a_tdvp; |
---|
2057 | struct componentname *cnp = ap->a_cnp; |
---|
2058 | struct nfsnode *np, *tdnp; |
---|
2059 | struct nfsvattr nfsva, dnfsva; |
---|
2060 | int error = 0, attrflag, dattrflag; |
---|
2061 | |
---|
2062 | /* |
---|
2063 | * Push all writes to the server, so that the attribute cache |
---|
2064 | * doesn't get "out of sync" with the server. |
---|
2065 | * XXX There should be a better way! |
---|
2066 | */ |
---|
2067 | VOP_FSYNC(vp, MNT_WAIT, cnp->cn_thread); |
---|
2068 | |
---|
2069 | error = nfsrpc_link(tdvp, vp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2070 | cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &attrflag, |
---|
2071 | &dattrflag, NULL); |
---|
2072 | tdnp = VTONFS(tdvp); |
---|
2073 | NFSLOCKNODE(tdnp); |
---|
2074 | tdnp->n_flag |= NMODIFIED; |
---|
2075 | if (dattrflag != 0) { |
---|
2076 | NFSUNLOCKNODE(tdnp); |
---|
2077 | (void) nfscl_loadattrcache(&tdvp, &dnfsva, NULL, NULL, 0, 1); |
---|
2078 | } else { |
---|
2079 | tdnp->n_attrstamp = 0; |
---|
2080 | NFSUNLOCKNODE(tdnp); |
---|
2081 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp); |
---|
2082 | } |
---|
2083 | if (attrflag) |
---|
2084 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
2085 | else { |
---|
2086 | np = VTONFS(vp); |
---|
2087 | NFSLOCKNODE(np); |
---|
2088 | np->n_attrstamp = 0; |
---|
2089 | NFSUNLOCKNODE(np); |
---|
2090 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
2091 | } |
---|
2092 | /* |
---|
2093 | * If negative lookup caching is enabled, I might as well |
---|
2094 | * add an entry for this node. Not necessary for correctness, |
---|
2095 | * but if negative caching is enabled, then the system |
---|
2096 | * must care about lookup caching hit rate, so... |
---|
2097 | */ |
---|
2098 | if (VFSTONFS(vp->v_mount)->nm_negnametimeo != 0 && |
---|
2099 | (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) { |
---|
2100 | cache_enter_time(tdvp, vp, cnp, &nfsva.na_ctime, NULL); |
---|
2101 | } |
---|
2102 | if (error && NFS_ISV4(vp)) |
---|
2103 | error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0, |
---|
2104 | (gid_t)0); |
---|
2105 | return (error); |
---|
2106 | } |
---|
2107 | |
---|
2108 | /* |
---|
2109 | * nfs symbolic link create call |
---|
2110 | */ |
---|
2111 | static int |
---|
2112 | nfs_symlink(struct vop_symlink_args *ap) |
---|
2113 | { |
---|
2114 | struct vnode *dvp = ap->a_dvp; |
---|
2115 | struct vattr *vap = ap->a_vap; |
---|
2116 | struct componentname *cnp = ap->a_cnp; |
---|
2117 | struct nfsvattr nfsva, dnfsva; |
---|
2118 | struct nfsfh *nfhp; |
---|
2119 | struct nfsnode *np = NULL, *dnp; |
---|
2120 | struct vnode *newvp = NULL; |
---|
2121 | int error = 0, attrflag, dattrflag, ret; |
---|
2122 | |
---|
2123 | vap->va_type = VLNK; |
---|
2124 | error = nfsrpc_symlink(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2125 | ap->a_target, vap, cnp->cn_cred, cnp->cn_thread, &dnfsva, |
---|
2126 | &nfsva, &nfhp, &attrflag, &dattrflag, NULL); |
---|
2127 | if (nfhp) { |
---|
2128 | ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread, |
---|
2129 | &np, NULL, LK_EXCLUSIVE); |
---|
2130 | if (!ret) |
---|
2131 | newvp = NFSTOV(np); |
---|
2132 | else if (!error) |
---|
2133 | error = ret; |
---|
2134 | } |
---|
2135 | if (newvp != NULL) { |
---|
2136 | if (attrflag) |
---|
2137 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
2138 | 0, 1); |
---|
2139 | } else if (!error) { |
---|
2140 | /* |
---|
2141 | * If we do not have an error and we could not extract the |
---|
2142 | * newvp from the response due to the request being NFSv2, we |
---|
2143 | * have to do a lookup in order to obtain a newvp to return. |
---|
2144 | */ |
---|
2145 | error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2146 | cnp->cn_cred, cnp->cn_thread, &np); |
---|
2147 | if (!error) |
---|
2148 | newvp = NFSTOV(np); |
---|
2149 | } |
---|
2150 | if (error) { |
---|
2151 | if (newvp) |
---|
2152 | vput(newvp); |
---|
2153 | if (NFS_ISV4(dvp)) |
---|
2154 | error = nfscl_maperr(cnp->cn_thread, error, |
---|
2155 | vap->va_uid, vap->va_gid); |
---|
2156 | } else { |
---|
2157 | *ap->a_vpp = newvp; |
---|
2158 | } |
---|
2159 | |
---|
2160 | dnp = VTONFS(dvp); |
---|
2161 | NFSLOCKNODE(dnp); |
---|
2162 | dnp->n_flag |= NMODIFIED; |
---|
2163 | if (dattrflag != 0) { |
---|
2164 | NFSUNLOCKNODE(dnp); |
---|
2165 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
2166 | } else { |
---|
2167 | dnp->n_attrstamp = 0; |
---|
2168 | NFSUNLOCKNODE(dnp); |
---|
2169 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
2170 | } |
---|
2171 | /* |
---|
2172 | * If negative lookup caching is enabled, I might as well |
---|
2173 | * add an entry for this node. Not necessary for correctness, |
---|
2174 | * but if negative caching is enabled, then the system |
---|
2175 | * must care about lookup caching hit rate, so... |
---|
2176 | */ |
---|
2177 | if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 && |
---|
2178 | (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) { |
---|
2179 | cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, NULL); |
---|
2180 | } |
---|
2181 | return (error); |
---|
2182 | } |
---|
2183 | |
---|
2184 | /* |
---|
2185 | * nfs make dir call |
---|
2186 | */ |
---|
2187 | static int |
---|
2188 | nfs_mkdir(struct vop_mkdir_args *ap) |
---|
2189 | { |
---|
2190 | struct vnode *dvp = ap->a_dvp; |
---|
2191 | struct vattr *vap = ap->a_vap; |
---|
2192 | struct componentname *cnp = ap->a_cnp; |
---|
2193 | struct nfsnode *np = NULL, *dnp; |
---|
2194 | struct vnode *newvp = NULL; |
---|
2195 | struct vattr vattr; |
---|
2196 | struct nfsfh *nfhp; |
---|
2197 | struct nfsvattr nfsva, dnfsva; |
---|
2198 | int error = 0, attrflag, dattrflag, ret; |
---|
2199 | |
---|
2200 | if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)) != 0) |
---|
2201 | return (error); |
---|
2202 | vap->va_type = VDIR; |
---|
2203 | error = nfsrpc_mkdir(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2204 | vap, cnp->cn_cred, cnp->cn_thread, &dnfsva, &nfsva, &nfhp, |
---|
2205 | &attrflag, &dattrflag, NULL); |
---|
2206 | dnp = VTONFS(dvp); |
---|
2207 | NFSLOCKNODE(dnp); |
---|
2208 | dnp->n_flag |= NMODIFIED; |
---|
2209 | if (dattrflag != 0) { |
---|
2210 | NFSUNLOCKNODE(dnp); |
---|
2211 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
2212 | } else { |
---|
2213 | dnp->n_attrstamp = 0; |
---|
2214 | NFSUNLOCKNODE(dnp); |
---|
2215 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
2216 | } |
---|
2217 | if (nfhp) { |
---|
2218 | ret = nfscl_nget(dvp->v_mount, dvp, nfhp, cnp, cnp->cn_thread, |
---|
2219 | &np, NULL, LK_EXCLUSIVE); |
---|
2220 | if (!ret) { |
---|
2221 | newvp = NFSTOV(np); |
---|
2222 | if (attrflag) |
---|
2223 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, |
---|
2224 | NULL, 0, 1); |
---|
2225 | } else if (!error) |
---|
2226 | error = ret; |
---|
2227 | } |
---|
2228 | if (!error && newvp == NULL) { |
---|
2229 | error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2230 | cnp->cn_cred, cnp->cn_thread, &np); |
---|
2231 | if (!error) { |
---|
2232 | newvp = NFSTOV(np); |
---|
2233 | if (newvp->v_type != VDIR) |
---|
2234 | error = EEXIST; |
---|
2235 | } |
---|
2236 | } |
---|
2237 | if (error) { |
---|
2238 | if (newvp) |
---|
2239 | vput(newvp); |
---|
2240 | if (NFS_ISV4(dvp)) |
---|
2241 | error = nfscl_maperr(cnp->cn_thread, error, |
---|
2242 | vap->va_uid, vap->va_gid); |
---|
2243 | } else { |
---|
2244 | /* |
---|
2245 | * If negative lookup caching is enabled, I might as well |
---|
2246 | * add an entry for this node. Not necessary for correctness, |
---|
2247 | * but if negative caching is enabled, then the system |
---|
2248 | * must care about lookup caching hit rate, so... |
---|
2249 | */ |
---|
2250 | if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 && |
---|
2251 | (cnp->cn_flags & MAKEENTRY) && |
---|
2252 | attrflag != 0 && dattrflag != 0) |
---|
2253 | cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, |
---|
2254 | &dnfsva.na_ctime); |
---|
2255 | *ap->a_vpp = newvp; |
---|
2256 | } |
---|
2257 | return (error); |
---|
2258 | } |
---|
2259 | |
---|
2260 | /* |
---|
2261 | * nfs remove directory call |
---|
2262 | */ |
---|
2263 | static int |
---|
2264 | nfs_rmdir(struct vop_rmdir_args *ap) |
---|
2265 | { |
---|
2266 | struct vnode *vp = ap->a_vp; |
---|
2267 | struct vnode *dvp = ap->a_dvp; |
---|
2268 | struct componentname *cnp = ap->a_cnp; |
---|
2269 | struct nfsnode *dnp; |
---|
2270 | struct nfsvattr dnfsva; |
---|
2271 | int error, dattrflag; |
---|
2272 | |
---|
2273 | if (dvp == vp) |
---|
2274 | return (EINVAL); |
---|
2275 | error = nfsrpc_rmdir(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
---|
2276 | cnp->cn_cred, cnp->cn_thread, &dnfsva, &dattrflag, NULL); |
---|
2277 | dnp = VTONFS(dvp); |
---|
2278 | NFSLOCKNODE(dnp); |
---|
2279 | dnp->n_flag |= NMODIFIED; |
---|
2280 | if (dattrflag != 0) { |
---|
2281 | NFSUNLOCKNODE(dnp); |
---|
2282 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
2283 | } else { |
---|
2284 | dnp->n_attrstamp = 0; |
---|
2285 | NFSUNLOCKNODE(dnp); |
---|
2286 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); |
---|
2287 | } |
---|
2288 | |
---|
2289 | cache_purge(dvp); |
---|
2290 | cache_purge(vp); |
---|
2291 | if (error && NFS_ISV4(dvp)) |
---|
2292 | error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0, |
---|
2293 | (gid_t)0); |
---|
2294 | /* |
---|
2295 | * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. |
---|
2296 | */ |
---|
2297 | if (error == ENOENT) |
---|
2298 | error = 0; |
---|
2299 | return (error); |
---|
2300 | } |
---|
2301 | |
---|
2302 | /* |
---|
2303 | * nfs readdir call |
---|
2304 | */ |
---|
2305 | static int |
---|
2306 | nfs_readdir(struct vop_readdir_args *ap) |
---|
2307 | { |
---|
2308 | struct vnode *vp = ap->a_vp; |
---|
2309 | struct nfsnode *np = VTONFS(vp); |
---|
2310 | struct uio *uio = ap->a_uio; |
---|
2311 | ssize_t tresid, left; |
---|
2312 | int error = 0; |
---|
2313 | struct vattr vattr; |
---|
2314 | |
---|
2315 | if (ap->a_eofflag != NULL) |
---|
2316 | *ap->a_eofflag = 0; |
---|
2317 | if (vp->v_type != VDIR) |
---|
2318 | return(EPERM); |
---|
2319 | |
---|
2320 | /* |
---|
2321 | * First, check for hit on the EOF offset cache |
---|
2322 | */ |
---|
2323 | if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset && |
---|
2324 | (np->n_flag & NMODIFIED) == 0) { |
---|
2325 | if (VOP_GETATTR(vp, &vattr, ap->a_cred) == 0) { |
---|
2326 | NFSLOCKNODE(np); |
---|
2327 | if ((NFS_ISV4(vp) && np->n_change == vattr.va_filerev) || |
---|
2328 | !NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) { |
---|
2329 | NFSUNLOCKNODE(np); |
---|
2330 | NFSINCRGLOBAL(nfsstatsv1.direofcache_hits); |
---|
2331 | if (ap->a_eofflag != NULL) |
---|
2332 | *ap->a_eofflag = 1; |
---|
2333 | return (0); |
---|
2334 | } else |
---|
2335 | NFSUNLOCKNODE(np); |
---|
2336 | } |
---|
2337 | } |
---|
2338 | |
---|
2339 | /* |
---|
2340 | * NFS always guarantees that directory entries don't straddle |
---|
2341 | * DIRBLKSIZ boundaries. As such, we need to limit the size |
---|
2342 | * to an exact multiple of DIRBLKSIZ, to avoid copying a partial |
---|
2343 | * directory entry. |
---|
2344 | */ |
---|
2345 | left = uio->uio_resid % DIRBLKSIZ; |
---|
2346 | if (left == uio->uio_resid) |
---|
2347 | return (EINVAL); |
---|
2348 | uio->uio_resid -= left; |
---|
2349 | |
---|
2350 | /* |
---|
2351 | * Call ncl_bioread() to do the real work. |
---|
2352 | */ |
---|
2353 | tresid = uio->uio_resid; |
---|
2354 | error = ncl_bioread(vp, uio, 0, ap->a_cred); |
---|
2355 | |
---|
2356 | if (!error && uio->uio_resid == tresid) { |
---|
2357 | NFSINCRGLOBAL(nfsstatsv1.direofcache_misses); |
---|
2358 | if (ap->a_eofflag != NULL) |
---|
2359 | *ap->a_eofflag = 1; |
---|
2360 | } |
---|
2361 | |
---|
2362 | /* Add the partial DIRBLKSIZ (left) back in. */ |
---|
2363 | uio->uio_resid += left; |
---|
2364 | return (error); |
---|
2365 | } |
---|
2366 | |
---|
2367 | /* |
---|
2368 | * Readdir rpc call. |
---|
2369 | * Called from below the buffer cache by ncl_doio(). |
---|
2370 | */ |
---|
2371 | int |
---|
2372 | ncl_readdirrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred, |
---|
2373 | struct thread *td) |
---|
2374 | { |
---|
2375 | struct nfsvattr nfsva; |
---|
2376 | nfsuint64 *cookiep, cookie; |
---|
2377 | struct nfsnode *dnp = VTONFS(vp); |
---|
2378 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); |
---|
2379 | int error = 0, eof, attrflag; |
---|
2380 | |
---|
2381 | KASSERT(uiop->uio_iovcnt == 1 && |
---|
2382 | (uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 && |
---|
2383 | (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0, |
---|
2384 | ("nfs readdirrpc bad uio")); |
---|
2385 | |
---|
2386 | /* |
---|
2387 | * If there is no cookie, assume directory was stale. |
---|
2388 | */ |
---|
2389 | ncl_dircookie_lock(dnp); |
---|
2390 | cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0); |
---|
2391 | if (cookiep) { |
---|
2392 | cookie = *cookiep; |
---|
2393 | ncl_dircookie_unlock(dnp); |
---|
2394 | } else { |
---|
2395 | ncl_dircookie_unlock(dnp); |
---|
2396 | return (NFSERR_BAD_COOKIE); |
---|
2397 | } |
---|
2398 | |
---|
2399 | if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) |
---|
2400 | (void)ncl_fsinfo(nmp, vp, cred, td); |
---|
2401 | |
---|
2402 | error = nfsrpc_readdir(vp, uiop, &cookie, cred, td, &nfsva, |
---|
2403 | &attrflag, &eof, NULL); |
---|
2404 | if (attrflag) |
---|
2405 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
2406 | |
---|
2407 | if (!error) { |
---|
2408 | /* |
---|
2409 | * We are now either at the end of the directory or have filled |
---|
2410 | * the block. |
---|
2411 | */ |
---|
2412 | if (eof) |
---|
2413 | dnp->n_direofoffset = uiop->uio_offset; |
---|
2414 | else { |
---|
2415 | if (uiop->uio_resid > 0) |
---|
2416 | printf("EEK! readdirrpc resid > 0\n"); |
---|
2417 | ncl_dircookie_lock(dnp); |
---|
2418 | cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1); |
---|
2419 | *cookiep = cookie; |
---|
2420 | ncl_dircookie_unlock(dnp); |
---|
2421 | } |
---|
2422 | } else if (NFS_ISV4(vp)) { |
---|
2423 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
2424 | } |
---|
2425 | return (error); |
---|
2426 | } |
---|
2427 | |
---|
2428 | /* |
---|
2429 | * NFS V3 readdir plus RPC. Used in place of ncl_readdirrpc(). |
---|
2430 | */ |
---|
2431 | int |
---|
2432 | ncl_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred, |
---|
2433 | struct thread *td) |
---|
2434 | { |
---|
2435 | struct nfsvattr nfsva; |
---|
2436 | nfsuint64 *cookiep, cookie; |
---|
2437 | struct nfsnode *dnp = VTONFS(vp); |
---|
2438 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); |
---|
2439 | int error = 0, attrflag, eof; |
---|
2440 | |
---|
2441 | KASSERT(uiop->uio_iovcnt == 1 && |
---|
2442 | (uiop->uio_offset & (DIRBLKSIZ - 1)) == 0 && |
---|
2443 | (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0, |
---|
2444 | ("nfs readdirplusrpc bad uio")); |
---|
2445 | |
---|
2446 | /* |
---|
2447 | * If there is no cookie, assume directory was stale. |
---|
2448 | */ |
---|
2449 | ncl_dircookie_lock(dnp); |
---|
2450 | cookiep = ncl_getcookie(dnp, uiop->uio_offset, 0); |
---|
2451 | if (cookiep) { |
---|
2452 | cookie = *cookiep; |
---|
2453 | ncl_dircookie_unlock(dnp); |
---|
2454 | } else { |
---|
2455 | ncl_dircookie_unlock(dnp); |
---|
2456 | return (NFSERR_BAD_COOKIE); |
---|
2457 | } |
---|
2458 | |
---|
2459 | if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) |
---|
2460 | (void)ncl_fsinfo(nmp, vp, cred, td); |
---|
2461 | error = nfsrpc_readdirplus(vp, uiop, &cookie, cred, td, &nfsva, |
---|
2462 | &attrflag, &eof, NULL); |
---|
2463 | if (attrflag) |
---|
2464 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); |
---|
2465 | |
---|
2466 | if (!error) { |
---|
2467 | /* |
---|
2468 | * We are now either at end of the directory or have filled the |
---|
2469 | * the block. |
---|
2470 | */ |
---|
2471 | if (eof) |
---|
2472 | dnp->n_direofoffset = uiop->uio_offset; |
---|
2473 | else { |
---|
2474 | if (uiop->uio_resid > 0) |
---|
2475 | printf("EEK! readdirplusrpc resid > 0\n"); |
---|
2476 | ncl_dircookie_lock(dnp); |
---|
2477 | cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1); |
---|
2478 | *cookiep = cookie; |
---|
2479 | ncl_dircookie_unlock(dnp); |
---|
2480 | } |
---|
2481 | } else if (NFS_ISV4(vp)) { |
---|
2482 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
2483 | } |
---|
2484 | return (error); |
---|
2485 | } |
---|
2486 | |
---|
2487 | /* |
---|
2488 | * Silly rename. To make the NFS filesystem that is stateless look a little |
---|
2489 | * more like the "ufs" a remove of an active vnode is translated to a rename |
---|
2490 | * to a funny looking filename that is removed by nfs_inactive on the |
---|
2491 | * nfsnode. There is the potential for another process on a different client |
---|
2492 | * to create the same funny name between the nfs_lookitup() fails and the |
---|
2493 | * nfs_rename() completes, but... |
---|
2494 | */ |
---|
2495 | static int |
---|
2496 | nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) |
---|
2497 | { |
---|
2498 | struct sillyrename *sp; |
---|
2499 | struct nfsnode *np; |
---|
2500 | int error; |
---|
2501 | short pid; |
---|
2502 | unsigned int lticks; |
---|
2503 | |
---|
2504 | cache_purge(dvp); |
---|
2505 | np = VTONFS(vp); |
---|
2506 | KASSERT(vp->v_type != VDIR, ("nfs: sillyrename dir")); |
---|
2507 | sp = malloc(sizeof (struct sillyrename), |
---|
2508 | M_NEWNFSREQ, M_WAITOK); |
---|
2509 | sp->s_cred = crhold(cnp->cn_cred); |
---|
2510 | sp->s_dvp = dvp; |
---|
2511 | VREF(dvp); |
---|
2512 | |
---|
2513 | /* |
---|
2514 | * Fudge together a funny name. |
---|
2515 | * Changing the format of the funny name to accommodate more |
---|
2516 | * sillynames per directory. |
---|
2517 | * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is |
---|
2518 | * CPU ticks since boot. |
---|
2519 | */ |
---|
2520 | #ifndef __rtems__ |
---|
2521 | pid = cnp->cn_thread->td_proc->p_pid; |
---|
2522 | #else /* __rtems__ */ |
---|
2523 | pid = 1; |
---|
2524 | #endif /* __rtems__ */ |
---|
2525 | lticks = (unsigned int)ticks; |
---|
2526 | for ( ; ; ) { |
---|
2527 | sp->s_namlen = sprintf(sp->s_name, |
---|
2528 | ".nfs.%08x.%04x4.4", lticks, |
---|
2529 | pid); |
---|
2530 | if (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, |
---|
2531 | cnp->cn_thread, NULL)) |
---|
2532 | break; |
---|
2533 | lticks++; |
---|
2534 | } |
---|
2535 | error = nfs_renameit(dvp, vp, cnp, sp); |
---|
2536 | if (error) |
---|
2537 | goto bad; |
---|
2538 | error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, |
---|
2539 | cnp->cn_thread, &np); |
---|
2540 | np->n_sillyrename = sp; |
---|
2541 | return (0); |
---|
2542 | bad: |
---|
2543 | vrele(sp->s_dvp); |
---|
2544 | crfree(sp->s_cred); |
---|
2545 | free(sp, M_NEWNFSREQ); |
---|
2546 | return (error); |
---|
2547 | } |
---|
2548 | |
---|
2549 | /* |
---|
2550 | * Look up a file name and optionally either update the file handle or |
---|
2551 | * allocate an nfsnode, depending on the value of npp. |
---|
2552 | * npp == NULL --> just do the lookup |
---|
2553 | * *npp == NULL --> allocate a new nfsnode and make sure attributes are |
---|
2554 | * handled too |
---|
2555 | * *npp != NULL --> update the file handle in the vnode |
---|
2556 | */ |
---|
2557 | static int |
---|
2558 | nfs_lookitup(struct vnode *dvp, char *name, int len, struct ucred *cred, |
---|
2559 | struct thread *td, struct nfsnode **npp) |
---|
2560 | { |
---|
2561 | struct vnode *newvp = NULL, *vp; |
---|
2562 | struct nfsnode *np, *dnp = VTONFS(dvp); |
---|
2563 | struct nfsfh *nfhp, *onfhp; |
---|
2564 | struct nfsvattr nfsva, dnfsva; |
---|
2565 | struct componentname cn; |
---|
2566 | int error = 0, attrflag, dattrflag; |
---|
2567 | u_int hash; |
---|
2568 | |
---|
2569 | error = nfsrpc_lookup(dvp, name, len, cred, td, &dnfsva, &nfsva, |
---|
2570 | &nfhp, &attrflag, &dattrflag, NULL); |
---|
2571 | if (dattrflag) |
---|
2572 | (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); |
---|
2573 | if (npp && !error) { |
---|
2574 | if (*npp != NULL) { |
---|
2575 | np = *npp; |
---|
2576 | vp = NFSTOV(np); |
---|
2577 | /* |
---|
2578 | * For NFSv4, check to see if it is the same name and |
---|
2579 | * replace the name, if it is different. |
---|
2580 | */ |
---|
2581 | if (np->n_v4 != NULL && nfsva.na_type == VREG && |
---|
2582 | (np->n_v4->n4_namelen != len || |
---|
2583 | NFSBCMP(name, NFS4NODENAME(np->n_v4), len) || |
---|
2584 | dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen || |
---|
2585 | NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data, |
---|
2586 | dnp->n_fhp->nfh_len))) { |
---|
2587 | #ifdef notdef |
---|
2588 | { char nnn[100]; int nnnl; |
---|
2589 | nnnl = (len < 100) ? len : 99; |
---|
2590 | bcopy(name, nnn, nnnl); |
---|
2591 | nnn[nnnl] = '\0'; |
---|
2592 | printf("replace=%s\n",nnn); |
---|
2593 | } |
---|
2594 | #endif |
---|
2595 | free(np->n_v4, M_NFSV4NODE); |
---|
2596 | np->n_v4 = malloc( |
---|
2597 | sizeof (struct nfsv4node) + |
---|
2598 | dnp->n_fhp->nfh_len + len - 1, |
---|
2599 | M_NFSV4NODE, M_WAITOK); |
---|
2600 | np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len; |
---|
2601 | np->n_v4->n4_namelen = len; |
---|
2602 | NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data, |
---|
2603 | dnp->n_fhp->nfh_len); |
---|
2604 | NFSBCOPY(name, NFS4NODENAME(np->n_v4), len); |
---|
2605 | } |
---|
2606 | hash = fnv_32_buf(nfhp->nfh_fh, nfhp->nfh_len, |
---|
2607 | FNV1_32_INIT); |
---|
2608 | onfhp = np->n_fhp; |
---|
2609 | /* |
---|
2610 | * Rehash node for new file handle. |
---|
2611 | */ |
---|
2612 | vfs_hash_rehash(vp, hash); |
---|
2613 | np->n_fhp = nfhp; |
---|
2614 | if (onfhp != NULL) |
---|
2615 | free(onfhp, M_NFSFH); |
---|
2616 | newvp = NFSTOV(np); |
---|
2617 | } else if (NFS_CMPFH(dnp, nfhp->nfh_fh, nfhp->nfh_len)) { |
---|
2618 | free(nfhp, M_NFSFH); |
---|
2619 | VREF(dvp); |
---|
2620 | newvp = dvp; |
---|
2621 | } else { |
---|
2622 | cn.cn_nameptr = name; |
---|
2623 | cn.cn_namelen = len; |
---|
2624 | error = nfscl_nget(dvp->v_mount, dvp, nfhp, &cn, td, |
---|
2625 | &np, NULL, LK_EXCLUSIVE); |
---|
2626 | if (error) |
---|
2627 | return (error); |
---|
2628 | newvp = NFSTOV(np); |
---|
2629 | } |
---|
2630 | if (!attrflag && *npp == NULL) { |
---|
2631 | if (newvp == dvp) |
---|
2632 | vrele(newvp); |
---|
2633 | else |
---|
2634 | vput(newvp); |
---|
2635 | return (ENOENT); |
---|
2636 | } |
---|
2637 | if (attrflag) |
---|
2638 | (void) nfscl_loadattrcache(&newvp, &nfsva, NULL, NULL, |
---|
2639 | 0, 1); |
---|
2640 | } |
---|
2641 | if (npp && *npp == NULL) { |
---|
2642 | if (error) { |
---|
2643 | if (newvp) { |
---|
2644 | if (newvp == dvp) |
---|
2645 | vrele(newvp); |
---|
2646 | else |
---|
2647 | vput(newvp); |
---|
2648 | } |
---|
2649 | } else |
---|
2650 | *npp = np; |
---|
2651 | } |
---|
2652 | if (error && NFS_ISV4(dvp)) |
---|
2653 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
2654 | return (error); |
---|
2655 | } |
---|
2656 | |
---|
2657 | /* |
---|
2658 | * Nfs Version 3 and 4 commit rpc |
---|
2659 | */ |
---|
2660 | int |
---|
2661 | ncl_commit(struct vnode *vp, u_quad_t offset, int cnt, struct ucred *cred, |
---|
2662 | struct thread *td) |
---|
2663 | { |
---|
2664 | struct nfsvattr nfsva; |
---|
2665 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); |
---|
2666 | struct nfsnode *np; |
---|
2667 | struct uio uio; |
---|
2668 | int error, attrflag; |
---|
2669 | |
---|
2670 | np = VTONFS(vp); |
---|
2671 | error = EIO; |
---|
2672 | attrflag = 0; |
---|
2673 | if (NFSHASPNFS(nmp) && (np->n_flag & NDSCOMMIT) != 0) { |
---|
2674 | uio.uio_offset = offset; |
---|
2675 | uio.uio_resid = cnt; |
---|
2676 | error = nfscl_doiods(vp, &uio, NULL, NULL, |
---|
2677 | NFSV4OPEN_ACCESSWRITE, 1, cred, td); |
---|
2678 | if (error != 0) { |
---|
2679 | NFSLOCKNODE(np); |
---|
2680 | np->n_flag &= ~NDSCOMMIT; |
---|
2681 | NFSUNLOCKNODE(np); |
---|
2682 | } |
---|
2683 | } |
---|
2684 | if (error != 0) { |
---|
2685 | mtx_lock(&nmp->nm_mtx); |
---|
2686 | if ((nmp->nm_state & NFSSTA_HASWRITEVERF) == 0) { |
---|
2687 | mtx_unlock(&nmp->nm_mtx); |
---|
2688 | return (0); |
---|
2689 | } |
---|
2690 | mtx_unlock(&nmp->nm_mtx); |
---|
2691 | error = nfsrpc_commit(vp, offset, cnt, cred, td, &nfsva, |
---|
2692 | &attrflag, NULL); |
---|
2693 | } |
---|
2694 | if (attrflag != 0) |
---|
2695 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, |
---|
2696 | 0, 1); |
---|
2697 | if (error != 0 && NFS_ISV4(vp)) |
---|
2698 | error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); |
---|
2699 | return (error); |
---|
2700 | } |
---|
2701 | |
---|
2702 | /* |
---|
2703 | * Strategy routine. |
---|
2704 | * For async requests when nfsiod(s) are running, queue the request by |
---|
2705 | * calling ncl_asyncio(), otherwise just all ncl_doio() to do the |
---|
2706 | * request. |
---|
2707 | */ |
---|
2708 | static int |
---|
2709 | nfs_strategy(struct vop_strategy_args *ap) |
---|
2710 | { |
---|
2711 | struct buf *bp; |
---|
2712 | struct vnode *vp; |
---|
2713 | struct ucred *cr; |
---|
2714 | |
---|
2715 | bp = ap->a_bp; |
---|
2716 | vp = ap->a_vp; |
---|
2717 | KASSERT(bp->b_vp == vp, ("missing b_getvp")); |
---|
2718 | KASSERT(!(bp->b_flags & B_DONE), |
---|
2719 | ("nfs_strategy: buffer %p unexpectedly marked B_DONE", bp)); |
---|
2720 | BUF_ASSERT_HELD(bp); |
---|
2721 | |
---|
2722 | if (vp->v_type == VREG && bp->b_blkno == bp->b_lblkno) |
---|
2723 | bp->b_blkno = bp->b_lblkno * (vp->v_bufobj.bo_bsize / |
---|
2724 | DEV_BSIZE); |
---|
2725 | if (bp->b_iocmd == BIO_READ) |
---|
2726 | cr = bp->b_rcred; |
---|
2727 | else |
---|
2728 | cr = bp->b_wcred; |
---|
2729 | |
---|
2730 | /* |
---|
2731 | * If the op is asynchronous and an i/o daemon is waiting |
---|
2732 | * queue the request, wake it up and wait for completion |
---|
2733 | * otherwise just do it ourselves. |
---|
2734 | */ |
---|
2735 | if ((bp->b_flags & B_ASYNC) == 0 || |
---|
2736 | ncl_asyncio(VFSTONFS(vp->v_mount), bp, NOCRED, curthread)) |
---|
2737 | (void) ncl_doio(vp, bp, cr, curthread, 1); |
---|
2738 | return (0); |
---|
2739 | } |
---|
2740 | |
---|
2741 | /* |
---|
2742 | * fsync vnode op. Just call ncl_flush() with commit == 1. |
---|
2743 | */ |
---|
2744 | /* ARGSUSED */ |
---|
2745 | static int |
---|
2746 | nfs_fsync(struct vop_fsync_args *ap) |
---|
2747 | { |
---|
2748 | |
---|
2749 | if (ap->a_vp->v_type != VREG) { |
---|
2750 | /* |
---|
2751 | * For NFS, metadata is changed synchronously on the server, |
---|
2752 | * so there is nothing to flush. Also, ncl_flush() clears |
---|
2753 | * the NMODIFIED flag and that shouldn't be done here for |
---|
2754 | * directories. |
---|
2755 | */ |
---|
2756 | return (0); |
---|
2757 | } |
---|
2758 | return (ncl_flush(ap->a_vp, ap->a_waitfor, ap->a_td, 1, 0)); |
---|
2759 | } |
---|
2760 | |
---|
2761 | /* |
---|
2762 | * Flush all the blocks associated with a vnode. |
---|
2763 | * Walk through the buffer pool and push any dirty pages |
---|
2764 | * associated with the vnode. |
---|
2765 | * If the called_from_renewthread argument is TRUE, it has been called |
---|
2766 | * from the NFSv4 renew thread and, as such, cannot block indefinitely |
---|
2767 | * waiting for a buffer write to complete. |
---|
2768 | */ |
---|
2769 | int |
---|
2770 | ncl_flush(struct vnode *vp, int waitfor, struct thread *td, |
---|
2771 | int commit, int called_from_renewthread) |
---|
2772 | { |
---|
2773 | struct nfsnode *np = VTONFS(vp); |
---|
2774 | struct buf *bp; |
---|
2775 | int i; |
---|
2776 | struct buf *nbp; |
---|
2777 | struct nfsmount *nmp = VFSTONFS(vp->v_mount); |
---|
2778 | int error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos; |
---|
2779 | int passone = 1, trycnt = 0; |
---|
2780 | u_quad_t off, endoff, toff; |
---|
2781 | struct ucred* wcred = NULL; |
---|
2782 | struct buf **bvec = NULL; |
---|
2783 | struct bufobj *bo; |
---|
2784 | #ifndef NFS_COMMITBVECSIZ |
---|
2785 | #define NFS_COMMITBVECSIZ 20 |
---|
2786 | #endif |
---|
2787 | struct buf *bvec_on_stack[NFS_COMMITBVECSIZ]; |
---|
2788 | u_int bvecsize = 0, bveccount; |
---|
2789 | |
---|
2790 | if (called_from_renewthread != 0) |
---|
2791 | slptimeo = hz; |
---|
2792 | if (nmp->nm_flag & NFSMNT_INT) |
---|
2793 | slpflag = PCATCH; |
---|
2794 | if (!commit) |
---|
2795 | passone = 0; |
---|
2796 | bo = &vp->v_bufobj; |
---|
2797 | /* |
---|
2798 | * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the |
---|
2799 | * server, but has not been committed to stable storage on the server |
---|
2800 | * yet. On the first pass, the byte range is worked out and the commit |
---|
2801 | * rpc is done. On the second pass, ncl_writebp() is called to do the |
---|
2802 | * job. |
---|
2803 | */ |
---|
2804 | again: |
---|
2805 | off = (u_quad_t)-1; |
---|
2806 | endoff = 0; |
---|
2807 | bvecpos = 0; |
---|
2808 | if (NFS_ISV34(vp) && commit) { |
---|
2809 | if (bvec != NULL && bvec != bvec_on_stack) |
---|
2810 | free(bvec, M_TEMP); |
---|
2811 | /* |
---|
2812 | * Count up how many buffers waiting for a commit. |
---|
2813 | */ |
---|
2814 | bveccount = 0; |
---|
2815 | BO_LOCK(bo); |
---|
2816 | TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { |
---|
2817 | if (!BUF_ISLOCKED(bp) && |
---|
2818 | (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) |
---|
2819 | == (B_DELWRI | B_NEEDCOMMIT)) |
---|
2820 | bveccount++; |
---|
2821 | } |
---|
2822 | /* |
---|
2823 | * Allocate space to remember the list of bufs to commit. It is |
---|
2824 | * important to use M_NOWAIT here to avoid a race with nfs_write. |
---|
2825 | * If we can't get memory (for whatever reason), we will end up |
---|
2826 | * committing the buffers one-by-one in the loop below. |
---|
2827 | */ |
---|
2828 | if (bveccount > NFS_COMMITBVECSIZ) { |
---|
2829 | /* |
---|
2830 | * Release the vnode interlock to avoid a lock |
---|
2831 | * order reversal. |
---|
2832 | */ |
---|
2833 | BO_UNLOCK(bo); |
---|
2834 | bvec = (struct buf **) |
---|
2835 | malloc(bveccount * sizeof(struct buf *), |
---|
2836 | M_TEMP, M_NOWAIT); |
---|
2837 | BO_LOCK(bo); |
---|
2838 | if (bvec == NULL) { |
---|
2839 | bvec = bvec_on_stack; |
---|
2840 | bvecsize = NFS_COMMITBVECSIZ; |
---|
2841 | } else |
---|
2842 | bvecsize = bveccount; |
---|
2843 | } else { |
---|
2844 | bvec = bvec_on_stack; |
---|
2845 | bvecsize = NFS_COMMITBVECSIZ; |
---|
2846 | } |
---|
2847 | TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { |
---|
2848 | if (bvecpos >= bvecsize) |
---|
2849 | break; |
---|
2850 | if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) { |
---|
2851 | nbp = TAILQ_NEXT(bp, b_bobufs); |
---|
2852 | continue; |
---|
2853 | } |
---|
2854 | if ((bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) != |
---|
2855 | (B_DELWRI | B_NEEDCOMMIT)) { |
---|
2856 | BUF_UNLOCK(bp); |
---|
2857 | nbp = TAILQ_NEXT(bp, b_bobufs); |
---|
2858 | continue; |
---|
2859 | } |
---|
2860 | BO_UNLOCK(bo); |
---|
2861 | bremfree(bp); |
---|
2862 | /* |
---|
2863 | * Work out if all buffers are using the same cred |
---|
2864 | * so we can deal with them all with one commit. |
---|
2865 | * |
---|
2866 | * NOTE: we are not clearing B_DONE here, so we have |
---|
2867 | * to do it later on in this routine if we intend to |
---|
2868 | * initiate I/O on the bp. |
---|
2869 | * |
---|
2870 | * Note: to avoid loopback deadlocks, we do not |
---|
2871 | * assign b_runningbufspace. |
---|
2872 | */ |
---|
2873 | if (wcred == NULL) |
---|
2874 | wcred = bp->b_wcred; |
---|
2875 | else if (wcred != bp->b_wcred) |
---|
2876 | wcred = NOCRED; |
---|
2877 | vfs_busy_pages(bp, 1); |
---|
2878 | |
---|
2879 | BO_LOCK(bo); |
---|
2880 | /* |
---|
2881 | * bp is protected by being locked, but nbp is not |
---|
2882 | * and vfs_busy_pages() may sleep. We have to |
---|
2883 | * recalculate nbp. |
---|
2884 | */ |
---|
2885 | nbp = TAILQ_NEXT(bp, b_bobufs); |
---|
2886 | |
---|
2887 | /* |
---|
2888 | * A list of these buffers is kept so that the |
---|
2889 | * second loop knows which buffers have actually |
---|
2890 | * been committed. This is necessary, since there |
---|
2891 | * may be a race between the commit rpc and new |
---|
2892 | * uncommitted writes on the file. |
---|
2893 | */ |
---|
2894 | bvec[bvecpos++] = bp; |
---|
2895 | toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + |
---|
2896 | bp->b_dirtyoff; |
---|
2897 | if (toff < off) |
---|
2898 | off = toff; |
---|
2899 | toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); |
---|
2900 | if (toff > endoff) |
---|
2901 | endoff = toff; |
---|
2902 | } |
---|
2903 | BO_UNLOCK(bo); |
---|
2904 | } |
---|
2905 | if (bvecpos > 0) { |
---|
2906 | /* |
---|
2907 | * Commit data on the server, as required. |
---|
2908 | * If all bufs are using the same wcred, then use that with |
---|
2909 | * one call for all of them, otherwise commit each one |
---|
2910 | * separately. |
---|
2911 | */ |
---|
2912 | if (wcred != NOCRED) |
---|
2913 | retv = ncl_commit(vp, off, (int)(endoff - off), |
---|
2914 | wcred, td); |
---|
2915 | else { |
---|
2916 | retv = 0; |
---|
2917 | for (i = 0; i < bvecpos; i++) { |
---|
2918 | off_t off, size; |
---|
2919 | bp = bvec[i]; |
---|
2920 | off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + |
---|
2921 | bp->b_dirtyoff; |
---|
2922 | size = (u_quad_t)(bp->b_dirtyend |
---|
2923 | - bp->b_dirtyoff); |
---|
2924 | retv = ncl_commit(vp, off, (int)size, |
---|
2925 | bp->b_wcred, td); |
---|
2926 | if (retv) break; |
---|
2927 | } |
---|
2928 | } |
---|
2929 | |
---|
2930 | if (retv == NFSERR_STALEWRITEVERF) |
---|
2931 | ncl_clearcommit(vp->v_mount); |
---|
2932 | |
---|
2933 | /* |
---|
2934 | * Now, either mark the blocks I/O done or mark the |
---|
2935 | * blocks dirty, depending on whether the commit |
---|
2936 | * succeeded. |
---|
2937 | */ |
---|
2938 | for (i = 0; i < bvecpos; i++) { |
---|
2939 | bp = bvec[i]; |
---|
2940 | bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK); |
---|
2941 | if (retv) { |
---|
2942 | /* |
---|
2943 | * Error, leave B_DELWRI intact |
---|
2944 | */ |
---|
2945 | vfs_unbusy_pages(bp); |
---|
2946 | brelse(bp); |
---|
2947 | } else { |
---|
2948 | /* |
---|
2949 | * Success, remove B_DELWRI ( bundirty() ). |
---|
2950 | * |
---|
2951 | * b_dirtyoff/b_dirtyend seem to be NFS |
---|
2952 | * specific. We should probably move that |
---|
2953 | * into bundirty(). XXX |
---|
2954 | */ |
---|
2955 | bufobj_wref(bo); |
---|
2956 | bp->b_flags |= B_ASYNC; |
---|
2957 | bundirty(bp); |
---|
2958 | bp->b_flags &= ~B_DONE; |
---|
2959 | bp->b_ioflags &= ~BIO_ERROR; |
---|
2960 | bp->b_dirtyoff = bp->b_dirtyend = 0; |
---|
2961 | bufdone(bp); |
---|
2962 | } |
---|
2963 | } |
---|
2964 | } |
---|
2965 | |
---|
2966 | /* |
---|
2967 | * Start/do any write(s) that are required. |
---|
2968 | */ |
---|
2969 | loop: |
---|
2970 | BO_LOCK(bo); |
---|
2971 | TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { |
---|
2972 | if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) { |
---|
2973 | if (waitfor != MNT_WAIT || passone) |
---|
2974 | continue; |
---|
2975 | |
---|
2976 | error = BUF_TIMELOCK(bp, |
---|
2977 | LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, |
---|
2978 | BO_LOCKPTR(bo), "nfsfsync", slpflag, slptimeo); |
---|
2979 | if (error == 0) { |
---|
2980 | BUF_UNLOCK(bp); |
---|
2981 | goto loop; |
---|
2982 | } |
---|
2983 | if (error == ENOLCK) { |
---|
2984 | error = 0; |
---|
2985 | goto loop; |
---|
2986 | } |
---|
2987 | if (called_from_renewthread != 0) { |
---|
2988 | /* |
---|
2989 | * Return EIO so the flush will be retried |
---|
2990 | * later. |
---|
2991 | */ |
---|
2992 | error = EIO; |
---|
2993 | goto done; |
---|
2994 | } |
---|
2995 | if (newnfs_sigintr(nmp, td)) { |
---|
2996 | error = EINTR; |
---|
2997 | goto done; |
---|
2998 | } |
---|
2999 | if (slpflag == PCATCH) { |
---|
3000 | slpflag = 0; |
---|
3001 | slptimeo = 2 * hz; |
---|
3002 | } |
---|
3003 | goto loop; |
---|
3004 | } |
---|
3005 | if ((bp->b_flags & B_DELWRI) == 0) |
---|
3006 | panic("nfs_fsync: not dirty"); |
---|
3007 | if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) { |
---|
3008 | BUF_UNLOCK(bp); |
---|
3009 | continue; |
---|
3010 | } |
---|
3011 | BO_UNLOCK(bo); |
---|
3012 | bremfree(bp); |
---|
3013 | if (passone || !commit) |
---|
3014 | bp->b_flags |= B_ASYNC; |
---|
3015 | else |
---|
3016 | bp->b_flags |= B_ASYNC; |
---|
3017 | bwrite(bp); |
---|
3018 | if (newnfs_sigintr(nmp, td)) { |
---|
3019 | error = EINTR; |
---|
3020 | goto done; |
---|
3021 | } |
---|
3022 | goto loop; |
---|
3023 | } |
---|
3024 | if (passone) { |
---|
3025 | passone = 0; |
---|
3026 | BO_UNLOCK(bo); |
---|
3027 | goto again; |
---|
3028 | } |
---|
3029 | if (waitfor == MNT_WAIT) { |
---|
3030 | while (bo->bo_numoutput) { |
---|
3031 | error = bufobj_wwait(bo, slpflag, slptimeo); |
---|
3032 | if (error) { |
---|
3033 | BO_UNLOCK(bo); |
---|
3034 | if (called_from_renewthread != 0) { |
---|
3035 | /* |
---|
3036 | * Return EIO so that the flush will be |
---|
3037 | * retried later. |
---|
3038 | */ |
---|
3039 | error = EIO; |
---|
3040 | goto done; |
---|
3041 | } |
---|
3042 | error = newnfs_sigintr(nmp, td); |
---|
3043 | if (error) |
---|
3044 | goto done; |
---|
3045 | if (slpflag == PCATCH) { |
---|
3046 | slpflag = 0; |
---|
3047 | slptimeo = 2 * hz; |
---|
3048 | } |
---|
3049 | BO_LOCK(bo); |
---|
3050 | } |
---|
3051 | } |
---|
3052 | if (bo->bo_dirty.bv_cnt != 0 && commit) { |
---|
3053 | BO_UNLOCK(bo); |
---|
3054 | goto loop; |
---|
3055 | } |
---|
3056 | /* |
---|
3057 | * Wait for all the async IO requests to drain |
---|
3058 | */ |
---|
3059 | BO_UNLOCK(bo); |
---|
3060 | NFSLOCKNODE(np); |
---|
3061 | while (np->n_directio_asyncwr > 0) { |
---|
3062 | np->n_flag |= NFSYNCWAIT; |
---|
3063 | error = newnfs_msleep(td, &np->n_directio_asyncwr, |
---|
3064 | &np->n_mtx, slpflag | (PRIBIO + 1), |
---|
3065 | "nfsfsync", 0); |
---|
3066 | if (error) { |
---|
3067 | if (newnfs_sigintr(nmp, td)) { |
---|
3068 | NFSUNLOCKNODE(np); |
---|
3069 | error = EINTR; |
---|
3070 | goto done; |
---|
3071 | } |
---|
3072 | } |
---|
3073 | } |
---|
3074 | NFSUNLOCKNODE(np); |
---|
3075 | } else |
---|
3076 | BO_UNLOCK(bo); |
---|
3077 | if (NFSHASPNFS(nmp)) { |
---|
3078 | nfscl_layoutcommit(vp, td); |
---|
3079 | /* |
---|
3080 | * Invalidate the attribute cache, since writes to a DS |
---|
3081 | * won't update the size attribute. |
---|
3082 | */ |
---|
3083 | NFSLOCKNODE(np); |
---|
3084 | np->n_attrstamp = 0; |
---|
3085 | } else |
---|
3086 | NFSLOCKNODE(np); |
---|
3087 | if (np->n_flag & NWRITEERR) { |
---|
3088 | error = np->n_error; |
---|
3089 | np->n_flag &= ~NWRITEERR; |
---|
3090 | } |
---|
3091 | if (commit && bo->bo_dirty.bv_cnt == 0 && |
---|
3092 | bo->bo_numoutput == 0 && np->n_directio_asyncwr == 0) |
---|
3093 | np->n_flag &= ~NMODIFIED; |
---|
3094 | NFSUNLOCKNODE(np); |
---|
3095 | done: |
---|
3096 | if (bvec != NULL && bvec != bvec_on_stack) |
---|
3097 | free(bvec, M_TEMP); |
---|
3098 | if (error == 0 && commit != 0 && waitfor == MNT_WAIT && |
---|
3099 | (bo->bo_dirty.bv_cnt != 0 || bo->bo_numoutput != 0 || |
---|
3100 | np->n_directio_asyncwr != 0)) { |
---|
3101 | if (trycnt++ < 5) { |
---|
3102 | /* try, try again... */ |
---|
3103 | passone = 1; |
---|
3104 | wcred = NULL; |
---|
3105 | bvec = NULL; |
---|
3106 | bvecsize = 0; |
---|
3107 | goto again; |
---|
3108 | } |
---|
3109 | vn_printf(vp, "ncl_flush failed"); |
---|
3110 | error = called_from_renewthread != 0 ? EIO : EBUSY; |
---|
3111 | } |
---|
3112 | return (error); |
---|
3113 | } |
---|
3114 | |
---|
3115 | /* |
---|
3116 | * NFS advisory byte-level locks. |
---|
3117 | */ |
---|
3118 | static int |
---|
3119 | nfs_advlock(struct vop_advlock_args *ap) |
---|
3120 | { |
---|
3121 | struct vnode *vp = ap->a_vp; |
---|
3122 | struct ucred *cred; |
---|
3123 | struct nfsnode *np = VTONFS(ap->a_vp); |
---|
3124 | struct proc *p = (struct proc *)ap->a_id; |
---|
3125 | struct thread *td = curthread; /* XXX */ |
---|
3126 | struct vattr va; |
---|
3127 | int ret, error = EOPNOTSUPP; |
---|
3128 | u_quad_t size; |
---|
3129 | |
---|
3130 | ret = NFSVOPLOCK(vp, LK_SHARED); |
---|
3131 | if (ret != 0) |
---|
3132 | return (EBADF); |
---|
3133 | if (NFS_ISV4(vp) && (ap->a_flags & (F_POSIX | F_FLOCK)) != 0) { |
---|
3134 | if (vp->v_type != VREG) { |
---|
3135 | NFSVOPUNLOCK(vp, 0); |
---|
3136 | return (EINVAL); |
---|
3137 | } |
---|
3138 | #ifndef __rtems__ |
---|
3139 | if ((ap->a_flags & F_POSIX) != 0) |
---|
3140 | cred = p->p_ucred; |
---|
3141 | else |
---|
3142 | #endif /* __rtems__ */ |
---|
3143 | cred = td->td_ucred; |
---|
3144 | NFSVOPLOCK(vp, LK_UPGRADE | LK_RETRY); |
---|
3145 | if (vp->v_iflag & VI_DOOMED) { |
---|
3146 | NFSVOPUNLOCK(vp, 0); |
---|
3147 | return (EBADF); |
---|
3148 | } |
---|
3149 | |
---|
3150 | /* |
---|
3151 | * If this is unlocking a write locked region, flush and |
---|
3152 | * commit them before unlocking. This is required by |
---|
3153 | * RFC3530 Sec. 9.3.2. |
---|
3154 | */ |
---|
3155 | if (ap->a_op == F_UNLCK && |
---|
3156 | nfscl_checkwritelocked(vp, ap->a_fl, cred, td, ap->a_id, |
---|
3157 | ap->a_flags)) |
---|
3158 | (void) ncl_flush(vp, MNT_WAIT, td, 1, 0); |
---|
3159 | |
---|
3160 | /* |
---|
3161 | * Loop around doing the lock op, while a blocking lock |
---|
3162 | * must wait for the lock op to succeed. |
---|
3163 | */ |
---|
3164 | do { |
---|
3165 | ret = nfsrpc_advlock(vp, np->n_size, ap->a_op, |
---|
3166 | ap->a_fl, 0, cred, td, ap->a_id, ap->a_flags); |
---|
3167 | if (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) && |
---|
3168 | ap->a_op == F_SETLK) { |
---|
3169 | NFSVOPUNLOCK(vp, 0); |
---|
3170 | error = nfs_catnap(PZERO | PCATCH, ret, |
---|
3171 | "ncladvl"); |
---|
3172 | if (error) |
---|
3173 | return (EINTR); |
---|
3174 | NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); |
---|
3175 | if (vp->v_iflag & VI_DOOMED) { |
---|
3176 | NFSVOPUNLOCK(vp, 0); |
---|
3177 | return (EBADF); |
---|
3178 | } |
---|
3179 | } |
---|
3180 | } while (ret == NFSERR_DENIED && (ap->a_flags & F_WAIT) && |
---|
3181 | ap->a_op == F_SETLK); |
---|
3182 | if (ret == NFSERR_DENIED) { |
---|
3183 | NFSVOPUNLOCK(vp, 0); |
---|
3184 | return (EAGAIN); |
---|
3185 | } else if (ret == EINVAL || ret == EBADF || ret == EINTR) { |
---|
3186 | NFSVOPUNLOCK(vp, 0); |
---|
3187 | return (ret); |
---|
3188 | } else if (ret != 0) { |
---|
3189 | NFSVOPUNLOCK(vp, 0); |
---|
3190 | return (EACCES); |
---|
3191 | } |
---|
3192 | |
---|
3193 | /* |
---|
3194 | * Now, if we just got a lock, invalidate data in the buffer |
---|
3195 | * cache, as required, so that the coherency conforms with |
---|
3196 | * RFC3530 Sec. 9.3.2. |
---|
3197 | */ |
---|
3198 | if (ap->a_op == F_SETLK) { |
---|
3199 | if ((np->n_flag & NMODIFIED) == 0) { |
---|
3200 | np->n_attrstamp = 0; |
---|
3201 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
3202 | ret = VOP_GETATTR(vp, &va, cred); |
---|
3203 | } |
---|
3204 | if ((np->n_flag & NMODIFIED) || ret || |
---|
3205 | np->n_change != va.va_filerev) { |
---|
3206 | (void) ncl_vinvalbuf(vp, V_SAVE, td, 1); |
---|
3207 | np->n_attrstamp = 0; |
---|
3208 | KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); |
---|
3209 | ret = VOP_GETATTR(vp, &va, cred); |
---|
3210 | if (!ret) { |
---|
3211 | np->n_mtime = va.va_mtime; |
---|
3212 | np->n_change = va.va_filerev; |
---|
3213 | } |
---|
3214 | } |
---|
3215 | /* Mark that a file lock has been acquired. */ |
---|
3216 | NFSLOCKNODE(np); |
---|
3217 | np->n_flag |= NHASBEENLOCKED; |
---|
3218 | NFSUNLOCKNODE(np); |
---|
3219 | } |
---|
3220 | NFSVOPUNLOCK(vp, 0); |
---|
3221 | return (0); |
---|
3222 | } else if (!NFS_ISV4(vp)) { |
---|
3223 | #ifndef __rtems__ |
---|
3224 | if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) { |
---|
3225 | size = VTONFS(vp)->n_size; |
---|
3226 | NFSVOPUNLOCK(vp, 0); |
---|
3227 | error = lf_advlock(ap, &(vp->v_lockf), size); |
---|
3228 | } else { |
---|
3229 | if (nfs_advlock_p != NULL) |
---|
3230 | error = nfs_advlock_p(ap); |
---|
3231 | else { |
---|
3232 | NFSVOPUNLOCK(vp, 0); |
---|
3233 | error = ENOLCK; |
---|
3234 | } |
---|
3235 | } |
---|
3236 | if (error == 0 && ap->a_op == F_SETLK) { |
---|
3237 | error = NFSVOPLOCK(vp, LK_SHARED); |
---|
3238 | if (error == 0) { |
---|
3239 | /* Mark that a file lock has been acquired. */ |
---|
3240 | NFSLOCKNODE(np); |
---|
3241 | np->n_flag |= NHASBEENLOCKED; |
---|
3242 | NFSUNLOCKNODE(np); |
---|
3243 | NFSVOPUNLOCK(vp, 0); |
---|
3244 | } |
---|
3245 | } |
---|
3246 | #else /* __rtems__ */ |
---|
3247 | NFSVOPUNLOCK(vp, 0); |
---|
3248 | return (0); |
---|
3249 | #endif /* __rtems__ */ |
---|
3250 | } else |
---|
3251 | NFSVOPUNLOCK(vp, 0); |
---|
3252 | return (error); |
---|
3253 | } |
---|
3254 | |
---|
3255 | /* |
---|
3256 | * NFS advisory byte-level locks. |
---|
3257 | */ |
---|
3258 | static int |
---|
3259 | nfs_advlockasync(struct vop_advlockasync_args *ap) |
---|
3260 | { |
---|
3261 | struct vnode *vp = ap->a_vp; |
---|
3262 | u_quad_t size; |
---|
3263 | int error; |
---|
3264 | |
---|
3265 | if (NFS_ISV4(vp)) |
---|
3266 | return (EOPNOTSUPP); |
---|
3267 | error = NFSVOPLOCK(vp, LK_SHARED); |
---|
3268 | if (error) |
---|
3269 | return (error); |
---|
3270 | if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOLOCKD) != 0) { |
---|
3271 | size = VTONFS(vp)->n_size; |
---|
3272 | NFSVOPUNLOCK(vp, 0); |
---|
3273 | #ifndef __rtems__ |
---|
3274 | error = lf_advlockasync(ap, &(vp->v_lockf), size); |
---|
3275 | #endif /* __rtems__ */ |
---|
3276 | } else { |
---|
3277 | NFSVOPUNLOCK(vp, 0); |
---|
3278 | error = EOPNOTSUPP; |
---|
3279 | } |
---|
3280 | return (error); |
---|
3281 | } |
---|
3282 | |
---|
3283 | /* |
---|
3284 | * Print out the contents of an nfsnode. |
---|
3285 | */ |
---|
3286 | static int |
---|
3287 | nfs_print(struct vop_print_args *ap) |
---|
3288 | { |
---|
3289 | struct vnode *vp = ap->a_vp; |
---|
3290 | struct nfsnode *np = VTONFS(vp); |
---|
3291 | |
---|
3292 | printf("\tfileid %jd fsid 0x%jx", (uintmax_t)np->n_vattr.na_fileid, |
---|
3293 | (uintmax_t)np->n_vattr.na_fsid); |
---|
3294 | #ifndef __rtems__ |
---|
3295 | if (vp->v_type == VFIFO) |
---|
3296 | fifo_printinfo(vp); |
---|
3297 | #endif /* __rtems__ */ |
---|
3298 | printf("\n"); |
---|
3299 | return (0); |
---|
3300 | } |
---|
3301 | |
---|
3302 | /* |
---|
3303 | * This is the "real" nfs::bwrite(struct buf*). |
---|
3304 | * We set B_CACHE if this is a VMIO buffer. |
---|
3305 | */ |
---|
3306 | int |
---|
3307 | ncl_writebp(struct buf *bp, int force __unused, struct thread *td) |
---|
3308 | { |
---|
3309 | int oldflags, rtval; |
---|
3310 | |
---|
3311 | BUF_ASSERT_HELD(bp); |
---|
3312 | |
---|
3313 | if (bp->b_flags & B_INVAL) { |
---|
3314 | brelse(bp); |
---|
3315 | return (0); |
---|
3316 | } |
---|
3317 | |
---|
3318 | oldflags = bp->b_flags; |
---|
3319 | bp->b_flags |= B_CACHE; |
---|
3320 | |
---|
3321 | /* |
---|
3322 | * Undirty the bp. We will redirty it later if the I/O fails. |
---|
3323 | */ |
---|
3324 | bundirty(bp); |
---|
3325 | bp->b_flags &= ~B_DONE; |
---|
3326 | bp->b_ioflags &= ~BIO_ERROR; |
---|
3327 | bp->b_iocmd = BIO_WRITE; |
---|
3328 | |
---|
3329 | bufobj_wref(bp->b_bufobj); |
---|
3330 | #ifndef __rtems__ |
---|
3331 | curthread->td_ru.ru_oublock++; |
---|
3332 | #endif /* __rtems__ */ |
---|
3333 | |
---|
3334 | /* |
---|
3335 | * Note: to avoid loopback deadlocks, we do not |
---|
3336 | * assign b_runningbufspace. |
---|
3337 | */ |
---|
3338 | vfs_busy_pages(bp, 1); |
---|
3339 | |
---|
3340 | BUF_KERNPROC(bp); |
---|
3341 | bp->b_iooffset = dbtob(bp->b_blkno); |
---|
3342 | bstrategy(bp); |
---|
3343 | |
---|
3344 | if ((oldflags & B_ASYNC) != 0) |
---|
3345 | return (0); |
---|
3346 | |
---|
3347 | rtval = bufwait(bp); |
---|
3348 | if (oldflags & B_DELWRI) |
---|
3349 | reassignbuf(bp); |
---|
3350 | brelse(bp); |
---|
3351 | return (rtval); |
---|
3352 | } |
---|
3353 | |
---|
3354 | /* |
---|
3355 | * nfs special file access vnode op. |
---|
3356 | * Essentially just get vattr and then imitate iaccess() since the device is |
---|
3357 | * local to the client. |
---|
3358 | */ |
---|
3359 | static int |
---|
3360 | nfsspec_access(struct vop_access_args *ap) |
---|
3361 | { |
---|
3362 | struct vattr *vap; |
---|
3363 | struct ucred *cred = ap->a_cred; |
---|
3364 | struct vnode *vp = ap->a_vp; |
---|
3365 | accmode_t accmode = ap->a_accmode; |
---|
3366 | struct vattr vattr; |
---|
3367 | int error; |
---|
3368 | |
---|
3369 | /* |
---|
3370 | * Disallow write attempts on filesystems mounted read-only; |
---|
3371 | * unless the file is a socket, fifo, or a block or character |
---|
3372 | * device resident on the filesystem. |
---|
3373 | */ |
---|
3374 | if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { |
---|
3375 | switch (vp->v_type) { |
---|
3376 | case VREG: |
---|
3377 | case VDIR: |
---|
3378 | case VLNK: |
---|
3379 | return (EROFS); |
---|
3380 | default: |
---|
3381 | break; |
---|
3382 | } |
---|
3383 | } |
---|
3384 | vap = &vattr; |
---|
3385 | error = VOP_GETATTR(vp, vap, cred); |
---|
3386 | if (error) |
---|
3387 | goto out; |
---|
3388 | error = vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid, |
---|
3389 | accmode, cred, NULL); |
---|
3390 | out: |
---|
3391 | return error; |
---|
3392 | } |
---|
3393 | |
---|
3394 | /* |
---|
3395 | * Read wrapper for fifos. |
---|
3396 | */ |
---|
3397 | static int |
---|
3398 | nfsfifo_read(struct vop_read_args *ap) |
---|
3399 | { |
---|
3400 | #ifndef __rtems__ |
---|
3401 | struct nfsnode *np = VTONFS(ap->a_vp); |
---|
3402 | int error; |
---|
3403 | |
---|
3404 | /* |
---|
3405 | * Set access flag. |
---|
3406 | */ |
---|
3407 | NFSLOCKNODE(np); |
---|
3408 | np->n_flag |= NACC; |
---|
3409 | vfs_timestamp(&np->n_atim); |
---|
3410 | NFSUNLOCKNODE(np); |
---|
3411 | error = fifo_specops.vop_read(ap); |
---|
3412 | return error; |
---|
3413 | #else /* __rtems__ */ |
---|
3414 | return (EINVAL); |
---|
3415 | #endif /* __rtems__ */ |
---|
3416 | } |
---|
3417 | |
---|
3418 | /* |
---|
3419 | * Write wrapper for fifos. |
---|
3420 | */ |
---|
3421 | static int |
---|
3422 | nfsfifo_write(struct vop_write_args *ap) |
---|
3423 | { |
---|
3424 | #ifndef __rtems__ |
---|
3425 | struct nfsnode *np = VTONFS(ap->a_vp); |
---|
3426 | |
---|
3427 | /* |
---|
3428 | * Set update flag. |
---|
3429 | */ |
---|
3430 | NFSLOCKNODE(np); |
---|
3431 | np->n_flag |= NUPD; |
---|
3432 | vfs_timestamp(&np->n_mtim); |
---|
3433 | NFSUNLOCKNODE(np); |
---|
3434 | return(fifo_specops.vop_write(ap)); |
---|
3435 | #else /* __rtems__ */ |
---|
3436 | return (EINVAL); |
---|
3437 | #endif /* __rtems__ */ |
---|
3438 | } |
---|
3439 | |
---|
3440 | /* |
---|
3441 | * Close wrapper for fifos. |
---|
3442 | * |
---|
3443 | * Update the times on the nfsnode then do fifo close. |
---|
3444 | */ |
---|
3445 | static int |
---|
3446 | nfsfifo_close(struct vop_close_args *ap) |
---|
3447 | { |
---|
3448 | #ifndef __rtems__ |
---|
3449 | struct vnode *vp = ap->a_vp; |
---|
3450 | struct nfsnode *np = VTONFS(vp); |
---|
3451 | struct vattr vattr; |
---|
3452 | struct timespec ts; |
---|
3453 | |
---|
3454 | NFSLOCKNODE(np); |
---|
3455 | if (np->n_flag & (NACC | NUPD)) { |
---|
3456 | vfs_timestamp(&ts); |
---|
3457 | if (np->n_flag & NACC) |
---|
3458 | np->n_atim = ts; |
---|
3459 | if (np->n_flag & NUPD) |
---|
3460 | np->n_mtim = ts; |
---|
3461 | np->n_flag |= NCHG; |
---|
3462 | if (vrefcnt(vp) == 1 && |
---|
3463 | (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { |
---|
3464 | VATTR_NULL(&vattr); |
---|
3465 | if (np->n_flag & NACC) |
---|
3466 | vattr.va_atime = np->n_atim; |
---|
3467 | if (np->n_flag & NUPD) |
---|
3468 | vattr.va_mtime = np->n_mtim; |
---|
3469 | NFSUNLOCKNODE(np); |
---|
3470 | (void)VOP_SETATTR(vp, &vattr, ap->a_cred); |
---|
3471 | goto out; |
---|
3472 | } |
---|
3473 | } |
---|
3474 | NFSUNLOCKNODE(np); |
---|
3475 | out: |
---|
3476 | return (fifo_specops.vop_close(ap)); |
---|
3477 | #else /* __rtems__ */ |
---|
3478 | return (EINVAL); |
---|
3479 | #endif /* __rtems__ */ |
---|
3480 | } |
---|
3481 | |
---|
3482 | /* |
---|
3483 | * Just call ncl_writebp() with the force argument set to 1. |
---|
3484 | * |
---|
3485 | * NOTE: B_DONE may or may not be set in a_bp on call. |
---|
3486 | */ |
---|
3487 | static int |
---|
3488 | nfs_bwrite(struct buf *bp) |
---|
3489 | { |
---|
3490 | |
---|
3491 | return (ncl_writebp(bp, 1, curthread)); |
---|
3492 | } |
---|
3493 | |
---|
3494 | struct buf_ops buf_ops_newnfs = { |
---|
3495 | .bop_name = "buf_ops_nfs", |
---|
3496 | .bop_write = nfs_bwrite, |
---|
3497 | .bop_strategy = bufstrategy, |
---|
3498 | .bop_sync = bufsync, |
---|
3499 | .bop_bdflush = bufbdflush, |
---|
3500 | }; |
---|
3501 | |
---|
3502 | static int |
---|
3503 | nfs_getacl(struct vop_getacl_args *ap) |
---|
3504 | { |
---|
3505 | int error; |
---|
3506 | |
---|
3507 | if (ap->a_type != ACL_TYPE_NFS4) |
---|
3508 | return (EOPNOTSUPP); |
---|
3509 | error = nfsrpc_getacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp, |
---|
3510 | NULL); |
---|
3511 | if (error > NFSERR_STALE) { |
---|
3512 | (void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0); |
---|
3513 | error = EPERM; |
---|
3514 | } |
---|
3515 | return (error); |
---|
3516 | } |
---|
3517 | |
---|
3518 | static int |
---|
3519 | nfs_setacl(struct vop_setacl_args *ap) |
---|
3520 | { |
---|
3521 | int error; |
---|
3522 | |
---|
3523 | if (ap->a_type != ACL_TYPE_NFS4) |
---|
3524 | return (EOPNOTSUPP); |
---|
3525 | error = nfsrpc_setacl(ap->a_vp, ap->a_cred, ap->a_td, ap->a_aclp, |
---|
3526 | NULL); |
---|
3527 | if (error > NFSERR_STALE) { |
---|
3528 | (void) nfscl_maperr(ap->a_td, error, (uid_t)0, (gid_t)0); |
---|
3529 | error = EPERM; |
---|
3530 | } |
---|
3531 | return (error); |
---|
3532 | } |
---|
3533 | |
---|
3534 | /* |
---|
3535 | * Return POSIX pathconf information applicable to nfs filesystems. |
---|
3536 | */ |
---|
3537 | static int |
---|
3538 | nfs_pathconf(struct vop_pathconf_args *ap) |
---|
3539 | { |
---|
3540 | struct nfsv3_pathconf pc; |
---|
3541 | struct nfsvattr nfsva; |
---|
3542 | struct vnode *vp = ap->a_vp; |
---|
3543 | struct thread *td = curthread; |
---|
3544 | int attrflag, error; |
---|
3545 | |
---|
3546 | if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX || |
---|
3547 | ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED || |
---|
3548 | ap->a_name == _PC_NO_TRUNC)) || |
---|
3549 | (NFS_ISV4(vp) && ap->a_name == _PC_ACL_NFS4)) { |
---|
3550 | /* |
---|
3551 | * Since only the above 4 a_names are returned by the NFSv3 |
---|
3552 | * Pathconf RPC, there is no point in doing it for others. |
---|
3553 | * For NFSv4, the Pathconf RPC (actually a Getattr Op.) can |
---|
3554 | * be used for _PC_NFS4_ACL as well. |
---|
3555 | */ |
---|
3556 | error = nfsrpc_pathconf(vp, &pc, td->td_ucred, td, &nfsva, |
---|
3557 | &attrflag, NULL); |
---|
3558 | if (attrflag != 0) |
---|
3559 | (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, |
---|
3560 | 1); |
---|
3561 | if (error != 0) |
---|
3562 | return (error); |
---|
3563 | } else { |
---|
3564 | /* |
---|
3565 | * For NFSv2 (or NFSv3 when not one of the above 4 a_names), |
---|
3566 | * just fake them. |
---|
3567 | */ |
---|
3568 | pc.pc_linkmax = NFS_LINK_MAX; |
---|
3569 | pc.pc_namemax = NFS_MAXNAMLEN; |
---|
3570 | pc.pc_notrunc = 1; |
---|
3571 | pc.pc_chownrestricted = 1; |
---|
3572 | pc.pc_caseinsensitive = 0; |
---|
3573 | pc.pc_casepreserving = 1; |
---|
3574 | error = 0; |
---|
3575 | } |
---|
3576 | switch (ap->a_name) { |
---|
3577 | case _PC_LINK_MAX: |
---|
3578 | #ifdef _LP64 |
---|
3579 | *ap->a_retval = pc.pc_linkmax; |
---|
3580 | #else |
---|
3581 | *ap->a_retval = MIN(LONG_MAX, pc.pc_linkmax); |
---|
3582 | #endif |
---|
3583 | break; |
---|
3584 | case _PC_NAME_MAX: |
---|
3585 | *ap->a_retval = pc.pc_namemax; |
---|
3586 | break; |
---|
3587 | case _PC_PIPE_BUF: |
---|
3588 | if (ap->a_vp->v_type == VDIR || ap->a_vp->v_type == VFIFO) |
---|
3589 | *ap->a_retval = PIPE_BUF; |
---|
3590 | else |
---|
3591 | error = EINVAL; |
---|
3592 | break; |
---|
3593 | case _PC_CHOWN_RESTRICTED: |
---|
3594 | *ap->a_retval = pc.pc_chownrestricted; |
---|
3595 | break; |
---|
3596 | case _PC_NO_TRUNC: |
---|
3597 | *ap->a_retval = pc.pc_notrunc; |
---|
3598 | break; |
---|
3599 | case _PC_ACL_NFS4: |
---|
3600 | if (NFS_ISV4(vp) && nfsrv_useacl != 0 && attrflag != 0 && |
---|
3601 | NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) |
---|
3602 | *ap->a_retval = 1; |
---|
3603 | else |
---|
3604 | *ap->a_retval = 0; |
---|
3605 | break; |
---|
3606 | case _PC_ACL_PATH_MAX: |
---|
3607 | if (NFS_ISV4(vp)) |
---|
3608 | *ap->a_retval = ACL_MAX_ENTRIES; |
---|
3609 | else |
---|
3610 | *ap->a_retval = 3; |
---|
3611 | break; |
---|
3612 | case _PC_PRIO_IO: |
---|
3613 | *ap->a_retval = 0; |
---|
3614 | break; |
---|
3615 | case _PC_SYNC_IO: |
---|
3616 | *ap->a_retval = 0; |
---|
3617 | break; |
---|
3618 | case _PC_ALLOC_SIZE_MIN: |
---|
3619 | *ap->a_retval = vp->v_mount->mnt_stat.f_bsize; |
---|
3620 | break; |
---|
3621 | case _PC_FILESIZEBITS: |
---|
3622 | if (NFS_ISV34(vp)) |
---|
3623 | *ap->a_retval = 64; |
---|
3624 | else |
---|
3625 | *ap->a_retval = 32; |
---|
3626 | break; |
---|
3627 | case _PC_REC_INCR_XFER_SIZE: |
---|
3628 | *ap->a_retval = vp->v_mount->mnt_stat.f_iosize; |
---|
3629 | break; |
---|
3630 | case _PC_REC_MAX_XFER_SIZE: |
---|
3631 | *ap->a_retval = -1; /* means ``unlimited'' */ |
---|
3632 | break; |
---|
3633 | case _PC_REC_MIN_XFER_SIZE: |
---|
3634 | *ap->a_retval = vp->v_mount->mnt_stat.f_iosize; |
---|
3635 | break; |
---|
3636 | case _PC_REC_XFER_ALIGN: |
---|
3637 | *ap->a_retval = PAGE_SIZE; |
---|
3638 | break; |
---|
3639 | case _PC_SYMLINK_MAX: |
---|
3640 | *ap->a_retval = NFS_MAXPATHLEN; |
---|
3641 | break; |
---|
3642 | |
---|
3643 | default: |
---|
3644 | error = vop_stdpathconf(ap); |
---|
3645 | break; |
---|
3646 | } |
---|
3647 | return (error); |
---|
3648 | } |
---|