X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..eb6b6ca394357805f2bdba989abae309f718b4d8:/bsd/nfs/nfs_node.c?ds=sidebyside diff --git a/bsd/nfs/nfs_node.c b/bsd/nfs/nfs_node.c index b3f2a47b9..60bd5609f 100644 --- a/bsd/nfs/nfs_node.c +++ b/bsd/nfs/nfs_node.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2011 Apple Inc. All rights reserved. + * Copyright (c) 2000-2019 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ @@ -65,6 +65,8 @@ * FreeBSD-Id: nfs_node.c,v 1.22 1997/10/28 14:06:20 bde Exp $ */ +#include +#if CONFIG_NFS_CLIENT #include #include @@ -72,10 +74,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -84,9 +88,9 @@ #include #include -#define NFSNOHASH(fhsum) \ +#define NFSNOHASH(fhsum) \ (&nfsnodehashtbl[(fhsum) & nfsnodehash]) -static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl; +static LIST_HEAD(nfsnodehashhead, nfsnode) * nfsnodehashtbl; static u_long nfsnodehash; static lck_grp_t *nfs_node_hash_lck_grp; @@ -94,6 +98,8 @@ static lck_grp_t *nfs_node_lck_grp; static lck_grp_t *nfs_data_lck_grp; lck_mtx_t *nfs_node_hash_mutex; +#define NFS_NODE_DBG(...) NFS_DBG(NFS_FAC_NODE, 7, ## __VA_ARGS__) + /* * Initialize hash links for nfsnodes * and build nfsnode free list. @@ -111,8 +117,9 @@ void nfs_nhinit_finish(void) { lck_mtx_lock(nfs_node_hash_mutex); - if (!nfsnodehashtbl) + if (!nfsnodehashtbl) { nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash); + } lck_mtx_unlock(nfs_node_hash_mutex); } @@ -126,11 +133,55 @@ nfs_hash(u_char *fhp, int fhsize) int i; fhsum = 0; - for (i = 0; i < fhsize; i++) + for (i = 0; i < fhsize; i++) { fhsum += *fhp++; - return (fhsum); + } + return fhsum; } + +int nfs_case_insensitive(mount_t); + +int +nfs_case_insensitive(mount_t mp) +{ + struct nfsmount *nmp = VFSTONFS(mp); + int answer = 0; + int skip = 0; + + if (nfs_mount_gone(nmp)) { + return 0; + } + + if (nmp->nm_vers == NFS_VER2) { + /* V2 has no way to know */ + return 0; + } + + lck_mtx_lock(&nmp->nm_lock); + if (nmp->nm_vers == NFS_VER3) { + if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) { + /* We're holding the node lock so we just return + * with answer as case sensitive. Is very rare + * for file systems not to be homogenous w.r.t. pathconf + */ + skip = 1; + } + } else if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS)) { + /* no pathconf info cached */ + skip = 1; + } + + if (!skip && (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_CASE_INSENSITIVE)) { + answer = 1; + } + + lck_mtx_unlock(&nmp->nm_lock); + + return answer; +} + + /* * Look up a vnode/nfsnode by file handle. * Callers must check for mount points!! @@ -161,11 +212,11 @@ nfs_nget( FSDBG_TOP(263, mp, dnp, flags, npp); /* Check for unmount in progress */ - if (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)) { + if (!mp || vfs_isforce(mp)) { *npp = NULL; error = ENXIO; FSDBG_BOT(263, mp, dnp, 0xd1e, error); - return (error); + return error; } nfsvers = VFSTONFS(mp)->nm_vers; @@ -175,22 +226,25 @@ loop: for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np); if (mp != mp2 || np->n_fhsize != fhsize || - bcmp(fhp, np->n_fhp, fhsize)) + bcmp(fhp, np->n_fhp, fhsize)) { continue; + } if (nvap && (nvap->nva_flags & NFS_FFLAG_TRIGGER_REFERRAL) && cnp && (cnp->cn_namelen > (fhsize - (int)sizeof(dnp)))) { /* The name was too long to fit in the file handle. Check it against the node's name. */ int namecmp = 0; const char *vname = vnode_getname(NFSTOV(np)); if (vname) { - if (cnp->cn_namelen != (int)strlen(vname)) + if (cnp->cn_namelen != (int)strlen(vname)) { namecmp = 1; - else + } else { namecmp = strncmp(vname, cnp->cn_nameptr, cnp->cn_namelen); + } vnode_putname(vname); } - if (namecmp) /* full name didn't match */ + if (namecmp) { /* full name didn't match */ continue; + } } FSDBG(263, dnp, np, np->n_flag, 0xcace0000); /* if the node is locked, sleep on it */ @@ -210,7 +264,7 @@ loop: * changed identity, no need to wait. */ FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error); - return (error); + return error; } if ((error = nfs_node_lock(np))) { /* this only fails if the node is now unhashed */ @@ -220,23 +274,111 @@ loop: if (flags & NG_NOCREATE) { *npp = 0; FSDBG_BOT(263, dnp, *npp, 0xcaced1e0, ENOENT); - return (ENOENT); + return ENOENT; } goto loop; } /* update attributes */ - if (nvap) + if (nvap) { error = nfs_loadattrcache(np, nvap, xidp, 0); + } if (error) { nfs_node_unlock(np); vnode_put(vp); } else { - if (dnp && cnp && (flags & NG_MAKEENTRY)) + if (dnp && cnp && (flags & NG_MAKEENTRY)) { cache_enter(NFSTOV(dnp), vp, cnp); + } + /* + * Update the vnode if the name/and or the parent has + * changed. We need to do this so that if getattrlist is + * called asking for ATTR_CMN_NAME, that the "most" + * correct name is being returned. In addition for + * monitored vnodes we need to kick the vnode out of the + * name cache. We do this so that if there are hard + * links in the same directory the link will not be + * found and a lookup will get us here to return the + * name of the current link. In addition by removing the + * name from the name cache the old name will not be + * found after a rename done on another client or the + * server. The principle reason to do this is because + * Finder is asking for notifications on a directory. + * The directory changes, Finder gets notified, reads + * the directory (which we have purged) and for each + * entry returned calls getattrlist with the name + * returned from readdir. gettattrlist has to call + * namei/lookup to resolve the name, because its not in + * the cache we end up here. We need to update the name + * so Finder will get the name it called us with. + * + * We had an imperfect solution with respect to case + * sensitivity. There is a test that is run in + * FileBuster that does renames from some name to + * another name differing only in case. It then reads + * the directory looking for the new name, after it + * finds that new name, it ask gettattrlist to verify + * that the name is the new name. Usually that works, + * but renames generate fsevents and fseventsd will do a + * lookup on the name via lstat. Since that test renames + * old name to new name back and forth there is a race + * that an fsevent will be behind and will access the + * file by the old name, on a case insensitive file + * system that will work. Problem is if we do a case + * sensitive compare, we're going to change the name, + * which the test's getattrlist verification step is + * going to fail. So we will check the case sensitivity + * of the file system and do the appropriate compare. In + * a rare instance for non homogeneous file systems + * w.r.t. pathconf we will use case sensitive compares. + * That could break if the file system is actually case + * insensitive. + * + * Note that V2 does not know the case, so we just + * assume case sensitivity. + * + * This is clearly not perfect due to races, but this is + * as good as its going to get. You can defeat the + * handling of hard links simply by doing: + * + * while :; do ls -l > /dev/null; done + * + * in a terminal window. Even a single ls -l can cause a + * race. + * + * What we really need is for the caller, that + * knows the name being used is valid since it got it + * from a readdir to use that name and not ask for the + * ATTR_CMN_NAME + */ + if (dnp && cnp && (vp != NFSTOV(dnp))) { + int update_flags = (vnode_ismonitored((NFSTOV(dnp)))) ? VNODE_UPDATE_CACHE : 0; + int (*cmp)(const char *s1, const char *s2, size_t n); + + cmp = nfs_case_insensitive(mp) ? strncasecmp : strncmp; + + if (vp->v_name && (size_t)cnp->cn_namelen != strnlen(vp->v_name, MAXPATHLEN)) { + update_flags |= VNODE_UPDATE_NAME; + } + if (vp->v_name && cnp->cn_namelen && (*cmp)(cnp->cn_nameptr, vp->v_name, cnp->cn_namelen)) { + update_flags |= VNODE_UPDATE_NAME; + } + if ((vp->v_name == NULL && cnp->cn_namelen != 0) || (vp->v_name != NULL && cnp->cn_namelen == 0)) { + update_flags |= VNODE_UPDATE_NAME; + } + if (vnode_parent(vp) != NFSTOV(dnp)) { + update_flags |= VNODE_UPDATE_PARENT; + } + if (update_flags) { + NFS_NODE_DBG("vnode_update_identity old name %s new name %.*s update flags = %x\n", + vp->v_name, cnp->cn_namelen, cnp->cn_nameptr ? cnp->cn_nameptr : "", update_flags); + vnode_update_identity(vp, NFSTOV(dnp), cnp->cn_nameptr, cnp->cn_namelen, 0, update_flags); + } + } + *npp = np; } FSDBG_BOT(263, dnp, *npp, 0xcace0000, error); - return(error); + return error; } FSDBG(263, mp, dnp, npp, 0xaaaaaaaa); @@ -245,7 +387,7 @@ loop: lck_mtx_unlock(nfs_node_hash_mutex); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOENT); - return (ENOENT); + return ENOENT; } /* @@ -258,7 +400,7 @@ loop: lck_mtx_unlock(nfs_node_hash_mutex); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM); - return (ENOMEM); + return ENOMEM; } bzero(np, sizeof *np); np->n_hflag |= (NHINIT | NHLOCKED); @@ -274,17 +416,20 @@ loop: /* ugh... need to keep track of ".zfs" directories to workaround server bugs */ if ((nvap->nva_type == VDIR) && cnp && (cnp->cn_namelen == 4) && (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == 'z') && - (cnp->cn_nameptr[2] == 'f') && (cnp->cn_nameptr[3] == 's')) + (cnp->cn_nameptr[2] == 'f') && (cnp->cn_nameptr[3] == 's')) { np->n_flag |= NISDOTZFS; - if (dnp && (dnp->n_flag & NISDOTZFS)) + } + if (dnp && (dnp->n_flag & NISDOTZFS)) { np->n_flag |= NISDOTZFSCHILD; + } if (dnp && cnp && ((cnp->cn_namelen != 2) || (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) { vnode_t dvp = NFSTOV(dnp); if (!vnode_get(dvp)) { - if (!vnode_ref(dvp)) + if (!vnode_ref(dvp)) { np->n_parent = dvp; + } vnode_put(dvp); } } @@ -292,13 +437,13 @@ loop: /* setup node's file handle */ if (fhsize > NFS_SMALLFH) { MALLOC_ZONE(np->n_fhp, u_char *, - fhsize, M_NFSBIGFH, M_WAITOK); + fhsize, M_NFSBIGFH, M_WAITOK); if (!np->n_fhp) { lck_mtx_unlock(nfs_node_hash_mutex); FREE_ZONE(np, sizeof *np, M_NFSNODE); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM); - return (ENOMEM); + return ENOMEM; } } else { np->n_fhp = &np->n_fh[0]; @@ -329,7 +474,7 @@ loop: nfs_node_unlock(np); lck_mtx_lock(nfs_node_hash_mutex); LIST_REMOVE(np, n_hash); - np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED); + np->n_hflag &= ~(NHHASHED | NHINIT | NHLOCKED); if (np->n_hflag & NHLOCKWANT) { np->n_hflag &= ~NHLOCKWANT; wakeup(np); @@ -345,16 +490,18 @@ loop: lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp); lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp); lck_mtx_destroy(&np->n_openlock, nfs_open_grp); - if (np->n_fhsize > NFS_SMALLFH) + if (np->n_fhsize > NFS_SMALLFH) { FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); + } FREE_ZONE(np, sizeof *np, M_NFSNODE); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000003, error); - return (error); + return error; } NFS_CHANGED_UPDATE(nfsvers, np, nvap); - if (nvap->nva_type == VDIR) + if (nvap->nva_type == VDIR) { NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap); + } /* now, attempt to get a new vnode */ vfsp.vnfs_mp = mp; @@ -362,26 +509,31 @@ loop: vfsp.vnfs_str = "nfs"; vfsp.vnfs_dvp = dnp ? NFSTOV(dnp) : NULL; vfsp.vnfs_fsnode = np; +#if CONFIG_NFS4 if (nfsvers == NFS_VER4) { #if FIFO - if (nvap->nva_type == VFIFO) + if (nvap->nva_type == VFIFO) { vfsp.vnfs_vops = fifo_nfsv4nodeop_p; - else + } else #endif /* FIFO */ - if (nvap->nva_type == VBLK || nvap->nva_type == VCHR) + if (nvap->nva_type == VBLK || nvap->nva_type == VCHR) { vfsp.vnfs_vops = spec_nfsv4nodeop_p; - else + } else { vfsp.vnfs_vops = nfsv4_vnodeop_p; - } else { + } + } else +#endif /* CONFIG_NFS4 */ + { #if FIFO - if (nvap->nva_type == VFIFO) + if (nvap->nva_type == VFIFO) { vfsp.vnfs_vops = fifo_nfsv2nodeop_p; - else + } else #endif /* FIFO */ - if (nvap->nva_type == VBLK || nvap->nva_type == VCHR) + if (nvap->nva_type == VBLK || nvap->nva_type == VCHR) { vfsp.vnfs_vops = spec_nfsv2nodeop_p; - else + } else { vfsp.vnfs_vops = nfsv2_vnodeop_p; + } } vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0; vfsp.vnfs_marksystem = 0; @@ -389,18 +541,22 @@ loop: vfsp.vnfs_filesize = nvap->nva_size; vfsp.vnfs_cnp = cnp; vfsp.vnfs_flags = VNFS_ADDFSREF; - if (!dnp || !cnp || !(flags & NG_MAKEENTRY)) + if (!dnp || !cnp || !(flags & NG_MAKEENTRY)) { vfsp.vnfs_flags |= VNFS_NOCACHE; + } #if CONFIG_TRIGGERS - if ((nfsvers >= NFS_VER4) && (nvap->nva_type == VDIR) && (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER)) { + if (((nfsvers >= NFS_VER4) + ) + && (nvap->nva_type == VDIR) && (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER) + && !(flags & NG_MARKROOT)) { struct vnode_trigger_param vtp; bzero(&vtp, sizeof(vtp)); bcopy(&vfsp, &vtp.vnt_params, sizeof(vfsp)); vtp.vnt_resolve_func = nfs_mirror_mount_trigger_resolve; vtp.vnt_unresolve_func = nfs_mirror_mount_trigger_unresolve; vtp.vnt_rearm_func = nfs_mirror_mount_trigger_rearm; - vtp.vnt_flags = VNT_AUTO_REARM; + vtp.vnt_flags = VNT_AUTO_REARM | VNT_KERN_RESOLVE; error = vnode_create(VNCREATE_TRIGGER, VNCREATE_TRIGGER_SIZE, &vtp, &np->n_vnode); } else #endif @@ -412,7 +568,7 @@ loop: nfs_node_unlock(np); lck_mtx_lock(nfs_node_hash_mutex); LIST_REMOVE(np, n_hash); - np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED); + np->n_hflag &= ~(NHHASHED | NHINIT | NHLOCKED); if (np->n_hflag & NHLOCKWANT) { np->n_hflag &= ~NHLOCKWANT; wakeup(np); @@ -428,12 +584,13 @@ loop: lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp); lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp); lck_mtx_destroy(&np->n_openlock, nfs_open_grp); - if (np->n_fhsize > NFS_SMALLFH) + if (np->n_fhsize > NFS_SMALLFH) { FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); + } FREE_ZONE(np, sizeof *np, M_NFSNODE); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000004, error); - return (error); + return error; } vp = np->n_vnode; vnode_settag(vp, VT_NFS); @@ -441,7 +598,7 @@ loop: /* check if anyone's waiting on this node */ lck_mtx_lock(nfs_node_hash_mutex); - np->n_hflag &= ~(NHINIT|NHLOCKED); + np->n_hflag &= ~(NHINIT | NHLOCKED); if (np->n_hflag & NHLOCKWANT) { np->n_hflag &= ~NHLOCKWANT; wakeup(np); @@ -451,43 +608,68 @@ loop: *npp = np; FSDBG_BOT(263, dnp, vp, *npp, error); - return (error); + return error; } int -nfs_vnop_inactive(ap) +nfs_vnop_inactive( struct vnop_inactive_args /* { - struct vnodeop_desc *a_desc; - vnode_t a_vp; - vfs_context_t a_context; - } */ *ap; + * struct vnodeop_desc *a_desc; + * vnode_t a_vp; + * vfs_context_t a_context; + * } */*ap) { vnode_t vp = ap->a_vp; vfs_context_t ctx = ap->a_context; - nfsnode_t np = VTONFS(ap->a_vp); + nfsnode_t np; struct nfs_sillyrename *nsp; struct nfs_vattr nvattr; int unhash, attrerr, busyerror, error, inuse, busied, force; struct nfs_open_file *nofp; struct componentname cn; - struct nfsmount *nmp = NFSTONMP(np); - mount_t mp = vnode_mount(vp); + struct nfsmount *nmp; + mount_t mp; + + if (vp == NULL) { + panic("nfs_vnop_inactive: vp == NULL"); + } + np = VTONFS(vp); + if (np == NULL) { + panic("nfs_vnop_inactive: np == NULL"); + } + + nmp = NFSTONMP(np); + mp = vnode_mount(vp); restart: - force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)); + force = (!mp || vfs_isforce(mp)); error = 0; inuse = (nfs_mount_state_in_use_start(nmp, NULL) == 0); /* There shouldn't be any open or lock state at this point */ lck_mtx_lock(&np->n_openlock); - if (np->n_openrefcnt && !force) + if (np->n_openrefcnt && !force) { + /* + * vnode_rele and vnode_put drop the vnode lock before + * calling VNOP_INACTIVE, so there is a race were the + * vnode could become active again. Perhaps there are + * other places where this can happen, so if we've got + * here we need to get out. + */ +#ifdef NFS_NODE_DEBUG NP(np, "nfs_vnop_inactive: still open: %d", np->n_openrefcnt); +#endif + lck_mtx_unlock(&np->n_openlock); + return 0; + } + TAILQ_FOREACH(nofp, &np->n_opens, nof_link) { lck_mtx_lock(&nofp->nof_lock); if (nofp->nof_flags & NFS_OPEN_FILE_BUSY) { - if (!force) + if (!force) { NP(np, "nfs_vnop_inactive: open file busy"); + } busied = 0; } else { nofp->nof_flags |= NFS_OPEN_FILE_BUSY; @@ -495,8 +677,9 @@ restart: } lck_mtx_unlock(&nofp->nof_lock); if ((np->n_flag & NREVOKE) || (nofp->nof_flags & NFS_OPEN_FILE_LOST)) { - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); + } continue; } /* @@ -505,15 +688,19 @@ restart: * node has gone inactive without being open, we need to * clean up (close) the open done in the create. */ +#if CONFIG_NFS4 if ((nofp->nof_flags & NFS_OPEN_FILE_CREATE) && nofp->nof_creator && !force) { if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) { lck_mtx_unlock(&np->n_openlock); - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); - if (inuse) + } + if (inuse) { nfs_mount_state_in_use_end(nmp, 0); - if (!nfs4_reopen(nofp, NULL)) + } + if (!nfs4_reopen(nofp, NULL)) { goto restart; + } } nofp->nof_flags &= ~NFS_OPEN_FILE_CREATE; lck_mtx_unlock(&np->n_openlock); @@ -522,12 +709,15 @@ restart: NP(np, "nfs_vnop_inactive: create close error: %d", error); nofp->nof_flags |= NFS_OPEN_FILE_CREATE; } - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); - if (inuse) + } + if (inuse) { nfs_mount_state_in_use_end(nmp, error); + } goto restart; } +#endif if (nofp->nof_flags & NFS_OPEN_FILE_NEEDCLOSE) { /* * If the file is marked as needing reopen, but this was the only @@ -542,27 +732,35 @@ restart: } else if (!force) { lck_mtx_unlock(&np->n_openlock); if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) { - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); - if (inuse) + } + if (inuse) { nfs_mount_state_in_use_end(nmp, 0); - if (!nfs4_reopen(nofp, NULL)) + } +#if CONFIG_NFS4 + if (!nfs4_reopen(nofp, NULL)) { goto restart; + } +#endif } error = nfs_close(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx); if (error) { NP(np, "nfs_vnop_inactive: need close error: %d", error); nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE; } - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); - if (inuse) + } + if (inuse) { nfs_mount_state_in_use_end(nmp, error); + } goto restart; } } - if (nofp->nof_opencnt && !force) + if (nofp->nof_opencnt && !force) { NP(np, "nfs_vnop_inactive: file still open: %d", nofp->nof_opencnt); + } if (!force && (nofp->nof_access || nofp->nof_deny || nofp->nof_mmap_access || nofp->nof_mmap_deny || nofp->nof_r || nofp->nof_w || nofp->nof_rw || @@ -572,30 +770,32 @@ restart: nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw || nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) { NP(np, "nfs_vnop_inactive: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u", - nofp->nof_access, nofp->nof_deny, - nofp->nof_mmap_access, nofp->nof_mmap_deny, - nofp->nof_r, nofp->nof_d_r, - nofp->nof_w, nofp->nof_d_w, - nofp->nof_rw, nofp->nof_d_rw, - nofp->nof_r_dw, nofp->nof_d_r_dw, - nofp->nof_w_dw, nofp->nof_d_w_dw, - nofp->nof_rw_dw, nofp->nof_d_rw_dw, - nofp->nof_r_drw, nofp->nof_d_r_drw, - nofp->nof_w_drw, nofp->nof_d_w_drw, - nofp->nof_rw_drw, nofp->nof_d_rw_drw); + nofp->nof_access, nofp->nof_deny, + nofp->nof_mmap_access, nofp->nof_mmap_deny, + nofp->nof_r, nofp->nof_d_r, + nofp->nof_w, nofp->nof_d_w, + nofp->nof_rw, nofp->nof_d_rw, + nofp->nof_r_dw, nofp->nof_d_r_dw, + nofp->nof_w_dw, nofp->nof_d_w_dw, + nofp->nof_rw_dw, nofp->nof_d_rw_dw, + nofp->nof_r_drw, nofp->nof_d_r_drw, + nofp->nof_w_drw, nofp->nof_d_w_drw, + nofp->nof_rw_drw, nofp->nof_d_rw_drw); } - if (busied) + if (busied) { nfs_open_file_clear_busy(nofp); + } } lck_mtx_unlock(&np->n_openlock); - if (inuse && nfs_mount_state_in_use_end(nmp, error)) + if (inuse && nfs_mount_state_in_use_end(nmp, error)) { goto restart; + } nfs_node_lock_force(np); if (vnode_vtype(vp) != VDIR) { - nsp = np->n_sillyrename; + nsp = np->n_sillyrename; np->n_sillyrename = NULL; } else { nsp = NULL; @@ -609,7 +809,7 @@ restart: np->n_flag &= (NMODIFIED); nfs_node_unlock(np); FSDBG_BOT(264, vp, np, np->n_flag, 0); - return (0); + return 0; } nfs_node_unlock(np); @@ -658,8 +858,9 @@ restart: np->n_flag &= (NMODIFIED); nfs_node_unlock(np); - if (!busyerror) + if (!busyerror) { nfs_node_clear_busy2(nsp->nsr_dnp, np); + } if (unhash && vnode_isinuse(vp, 0)) { /* vnode now inuse after silly remove? */ @@ -690,29 +891,29 @@ restart: lck_mtx_unlock(nfs_node_hash_mutex); /* cleanup sillyrename info */ - if (nsp->nsr_cred != NOCRED) + if (nsp->nsr_cred != NOCRED) { kauth_cred_unref(&nsp->nsr_cred); + } vnode_rele(NFSTOV(nsp->nsr_dnp)); FREE_ZONE(nsp, sizeof(*nsp), M_NFSREQ); FSDBG_BOT(264, vp, np, np->n_flag, 0); - return (0); + return 0; } /* * Reclaim an nfsnode so that it can be used for other purposes. */ int -nfs_vnop_reclaim(ap) +nfs_vnop_reclaim( struct vnop_reclaim_args /* { - struct vnodeop_desc *a_desc; - vnode_t a_vp; - vfs_context_t a_context; - } */ *ap; + * struct vnodeop_desc *a_desc; + * vnode_t a_vp; + * vfs_context_t a_context; + * } */*ap) { vnode_t vp = ap->a_vp; nfsnode_t np = VTONFS(vp); - vfs_context_t ctx = ap->a_context; struct nfs_open_file *nofp, *nextnofp; struct nfs_file_lock *nflp, *nextnflp; struct nfs_lock_owner *nlop, *nextnlop; @@ -721,11 +922,13 @@ nfs_vnop_reclaim(ap) int force; FSDBG_TOP(265, vp, np, np->n_flag, 0); - force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)); + force = (!mp || vfs_isforce(mp) || nfs_mount_gone(nmp)); + /* There shouldn't be any open or lock state at this point */ lck_mtx_lock(&np->n_openlock); +#if CONFIG_NFS4 if (nmp && (nmp->nm_vers >= NFS_VER4)) { /* need to drop a delegation */ if (np->n_dreturn.tqe_next != NFSNOLIST) { @@ -750,25 +953,27 @@ nfs_vnop_reclaim(ap) /* try to return the delegation */ np->n_openflags &= ~N_DELEG_MASK; nfs4_delegreturn_rpc(nmp, np->n_fhp, np->n_fhsize, &np->n_dstateid, - R_RECOVER, vfs_context_thread(ctx), vfs_context_ucred(ctx)); + R_RECOVER, vfs_context_thread(ctx), vfs_context_ucred(ctx)); } if (np->n_attrdirfh) { FREE(np->n_attrdirfh, M_TEMP); np->n_attrdirfh = NULL; } } +#endif /* clean up file locks */ TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) { if (!(nflp->nfl_flags & NFS_FILE_LOCK_DEAD) && !force) { NP(np, "nfs_vnop_reclaim: lock 0x%llx 0x%llx 0x%x (bc %d)", - nflp->nfl_start, nflp->nfl_end, nflp->nfl_flags, nflp->nfl_blockcnt); + nflp->nfl_start, nflp->nfl_end, nflp->nfl_flags, nflp->nfl_blockcnt); } - if (!(nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))) { + if (!(nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED | NFS_FILE_LOCK_DEAD))) { /* try sending an unlock RPC if it wasn't delegated */ - if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED) && !force) + if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED) && !force) { nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER, - NULL, nflp->nfl_owner->nlo_open_owner->noo_cred); + NULL, nflp->nfl_owner->nlo_open_owner->noo_cred); + } lck_mtx_lock(&nflp->nfl_owner->nlo_lock); TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink); lck_mtx_unlock(&nflp->nfl_owner->nlo_lock); @@ -778,20 +983,24 @@ nfs_vnop_reclaim(ap) } /* clean up lock owners */ TAILQ_FOREACH_SAFE(nlop, &np->n_lock_owners, nlo_link, nextnlop) { - if (!TAILQ_EMPTY(&nlop->nlo_locks) && !force) + if (!TAILQ_EMPTY(&nlop->nlo_locks) && !force) { NP(np, "nfs_vnop_reclaim: lock owner with locks"); + } TAILQ_REMOVE(&np->n_lock_owners, nlop, nlo_link); nfs_lock_owner_destroy(nlop); } /* clean up open state */ - if (np->n_openrefcnt && !force) + if (np->n_openrefcnt && !force) { NP(np, "nfs_vnop_reclaim: still open: %d", np->n_openrefcnt); + } TAILQ_FOREACH_SAFE(nofp, &np->n_opens, nof_link, nextnofp) { - if (nofp->nof_flags & NFS_OPEN_FILE_BUSY) + if (nofp->nof_flags & NFS_OPEN_FILE_BUSY) { NP(np, "nfs_vnop_reclaim: open file busy"); + } if (!(np->n_flag & NREVOKE) && !(nofp->nof_flags & NFS_OPEN_FILE_LOST)) { - if (nofp->nof_opencnt && !force) + if (nofp->nof_opencnt && !force) { NP(np, "nfs_vnop_reclaim: file still open: %d", nofp->nof_opencnt); + } if (!force && (nofp->nof_access || nofp->nof_deny || nofp->nof_mmap_access || nofp->nof_mmap_deny || nofp->nof_r || nofp->nof_w || nofp->nof_rw || @@ -801,22 +1010,25 @@ nfs_vnop_reclaim(ap) nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw || nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) { NP(np, "nfs_vnop_reclaim: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u", - nofp->nof_access, nofp->nof_deny, - nofp->nof_mmap_access, nofp->nof_mmap_deny, - nofp->nof_r, nofp->nof_d_r, - nofp->nof_w, nofp->nof_d_w, - nofp->nof_rw, nofp->nof_d_rw, - nofp->nof_r_dw, nofp->nof_d_r_dw, - nofp->nof_w_dw, nofp->nof_d_w_dw, - nofp->nof_rw_dw, nofp->nof_d_rw_dw, - nofp->nof_r_drw, nofp->nof_d_r_drw, - nofp->nof_w_drw, nofp->nof_d_w_drw, - nofp->nof_rw_drw, nofp->nof_d_rw_drw); + nofp->nof_access, nofp->nof_deny, + nofp->nof_mmap_access, nofp->nof_mmap_deny, + nofp->nof_r, nofp->nof_d_r, + nofp->nof_w, nofp->nof_d_w, + nofp->nof_rw, nofp->nof_d_rw, + nofp->nof_r_dw, nofp->nof_d_r_dw, + nofp->nof_w_dw, nofp->nof_d_w_dw, + nofp->nof_rw_dw, nofp->nof_d_rw_dw, + nofp->nof_r_drw, nofp->nof_d_r_drw, + nofp->nof_w_drw, nofp->nof_d_w_drw, + nofp->nof_rw_drw, nofp->nof_d_rw_drw); +#if CONFIG_NFS4 /* try sending a close RPC if it wasn't delegated */ if (nofp->nof_r || nofp->nof_w || nofp->nof_rw || nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw || - nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw) + nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw) { nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER); + } +#endif } } TAILQ_REMOVE(&np->n_opens, nofp, nof_link); @@ -829,9 +1041,9 @@ nfs_vnop_reclaim(ap) /* then remove this node from the monitored node list. */ lck_mtx_lock(&nmp->nm_lock); while (np->n_mflag & NMMONSCANINPROG) { - struct timespec ts = { 1, 0 }; + struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 }; np->n_mflag |= NMMONSCANWANT; - msleep(&np->n_mflag, &nmp->nm_lock, PZERO-1, "nfswaitmonscan", &ts); + msleep(&np->n_mflag, &nmp->nm_lock, PZERO - 1, "nfswaitmonscan", &ts); } if (np->n_monlink.le_next != NFSNOLIST) { LIST_REMOVE(np, n_monlink); @@ -841,18 +1053,21 @@ nfs_vnop_reclaim(ap) } lck_mtx_lock(nfs_buf_mutex); - if (!force && (!LIST_EMPTY(&np->n_dirtyblkhd) || !LIST_EMPTY(&np->n_cleanblkhd))) + if (!force && (!LIST_EMPTY(&np->n_dirtyblkhd) || !LIST_EMPTY(&np->n_cleanblkhd))) { NP(np, "nfs_reclaim: dropping %s buffers", (!LIST_EMPTY(&np->n_dirtyblkhd) ? "dirty" : "clean")); + } lck_mtx_unlock(nfs_buf_mutex); nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ap->a_context, 0); lck_mtx_lock(nfs_node_hash_mutex); if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename) { - if (!force) + if (!force) { NP(np, "nfs_reclaim: leaving unlinked file %s", np->n_sillyrename->nsr_name); - if (np->n_sillyrename->nsr_cred != NOCRED) + } + if (np->n_sillyrename->nsr_cred != NOCRED) { kauth_cred_unref(&np->n_sillyrename->nsr_cred); + } vnode_rele(NFSTOV(np->n_sillyrename->nsr_dnp)); FREE_ZONE(np->n_sillyrename, sizeof(*np->n_sillyrename), M_NFSREQ); } @@ -871,12 +1086,15 @@ nfs_vnop_reclaim(ap) * structures that might be associated with this nfs node. */ nfs_node_lock_force(np); - if ((vnode_vtype(vp) == VDIR) && np->n_cookiecache) + if ((vnode_vtype(vp) == VDIR) && np->n_cookiecache) { FREE_ZONE(np->n_cookiecache, sizeof(struct nfsdmap), M_NFSDIROFF); - if (np->n_fhsize > NFS_SMALLFH) + } + if (np->n_fhsize > NFS_SMALLFH) { FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); - if (np->n_vattr.nva_acl) + } + if (np->n_vattr.nva_acl) { kauth_acl_free(np->n_vattr.nva_acl); + } nfs_node_unlock(np); vnode_clearfsnode(vp); @@ -894,7 +1112,7 @@ nfs_vnop_reclaim(ap) FSDBG_BOT(265, vp, np, np->n_flag, 0xd1ed1e); FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE); - return (0); + return 0; } /* @@ -909,10 +1127,10 @@ nfs_node_lock_internal(nfsnode_t np, int force) if (!force && !(np->n_hflag && NHHASHED)) { FSDBG_BOT(268, np, 0xdead, 0, 0); lck_mtx_unlock(&np->n_lock); - return (ENOENT); + return ENOENT; } FSDBG_BOT(268, np, force, 0, 0); - return (0); + return 0; } int @@ -951,21 +1169,25 @@ nfs_node_lock2(nfsnode_t np1, nfsnode_t np2) first = (np1 > np2) ? np1 : np2; second = (np1 > np2) ? np2 : np1; - if ((error = nfs_node_lock(first))) - return (error); - if (np1 == np2) - return (error); - if ((error = nfs_node_lock(second))) + if ((error = nfs_node_lock(first))) { + return error; + } + if (np1 == np2) { + return error; + } + if ((error = nfs_node_lock(second))) { nfs_node_unlock(first); - return (error); + } + return error; } void nfs_node_unlock2(nfsnode_t np1, nfsnode_t np2) { nfs_node_unlock(np1); - if (np1 != np2) + if (np1 != np2) { nfs_node_unlock(np2); + } } /* @@ -975,21 +1197,24 @@ nfs_node_unlock2(nfsnode_t np1, nfsnode_t np2) int nfs_node_set_busy(nfsnode_t np, thread_t thd) { - struct timespec ts = { 2, 0 }; + struct timespec ts = { .tv_sec = 2, .tv_nsec = 0 }; int error; - if ((error = nfs_node_lock(np))) - return (error); + if ((error = nfs_node_lock(np))) { + return error; + } while (ISSET(np->n_flag, NBUSY)) { SET(np->n_flag, NBUSYWANT); - msleep(np, &np->n_lock, PZERO-1, "nfsbusywant", &ts); - if ((error = nfs_sigintr(NFSTONMP(np), NULL, thd, 0))) + msleep(np, &np->n_lock, PZERO - 1, "nfsbusywant", &ts); + if ((error = nfs_sigintr(NFSTONMP(np), NULL, thd, 0))) { break; + } } - if (!error) + if (!error) { SET(np->n_flag, NBUSY); + } nfs_node_unlock(np); - return (error); + return error; } void @@ -999,10 +1224,11 @@ nfs_node_clear_busy(nfsnode_t np) nfs_node_lock_force(np); wanted = ISSET(np->n_flag, NBUSYWANT); - CLR(np->n_flag, NBUSY|NBUSYWANT); + CLR(np->n_flag, NBUSY | NBUSYWANT); nfs_node_unlock(np); - if (wanted) + if (wanted) { wakeup(np); + } } int @@ -1013,21 +1239,25 @@ nfs_node_set_busy2(nfsnode_t np1, nfsnode_t np2, thread_t thd) first = (np1 > np2) ? np1 : np2; second = (np1 > np2) ? np2 : np1; - if ((error = nfs_node_set_busy(first, thd))) - return (error); - if (np1 == np2) - return (error); - if ((error = nfs_node_set_busy(second, thd))) + if ((error = nfs_node_set_busy(first, thd))) { + return error; + } + if (np1 == np2) { + return error; + } + if ((error = nfs_node_set_busy(second, thd))) { nfs_node_clear_busy(first); - return (error); + } + return error; } void nfs_node_clear_busy2(nfsnode_t np1, nfsnode_t np2) { nfs_node_clear_busy(np1); - if (np1 != np2) + if (np1 != np2) { nfs_node_clear_busy(np2); + } } /* helper function to sort four nodes in reverse address order (no dupes) */ @@ -1043,17 +1273,20 @@ nfs_node_sort4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, nfsno nb[0] = (np3 > np4) ? np3 : np4; nb[1] = (np3 > np4) ? np4 : np3; for (a = b = i = lcnt = 0; i < 4; i++) { - if (a >= 2) + if (a >= 2) { list[lcnt] = nb[b++]; - else if ((b >= 2) || (na[a] >= nb[b])) + } else if ((b >= 2) || (na[a] >= nb[b])) { list[lcnt] = na[a++]; - else + } else { list[lcnt] = nb[b++]; - if ((lcnt <= 0) || (list[lcnt] != list[lcnt-1])) + } + if ((lcnt <= 0) || (list[lcnt] != list[lcnt - 1])) { lcnt++; /* omit dups */ + } } - if (list[lcnt-1] == NULL) + if (list[lcnt - 1] == NULL) { lcnt--; + } *lcntp = lcnt; } @@ -1066,14 +1299,16 @@ nfs_node_set_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, t nfs_node_sort4(np1, np2, np3, np4, list, &lcnt); /* Now we can lock using list[0 - lcnt-1] */ - for (i = 0; i < lcnt; ++i) + for (i = 0; i < lcnt; ++i) { if ((error = nfs_node_set_busy(list[i], thd))) { /* Drop any locks we acquired. */ - while (--i >= 0) + while (--i >= 0) { nfs_node_clear_busy(list[i]); - return (error); + } + return error; } - return (0); + } + return 0; } void @@ -1083,8 +1318,9 @@ nfs_node_clear_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4) int lcnt; nfs_node_sort4(np1, np2, np3, np4, list, &lcnt); - while (--lcnt >= 0) + while (--lcnt >= 0) { nfs_node_clear_busy(list[lcnt]); + } } /* @@ -1105,14 +1341,16 @@ nfs_data_lock_internal(nfsnode_t np, int locktype, int updatesize) { FSDBG_TOP(270, np, locktype, np->n_datalockowner, 0); if (locktype == NFS_DATA_LOCK_SHARED) { - if (updatesize && ISSET(np->n_flag, NUPDATESIZE)) + if (updatesize && ISSET(np->n_flag, NUPDATESIZE)) { nfs_data_update_size(np, 0); + } lck_rw_lock_shared(&np->n_datalock); } else { lck_rw_lock_exclusive(&np->n_datalock); np->n_datalockowner = current_thread(); - if (updatesize && ISSET(np->n_flag, NUPDATESIZE)) + if (updatesize && ISSET(np->n_flag, NUPDATESIZE)) { nfs_data_update_size(np, 1); + } } FSDBG_BOT(270, np, locktype, np->n_datalockowner, 0); } @@ -1135,12 +1373,14 @@ nfs_data_unlock_internal(nfsnode_t np, int updatesize) { int mine = (np->n_datalockowner == current_thread()); FSDBG_TOP(271, np, np->n_datalockowner, current_thread(), 0); - if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE)) + if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE)) { nfs_data_update_size(np, 1); + } np->n_datalockowner = NULL; lck_rw_done(&np->n_datalock); - if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE)) + if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE)) { nfs_data_update_size(np, 0); + } FSDBG_BOT(271, np, np->n_datalockowner, current_thread(), 0); } @@ -1163,8 +1403,9 @@ nfs_data_update_size(nfsnode_t np, int datalocked) } error = nfs_node_lock(np); if (error || !ISSET(np->n_flag, NUPDATESIZE)) { - if (!error) + if (!error) { nfs_node_unlock(np); + } FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize); return; } @@ -1177,3 +1418,40 @@ nfs_data_update_size(nfsnode_t np, int datalocked) FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize); } +#define DODEBUG 1 + +int +nfs_mount_is_dirty(mount_t mp) +{ + u_long i; + nfsnode_t np; +#ifdef DODEBUG + struct timeval now, then, diff; + u_long ncnt = 0; + microuptime(&now); +#endif + lck_mtx_lock(nfs_node_hash_mutex); + for (i = 0; i <= nfsnodehash; i++) { + LIST_FOREACH(np, &nfsnodehashtbl[i], n_hash) { +#ifdef DODEBUG + ncnt++; +#endif + if (np->n_mount == mp && !LIST_EMPTY(&np->n_dirtyblkhd)) { + goto out; + } + } + } +out: + lck_mtx_unlock(nfs_node_hash_mutex); +#ifdef DODEBUG + microuptime(&then); + timersub(&then, &now, &diff); + + NFS_DBG(NFS_FAC_SOCK, 7, "mount_is_dirty for %s took %lld mics for %ld slots and %ld nodes return %d\n", + vfs_statfs(mp)->f_mntfromname, (uint64_t)diff.tv_sec * 1000000LL + diff.tv_usec, i, ncnt, (i <= nfsnodehash)); +#endif + + return i <= nfsnodehash; +} + +#endif /* CONFIG_NFS_CLIENT */