X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/15129b1c8dbb3650c63b70adb1cad9af601c6c17..ecc0ceb4089d506a0b8d16686a95817b331af9cb:/bsd/nfs/nfs_vnops.c diff --git a/bsd/nfs/nfs_vnops.c b/bsd/nfs/nfs_vnops.c index a8c2017b4..ae1906aed 100644 --- a/bsd/nfs/nfs_vnops.c +++ b/bsd/nfs/nfs_vnops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Apple Inc. All rights reserved. + * Copyright (c) 2000-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -120,6 +120,7 @@ #include <kern/sched_prim.h> #define NFS_VNOP_DBG(...) NFS_DBG(NFS_FAC_VNOP, 7, ## __VA_ARGS__) +#define DEFAULT_READLINK_NOCACHE 0 /* * NFS vnode ops @@ -445,8 +446,9 @@ struct vnodeopv_desc fifo_nfsv4nodeop_opv_desc = { &fifo_nfsv4nodeop_p, fifo_nfsv4nodeop_entries }; #endif /* FIFO */ - int nfs_sillyrename(nfsnode_t,nfsnode_t,struct componentname *,vfs_context_t); +int nfs_getattr_internal(nfsnode_t, struct nfs_vattr *, vfs_context_t, int); +int nfs_refresh_fh(nfsnode_t, vfs_context_t); /* * Find the slot in the access cache for this UID. @@ -471,7 +473,7 @@ nfs_node_access_slot(nfsnode_t np, uid_t uid, int add) } int -nfs3_access_rpc(nfsnode_t np, u_int32_t *access, vfs_context_t ctx) +nfs3_access_rpc(nfsnode_t np, u_int32_t *access, int rpcflags, vfs_context_t ctx) { int error = 0, lockerror = ENOENT, status, slot; uint32_t access_result = 0; @@ -488,7 +490,9 @@ nfs3_access_rpc(nfsnode_t np, u_int32_t *access, vfs_context_t ctx) nfsm_chain_add_32(error, &nmreq, *access); nfsm_chain_build_done(error, &nmreq); nfsmout_if(error); - error = nfs_request(np, NULL, &nmreq, NFSPROC_ACCESS, ctx, NULL, &nmrep, &xid, &status); + error = nfs_request2(np, NULL, &nmreq, NFSPROC_ACCESS, + vfs_context_thread(ctx), vfs_context_ucred(ctx), + NULL, rpcflags, &nmrep, &xid, &status); if ((lockerror = nfs_node_lock(np))) error = lockerror; nfsm_chain_postop_attr_update(error, &nmrep, np, &xid); @@ -527,19 +531,6 @@ nfsmout: return (error); } -/* - * See if our mount is in trouble. Note this is inherently racey. - */ -static int -nfs_notresponding(struct nfsmount *nmp) -{ - int timeoutmask = NFSSTA_TIMEO | NFSSTA_LOCKTIMEO | NFSSTA_JUKEBOXTIMEO; - if (NMFLAG(nmp, MUTEJUKEBOX)) /* jukebox timeouts don't count as unresponsive if muted */ - timeoutmask &= ~NFSSTA_JUKEBOXTIMEO; - - return ((nmp->nm_state & timeoutmask) || !(nmp->nm_sockflags & NMSOCK_READY)); -} - /* * NFS access vnode op. * For NFS version 2, just return ok. File accesses may fail later. @@ -557,7 +548,7 @@ nfs_vnop_access( { vfs_context_t ctx = ap->a_context; vnode_t vp = ap->a_vp; - int error = 0, slot, dorpc; + int error = 0, slot, dorpc, rpcflags = 0; u_int32_t access, waccess; nfsnode_t np = VTONFS(vp); struct nfsmount *nmp; @@ -566,7 +557,7 @@ nfs_vnop_access( uid_t uid; nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -645,7 +636,10 @@ nfs_vnop_access( * Does our cached result allow us to give a definite yes to * this request? */ - uid = kauth_cred_getuid(vfs_context_ucred(ctx)); + if (auth_is_kerberized(np->n_auth) || auth_is_kerberized(nmp->nm_auth)) + uid = nfs_cred_getasid2uid(vfs_context_ucred(ctx)); + else + uid = kauth_cred_getuid(vfs_context_ucred(ctx)); slot = nfs_node_access_slot(np, uid, 0); dorpc = 1; if (access == 0) { @@ -654,46 +648,35 @@ nfs_vnop_access( dorpc = 0; waccess = 0; } else if (NACCESSVALID(np, slot)) { - /* - * In addition if the kernel is checking for access, i.e., - * KAUTH_VNODE_ACCESS is not set, and the server does not seem - * to be responding just return if we have something in the - * cache even if its stale for the user. If were granted access - * by the cache and we're a kernel access, then call it good - * enough. We want to avoid having this particular request going - * over the wire causing a hang. This is because at this moment - * we do not know what the state of the server is and what ever - * we get back be it either yea or nay is going to be stale. - * Finder (Desktop services/FileURL) might hang when going over - * the wire when just asking getattrlist for the roots FSID - * since we are going to be called to see if we're authorized - * for search. - * - * N.B. This is also the strategy that SMB is using. - */ - int granted = ((np->n_access[slot] & access) == access); - - if (!(ap->a_action & KAUTH_VNODE_ACCESS)) { - if (granted || nfs_notresponding(nmp)) { - dorpc = 0; - waccess = np->n_access[slot]; - } - } else { - int stale; - microuptime(&now); - stale = (now.tv_sec >= (np->n_accessstamp[slot] + nfs_access_cache_timeout)); - if (granted && !stale) { + microuptime(&now); + if (((now.tv_sec < (np->n_accessstamp[slot] + nfs_access_cache_timeout)) && + ((np->n_access[slot] & access) == access)) || nfs_use_cache(nmp)) { /* OSAddAtomic(1, &nfsstats.accesscache_hits); */ - dorpc = 0; - waccess = np->n_access[slot]; - } + dorpc = 0; + waccess = np->n_access[slot]; } } nfs_node_unlock(np); if (dorpc) { /* Either a no, or a don't know. Go to the wire. */ /* OSAddAtomic(1, &nfsstats.accesscache_misses); */ - error = nmp->nm_funcs->nf_access_rpc(np, &waccess, ctx); + + /* + * Allow an access call to timeout if we have it cached + * so we won't hang if the server isn't responding. + */ + if (NACCESSVALID(np, slot)) + rpcflags |= R_SOFT; + + error = nmp->nm_funcs->nf_access_rpc(np, &waccess, rpcflags, ctx); + + /* + * If the server didn't respond return the cached access. + */ + if ((error == ETIMEDOUT) && (rpcflags & R_SOFT)) { + error = 0; + waccess = np->n_access[slot]; + } } if (!error && ((waccess & access) != access)) error = EACCES; @@ -731,7 +714,7 @@ nfs_vnop_open( return (EINVAL); nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); if (np->n_flag & NREVOKE) return (EIO); @@ -1059,7 +1042,7 @@ nfs_vnop_close( */ uint32_t writers; mount_t mp = vnode_mount(vp); - int force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)); + int force = (!mp || vfs_isforce(mp)); writers = nfs_no_of_open_file_writers(np); nfs_release_open_state_for_node(np, force); @@ -1288,8 +1271,6 @@ v3close: } - - int nfs3_getattr_rpc( nfsnode_t np, @@ -1305,13 +1286,16 @@ nfs3_getattr_rpc( int error = 0, status, nfsvers, rpcflags = 0; struct nfsm_chain nmreq, nmrep; - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if (flags & NGA_MONITOR) /* vnode monitor requests should be soft */ rpcflags = R_RECOVER; + if (flags & NGA_SOFT) /* Return ETIMEDOUT if server not responding */ + rpcflags |= R_SOFT; + nfsm_chain_null(&nmreq); nfsm_chain_null(&nmrep); @@ -1334,9 +1318,159 @@ nfsmout: return (error); } +/* + * nfs_refresh_fh will attempt to update the file handle for the node. + * + * It only does this for symbolic links and regular files that are not currently opened. + * + * On Success returns 0 and the nodes file handle is updated, or ESTALE on failure. + */ +int +nfs_refresh_fh(nfsnode_t np, vfs_context_t ctx) +{ + vnode_t dvp, vp = NFSTOV(np); + nfsnode_t dnp; + const char *v_name = vnode_getname(vp); + char *name; + int namelen, fhsize, refreshed; + int error, wanted = 0; + uint8_t *fhp; + struct timespec ts = {2, 0}; + + NFS_VNOP_DBG("vnode is %d\n", vnode_vtype(vp)); + + dvp = vnode_parent(vp); + if ((vnode_vtype(vp) != VREG && vnode_vtype(vp) != VLNK) || + v_name == NULL || *v_name == '\0' || dvp == NULL) { + if (v_name != NULL) + vnode_putname(v_name); + return (ESTALE); + } + dnp = VTONFS(dvp); + + namelen = strlen(v_name); + MALLOC(name, char *, namelen + 1, M_TEMP, M_WAITOK); + if (name == NULL) { + vnode_putname(v_name); + return (ESTALE); + } + bcopy(v_name, name, namelen+1); + NFS_VNOP_DBG("Trying to refresh %s : %s\n", v_name, name); + vnode_putname(v_name); + + /* Allocate the maximum size file handle */ + MALLOC(fhp, uint8_t *, NFS4_FHSIZE, M_TEMP, M_WAITOK); + if (fhp == NULL) { + FREE(name, M_TEMP); + return (ESTALE); + } + + if ((error = nfs_node_lock(np))) { + FREE(name, M_TEMP); + FREE(fhp, M_TEMP); + return (ESTALE); + } + + fhsize = np->n_fhsize; + bcopy(np->n_fhp, fhp, fhsize); + while (ISSET(np->n_flag, NREFRESH)) { + SET(np->n_flag, NREFRESHWANT); + NFS_VNOP_DBG("Waiting for refresh of %s\n", name); + msleep(np, &np->n_lock, PZERO-1, "nfsrefreshwant", &ts); + if ((error = nfs_sigintr(NFSTONMP(np), NULL, vfs_context_thread(ctx), 0))) + break; + } + refreshed = error ? 0 : !NFS_CMPFH(np, fhp, fhsize); + SET(np->n_flag, NREFRESH); + nfs_node_unlock(np); + + NFS_VNOP_DBG("error = %d, refreshed = %d\n", error, refreshed); + if (error || refreshed) + goto nfsmout; + + /* Check that there are no open references for this file */ + lck_mtx_lock(&np->n_openlock); + if (np->n_openrefcnt || !TAILQ_EMPTY(&np->n_opens) || !TAILQ_EMPTY(&np->n_lock_owners)) { + int cnt = 0; + struct nfs_open_file *ofp; + + TAILQ_FOREACH(ofp, &np->n_opens, nof_link) { + cnt += ofp->nof_opencnt; + } + if (cnt) { + lck_mtx_unlock(&np->n_openlock); + NFS_VNOP_DBG("Can not refresh file handle for %s with open state\n", name); + NFS_VNOP_DBG("\topenrefcnt = %d, opens = %d lock_owners = %d\n", + np->n_openrefcnt, cnt, !TAILQ_EMPTY(&np->n_lock_owners)); + error = ESTALE; + goto nfsmout; + } + } + lck_mtx_unlock(&np->n_openlock); + /* + * Since the FH is currently stale we should not be able to + * establish any open state until the FH is refreshed. + */ + + error = nfs_node_lock(np); + nfsmout_if(error); + /* + * Symlinks should never need invalidations and are holding + * the one and only nfsbuf in an uncached acquired state + * trying to do a readlink. So we will hang if we invalidate + * in that case. Only in in the VREG case do we need to + * invalidate. + */ + if (vnode_vtype(vp) == VREG) { + np->n_flag &= ~NNEEDINVALIDATE; + nfs_node_unlock(np); + error = nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ctx, 1); + if (error) + NFS_VNOP_DBG("nfs_vinvalbuf returned %d\n", error); + nfsmout_if(error); + } else { + nfs_node_unlock(np); + } + + NFS_VNOP_DBG("Looking up %s\n", name); + error = nfs_lookitup(dnp, name, namelen, ctx, &np); + if (error) + NFS_VNOP_DBG("nfs_lookitup returned %d\n", error); + +nfsmout: + nfs_node_lock_force(np); + wanted = ISSET(np->n_flag, NREFRESHWANT); + CLR(np->n_flag, NREFRESH|NREFRESHWANT); + nfs_node_unlock(np); + if (wanted) + wakeup(np); + + if (error == 0) + NFS_VNOP_DBG("%s refreshed file handle\n", name); + + FREE(name, M_TEMP); + FREE(fhp, M_TEMP); + + return (error ? ESTALE : 0); +} int nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) +{ + int error; + +retry: + error = nfs_getattr_internal(np, nvap, ctx, flags); + if (error == ESTALE) { + error = nfs_refresh_fh(np, ctx); + if (!error) + goto retry; + } + return (error); +} + +int +nfs_getattr_internal(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) { struct nfsmount *nmp; int error = 0, nfsvers, inprogset = 0, wanted = 0, avoidfloods; @@ -1346,7 +1480,9 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) FSDBG_TOP(513, np->n_size, np, np->n_vattr.nva_size, np->n_flag); - if (!(nmp = NFSTONMP(np))) + nmp = NFSTONMP(np); + + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -1370,7 +1506,8 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) /* * Use the cache or wait for any getattr in progress if: * - it's a cached request, or - * - we have a delegation + * - we have a delegation, or + * - the server isn't responding */ while (1) { error = nfs_getattrcache(np, nvap, flags); @@ -1406,11 +1543,19 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) nfs_node_unlock(np); nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) error = ENXIO; if (error) goto nfsmout; + /* + * Return cached attributes if they are valid, + * if the server doesn't respond, and this is + * some softened up style of mount. + */ + if (NATTRVALID(np) && nfs_use_cache(nmp)) + flags |= NGA_SOFT; + /* * We might want to try to get both the attributes and access info by * making an ACCESS call and seeing if it returns updated attributes. @@ -1421,7 +1566,17 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) if (nfs_attrcachetimeout(np) > 0) { /* OSAddAtomic(1, &nfsstats.accesscache_misses); */ u_int32_t access = NFS_ACCESS_ALL; - error = nmp->nm_funcs->nf_access_rpc(np, &access, ctx); + int rpcflags = 0; + + /* Return cached attrs if server doesn't respond */ + if (flags & NGA_SOFT) + rpcflags |= R_SOFT; + + error = nmp->nm_funcs->nf_access_rpc(np, &access, rpcflags, ctx); + + if (error == ETIMEDOUT) + goto returncached; + if (error) goto nfsmout; nfs_node_lock_force(np); @@ -1435,6 +1590,7 @@ nfs_getattr(nfsnode_t np, struct nfs_vattr *nvap, vfs_context_t ctx, int flags) } avoidfloods = 0; + tryagain: error = nmp->nm_funcs->nf_getattr_rpc(np, NULL, np->n_fhp, np->n_fhsize, flags, ctx, nvap, &xid); if (!error) { @@ -1442,7 +1598,22 @@ tryagain: error = nfs_loadattrcache(np, nvap, &xid, 0); nfs_node_unlock(np); } + + /* + * If the server didn't respond, return cached attributes. + */ +returncached: + if ((flags & NGA_SOFT) && (error == ETIMEDOUT)) { + nfs_node_lock_force(np); + error = nfs_getattrcache(np, nvap, flags); + if (!error || (error != ENOENT)) { + nfs_node_unlock(np); + goto nfsmout; + } + nfs_node_unlock(np); + } nfsmout_if(error); + if (!xid) { /* out-of-order rpc - attributes were dropped */ FSDBG(513, -1, np, np->n_xid >> 32, np->n_xid); if (avoidfloods++ < 20) @@ -1620,7 +1791,7 @@ nfs_vnop_setattr( struct nfs_open_file *nofp = NULL; nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR); @@ -1923,7 +2094,7 @@ nfs3_setattr_rpc( u_int64_t xid, nextxid; struct nfsm_chain nmreq, nmrep; - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -2109,7 +2280,7 @@ nfs_vnop_lookup( mp = vnode_mount(dvp); nmp = VFSTONFS(mp); - if (!nmp) { + if (nfs_mount_gone(nmp)) { error = ENXIO; goto error_return; } @@ -2182,7 +2353,7 @@ nfs_vnop_lookup( /* do we know this name is too long? */ nmp = VTONMP(dvp); - if (!nmp) { + if (nfs_mount_gone(nmp)) { error = ENXIO; goto error_return; } @@ -2275,6 +2446,8 @@ error_return: return (error); } +int nfs_readlink_nocache = DEFAULT_READLINK_NOCACHE; + /* * NFS readlink call */ @@ -2294,6 +2467,8 @@ nfs_vnop_readlink( uint32_t buflen; uio_t uio = ap->a_uio; struct nfsbuf *bp = NULL; + struct timespec ts; + int timeo; if (vnode_vtype(ap->a_vp) != VLNK) return (EPERM); @@ -2304,34 +2479,66 @@ nfs_vnop_readlink( return (EINVAL); nmp = VTONMP(ap->a_vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; + /* nfs_getattr() will check changed and purge caches */ - if ((error = nfs_getattr(np, NULL, ctx, NGA_CACHED))) { + if ((error = nfs_getattr(np, NULL, ctx, nfs_readlink_nocache ? NGA_UNCACHED : NGA_CACHED))) { FSDBG(531, np, 0xd1e0001, 0, error); return (error); } + if (nfs_readlink_nocache) { + timeo = nfs_attrcachetimeout(np); + nanouptime(&ts); + } + +retry: OSAddAtomic64(1, &nfsstats.biocache_readlinks); - error = nfs_buf_get(np, 0, NFS_MAXPATHLEN, vfs_context_thread(ctx), NBLK_READ, &bp); + error = nfs_buf_get(np, 0, NFS_MAXPATHLEN, vfs_context_thread(ctx), NBLK_META, &bp); if (error) { FSDBG(531, np, 0xd1e0002, 0, error); return (error); } + + if (nfs_readlink_nocache) { + NFS_VNOP_DBG("timeo = %d ts.tv_sec = %ld need refresh = %d cached = %d\n", timeo, ts.tv_sec, + (np->n_rltim.tv_sec + timeo) < ts.tv_sec || nfs_readlink_nocache > 1, + ISSET(bp->nb_flags, NB_CACHE) == NB_CACHE); + /* n_rltim is synchronized by the associated nfs buf */ + if (ISSET(bp->nb_flags, NB_CACHE) && ((nfs_readlink_nocache > 1) || ((np->n_rltim.tv_sec + timeo) < ts.tv_sec))) { + SET(bp->nb_flags, NB_INVAL); + nfs_buf_release(bp, 0); + goto retry; + } + } if (!ISSET(bp->nb_flags, NB_CACHE)) { +readagain: OSAddAtomic64(1, &nfsstats.readlink_bios); buflen = bp->nb_bufsize; error = nmp->nm_funcs->nf_readlink_rpc(np, bp->nb_data, &buflen, ctx); if (error) { + if (error == ESTALE) { + NFS_VNOP_DBG("Stale FH from readlink rpc\n"); + error = nfs_refresh_fh(np, ctx); + if (error == 0) + goto readagain; + } SET(bp->nb_flags, NB_ERROR); bp->nb_error = error; + NFS_VNOP_DBG("readlink failed %d\n", error); } else { bp->nb_validoff = 0; bp->nb_validend = buflen; + np->n_rltim = ts; + NFS_VNOP_DBG("readlink of %.*s\n", bp->nb_validend, (char *)bp->nb_data); } + } else { + NFS_VNOP_DBG("got cached link of %.*s\n", bp->nb_validend, (char *)bp->nb_data); } + if (!error && (bp->nb_validend > 0)) error = uiomove(bp->nb_data, bp->nb_validend, uio); FSDBG(531, np, bp->nb_validend, 0, error); @@ -2352,7 +2559,7 @@ nfs3_readlink_rpc(nfsnode_t np, char *buf, uint32_t *buflenp, vfs_context_t ctx) struct nfsm_chain nmreq, nmrep; nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; nfsm_chain_null(&nmreq); @@ -2409,7 +2616,7 @@ nfs_read_rpc(nfsnode_t np, uio_t uio, vfs_context_t ctx) FSDBG_TOP(536, np, uio_offset(uio), uio_resid(uio), 0); nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; nmrsize = nmp->nm_rsize; @@ -2481,7 +2688,7 @@ nfs3_read_rpc_async( struct nfsm_chain nmreq; nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -2519,7 +2726,7 @@ nfs3_read_rpc_async_finish( struct nfsm_chain nmrep; nmp = NFSTONMP(np); - if (!nmp) { + if (nfs_mount_gone(nmp)) { nfs_request_async_cancel(req); return (ENXIO); } @@ -2668,9 +2875,59 @@ nfs_vnop_write( goto out; if (((uio_offset(uio) + uio_resid(uio)) > (off_t)np->n_size) && !(ioflag & IO_APPEND)) { - /* it looks like we'll be extending the file, so take the data lock exclusive */ + /* + * It looks like we'll be extending the file, so take the data lock exclusive. + */ nfs_data_unlock(np); nfs_data_lock(np, NFS_DATA_LOCK_EXCLUSIVE); + + /* + * Also, if the write begins after the previous EOF buffer, make sure to zero + * and validate the new bytes in that buffer. + */ + struct nfsbuf *eofbp = NULL; + daddr64_t eofbn = np->n_size / biosize; + int eofoff = np->n_size % biosize; + lbn = uio_offset(uio) / biosize; + + if (eofoff && (eofbn < lbn)) { + if ((error = nfs_buf_get(np, eofbn, biosize, thd, NBLK_WRITE|NBLK_ONLYVALID, &eofbp))) + goto out; + np->n_size += (biosize - eofoff); + nfs_node_lock_force(np); + CLR(np->n_flag, NUPDATESIZE); + np->n_flag |= NMODIFIED; + nfs_node_unlock(np); + FSDBG(516, np, np->n_size, np->n_vattr.nva_size, 0xf00d0001); + ubc_setsize(vp, (off_t)np->n_size); /* XXX errors */ + if (eofbp) { + /* + * For the old last page, don't zero bytes if there + * are invalid bytes in that page (i.e. the page isn't + * currently valid). + * For pages after the old last page, zero them and + * mark them as valid. + */ + char *d; + int i; + if (ioflag & IO_NOCACHE) + SET(eofbp->nb_flags, NB_NOCACHE); + NFS_BUF_MAP(eofbp); + FSDBG(516, eofbp, eofoff, biosize - eofoff, 0xe0fff01e); + d = eofbp->nb_data; + i = eofoff/PAGE_SIZE; + while (eofoff < biosize) { + int poff = eofoff & PAGE_MASK; + if (!poff || NBPGVALID(eofbp,i)) { + bzero(d + eofoff, PAGE_SIZE - poff); + NBPGVALID_SET(eofbp, i); + } + eofoff += PAGE_SIZE - poff; + i++; + } + nfs_buf_release(eofbp, 1); + } + } } do { @@ -2770,17 +3027,11 @@ again: * and zero the new bytes. */ if ((uio_offset(uio) + n) > (off_t)np->n_size) { - struct nfsbuf *eofbp = NULL; daddr64_t eofbn = np->n_size / biosize; - int eofoff = np->n_size % biosize; int neweofoff = (uio_offset(uio) + n) % biosize; FSDBG(515, 0xb1ffa000, uio_offset(uio) + n, eofoff, neweofoff); - if (eofoff && (eofbn < lbn) && - ((error = nfs_buf_get(np, eofbn, biosize, thd, NBLK_WRITE|NBLK_ONLYVALID, &eofbp)))) - goto out; - /* if we're extending within the same last block */ /* and the block is flagged as being cached... */ if ((lbn == eofbn) && ISSET(bp->nb_flags, NB_CACHE)) { @@ -2817,38 +3068,6 @@ again: nfs_node_unlock(np); FSDBG(516, np, np->n_size, np->n_vattr.nva_size, 0xf00d0001); ubc_setsize(vp, (off_t)np->n_size); /* XXX errors */ - if (eofbp) { - /* - * We may need to zero any previously invalid data - * after the old EOF in the previous EOF buffer. - * - * For the old last page, don't zero bytes if there - * are invalid bytes in that page (i.e. the page isn't - * currently valid). - * For pages after the old last page, zero them and - * mark them as valid. - */ - char *d; - int i; - if (ioflag & IO_NOCACHE) - SET(eofbp->nb_flags, NB_NOCACHE); - NFS_BUF_MAP(eofbp); - FSDBG(516, eofbp, eofoff, biosize - eofoff, 0xe0fff01e); - d = eofbp->nb_data; - i = eofoff/PAGE_SIZE; - while (eofoff < biosize) { - int poff = eofoff & PAGE_MASK; - if (!poff || NBPGVALID(eofbp,i)) { - bzero(d + eofoff, PAGE_SIZE - poff); - NBPGVALID_SET(eofbp, i); - } - if (bp->nb_validend == eofoff) - bp->nb_validend += PAGE_SIZE - poff; - eofoff += PAGE_SIZE - poff; - i++; - } - nfs_buf_release(eofbp, 1); - } } /* * If dirtyend exceeds file size, chop it down. This should @@ -3166,7 +3385,7 @@ nfs_write_rpc2( #endif FSDBG_TOP(537, np, uio_offset(uio), uio_resid(uio), *iomodep); nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; nmwsize = nmp->nm_wsize; @@ -3198,7 +3417,7 @@ nfs_write_rpc2( if (!error) error = nmp->nm_funcs->nf_write_rpc_async_finish(np, req, &commit, &rlen, &wverf2); nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) error = ENXIO; if ((nmp->nm_vers >= NFS_VER4) && nfs_mount_state_error_should_restart(error) && (++restart <= nfs_mount_state_max_restarts(nmp))) { /* guard against no progress */ @@ -3283,7 +3502,7 @@ nfs3_write_rpc_async( struct nfsm_chain nmreq; nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -3332,7 +3551,7 @@ nfs3_write_rpc_async_finish( struct nfsm_chain nmrep; nmp = NFSTONMP(np); - if (!nmp) { + if (nfs_mount_gone(nmp)) { nfs_request_async_cancel(req); return (ENXIO); } @@ -3344,7 +3563,7 @@ nfs3_write_rpc_async_finish( if (error == EINPROGRESS) /* async request restarted */ return (error); nmp = NFSTONMP(np); - if (!nmp) + if (nfs_mount_gone(nmp)) error = ENXIO; if (!error && (lockerror = nfs_node_lock(np))) error = lockerror; @@ -3429,7 +3648,7 @@ nfs3_vnop_mknod( struct nfsreq rq, *req = &rq; nmp = VTONMP(dvp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -3579,7 +3798,7 @@ nfs3_vnop_create( struct nfs_dulookup dul; nmp = VTONMP(dvp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -3758,7 +3977,7 @@ nfs_vnop_remove( /* XXX prevent removing a sillyrenamed file? */ nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR); @@ -3912,7 +4131,7 @@ int nfs_removeit(struct nfs_sillyrename *nsp) { struct nfsmount *nmp = NFSTONMP(nsp->nsr_dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); return nmp->nm_funcs->nf_remove_rpc(nsp->nsr_dnp, nsp->nsr_name, nsp->nsr_namlen, NULL, nsp->nsr_cred); } @@ -3936,7 +4155,7 @@ nfs3_remove_rpc( struct nfsm_chain nmreq, nmrep; nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if ((nfsvers == NFS_VER2) && (namelen > NFS_MAXNAMLEN)) @@ -4010,7 +4229,7 @@ nfs_vnop_rename( tnp = tvp ? VTONFS(tvp) : NULL; nmp = NFSTONMP(fdnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -4169,7 +4388,7 @@ nfs3_rename_rpc( struct nfsm_chain nmreq, nmrep; nmp = NFSTONMP(fdnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if ((nfsvers == NFS_VER2) && @@ -4251,7 +4470,7 @@ nfs3_vnop_link( return (EXDEV); nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN)) @@ -4352,7 +4571,7 @@ nfs3_vnop_symlink( struct nfs_dulookup dul; nmp = VTONMP(dvp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -4508,7 +4727,7 @@ nfs3_vnop_mkdir( struct nfs_dulookup dul; nmp = VTONMP(dvp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN)) @@ -4653,7 +4872,7 @@ nfs3_vnop_rmdir( struct nfs_dulookup dul; nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; if ((nfsvers == NFS_VER2) && (cnp->cn_namelen > NFS_MAXNAMLEN)) @@ -4776,7 +4995,7 @@ nfs_vnop_readdir( thread_t thd; nmp = VTONMP(dvp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; bigcookies = (nmp->nm_state & NFSSTA_BIGCOOKIES); @@ -5138,7 +5357,7 @@ nfs_dir_cookie_to_lbn(nfsnode_t dnp, uint64_t cookie, int *ptc, uint64_t *lbnp) * Let's search the directory's buffers for the cookie. */ nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); dpptc = NULL; found = 0; @@ -5309,7 +5528,8 @@ nfs_dir_buf_cache_lookup(nfsnode_t dnp, nfsnode_t *npp, struct componentname *cn daddr64_t lbn, nextlbn; int dotunder = (cnp->cn_namelen > 2) && (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == '_'); - if (!(nmp = NFSTONMP(dnp))) + nmp = NFSTONMP(dnp); + if (nfs_mount_gone(nmp)) return (ENXIO); if (!purge) *npp = NULL; @@ -5450,7 +5670,7 @@ nfs3_readdir_rpc(nfsnode_t dnp, struct nfsbuf *bp, vfs_context_t ctx) struct timeval now; nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; nmreaddirsize = nmp->nm_readdirsize; @@ -5733,7 +5953,7 @@ nfs_sillyrename( struct nfsmount *nmp; nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfs_name_cache_purge(dnp, np, cnp, ctx); @@ -5812,7 +6032,7 @@ nfs3_lookup_rpc_async( int error = 0, nfsvers; nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -5848,6 +6068,8 @@ nfs3_lookup_rpc_async_finish( struct nfsm_chain nmrep; nmp = NFSTONMP(dnp); + if (nmp == NULL) + return (ENXIO); nfsvers = nmp->nm_vers; nfsm_chain_null(&nmrep); @@ -5911,7 +6133,7 @@ nfs_lookitup( struct nfsreq rq, *req = &rq; nmp = NFSTONMP(dnp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXNAME) && @@ -6112,7 +6334,7 @@ nfs3_commit_rpc( nmp = NFSTONMP(np); FSDBG(521, np, offset, count, nmp ? nmp->nm_state : 0); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); if (!(nmp->nm_state & NFSSTA_HASWRITEVERF)) return (0); @@ -6206,7 +6428,7 @@ nfs3_pathconf_rpc( struct nfsmount *nmp = NFSTONMP(np); uint32_t val = 0; - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); nfsvers = nmp->nm_vers; @@ -6228,6 +6450,7 @@ nfs3_pathconf_rpc( error = status; nfsm_chain_get_32(error, &nmrep, nfsap->nfsa_maxlink); nfsm_chain_get_32(error, &nmrep, nfsap->nfsa_maxname); + nfsap->nfsa_flags &= ~(NFS_FSFLAG_NO_TRUNC|NFS_FSFLAG_CHOWN_RESTRICTED|NFS_FSFLAG_CASE_INSENSITIVE|NFS_FSFLAG_CASE_PRESERVING); nfsm_chain_get_32(error, &nmrep, val); if (val) nfsap->nfsa_flags |= NFS_FSFLAG_NO_TRUNC; @@ -6258,6 +6481,7 @@ nfs3_pathconf_cache(struct nfsmount *nmp, struct nfs_fsattr *nfsap) { nmp->nm_fsattr.nfsa_maxlink = nfsap->nfsa_maxlink; nmp->nm_fsattr.nfsa_maxname = nfsap->nfsa_maxname; + nmp->nm_fsattr.nfsa_flags &= ~(NFS_FSFLAG_NO_TRUNC|NFS_FSFLAG_CHOWN_RESTRICTED|NFS_FSFLAG_CASE_INSENSITIVE|NFS_FSFLAG_CASE_PRESERVING); nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_NO_TRUNC; nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_CHOWN_RESTRICTED; nmp->nm_fsattr.nfsa_flags |= nfsap->nfsa_flags & NFS_FSFLAG_CASE_INSENSITIVE; @@ -6297,7 +6521,7 @@ nfs_vnop_pathconf( uint nbits; nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); switch (ap->a_name) { @@ -6338,7 +6562,7 @@ nfs_vnop_pathconf( if (error) return (error); nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); lck_mtx_lock(&nmp->nm_lock); if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS) { @@ -6358,7 +6582,7 @@ nfs_vnop_pathconf( if (error) return (error); nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); lck_mtx_lock(&nmp->nm_lock); nfsap = &nfsa; @@ -6687,6 +6911,8 @@ nfs_vnop_ioctl( vfs_context_t ctx = ap->a_context; vnode_t vp = ap->a_vp; struct nfsmount *mp = VTONMP(vp); + struct user_nfs_gss_principal gprinc; + uint32_t len; int error = ENOTTY; if (mp == NULL) @@ -6700,8 +6926,78 @@ nfs_vnop_ioctl( error = nfs_flush(VTONFS(vp), MNT_WAIT, vfs_context_thread(ctx), 0); break; case NFS_FSCTL_DESTROY_CRED: - error = nfs_gss_clnt_ctx_destroy(mp, vfs_context_ucred(ctx)); + if (!auth_is_kerberized(mp->nm_auth)) + return (ENOTSUP); + error = nfs_gss_clnt_ctx_remove(mp, vfs_context_ucred(ctx)); + break; + case NFS_FSCTL_SET_CRED: + if (!auth_is_kerberized(mp->nm_auth)) + return (ENOTSUP); + NFS_DBG(NFS_FAC_GSS, 7, "Enter NFS_FSCTL_SET_CRED (proc %d) data = %p\n", vfs_context_is64bit(ctx), (void *)ap->a_data); + if (vfs_context_is64bit(ctx)) { + gprinc = *(struct user_nfs_gss_principal *)ap->a_data; + } else { + struct nfs_gss_principal *tp; + tp = (struct nfs_gss_principal *)ap->a_data; + gprinc.princlen = tp->princlen; + gprinc.nametype = tp->nametype; + gprinc.principal = CAST_USER_ADDR_T(tp->principal); + } + if (gprinc.princlen > MAXPATHLEN) + return (EINVAL); + NFS_DBG(NFS_FAC_GSS, 7, "Received principal length %d name type = %d\n", gprinc.princlen, gprinc.nametype); + uint8_t *p; + MALLOC(p, uint8_t *, gprinc.princlen+1, M_TEMP, M_WAITOK|M_ZERO); + if (p == NULL) + return (ENOMEM); + error = copyin(gprinc.principal, p, gprinc.princlen); + if (error) { + NFS_DBG(NFS_FAC_GSS, 7, "NFS_FSCTL_SET_CRED could not copy in princiapl data of len %d: %d\n", + gprinc.princlen, error); + FREE(p, M_TEMP); + return (error); + } + NFS_DBG(NFS_FAC_GSS, 7, "Seting credential to principal %s\n", p); + error = nfs_gss_clnt_ctx_set_principal(mp, ctx, p, gprinc.princlen, gprinc.nametype); + NFS_DBG(NFS_FAC_GSS, 7, "Seting credential to principal %s returned %d\n", p, error); + FREE(p, M_TEMP); break; + case NFS_FSCTL_GET_CRED: + if (!auth_is_kerberized(mp->nm_auth)) + return (ENOTSUP); + error = nfs_gss_clnt_ctx_get_principal(mp, ctx, &gprinc); + if (error) + break; + if (vfs_context_is64bit(ctx)) { + struct user_nfs_gss_principal *upp = (struct user_nfs_gss_principal *)ap->a_data; + len = upp->princlen; + if (gprinc.princlen < len) + len = gprinc.princlen; + upp->princlen = gprinc.princlen; + upp->nametype = gprinc.nametype; + upp->flags = gprinc.flags; + if (gprinc.principal) + error = copyout((void *)gprinc.principal, upp->principal, len); + else + upp->principal = USER_ADDR_NULL; + } else { + struct nfs_gss_principal *u32pp = (struct nfs_gss_principal *)ap->a_data; + len = u32pp->princlen; + if (gprinc.princlen < len) + len = gprinc.princlen; + u32pp->princlen = gprinc.princlen; + u32pp->nametype = gprinc.nametype; + u32pp->flags = gprinc.flags; + if (gprinc.principal) + error = copyout((void *)gprinc.principal, u32pp->principal, len); + else + u32pp->principal = (user32_addr_t)0; + } + if (error) { + NFS_DBG(NFS_FAC_GSS, 7, "NFS_FSCTL_GET_CRED could not copy out princiapl data of len %d: %d\n", + gprinc.princlen, error); + } + FREE(gprinc.principal, M_TEMP); } return (error); @@ -6794,7 +7090,7 @@ nfs_vnop_pagein( &uio_buf, sizeof(uio_buf)); nmp = VTONMP(vp); - if (!nmp) { + if (nfs_mount_gone(nmp)) { if (!nofreeupl) ubc_upl_abort_range(pl, pl_offset, size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY); @@ -7497,7 +7793,7 @@ nfs_vnop_blktooff( vnode_t vp = ap->a_vp; struct nfsmount *nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); biosize = nmp->nm_biosize; @@ -7519,7 +7815,7 @@ nfs_vnop_offtoblk( vnode_t vp = ap->a_vp; struct nfsmount *nmp = VTONMP(vp); - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); biosize = nmp->nm_biosize; @@ -7546,7 +7842,7 @@ nfs_vnop_monitor( struct nfsmount *nmp = VTONMP(ap->a_vp); int error = 0; - if (!nmp) + if (nfs_mount_gone(nmp)) return (ENXIO); /* make sure that the vnode's monitoring status is up to date */