[882425f] | 1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
| 2 | |
---|
[6138f24] | 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 | |
---|
[882425f] | 46 | #include <rtems/bsd/local/opt_inet.h> |
---|
[6138f24] | 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> |
---|
[882425f] | 64 | #ifndef __rtems__ |
---|
[6138f24] | 65 | #include <sys/lockf.h> |
---|
[882425f] | 66 | #endif /* __rtems__ */ |
---|
[6138f24] | 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); |
---|
[882425f] | 341 | #ifndef __rtems__ |
---|
[6138f24] | 342 | vnode_pager_setsize(vp, nsize); |
---|
[882425f] | 343 | #endif /* __rtems__ */ |
---|
[6138f24] | 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); |
---|
[882425f] | 692 | #ifndef __rtems__ |
---|
[6138f24] | 693 | vnode_create_vobject(vp, vattr.va_size, ap->a_td); |
---|
[882425f] | 694 | #endif /* __rtems__ */ |
---|
[6138f24] | 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) { |
---|
[882425f] | 705 | #ifndef __rtems__ |
---|
[6138f24] | 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 | } |
---|
[882425f] | 712 | #endif /* __rtems__ */ |
---|
[6138f24] | 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 | */ |
---|
[882425f] | 791 | #ifndef __rtems__ |
---|
[6138f24] | 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 | } |
---|
[882425f] | 797 | #endif /* __rtems__ */ |
---|
[6138f24] | 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; */ |
---|
[6e4709b] | 820 | } else if (NFS_ISV4(vp)) { |
---|
[6138f24] | 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 | } |
---|
[6e4709b] | 836 | /* |
---|
[6138f24] | 837 | * Invalidate the attribute cache in all cases. |
---|
| 838 | * An open is going to fetch fresh attrs any way, other procs |
---|
[6e4709b] | 839 | * on this node that have file open will be forced to do an |
---|
[6138f24] | 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); |
---|
[6e4709b] | 894 | KASSERT((np->n_directio_opens > 0), |
---|
[6138f24] | 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) |
---|
[6e4709b] | 1024 | return (0); |
---|
[6138f24] | 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) { |
---|
[882425f] | 1051 | #ifndef __rtems__ |
---|
[6138f24] | 1052 | vnode_pager_setsize(vp, tsize); |
---|
[882425f] | 1053 | #endif /* __rtems__ */ |
---|
[6138f24] | 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); |
---|
[6e4709b] | 1075 | if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) && |
---|
[6138f24] | 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; |
---|
[882425f] | 1088 | #ifndef __rtems__ |
---|
[6138f24] | 1089 | vnode_pager_setsize(vp, tsize); |
---|
[882425f] | 1090 | #endif /* __rtems__ */ |
---|
[6138f24] | 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; |
---|
[6e4709b] | 1149 | |
---|
[6138f24] | 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); |
---|
[6e4709b] | 1226 | else |
---|
[6138f24] | 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) && |
---|
[6e4709b] | 1382 | !(np->n_flag & NMODIFIED)) { |
---|
[6138f24] | 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; |
---|
[6e4709b] | 1714 | error = nfsrpc_setattr(newvp, vap, NULL, |
---|
[6138f24] | 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. |
---|
[6e4709b] | 1902 | * |
---|
[6138f24] | 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; |
---|
[6e4709b] | 2314 | |
---|
[6138f24] | 2315 | if (ap->a_eofflag != NULL) |
---|
| 2316 | *ap->a_eofflag = 0; |
---|
[6e4709b] | 2317 | if (vp->v_type != VDIR) |
---|
[6138f24] | 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 | } |
---|
[6e4709b] | 2361 | |
---|
[6138f24] | 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 { |
---|
[6e4709b] | 2395 | ncl_dircookie_unlock(dnp); |
---|
[6138f24] | 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 | |
---|
[6e4709b] | 2513 | /* |
---|
[6138f24] | 2514 | * Fudge together a funny name. |
---|
[6e4709b] | 2515 | * Changing the format of the funny name to accommodate more |
---|
[6138f24] | 2516 | * sillynames per directory. |
---|
[6e4709b] | 2517 | * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is |
---|
[6138f24] | 2518 | * CPU ticks since boot. |
---|
| 2519 | */ |
---|
[882425f] | 2520 | #ifndef __rtems__ |
---|
[6138f24] | 2521 | pid = cnp->cn_thread->td_proc->p_pid; |
---|
[882425f] | 2522 | #else /* __rtems__ */ |
---|
| 2523 | pid = 1; |
---|
| 2524 | #endif /* __rtems__ */ |
---|
[6138f24] | 2525 | lticks = (unsigned int)ticks; |
---|
| 2526 | for ( ; ; ) { |
---|
[6e4709b] | 2527 | sp->s_namlen = sprintf(sp->s_name, |
---|
| 2528 | ".nfs.%08x.%04x4.4", lticks, |
---|
[6138f24] | 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, |
---|
[6e4709b] | 3064 | &np->n_mtx, slpflag | (PRIBIO + 1), |
---|
[6138f24] | 3065 | "nfsfsync", 0); |
---|
| 3066 | if (error) { |
---|
| 3067 | if (newnfs_sigintr(nmp, td)) { |
---|
| 3068 | NFSUNLOCKNODE(np); |
---|
[6e4709b] | 3069 | error = EINTR; |
---|
[6138f24] | 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; |
---|
[6e4709b] | 3129 | |
---|
[6138f24] | 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 | } |
---|
[882425f] | 3138 | #ifndef __rtems__ |
---|
[6138f24] | 3139 | if ((ap->a_flags & F_POSIX) != 0) |
---|
| 3140 | cred = p->p_ucred; |
---|
| 3141 | else |
---|
[882425f] | 3142 | #endif /* __rtems__ */ |
---|
[6138f24] | 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)) { |
---|
[882425f] | 3223 | #ifndef __rtems__ |
---|
[6138f24] | 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 | } |
---|
[882425f] | 3246 | #else /* __rtems__ */ |
---|
| 3247 | NFSVOPUNLOCK(vp, 0); |
---|
| 3248 | return (0); |
---|
| 3249 | #endif /* __rtems__ */ |
---|
[6138f24] | 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; |
---|
[6e4709b] | 3264 | |
---|
[6138f24] | 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); |
---|
[882425f] | 3273 | #ifndef __rtems__ |
---|
[6138f24] | 3274 | error = lf_advlockasync(ap, &(vp->v_lockf), size); |
---|
[882425f] | 3275 | #endif /* __rtems__ */ |
---|
[6138f24] | 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); |
---|
[882425f] | 3294 | #ifndef __rtems__ |
---|
[6138f24] | 3295 | if (vp->v_type == VFIFO) |
---|
| 3296 | fifo_printinfo(vp); |
---|
[882425f] | 3297 | #endif /* __rtems__ */ |
---|
[6138f24] | 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); |
---|
[882425f] | 3330 | #ifndef __rtems__ |
---|
[6138f24] | 3331 | curthread->td_ru.ru_oublock++; |
---|
[882425f] | 3332 | #endif /* __rtems__ */ |
---|
[6138f24] | 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 | { |
---|
[882425f] | 3400 | #ifndef __rtems__ |
---|
[6138f24] | 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); |
---|
[6e4709b] | 3412 | return error; |
---|
[882425f] | 3413 | #else /* __rtems__ */ |
---|
| 3414 | return (EINVAL); |
---|
| 3415 | #endif /* __rtems__ */ |
---|
[6138f24] | 3416 | } |
---|
| 3417 | |
---|
| 3418 | /* |
---|
| 3419 | * Write wrapper for fifos. |
---|
| 3420 | */ |
---|
| 3421 | static int |
---|
| 3422 | nfsfifo_write(struct vop_write_args *ap) |
---|
| 3423 | { |
---|
[882425f] | 3424 | #ifndef __rtems__ |
---|
[6138f24] | 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)); |
---|
[882425f] | 3435 | #else /* __rtems__ */ |
---|
| 3436 | return (EINVAL); |
---|
| 3437 | #endif /* __rtems__ */ |
---|
[6138f24] | 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 | { |
---|
[882425f] | 3448 | #ifndef __rtems__ |
---|
[6138f24] | 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)); |
---|
[882425f] | 3477 | #else /* __rtems__ */ |
---|
| 3478 | return (EINVAL); |
---|
| 3479 | #endif /* __rtems__ */ |
---|
[6138f24] | 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 | } |
---|
[6e4709b] | 3649 | |
---|