/* * Return root of a filesystem */ static int nfs_root(struct mount *mp, int flags, struct vnode **vpp) { struct vnode *vp; struct nfsmount *nmp; struct nfsnode *np; int error; nmp = VFSTONFS(mp); error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, flags); if (error) return error; vp = NFSTOV(np); /* * Get transfer parameters and attributes for root vnode once. */ mtx_lock(&nmp->nm_mtx); if ((nmp->nm_state & NFSSTA_GOTFSINFO) == 0 && (nmp->nm_flag & NFSMNT_NFSV3)) { mtx_unlock(&nmp->nm_mtx); nfs_fsinfo(nmp, vp, curthread->td_ucred, curthread); } else mtx_unlock(&nmp->nm_mtx); if (vp->v_type == VNON) vp->v_type = VDIR; vp->v_vflag |= VV_ROOT; *vpp = vp; return (0); }
static int nfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred) { struct vnode *vp; struct nfs_statfs *sfp; struct nfsmount *nmp = VFSTONFS(mp); thread_t td = curthread; int error = 0, retattr; struct nfsnode *np; struct nfsm_info info; info.mrep = NULL; info.v3 = (nmp->nm_flag & NFSMNT_NFSV3); lwkt_gettoken(&nmp->nm_token); #ifndef nolint sfp = NULL; #endif error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, NULL); if (error) { lwkt_reltoken(&nmp->nm_token); return (error); } vp = NFSTOV(np); /* ignore the passed cred */ cred = crget(); cred->cr_ngroups = 1; if (info.v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0) (void)nfs_fsinfo(nmp, vp, td); nfsstats.rpccnt[NFSPROC_FSSTAT]++; nfsm_reqhead(&info, vp, NFSPROC_FSSTAT, NFSX_FH(info.v3)); ERROROUT(nfsm_fhtom(&info, vp)); NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_FSSTAT, td, cred, &error)); if (info.v3) { ERROROUT(nfsm_postop_attr(&info, vp, &retattr, NFS_LATTR_NOSHRINK)); } if (error) { if (info.mrep != NULL) m_freem(info.mrep); goto nfsmout; } NULLOUT(sfp = nfsm_dissect(&info, NFSX_STATFS(info.v3))); sbp->f_flag = nmp->nm_flag; sbp->f_owner = nmp->nm_cred->cr_ruid; if (info.v3) { sbp->f_bsize = NFS_FABLKSIZE; sbp->f_frsize = NFS_FABLKSIZE; sbp->f_blocks = (fxdr_hyper(&sfp->sf_tbytes) / ((u_quad_t)NFS_FABLKSIZE)); sbp->f_bfree = (fxdr_hyper(&sfp->sf_fbytes) / ((u_quad_t)NFS_FABLKSIZE)); sbp->f_bavail = (fxdr_hyper(&sfp->sf_abytes) / ((u_quad_t)NFS_FABLKSIZE)); sbp->f_files = fxdr_hyper(&sfp->sf_tfiles); sbp->f_ffree = fxdr_hyper(&sfp->sf_ffiles); sbp->f_favail = fxdr_hyper(&sfp->sf_afiles); } else { sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail); sbp->f_files = 0; sbp->f_ffree = 0; sbp->f_favail = 0; } sbp->f_syncreads = 0; sbp->f_syncwrites = 0; sbp->f_asyncreads = 0; sbp->f_asyncwrites = 0; sbp->f_type = mp->mnt_vfc->vfc_typenum; m_freem(info.mrep); info.mrep = NULL; nfsmout: vput(vp); crfree(cred); lwkt_reltoken(&nmp->nm_token); return (error); }
/* * nfs statfs call */ int nfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) { struct vnode *vp; struct nfs_statfs *sfp; struct nfsmount *nmp = VFSTONFS(mp); thread_t td = curthread; int error = 0, retattr; struct nfsnode *np; u_quad_t tquad; struct nfsm_info info; info.mrep = NULL; info.v3 = (nmp->nm_flag & NFSMNT_NFSV3); lwkt_gettoken(&nmp->nm_token); #ifndef nolint sfp = NULL; #endif error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, NULL); if (error) { lwkt_reltoken(&nmp->nm_token); return (error); } vp = NFSTOV(np); /* ignore the passed cred */ cred = crget(); cred->cr_ngroups = 1; if (info.v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0) (void)nfs_fsinfo(nmp, vp, td); nfsstats.rpccnt[NFSPROC_FSSTAT]++; nfsm_reqhead(&info, vp, NFSPROC_FSSTAT, NFSX_FH(info.v3)); ERROROUT(nfsm_fhtom(&info, vp)); NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_FSSTAT, td, cred, &error)); if (info.v3) { ERROROUT(nfsm_postop_attr(&info, vp, &retattr, NFS_LATTR_NOSHRINK)); } if (error) { if (info.mrep != NULL) m_freem(info.mrep); goto nfsmout; } NULLOUT(sfp = nfsm_dissect(&info, NFSX_STATFS(info.v3))); sbp->f_flags = nmp->nm_flag; if (info.v3) { sbp->f_bsize = NFS_FABLKSIZE; tquad = fxdr_hyper(&sfp->sf_tbytes); sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); tquad = fxdr_hyper(&sfp->sf_fbytes); sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); tquad = fxdr_hyper(&sfp->sf_abytes); sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); sbp->f_files = (fxdr_unsigned(int32_t, sfp->sf_tfiles.nfsuquad[1]) & 0x7fffffff); sbp->f_ffree = (fxdr_unsigned(int32_t, sfp->sf_ffiles.nfsuquad[1]) & 0x7fffffff); } else { sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail); sbp->f_files = 0; sbp->f_ffree = 0; } /* * Some values are pre-set in mnt_stat. Note in particular f_iosize * cannot be changed once the filesystem is mounted as it is used * as the basis for BIOs. */ if (sbp != &mp->mnt_stat) { sbp->f_type = mp->mnt_vfc->vfc_typenum; bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); sbp->f_iosize = mp->mnt_stat.f_iosize; } m_freem(info.mrep); info.mrep = NULL; nfsmout: vput(vp); crfree(cred); lwkt_reltoken(&nmp->nm_token); return (error); }
int nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp) { struct nfsnode *np, *np2; struct nfsnodehashhead *nhpp; struct vnode *vp; int error; int lkflags; struct nfsmount *nmp; /* * Calculate nfs mount point and figure out whether the rslock should * be interruptable or not. */ nmp = VFSTONFS(mntp); if (nmp->nm_flag & NFSMNT_INT) lkflags = LK_PCATCH; else lkflags = 0; lwkt_gettoken(&nfsnhash_token); retry: nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT)); loop: for (np = nhpp->lh_first; np; np = np->n_hash.le_next) { if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize || bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) { continue; } vp = NFSTOV(np); if (vget(vp, LK_EXCLUSIVE)) goto loop; for (np = nhpp->lh_first; np; np = np->n_hash.le_next) { if (mntp == NFSTOV(np)->v_mount && np->n_fhsize == fhsize && bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize) == 0 ) { break; } } if (np == NULL || NFSTOV(np) != vp) { vput(vp); goto loop; } *npp = np; lwkt_reltoken(&nfsnhash_token); return(0); } /* * Obtain a lock to prevent a race condition if the getnewvnode() * or MALLOC() below happens to block. */ if (lockmgr(&nfsnhash_lock, LK_EXCLUSIVE | LK_SLEEPFAIL)) goto loop; /* * Allocate before getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if objcache should block. */ np = objcache_get(nfsnode_objcache, M_WAITOK); error = getnewvnode(VT_NFS, mntp, &vp, 0, 0); if (error) { lockmgr(&nfsnhash_lock, LK_RELEASE); *npp = NULL; objcache_put(nfsnode_objcache, np); lwkt_reltoken(&nfsnhash_token); return (error); } /* * Initialize most of (np). */ bzero(np, sizeof (*np)); if (fhsize > NFS_SMALLFH) { MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK); } else {
/* * Look up a vnode/nfsnode by file handle. * Callers must check for mount points!! * In all cases, a pointer to a * nfsnode structure is returned. */ int nfs_nget( mount_t mp, nfsnode_t dnp, struct componentname *cnp, u_char *fhp, int fhsize, struct nfs_vattr *nvap, u_int64_t *xidp, uint32_t auth, int flags, nfsnode_t *npp) { nfsnode_t np; struct nfsnodehashhead *nhpp; vnode_t vp; int error, nfsvers; mount_t mp2; struct vnode_fsparam vfsp; uint32_t vid; FSDBG_TOP(263, mp, dnp, flags, npp); /* Check for unmount in progress */ if (!mp || vfs_isforce(mp)) { *npp = NULL; error = ENXIO; FSDBG_BOT(263, mp, dnp, 0xd1e, error); return (error); } nfsvers = VFSTONFS(mp)->nm_vers; nhpp = NFSNOHASH(nfs_hash(fhp, fhsize)); loop: lck_mtx_lock(nfs_node_hash_mutex); 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)) 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)) namecmp = 1; else namecmp = strncmp(vname, cnp->cn_nameptr, cnp->cn_namelen); vnode_putname(vname); } if (namecmp) /* full name didn't match */ continue; } FSDBG(263, dnp, np, np->n_flag, 0xcace0000); /* if the node is locked, sleep on it */ if ((np->n_hflag & NHLOCKED) && !(flags & NG_NOCREATE)) { np->n_hflag |= NHLOCKWANT; FSDBG(263, dnp, np, np->n_flag, 0xcace2222); msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL); FSDBG(263, dnp, np, np->n_flag, 0xcace3333); goto loop; } vp = NFSTOV(np); vid = vnode_vid(vp); lck_mtx_unlock(nfs_node_hash_mutex); if ((error = vnode_getwithvid(vp, vid))) { /* * If vnode is being reclaimed or has already * changed identity, no need to wait. */ FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error); return (error); } if ((error = nfs_node_lock(np))) { /* this only fails if the node is now unhashed */ /* so let's see if we can find/create it again */ FSDBG(263, dnp, *npp, 0xcaced1e2, error); vnode_put(vp); if (flags & NG_NOCREATE) { *npp = 0; FSDBG_BOT(263, dnp, *npp, 0xcaced1e0, ENOENT); return (ENOENT); } goto loop; } /* update attributes */ 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)) 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. * * <rant>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</rant> */ 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 && 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); } FSDBG(263, mp, dnp, npp, 0xaaaaaaaa); if (flags & NG_NOCREATE) { lck_mtx_unlock(nfs_node_hash_mutex); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOENT); return (ENOENT); } /* * allocate and initialize nfsnode and stick it in the hash * before calling getnewvnode(). Anyone finding it in the * hash before initialization is complete will wait for it. */ MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK); if (!np) { lck_mtx_unlock(nfs_node_hash_mutex); *npp = 0; FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM); return (ENOMEM); } bzero(np, sizeof *np); np->n_hflag |= (NHINIT | NHLOCKED); np->n_mount = mp; np->n_auth = auth; TAILQ_INIT(&np->n_opens); TAILQ_INIT(&np->n_lock_owners); TAILQ_INIT(&np->n_locks); np->n_dlink.tqe_next = NFSNOLIST; np->n_dreturn.tqe_next = NFSNOLIST; np->n_monlink.le_next = NFSNOLIST; /* 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')) np->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)) np->n_parent = dvp; vnode_put(dvp); } } /* setup node's file handle */ if (fhsize > NFS_SMALLFH) { MALLOC_ZONE(np->n_fhp, u_char *, 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); } } else {
/* * nfs statfs call */ static int nfs_statfs(struct mount *mp, struct statfs *sbp) { struct vnode *vp; struct thread *td; struct nfs_statfs *sfp; caddr_t bpos, dpos; struct nfsmount *nmp = VFSTONFS(mp); int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr; struct mbuf *mreq, *mrep, *md, *mb; struct nfsnode *np; u_quad_t tquad; td = curthread; #ifndef nolint sfp = NULL; #endif error = vfs_busy(mp, MBF_NOWAIT); if (error) return (error); error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE); if (error) { vfs_unbusy(mp); return (error); } vp = NFSTOV(np); mtx_lock(&nmp->nm_mtx); if (v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0) { mtx_unlock(&nmp->nm_mtx); (void)nfs_fsinfo(nmp, vp, td->td_ucred, td); } else mtx_unlock(&nmp->nm_mtx); nfsstats.rpccnt[NFSPROC_FSSTAT]++; mreq = nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3)); mb = mreq; bpos = mtod(mb, caddr_t); nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_FSSTAT, td, td->td_ucred); if (v3) nfsm_postop_attr(vp, retattr); if (error) { if (mrep != NULL) m_freem(mrep); goto nfsmout; } sfp = nfsm_dissect(struct nfs_statfs *, NFSX_STATFS(v3)); mtx_lock(&nmp->nm_mtx); sbp->f_iosize = nfs_iosize(nmp); mtx_unlock(&nmp->nm_mtx); if (v3) { sbp->f_bsize = NFS_FABLKSIZE; tquad = fxdr_hyper(&sfp->sf_tbytes); sbp->f_blocks = tquad / NFS_FABLKSIZE; tquad = fxdr_hyper(&sfp->sf_fbytes); sbp->f_bfree = tquad / NFS_FABLKSIZE; tquad = fxdr_hyper(&sfp->sf_abytes); sbp->f_bavail = tquad / NFS_FABLKSIZE; sbp->f_files = (fxdr_unsigned(int32_t, sfp->sf_tfiles.nfsuquad[1]) & 0x7fffffff); sbp->f_ffree = (fxdr_unsigned(int32_t, sfp->sf_ffiles.nfsuquad[1]) & 0x7fffffff); } else { sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail); sbp->f_files = 0; sbp->f_ffree = 0; } m_freem(mrep); nfsmout: vput(vp); vfs_unbusy(mp); return (error); }
/* * Common code for mount and mountroot */ static int mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, char *hst, struct vnode **vpp, struct ucred *cred, int nametimeo, int negnametimeo) { struct nfsmount *nmp; struct nfsnode *np; int error; struct vattr attrs; if (mp->mnt_flag & MNT_UPDATE) { nmp = VFSTONFS(mp); printf("%s: MNT_UPDATE is no longer handled here\n", __func__); free(nam, M_SONAME); return (0); } else { nmp = uma_zalloc(nfsmount_zone, M_WAITOK); bzero((caddr_t)nmp, sizeof (struct nfsmount)); TAILQ_INIT(&nmp->nm_bufq); mp->mnt_data = nmp; nmp->nm_getinfo = nfs_getnlminfo; nmp->nm_vinvalbuf = nfs_vinvalbuf; } vfs_getnewfsid(mp); nmp->nm_mountp = mp; mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF); /* * V2 can only handle 32 bit filesizes. A 4GB-1 limit may be too * high, depending on whether we end up with negative offsets in * the client or server somewhere. 2GB-1 may be safer. * * For V3, nfs_fsinfo will adjust this as necessary. Assume maximum * that we can handle until we find out otherwise. */ if ((argp->flags & NFSMNT_NFSV3) == 0) nmp->nm_maxfilesize = 0xffffffffLL; else nmp->nm_maxfilesize = OFF_MAX; nmp->nm_timeo = NFS_TIMEO; nmp->nm_retry = NFS_RETRANS; if ((argp->flags & NFSMNT_NFSV3) && argp->sotype == SOCK_STREAM) { nmp->nm_wsize = nmp->nm_rsize = NFS_MAXDATA; } else { nmp->nm_wsize = NFS_WSIZE; nmp->nm_rsize = NFS_RSIZE; } nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000); nmp->nm_readdirsize = NFS_READDIRSIZE; nmp->nm_numgrps = NFS_MAXGRPS; nmp->nm_readahead = NFS_DEFRAHEAD; nmp->nm_deadthresh = NFS_MAXDEADTHRESH; nmp->nm_nametimeo = nametimeo; nmp->nm_negnametimeo = negnametimeo; nmp->nm_tprintf_delay = nfs_tprintf_delay; if (nmp->nm_tprintf_delay < 0) nmp->nm_tprintf_delay = 0; nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay; if (nmp->nm_tprintf_initial_delay < 0) nmp->nm_tprintf_initial_delay = 0; nmp->nm_fhsize = argp->fhsize; bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); nmp->nm_nam = nam; /* Set up the sockets and per-host congestion */ nmp->nm_sotype = argp->sotype; nmp->nm_soproto = argp->proto; nmp->nm_rpcops = &nfs_rpcops; nfs_decode_args(mp, nmp, argp, hst); /* * For Connection based sockets (TCP,...) defer the connect until * the first request, in case the server is not responding. */ if (nmp->nm_sotype == SOCK_DGRAM && (error = nfs_connect(nmp))) goto bad; /* * This is silly, but it has to be set so that vinifod() works. * We do not want to do an nfs_statfs() here since we can get * stuck on a dead server and we are holding a lock on the mount * point. */ mtx_lock(&nmp->nm_mtx); mp->mnt_stat.f_iosize = nfs_iosize(nmp); mtx_unlock(&nmp->nm_mtx); /* * A reference count is needed on the nfsnode representing the * remote root. If this object is not persistent, then backward * traversals of the mount point (i.e. "..") will not work if * the nfsnode gets flushed out of the cache. Ufs does not have * this problem, because one can identify root inodes by their * number == ROOTINO (2). */ error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE); if (error) goto bad; *vpp = NFSTOV(np); /* * Get file attributes and transfer parameters for the * mountpoint. This has the side effect of filling in * (*vpp)->v_type with the correct value. */ if (argp->flags & NFSMNT_NFSV3) nfs_fsinfo(nmp, *vpp, curthread->td_ucred, curthread); else VOP_GETATTR(*vpp, &attrs, curthread->td_ucred); /* * Lose the lock but keep the ref. */ VOP_UNLOCK(*vpp, 0); return (0); bad: nfs_disconnect(nmp); mtx_destroy(&nmp->nm_mtx); uma_zfree(nfsmount_zone, nmp); free(nam, M_SONAME); return (error); }
/* * Look up a vnode/nfsnode by file handle and store the pointer in *npp. * Callers must check for mount points!! * An error number is returned. */ int nfs_nget(struct mount *mnt, nfsfh_t *fh, int fhsize, struct nfsnode **npp) { struct nfsmount *nmp; struct nfsnode *np, find, *np2; struct vnode *vp, *nvp; struct proc *p = curproc; /* XXX */ int error; nmp = VFSTONFS(mnt); loop: rw_enter_write(&nfs_hashlock); find.n_fhp = fh; find.n_fhsize = fhsize; np = RB_FIND(nfs_nodetree, &nmp->nm_ntree, &find); if (np != NULL) { rw_exit_write(&nfs_hashlock); vp = NFSTOV(np); error = vget(vp, LK_EXCLUSIVE, p); if (error) goto loop; *npp = np; return (0); } /* * getnewvnode() could recycle a vnode, potentially formerly * owned by NFS. This will cause a VOP_RECLAIM() to happen, * which will cause recursive locking, so we unlock before * calling getnewvnode() lock again afterwards, but must check * to see if this nfsnode has been added while we did not hold * the lock. */ rw_exit_write(&nfs_hashlock); error = getnewvnode(VT_NFS, mnt, &nfs_vops, &nvp); /* note that we don't have this vnode set up completely yet */ rw_enter_write(&nfs_hashlock); if (error) { *npp = NULL; rw_exit_write(&nfs_hashlock); return (error); } nvp->v_flag |= VLARVAL; np = RB_FIND(nfs_nodetree, &nmp->nm_ntree, &find); if (np != NULL) { vgone(nvp); rw_exit_write(&nfs_hashlock); goto loop; } vp = nvp; np = pool_get(&nfs_node_pool, PR_WAITOK | PR_ZERO); vp->v_data = np; /* we now have an nfsnode on this vnode */ vp->v_flag &= ~VLARVAL; np->n_vnode = vp; rw_init(&np->n_commitlock, "nfs_commitlk"); /* * Are we getting the root? If so, make sure the vnode flags * are correct */ if ((fhsize == nmp->nm_fhsize) && !bcmp(fh, nmp->nm_fh, fhsize)) { if (vp->v_type == VNON) vp->v_type = VDIR; vp->v_flag |= VROOT; } np->n_fhp = &np->n_fh; bcopy(fh, np->n_fhp, fhsize); np->n_fhsize = fhsize; np2 = RB_INSERT(nfs_nodetree, &nmp->nm_ntree, np); KASSERT(np2 == NULL); np->n_accstamp = -1; rw_exit(&nfs_hashlock); *npp = np; return (0); }
/* * Look up a vnode/nfsnode by file handle. * Callers must check for mount points!! * In all cases, a pointer to a * nfsnode structure is returned. */ int nfs_nget1(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp, int lkflags) { struct nfsnode *np; struct vnode *vp; struct nfsmount *nmp = VFSTONFS(mntp); int error; struct fh_match fhm; fhm.fhm_fhp = fhp; fhm.fhm_fhsize = fhsize; loop: rw_enter(&nmp->nm_rbtlock, RW_READER); np = rb_tree_find_node(&nmp->nm_rbtree, &fhm); if (np != NULL) { vp = NFSTOV(np); mutex_enter(vp->v_interlock); rw_exit(&nmp->nm_rbtlock); error = vget(vp, LK_EXCLUSIVE | lkflags); if (error == EBUSY) return error; if (error) goto loop; *npp = np; return(0); } rw_exit(&nmp->nm_rbtlock); error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, NULL, &vp); if (error) { *npp = 0; return (error); } np = pool_get(&nfs_node_pool, PR_WAITOK); memset(np, 0, sizeof *np); np->n_vnode = vp; /* * Insert the nfsnode in the hash queue for its new file handle */ if (fhsize > NFS_SMALLFH) { np->n_fhp = kmem_alloc(fhsize, KM_SLEEP); } else np->n_fhp = &np->n_fh; memcpy(np->n_fhp, fhp, fhsize); np->n_fhsize = fhsize; np->n_accstamp = -1; np->n_vattr = pool_get(&nfs_vattr_pool, PR_WAITOK); rw_enter(&nmp->nm_rbtlock, RW_WRITER); if (NULL != rb_tree_find_node(&nmp->nm_rbtree, &fhm)) { rw_exit(&nmp->nm_rbtlock); if (fhsize > NFS_SMALLFH) { kmem_free(np->n_fhp, fhsize); } pool_put(&nfs_vattr_pool, np->n_vattr); pool_put(&nfs_node_pool, np); ungetnewvnode(vp); goto loop; } vp->v_data = np; genfs_node_init(vp, &nfs_genfsops); /* * Initalize read/write creds to useful values. VOP_OPEN will * overwrite these. */ np->n_rcred = curlwp->l_cred; kauth_cred_hold(np->n_rcred); np->n_wcred = curlwp->l_cred; kauth_cred_hold(np->n_wcred); VOP_LOCK(vp, LK_EXCLUSIVE); NFS_INVALIDATE_ATTRCACHE(np); uvm_vnp_setsize(vp, 0); (void)rb_tree_insert_node(&nmp->nm_rbtree, np); rw_exit(&nmp->nm_rbtlock); *npp = np; return (0); }