diff -u -r -N sys.dec4/conf/files sys.dec4.packrats/conf/files --- sys.dec4/conf/files 2011-12-02 21:03:28.000000000 -0500 +++ sys.dec4.packrats/conf/files 2011-12-04 19:59:46.000000000 -0500 @@ -2103,6 +2103,7 @@ fs/nfsclient/nfs_clsubs.c optional nfscl fs/nfsclient/nfs_clstate.c optional nfscl fs/nfsclient/nfs_clkrpc.c optional nfscl +fs/nfsclient/nfs_clpackrat.c optional nfscl fs/nfsclient/nfs_clrpcops.c optional nfscl fs/nfsclient/nfs_clvnops.c optional nfscl fs/nfsclient/nfs_clnode.c optional nfscl diff -u -r -N sys.dec4/fs/nfs/nfs_commonport.c sys.dec4.packrats/fs/nfs/nfs_commonport.c --- sys.dec4/fs/nfs/nfs_commonport.c 2011-07-17 13:50:44.000000000 -0400 +++ sys.dec4.packrats/fs/nfs/nfs_commonport.c 2011-12-04 19:59:46.000000000 -0500 @@ -104,6 +104,7 @@ "New NFS directory offset data"); MALLOC_DEFINE(M_NEWNFSDROLLBACK, "NFSD rollback", "New NFS local lock rollback"); +MALLOC_DEFINE(M_NEWNFSLDIRTY, "NFSCL ldirty", "NFSCL deleg locally dirty"); /* * Definition of mutex locks. diff -u -r -N sys.dec4/fs/nfs/nfs_var.h sys.dec4.packrats/fs/nfs/nfs_var.h --- sys.dec4/fs/nfs/nfs_var.h 2011-11-20 12:46:04.000000000 -0500 +++ sys.dec4.packrats/fs/nfs/nfs_var.h 2011-12-04 19:59:46.000000000 -0500 @@ -483,10 +483,11 @@ void nfscl_reclaimnode(vnode_t); void nfscl_newnode(vnode_t); void nfscl_delegmodtime(vnode_t); -void nfscl_deleggetmodtime(vnode_t, struct timespec *); +void nfscl_deleggetmod(vnode_t, struct timespec *, uint64_t *); int nfscl_tryclose(struct nfsclopen *, struct ucred *, struct nfsmount *, NFSPROC_T *); void nfscl_cleanup(NFSPROC_T *); +struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, int); /* nfs_clport.c */ int nfscl_nget(mount_t, vnode_t, struct nfsfh *, @@ -600,3 +601,16 @@ int nfscbd_addsock(struct file *); int nfscbd_nfsd(NFSPROC_T *, struct nfsd_nfscbd_args *); +/* nfs_clpackrat.c */ +void nfscl_packratsetup(struct nfscldeleg *, struct nfsmount *, struct ucred *, + NFSPROC_T *); +void nfscl_packratopen(vnode_t, NFSPROC_T *); +void nfscl_packratclose(vnode_t, NFSPROC_T *); +int nfscbd_packrat(char *); +int nfscl_packratread(vnode_t, struct uio *, int, struct ucred *, int *); +int nfscl_packratwrite(vnode_t, struct uio *, int, struct ucred *, + NFSPROC_T *, int *); +int nfscl_deleglocalflush(struct nfscldeleg *, struct nfsmount *, NFSPROC_T *, + int, int); +void nfscl_packratsetsize(vnode_t, uint64_t); + diff -u -r -N sys.dec4/fs/nfs/nfsclstate.h sys.dec4.packrats/fs/nfs/nfsclstate.h --- sys.dec4/fs/nfs/nfsclstate.h 2011-12-02 22:26:37.000000000 -0500 +++ sys.dec4.packrats/fs/nfs/nfsclstate.h 2011-12-04 19:59:46.000000000 -0500 @@ -87,6 +87,15 @@ }; /* + * List entry for dirty byte ranges for nfscldeleg. + */ +struct nfsldirty { + LIST_ENTRY(nfsldirty) nfsw_list; + uint64_t nfsw_first; + uint64_t nfsw_end; +}; + +/* * MALLOC'd to the correct length to accommodate the file handle. */ struct nfscldeleg { @@ -104,21 +113,41 @@ u_int64_t nfsdl_size; /* saved copy of file size */ u_int64_t nfsdl_change; /* and change attribute */ struct timespec nfsdl_modtime; /* local modify time */ + u_int64_t nfsdl_opensize; /* size at open */ + u_int64_t nfsdl_localsize;/* locally stored file size */ + LIST_HEAD(, nfsldirty) nfsdl_ldirty; /* locally mod. ranges */ + u_int32_t nfsdl_localiocnt; /* Local I/Os in prog */ + vnode_t nfsdl_filevp; /* Local copy of file */ + vnode_t nfsdl_delegvp; /* Delegation record file */ + struct proc *nfsdl_packratthread; /* Packrat copy thread */ u_int16_t nfsdl_fhlen; - u_int8_t nfsdl_flags; + u_int16_t nfsdl_flags; u_int8_t nfsdl_fh[1]; /* must be last */ }; /* * nfsdl_flags bits. */ -#define NFSCLDL_READ 0x01 -#define NFSCLDL_WRITE 0x02 -#define NFSCLDL_RECALL 0x04 -#define NFSCLDL_NEEDRECLAIM 0x08 -#define NFSCLDL_ZAPPED 0x10 -#define NFSCLDL_MODTIMESET 0x20 -#define NFSCLDL_DELEGRET 0x40 +#define NFSCLDL_READ 0x0001 +#define NFSCLDL_WRITE 0x0002 +#define NFSCLDL_RECALL 0x0004 +#define NFSCLDL_NEEDRECLAIM 0x0008 +#define NFSCLDL_ZAPPED 0x0010 +#define NFSCLDL_MODTIMESET 0x0020 +#define NFSCLDL_DELEGRET 0x0040 +#define NFSCLDL_COPYINPROG 0x0080 +#define NFSCLDL_LOCALSIZESET 0x0100 +#define NFSCLDL_HASCOPY 0x0200 +#define NFSCLDL_WAITRECALL 0x0400 + +/* + * Maximum length of a local filename used by the packrat daemons. + * 1 - indicates the type of file. + * 1 - indicates IPv4 vs IPv6 host address. + * 32 - maximum IP host address length. + * 174 - the maximum length of the file handle in ascii. + */ +#define NFSPCKRAT_MAXFILELEN (1 + 1 + 32 + 174) /* * MALLOC'd to the correct length to accommodate the file handle. diff -u -r -N sys.dec4/fs/nfs/nfsport.h sys.dec4.packrats/fs/nfs/nfsport.h --- sys.dec4/fs/nfs/nfsport.h 2011-07-16 09:21:12.000000000 -0400 +++ sys.dec4.packrats/fs/nfs/nfsport.h 2011-12-04 19:59:46.000000000 -0500 @@ -673,6 +673,7 @@ MALLOC_DECLARE(M_NEWNFSDIRECTIO); MALLOC_DECLARE(M_NEWNFSMNT); MALLOC_DECLARE(M_NEWNFSDROLLBACK); +MALLOC_DECLARE(M_NEWNFSLDIRTY); #define M_NFSRVCACHE M_NEWNFSRVCACHE #define M_NFSDCLIENT M_NEWNFSDCLIENT #define M_NFSDSTATE M_NEWNFSDSTATE @@ -692,6 +693,7 @@ #define M_NFSV4NODE M_NEWNFSV4NODE #define M_NFSDIRECTIO M_NEWNFSDIRECTIO #define M_NFSDROLLBACK M_NEWNFSDROLLBACK +#define M_NFSLDIRTY M_NEWNFSLDIRTY #define NFSINT_SIGMASK(set) \ (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \ diff -u -r -N sys.dec4/fs/nfsclient/nfs_clbio.c sys.dec4.packrats/fs/nfsclient/nfs_clbio.c --- sys.dec4/fs/nfsclient/nfs_clbio.c 2011-12-02 21:03:19.000000000 -0500 +++ sys.dec4.packrats/fs/nfsclient/nfs_clbio.c 2011-12-04 20:15:19.000000000 -0500 @@ -80,7 +80,7 @@ int ncl_getpages(struct vop_getpages_args *ap) { - int i, error, nextoff, size, toff, count, npages; + int i, error, nextoff, size, toff, count, npages, didread; struct uio uio; struct iovec iov; vm_offset_t kva; @@ -167,7 +167,9 @@ uio.uio_rw = UIO_READ; uio.uio_td = td; - error = ncl_readrpc(vp, &uio, cred); + error = nfscl_packratread(vp, &uio, 0, cred, &didread); + if (didread == 0) + error = ncl_readrpc(vp, &uio, cred); pmap_qremove(kva, npages); relpbuf(bp, &ncl_pbuf_freecnt); @@ -267,7 +269,7 @@ struct iovec iov; vm_offset_t kva; struct buf *bp; - int iomode, must_commit, i, error, npages, count; + int iomode, must_commit, i, error, npages, count, didwrite; off_t offset; int *rtvals; struct vnode *vp; @@ -343,7 +345,9 @@ else iomode = NFSWRITE_FILESYNC; - error = ncl_writerpc(vp, &uio, cred, &iomode, &must_commit, 0); + error = nfscl_packratwrite(vp, &uio, 0, cred, NULL, &didwrite); + if (didwrite == 0) + error = ncl_writerpc(vp, &uio, cred, &iomode, &must_commit, 0); pmap_qremove(kva, npages); relpbuf(bp, &ncl_pbuf_freecnt); @@ -873,7 +877,7 @@ struct vattr vattr; struct nfsmount *nmp = VFSTONFS(vp->v_mount); daddr_t lbn; - int bcount; + int bcount, didwrite; int n, on, error = 0; off_t tmp_off; @@ -901,6 +905,14 @@ mtx_unlock(&nmp->nm_mtx); /* + * Do the write locally if there is a write delegation and packrats + * have created a locally cached copy. + */ + error = nfscl_packratwrite(vp, uio, ioflag, cred, td, &didwrite); + if (didwrite != 0) + return (error); + + /* * Synchronously flush pending buffers if we are in synchronous * mode or if we are appending. */ @@ -1333,7 +1345,7 @@ error = vinvalbuf(vp, flags, 0, slptimeo); } mtx_lock(&np->n_mtx); - if (np->n_directio_asyncwr == 0) + if (np->n_directio_asyncwr == 0 && (np->n_flag & NLOCALCACHE) == 0) np->n_flag &= ~NMODIFIED; mtx_unlock(&np->n_mtx); out: @@ -1507,7 +1519,8 @@ mtx_lock(&np->n_mtx); np->n_directio_asyncwr--; if (np->n_directio_asyncwr == 0) { - np->n_flag &= ~NMODIFIED; + if ((np->n_flag & NLOCALCACHE) == 0) + np->n_flag &= ~NMODIFIED; if ((np->n_flag & NFSYNCWAIT)) { np->n_flag &= ~NFSYNCWAIT; wakeup((caddr_t)&np->n_directio_asyncwr); diff -u -r -N sys.dec4/fs/nfsclient/nfs_clpackrat.c sys.dec4.packrats/fs/nfsclient/nfs_clpackrat.c --- sys.dec4/fs/nfsclient/nfs_clpackrat.c 1969-12-31 19:00:00.000000000 -0500 +++ sys.dec4.packrats/fs/nfsclient/nfs_clpackrat.c 2011-12-04 20:14:10.000000000 -0500 @@ -0,0 +1,1211 @@ +/*- + * Copyright (c) 2009 Rick Macklem, University of Guelph + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +/* + * These functions provide the packrat support for the NFSv4 client. That + * is, they implement agressive client side whole file caching to a directory + * on stable storage, such as a disk. + */ +#ifndef APPLEKEXT +NFSCLSTATEMUTEX; +static char nfscl_packratpath[MAXPATHLEN + 1]; +static int nfscl_packratpathlen = 0; +static uint64_t nfscl_packratmaxsize = 10000000; +#endif /* !APPLEKEXT */ + +#define NFS_PACKRATIOSIZE NFS_MAXBSIZE + +static vnode_t nfscl_openfile(char *, boolean_t, struct ucred *, NFSPROC_T *); +static void nfscl_closefile(vnode_t, char *, boolean_t, struct ucred *, + NFSPROC_T *); +static void start_packrat(void *); +static void nfscl_start_packratthread(struct nfscldeleg *); +static void nfscl_packratthread(struct nfscldeleg *, NFSPROC_T *); +static int nfsrpc_readdp(struct nfscldeleg *, struct uio *, struct ucred *, + NFSPROC_T *); +static int nfsrpc_readrpcdp(struct nfscldeleg *, struct uio *, struct ucred *, + NFSPROC_T *); +static void nfscl_fhtofilename(u_int8_t *, u_int16_t, char *); +static void nfscl_packratbreakdown(struct nfscldeleg *, struct nfsmount *, + struct ucred *, NFSPROC_T *); +static void nfscl_updatewrite(struct nfscldeleg *, struct nfsldirty *); +static int nfscl_localwrite(struct nfscldeleg *, uint64_t, int, + struct ucred *, struct ucred *, NFSPROC_T *, int); +static int nfsrpc_writedp(struct nfscldeleg *, uint8_t *, off_t, int, + struct ucred *, NFSPROC_T *, int); +static int nfsrpc_writerpcdp(struct nfscldeleg *, uint8_t *, off_t, int, + struct ucred *, NFSPROC_T *); +static void nfscl_truncdirty(struct nfscldeleg *, uint64_t); +static int nfscl_packrathostaddr(struct nfsmount *, char *, int); +static void nfscl_packratgetvp(struct nfscldeleg *, struct nfsmount *, char *, + int, struct ucred *, NFSPROC_T *); + +/* + * This function opens/creates a file for reading and writing, returning the + * vnode pointer for it. + */ +static vnode_t +nfscl_openfile(char *path, boolean_t createit, struct ucred *cred, NFSPROC_T *p) +{ + struct nameidata nd; + int error, fmode; + + NDINIT_AT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, + path, AT_FDCWD, p); + if (createit) + fmode = O_CREAT | O_TRUNC | O_RDWR; + else + fmode = O_RDWR; + error = vn_open_cred(&nd, &fmode, 0600, VN_OPEN_NOAUDIT, cred, NULL); + if (error == 0) + NDFREE(&nd, NDF_ONLY_PNBUF); + else if (nd.ni_vp != NULL) + panic("nfsclopenf"); + return (nd.ni_vp); +} + +/* + * This function closes and removes a file that was previously created/opened + * by nfscl_openfile(). + */ +static void +nfscl_closefile(vnode_t vp, char *path, boolean_t unlinkit, struct ucred *cred, + NFSPROC_T *p) +{ + + (void) vn_close(vp, FREAD | FWRITE, cred, p); + if (unlinkit) + (void) kern_unlinkat(p, AT_FDCWD, path, UIO_SYSSPACE, 0); +} + +/* + * Start up a packrat thread to copy the file to local disk. + */ +static void +start_packrat(void *arg) +{ + struct nfscldeleg *dp; + struct thread *td; + + dp = (struct nfscldeleg *)arg; + td = TAILQ_FIRST(&dp->nfsdl_packratthread->p_threads); + nfscl_packratthread(dp, td); + kproc_exit(0); +} + +static void +nfscl_start_packratthread(struct nfscldeleg *dp) +{ + + kproc_create(start_packrat, (void *)dp, &dp->nfsdl_packratthread, 0, 0, + "nfspackrat"); +} + +/* + * This is the body of the packrat copy thread. Just copy the file to the + * local one on disk. + */ +static void +nfscl_packratthread(struct nfscldeleg *dp, NFSPROC_T *td) +{ + struct uio uio; + struct iovec io; + struct ucred *incred, *outcred; + char *iobuf; + int resid, error; + off_t off; + + incred = newnfs_getcred(); + outcred = newnfs_getcred(); + newnfs_copycred(&dp->nfsdl_cred, incred); + iobuf = (char *)malloc(NFS_PACKRATIOSIZE, M_TEMP, M_WAITOK); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_offset = 0; + + /* Loop around until eof */ + do { + off = uio.uio_offset; + uio.uio_resid = NFS_PACKRATIOSIZE; + io.iov_len = NFS_PACKRATIOSIZE; + io.iov_base = iobuf; + uio.uio_iovcnt = 1; + uio.uio_iov = &io; + uio.uio_td = td; + error = nfsrpc_readdp(dp, &uio, incred, td); + + /* and then write to the disk file */ + if (error == 0 && uio.uio_resid < NFS_PACKRATIOSIZE) { + error = vn_rdwr(UIO_WRITE, dp->nfsdl_filevp, + iobuf, NFS_PACKRATIOSIZE - uio.uio_resid, + off, UIO_SYSSPACE, IO_NOMACCHECK, outcred, + NOCRED, &resid, td); + if (error == 0) { + dp->nfsdl_localsize = uio.uio_offset; + wakeup(&dp->nfsdl_localsize); + } + } + } while (error == 0 && uio.uio_resid < NFS_PACKRATIOSIZE); + free(iobuf, M_TEMP); + + if (error == 0) { + vnode_pager_setsize(dp->nfsdl_filevp, dp->nfsdl_localsize); + NFSLOCKCLSTATE(); + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + dp->nfsdl_flags |= NFSCLDL_HASCOPY; + wakeup(&dp->nfsdl_flags); + wakeup(&dp->nfsdl_localsize); + NFSUNLOCKCLSTATE(); + } else + nfscl_packratbreakdown(dp, dp->nfsdl_clp->nfsc_nmp, outcred, + td); + NFSFREECRED(incred); + NFSFREECRED(outcred); +} + +/* + * Read operation against an NFSv4 delegation. + */ +static int +nfsrpc_readdp(struct nfscldeleg *dp, struct uio *uiop, struct ucred *cred, + NFSPROC_T *p) +{ + int error, expireret = 0, retrycnt; + u_int32_t clidrev = 0; + + clidrev = dp->nfsdl_clp->nfsc_clientidrev; + retrycnt = 0; + do { + error = nfsrpc_readrpcdp(dp, uiop, cred, p); + if (error == NFSERR_STALESTATEID) + nfscl_initiate_recovery(dp->nfsdl_clp); + if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + error == NFSERR_OLDSTATEID) { + (void) nfs_catnap(PZERO, error, "nfs_readdp"); + } else if ((error == NFSERR_EXPIRED || + error == NFSERR_BADSTATEID) && clidrev != 0) { + expireret = nfscl_hasexpired(dp->nfsdl_clp, clidrev, p); + } + retrycnt++; + } while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + (error == NFSERR_OLDSTATEID && retrycnt < 20) || + ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) && + expireret == 0 && clidrev != 0 && retrycnt < 4)); + if (error && retrycnt >= 4) + error = EIO; + return (error); +} + +/* + * The actual read RPC for the above. + */ +static int +nfsrpc_readrpcdp(struct nfscldeleg *dp, struct uio *uiop, struct ucred *cred, + NFSPROC_T *p) +{ + u_int32_t *tl; + int error = 0, len, retlen, tsiz, eof = 0; + struct nfsrv_descript nfsd; + struct nfsrv_descript *nd = &nfsd; + struct nfsmount *nmp; + + nmp = dp->nfsdl_clp->nfsc_nmp; + if (nmp == NULL) + return (0); + tsiz = uio_uio_resid(uiop); + nd->nd_mrep = NULL; + while (tsiz > 0) { + len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; + nfscl_reqstart(nd, NFSPROC_READ, nmp, dp->nfsdl_fh, + dp->nfsdl_fhlen, NULL); + nfsm_stateidtom(nd, &dp->nfsdl_stateid, NFSSTATEID_PUTSTATEID); + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3); + txdr_hyper(uiop->uio_offset, tl); + *(tl + 2) = txdr_unsigned(len); + error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, + p, cred, NFS_PROG, NFS_VER4, NULL, 1, NULL); + if (error != 0) + return (error); + if (nd->nd_repstat != 0 || error != 0) { + if (error == 0) + error = nd->nd_repstat; + goto nfsmout; + } + NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); + eof = fxdr_unsigned(int, *tl); + NFSM_STRSIZ(retlen, nmp->nm_rsize); + error = nfsm_mbufuio(nd, uiop, retlen); + if (error) + goto nfsmout; + mbuf_freem(nd->nd_mrep); + nd->nd_mrep = NULL; + tsiz -= retlen; + if (eof || retlen == 0) + tsiz = 0; + } + return (0); +nfsmout: + if (nd->nd_mrep != NULL) + mbuf_freem(nd->nd_mrep); + return (error); +} + +static u_int8_t *not64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$%"; +static u_int8_t *hexval = "0123456789abcdef"; +/* + * This function generates a file name for the file handle passed in as an + * argument. Since the file handle can be up to 128 bytes and a file name is + * limited to 255 in length, a simple hexadecimal representation doesn't + * work. + * The name looks like this: + * - 2 hex digits for the file handle length + * - fhlen/3 sequences of four characters, where each ascii character + * represents 6 bits (4 digits representing the 3 bytes) + * - 0 to 2 bytes represented as hexadecimal + * As such, a 128 byte fh creates a 174 byte file name. + * The name argument must point to sufficient storage. + */ +static void +nfscl_fhtofilename(u_int8_t *fh, u_int16_t fhlen, char *name) +{ + + *name++ = hexval[fhlen & 0xf]; + *name++ = hexval[fhlen >> 4]; + + /* + * Now loop around for the fh bytes until < 3 left. + */ + while (fhlen >= 3) { + *name++ = not64[*fh & 0x3f]; + *name++ = not64[((*fh & 0xc0) >> 2) | (*(fh + 1) & 0xf)]; + *name++ = not64[(*(fh + 1) >> 4) | ((*(fh + 2) & 0xc0) >> 2)]; + *name++ = not64[*(fh + 2) & 0x3f]; + fh += 3; + fhlen -= 3; + } + + /* + * and then do the last 0 to 2 bytes as hexadecimal. + */ + while (fhlen > 0) { + *name++ = hexval[*fh & 0xf]; + *name++ = hexval[*fh >> 4]; + fh++; + fhlen--; + } + *name = '\0'; +} + +/* + * Set up a new delegation for packrat support. + */ +void +nfscl_packratsetup(struct nfscldeleg *dp, struct nfsmount *nmp, + struct ucred *cred, NFSPROC_T *p) +{ + vnode_t delegvp, filevp; + char fname[MAXPATHLEN + 1]; + int pathlen, pathlen2; + + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || + dp->nfsdl_opensize > nfscl_packratmaxsize) { + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + wakeup(&dp->nfsdl_flags); + NFSUNLOCKCLSTATE(); + return; + } + pathlen = nfscl_packratpathlen; + bcopy(nfscl_packratpath, &fname[0], pathlen); + NFSUNLOCKCLSTATE(); + + /* + * The appropriate fields of the delegation structure are already + * initialized, so create the files and, if that is successful, + * fire up a packrat thread for it. + */ + pathlen2 = pathlen; + fname[pathlen++] = 'D'; + pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen); + if (pathlen == 0) { + NFSLOCKCLSTATE(); + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + wakeup(&dp->nfsdl_flags); + NFSUNLOCKCLSTATE(); + return; + } + nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen, &fname[pathlen]); + delegvp = nfscl_openfile(fname, TRUE, cred, p); + if (delegvp == NULL) { + NFSLOCKCLSTATE(); + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + wakeup(&dp->nfsdl_flags); + NFSUNLOCKCLSTATE(); + return; + } + VOP_UNLOCK(delegvp, 0); + fname[pathlen2] = 'F'; + filevp = nfscl_openfile(fname, TRUE, cred, p); + if (filevp == NULL) { + nfscl_closefile(delegvp, fname, TRUE, cred, p); + NFSLOCKCLSTATE(); + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + wakeup(&dp->nfsdl_flags); + NFSUNLOCKCLSTATE(); + return; + } + VOP_UNLOCK(filevp, 0); + + NFSLOCKCLSTATE(); + dp->nfsdl_delegvp = delegvp; + dp->nfsdl_filevp = filevp; + dp->nfsdl_localsize = 0; + NFSUNLOCKCLSTATE(); + + nfscl_start_packratthread(dp); +} + +/* + * Reopen the locally cached copy of the file, created by nfscl_packratsetup(). + */ +void +nfscl_packratopen(vnode_t vp, NFSPROC_T *p) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np = VTONFS(vp); + struct nfsmount *nmp; + struct ucred *cred; + char fname[MAXPATHLEN + 1]; + int pathlen; + + nmp = VFSTONFS(vnode_mount(vp)); + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) { + NFSUNLOCKCLSTATE(); + return; + } + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + return; + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp == NULL || (dp->nfsdl_flags & + (NFSCLDL_RECALL | NFSCLDL_WRITE | NFSCLDL_HASCOPY)) != + (NFSCLDL_WRITE | NFSCLDL_HASCOPY) || + dp->nfsdl_filevp != NULL) { + NFSUNLOCKCLSTATE(); + return; + } + + /* Now, try and reopen them. */ + pathlen = nfscl_packratpathlen; + bcopy(nfscl_packratpath, &fname[0], pathlen); + NFSUNLOCKCLSTATE(); + + cred = newnfs_getcred(); + nfscl_packratgetvp(dp, nmp, fname, pathlen, cred, p); + NFSFREECRED(cred); +} + +/* + * Do the actual reopens to get the vnodes. + */ +static void +nfscl_packratgetvp(struct nfscldeleg *dp, struct nfsmount *nmp, char *fname, + int pathlen, struct ucred *cred, NFSPROC_T *p) +{ + vnode_t delegvp, filevp; + boolean_t closethem; + int pathlen2; + + /* + * The appropriate fields of the delegation structure are already + * initialized, so create the files and, if that is successful, + * fire up a packrat thread for it. + */ + pathlen2 = pathlen; + fname[pathlen++] = 'D'; + pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen); + if (pathlen == 0) + return; + nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen, &fname[pathlen]); + delegvp = nfscl_openfile(fname, FALSE, cred, p); + if (delegvp == NULL) + return; + VOP_UNLOCK(delegvp, 0); + fname[pathlen2] = 'F'; + filevp = nfscl_openfile(fname, FALSE, cred, p); + if (filevp == NULL) { + nfscl_closefile(delegvp, fname, FALSE, cred, p); + return; + } + VOP_UNLOCK(filevp, 0); + + NFSLOCKCLSTATE(); + if (dp->nfsdl_delegvp == NULL) { + dp->nfsdl_delegvp = delegvp; + dp->nfsdl_filevp = filevp; + closethem = FALSE; + } else + closethem = TRUE; + NFSUNLOCKCLSTATE(); + + if (closethem) { + fname[pathlen2] = 'D'; + nfscl_closefile(delegvp, fname, FALSE, cred, p); + fname[pathlen2] = 'F'; + nfscl_closefile(filevp, fname, FALSE, cred, p); + } +} + +/* + * Get rid of the local files related to a delegation. + */ +static void +nfscl_packratbreakdown(struct nfscldeleg *dp, struct nfsmount *nmp, + struct ucred *cred, NFSPROC_T *p) +{ + vnode_t delegvp, filevp; + char fname[MAXPATHLEN + 1]; + int pathlen, pathlen2; + + /* + * Get the vnode pointers out of the delegation structure and + * null them out. + */ + NFSLOCKCLSTATE(); + pathlen = nfscl_packratpathlen; + if (pathlen > 0) + bcopy(nfscl_packratpath, &fname[0], pathlen); + delegvp = dp->nfsdl_delegvp; + filevp = dp->nfsdl_filevp; + dp->nfsdl_delegvp = NULL; + dp->nfsdl_filevp = NULL; + pathlen2 = pathlen; + if (pathlen > 0) { + fname[pathlen++] = 'D'; + pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen); + } + if (pathlen > 0) + nfscl_fhtofilename(dp->nfsdl_fh, dp->nfsdl_fhlen, + &fname[pathlen]); + while (dp->nfsdl_localiocnt > 0) + (void) nfsmsleep(&dp->nfsdl_localiocnt, NFSCLSTATEMUTEXPTR, + PZERO, "nfspckbr", NULL); + if ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0) { + dp->nfsdl_flags &= ~NFSCLDL_COPYINPROG; + wakeup(&dp->nfsdl_flags); + } + NFSUNLOCKCLSTATE(); + if (pathlen == 0) + return; + + /* + * Close/remove the 2 files. + */ + if (delegvp != NULL) + nfscl_closefile(delegvp, fname, TRUE, cred, p); + fname[pathlen2] = 'F'; + if (filevp != NULL) + nfscl_closefile(filevp, fname, TRUE, cred, p); +} + +/* + * Close the local files related to a delegation in order to release the + * vnodes. + */ +void +nfscl_packratclose(vnode_t vp, NFSPROC_T *p) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np = VTONFS(vp); + struct nfsmount *nmp; + struct ucred *cred; + vnode_t delegvp, filevp; + char fname[MAXPATHLEN + 1]; + int pathlen, pathlen2; + + nmp = VFSTONFS(vnode_mount(vp)); + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0 || + vp->v_usecount > 1) { + NFSUNLOCKCLSTATE(); + return; + } + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + return; + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp == NULL || (dp->nfsdl_flags & + (NFSCLDL_RECALL | NFSCLDL_WRITE | NFSCLDL_HASCOPY)) != + (NFSCLDL_WRITE | NFSCLDL_HASCOPY) || + dp->nfsdl_filevp == NULL) { + NFSUNLOCKCLSTATE(); + return; + } + pathlen = nfscl_packratpathlen; + bcopy(nfscl_packratpath, &fname[0], pathlen); + delegvp = dp->nfsdl_delegvp; + filevp = dp->nfsdl_filevp; + dp->nfsdl_delegvp = NULL; + dp->nfsdl_filevp = NULL; + while (dp->nfsdl_localiocnt > 0) + (void) nfsmsleep(&dp->nfsdl_localiocnt, NFSCLSTATEMUTEXPTR, + PZERO, "nfspckbr", NULL); + NFSUNLOCKCLSTATE(); + pathlen2 = pathlen; + fname[pathlen++] = 'D'; + pathlen = nfscl_packrathostaddr(nmp, &fname[0], pathlen); + if (pathlen == 0) + return; + nfscl_fhtofilename(np->n_fhp->nfh_fh, np->n_fhp->nfh_len, + &fname[pathlen]); + + /* + * Close the 2 files. + */ + cred = newnfs_getcred(); + nfscl_closefile(delegvp, fname, FALSE, cred, p); + fname[pathlen2] = 'F'; + nfscl_closefile(filevp, fname, FALSE, cred, p); + NFSFREECRED(cred); +} + +/* + * Set up the packrat directory. + */ +int +nfscbd_packrat(char *pathbuf) +{ + int pathlen; + + pathlen = strlen(pathbuf); + if (pathlen < 1 || pathbuf[pathlen - 1] != '/') + return (EINVAL); + NFSLOCKCLSTATE(); + strcpy(nfscl_packratpath, pathbuf); + nfscl_packratpathlen = pathlen; + NFSUNLOCKCLSTATE(); + return (0); +} + +/* + * Do a read via a local cached copy, if possible. + * Return whether or not the local read was possible via "didread". + */ +int +nfscl_packratread(vnode_t vp, struct uio *uio, int ioflag, struct ucred *cred, + int *didread) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np = VTONFS(vp); + struct nfsmount *nmp; + vnode_t filevp; + int error; + ssize_t tresid; + + *didread = 0; + nmp = VFSTONFS(vnode_mount(vp)); + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) { + NFSUNLOCKCLSTATE(); + return (0); + } + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + return (0); + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp == NULL || (dp->nfsdl_flags & + (NFSCLDL_RECALL | NFSCLDL_WRITE)) != NFSCLDL_WRITE || + dp->nfsdl_filevp == NULL || + ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0 && + uio->uio_offset + uio->uio_resid > dp->nfsdl_localsize)) { + if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) != 0) { + dp->nfsdl_flags |= NFSCLDL_WAITRECALL; + (void) nfsmsleep(&dp->nfsdl_ldirty, NFSCLSTATEMUTEXPTR, + PZERO, "nfspkrc", NULL); + } + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + NFSINVALATTRCACHE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); + return (0); + } + + *didread = 1; + if (uio->uio_offset >= dp->nfsdl_localsize) { + NFSUNLOCKCLSTATE(); + return (0); + } + if (uio->uio_offset + uio->uio_resid > dp->nfsdl_localsize) { + tresid = uio->uio_resid; + uio->uio_resid = dp->nfsdl_localsize - uio->uio_offset; + tresid -= uio->uio_resid; + } else + tresid = 0; + dp->nfsdl_localiocnt++; + filevp = dp->nfsdl_filevp; + NFSUNLOCKCLSTATE(); + error = 0; + vn_lock(filevp, LK_SHARED | LK_RETRY); + VI_LOCK(filevp); + if ((filevp->v_iflag & VI_DOOMED) != 0) + error = ENOENT; + VI_UNLOCK(filevp); + if (error == 0) + error = VOP_READ(filevp, uio, ioflag, cred); + VOP_UNLOCK(filevp, 0); + uio->uio_resid += tresid; + NFSLOCKCLSTATE(); + dp->nfsdl_localiocnt--; + if (dp->nfsdl_localiocnt == 0) + wakeup(&dp->nfsdl_localiocnt); + NFSUNLOCKCLSTATE(); + return (error); +} + +/* + * Do a write into a local copy cached on disk, if possible. + * Return whether or not the local write was possible via "didwrite". + */ +int +nfscl_packratwrite(vnode_t vp, struct uio *uio, int ioflag, struct ucred *cred, + NFSPROC_T *p, int *didwrite) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np = VTONFS(vp); + struct nfsmount *nmp; + struct mount *mp; + struct nfsldirty *wp; + vnode_t filevp; + uint64_t end; + int error; + off_t setsize; + + *didwrite = 0; + nmp = VFSTONFS(vnode_mount(vp)); + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) { + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); + return (0); + } + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); + return (0); + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp == NULL || (dp->nfsdl_flags & + (NFSCLDL_RECALL | NFSCLDL_WRITE)) != NFSCLDL_WRITE || + dp->nfsdl_filevp == NULL) { + if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_RECALL) != 0) { + dp->nfsdl_flags |= NFSCLDL_WAITRECALL; + (void) nfsmsleep(&dp->nfsdl_ldirty, NFSCLSTATEMUTEXPTR, + PZERO, "nfspkwc", NULL); + } + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); + return (0); + } + + /* + * We can now try and do the write. It cannot be done until the + * local copy has been read in to past the point at which we + * are writing, so we must loop until enough reading has been + * completed. + */ + end = (uint64_t)uio->uio_offset + uio->uio_resid; + while ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0 && + (end > dp->nfsdl_localsize || (ioflag & IO_APPEND) != 0)) + (void) nfsmsleep(&dp->nfsdl_localsize, NFSCLSTATEMUTEXPTR, + PZERO, "nfspckw", NULL); + filevp = dp->nfsdl_filevp; + if (filevp == NULL) { + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); + return (0); + } + if ((ioflag & IO_APPEND) != 0) { + uio->uio_offset = dp->nfsdl_localsize; + end = (uint64_t)uio->uio_offset + uio->uio_resid; + ioflag &= ~IO_APPEND; + } + *didwrite = 1; + if (uio->uio_offset < 0) { + NFSUNLOCKCLSTATE(); + return (EINVAL); + } + if (end > nmp->nm_maxfilesize) { + NFSUNLOCKCLSTATE(); + return (EFBIG); + } + if (uio->uio_resid == 0) { + NFSUNLOCKCLSTATE(); + return (0); + } + dp->nfsdl_localiocnt++; + NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag |= (NMODIFIED | NLOCALCACHE); + NFSUNLOCKNODE(np); + /* + * Maybe this should be above the vnode op call, but so long as + * file servers have no limits, i don't think it matters + */ + if (p != NULL && vn_rlimit_fsize(vp, uio, p)) + error = EFBIG; + else + error = 0; + + wp = (struct nfsldirty *)malloc(sizeof(struct nfsldirty), + M_NFSLDIRTY, M_WAITOK); + wp->nfsw_first = (uint64_t)uio->uio_offset; + wp->nfsw_end = end; + mp = NULL; + if (error == 0) + error = vn_start_write(filevp, &mp, V_WAIT); + if (error == 0) { + if (MNT_SHARED_WRITES(mp) || ((mp == NULL) && + MNT_SHARED_WRITES(filevp->v_mount))) + vn_lock(filevp, LK_SHARED | LK_RETRY); + else + vn_lock(filevp, LK_EXCLUSIVE | LK_RETRY); + VI_LOCK(filevp); + if ((filevp->v_iflag & VI_DOOMED) != 0) + error = ENOENT; + VI_UNLOCK(filevp); + if (error == 0) + error = VOP_WRITE(filevp, uio, ioflag, cred); + vn_finished_write(mp); + VOP_UNLOCK(filevp, 0); + } + setsize = 0; + NFSLOCKCLSTATE(); + if (error == 0) { + if (end > dp->nfsdl_localsize) { + dp->nfsdl_localsize = end; + dp->nfsdl_flags |= NFSCLDL_LOCALSIZESET; + setsize = end; + } + nfscl_updatewrite(dp, wp); + } else + free(wp, M_NFSLDIRTY); + dp->nfsdl_localiocnt--; + if (dp->nfsdl_localiocnt == 0) + wakeup(&dp->nfsdl_localiocnt); + NFSUNLOCKCLSTATE(); + if (setsize != 0) + vnode_pager_setsize(vp, setsize); + return (error); +} + +/* + * Add/merge this byte range to the list of modified byte ranges. + */ +static void +nfscl_updatewrite(struct nfscldeleg *dp, struct nfsldirty *newwp) +{ + struct nfsldirty *wp, *nwp, *owp, *onwp; + + /* Loop through the list of dirty byte ranges. */ + wp = LIST_FIRST(&dp->nfsdl_ldirty); + owp = NULL; + while (wp != NULL) { + nwp = LIST_NEXT(wp, nfsw_list); + if (newwp->nfsw_first > wp->nfsw_end) { + /* Not there yet. */ + owp = wp; + wp = nwp; + } else if (newwp->nfsw_end < wp->nfsw_first) { + /* No overlap between old and new, insert it. */ + LIST_INSERT_BEFORE(wp, newwp, nfsw_list); + newwp = NULL; + break; + } else { + /* Merge this range in. */ + if (newwp->nfsw_first < wp->nfsw_first) + wp->nfsw_first = newwp->nfsw_first; + if (newwp->nfsw_end > wp->nfsw_end) + wp->nfsw_end = newwp->nfsw_end; + free(newwp, M_NFSLDIRTY); + newwp = NULL; + + /* Now, free any entries comsumed by this one. */ + while (nwp != NULL && + nwp->nfsw_first <= wp->nfsw_end) { + if (nwp->nfsw_end > wp->nfsw_end) + /* Must be last overlapping range */ + wp->nfsw_end = nwp->nfsw_end; + onwp = nwp; + nwp = LIST_NEXT(nwp, nfsw_list); + LIST_REMOVE(onwp, nfsw_list); + free(onwp, M_NFSLDIRTY); + } + break; + } + } + /* If not yet handled, it goes at the end of the list. */ + if (newwp != NULL) { + if (owp != NULL) + LIST_INSERT_AFTER(owp, newwp, nfsw_list); + else + LIST_INSERT_HEAD(&dp->nfsdl_ldirty, newwp, nfsw_list); + } +} + +/* + * Flush any dirty byte ranges on a locally cached copy back to the server. + * Return non-zero if the write back fails. + */ +int +nfscl_deleglocalflush(struct nfscldeleg *dp, struct nfsmount *nmp, NFSPROC_T *p, + int called_from_renewthread, int called_from_remove) +{ + struct nfsldirty *wp, *nwp; + struct ucred *incred, *outcred; + char fname[MAXPATHLEN + 1]; + int error, pathlen; + + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0) { + if ((dp->nfsdl_flags & NFSCLDL_WAITRECALL) != 0) + wakeup(&dp->nfsdl_ldirty); + NFSUNLOCKCLSTATE(); + return (0); + } + + /* Wait for the packrat thread to complete. */ + while ((dp->nfsdl_flags & NFSCLDL_COPYINPROG) != 0) + (void) nfsmsleep(&dp->nfsdl_flags, NFSCLSTATEMUTEXPTR, + PZERO, "nfspckth", NULL); + + if ((dp->nfsdl_flags & NFSCLDL_WRITE) == 0 || + (dp->nfsdl_filevp == NULL && + (dp->nfsdl_flags & NFSCLDL_HASCOPY) == 0)) { + if ((dp->nfsdl_flags & NFSCLDL_WAITRECALL) != 0) + wakeup(&dp->nfsdl_ldirty); + NFSUNLOCKCLSTATE(); + return (0); + } + /* Now, try and reopen them. */ + pathlen = nfscl_packratpathlen; + bcopy(nfscl_packratpath, &fname[0], pathlen); + NFSUNLOCKCLSTATE(); + + incred = newnfs_getcred(); + outcred = newnfs_getcred(); + newnfs_copycred(&dp->nfsdl_cred, outcred); + + /* Open the files, as required. */ + if (dp->nfsdl_filevp == NULL) + nfscl_packratgetvp(dp, nmp, fname, pathlen, incred, p); + + /* Loop through the dirty byte range(s), writing them back. */ + error = 0; + wp = LIST_FIRST(&dp->nfsdl_ldirty); + while (wp != NULL && error == 0) { + nwp = LIST_NEXT(wp, nfsw_list); + if (called_from_remove == 0) + error = nfscl_localwrite(dp, wp->nfsw_first, + (int)(wp->nfsw_end - wp->nfsw_first), incred, + outcred, p, called_from_renewthread); + if (error == 0) { + /* Get rid of the byte range. */ + LIST_REMOVE(wp, nfsw_list); + free(wp, M_NFSLDIRTY); + } + wp = nwp; + } + + if (error == 0) { + NFSLOCKCLSTATE(); + if ((dp->nfsdl_flags & NFSCLDL_WAITRECALL) != 0) + wakeup(&dp->nfsdl_ldirty); + NFSUNLOCKCLSTATE(); + /* Now, the local files can be closed/deleted. */ + nfscl_packratbreakdown(dp, nmp, incred, p); + } + NFSFREECRED(incred); + NFSFREECRED(outcred); + return (error); +} + +/* + * Copy a byte range from the local cache file to the server. + */ +static int +nfscl_localwrite(struct nfscldeleg *dp, uint64_t start, int len, + struct ucred *incred, struct ucred *outcred, NFSPROC_T *td, + int called_from_renewthread) +{ + char *iobuf; + int resid, error, xfer; + off_t off; + + iobuf = (char *)malloc(NFS_PACKRATIOSIZE, M_TEMP, M_WAITOK); + off = start; + + /* Loop around until done. */ + do { + xfer = min(NFS_PACKRATIOSIZE, len); + + /* Read the bytes from the local file. */ + error = vn_rdwr(UIO_READ, dp->nfsdl_filevp, + iobuf, xfer, off, UIO_SYSSPACE, IO_NOMACCHECK, incred, + NOCRED, &resid, td); + if (error == 0) + error = nfsrpc_writedp(dp, iobuf, off, xfer, outcred, + td, called_from_renewthread); + len -= xfer; + off += xfer; + } while (error == 0 && len > 0); + free(iobuf, M_TEMP); + return (error); +} + +/* + * Write operation against an NFSv4 delegation. + */ +static int +nfsrpc_writedp(struct nfscldeleg *dp, uint8_t *buf, off_t off, int len, + struct ucred *cred, NFSPROC_T *p, int called_from_renewthread) +{ + int error, expireret = 0, retrycnt; + u_int32_t clidrev = 0; + + clidrev = dp->nfsdl_clp->nfsc_clientidrev; + retrycnt = 0; + do { + error = nfsrpc_writerpcdp(dp, buf, off, len, cred, p); + if (error == NFSERR_STALESTATEID) + nfscl_initiate_recovery(dp->nfsdl_clp); + if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + error == NFSERR_OLDSTATEID) { + (void) nfs_catnap(PZERO, error, "nfs_writedp"); + } else if ((error == NFSERR_EXPIRED || + error == NFSERR_BADSTATEID) && clidrev != 0) { + expireret = nfscl_hasexpired(dp->nfsdl_clp, clidrev, p); + } + retrycnt++; + } while (error == NFSERR_GRACE || error == NFSERR_DELAY || + ((error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER) && + called_from_renewthread == 0) || + (error == NFSERR_OLDSTATEID && retrycnt < 20) || + ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) && + expireret == 0 && clidrev != 0 && retrycnt < 4)); + if (error != 0 && (retrycnt >= 4 || + ((error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER) && + called_from_renewthread != 0))) + error = EIO; + return (error); +} + +/* + * The actual write RPC for the above. + */ +static int +nfsrpc_writerpcdp(struct nfscldeleg *dp, uint8_t *buf, off_t off, int tlen, + struct ucred *cred, NFSPROC_T *p) +{ + u_int32_t *tl; + int error = 0, len, pos; + struct nfsrv_descript nfsd; + struct nfsrv_descript *nd = &nfsd; + struct nfsmount *nmp; + nfsattrbit_t attrbits; + + nmp = dp->nfsdl_clp->nfsc_nmp; + if (nmp == NULL) + return (0); + NFSWRITEGETATTR_ATTRBIT(&attrbits); + pos = 0; + nd->nd_mrep = NULL; + while (tlen > 0) { + len = (tlen > nmp->nm_wsize) ? nmp->nm_wsize : tlen; + nfscl_reqstart(nd, NFSPROC_WRITE, nmp, dp->nfsdl_fh, + dp->nfsdl_fhlen, NULL); + nfsm_stateidtom(nd, &dp->nfsdl_stateid, NFSSTATEID_PUTSTATEID); + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3); + txdr_hyper(off, tl); + *(tl + 2) = txdr_unsigned(NFSWRITE_FILESYNC); + (void) nfsm_strtom(nd, &buf[pos], len); + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_GETATTR); + (void) nfsrv_putattrbit(nd, &attrbits); + error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, + p, cred, NFS_PROG, NFS_VER4, NULL, 1, NULL); + if (error != 0) + return (error); + if (nd->nd_repstat != 0 || error != 0) { + if (error == 0) + error = nd->nd_repstat; + goto nfsmout; + } + mbuf_freem(nd->nd_mrep); + nd->nd_mrep = NULL; + tlen -= len; + pos += len; + } + return (0); +nfsmout: + if (nd->nd_mrep != NULL) + mbuf_freem(nd->nd_mrep); + return (error); +} + +/* + * Handle a Setattr of size for a write delegated file. Basically + * update nfsdl_localsize plus get rid of any + * dirty region(s) that no longer apply. + */ +void +nfscl_packratsetsize(vnode_t vp, uint64_t size) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np = VTONFS(vp); + struct nfsmount *nmp; + + nmp = VFSTONFS(vnode_mount(vp)); + NFSLOCKCLSTATE(); + if (nfscl_packratpathlen == 0 || (nmp->nm_flag & NFSMNT_NFSV4) == 0) { + NFSUNLOCKCLSTATE(); + return; + } + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + return; + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp == NULL || (dp->nfsdl_flags & + (NFSCLDL_RECALL | NFSCLDL_WRITE)) != NFSCLDL_WRITE || + (dp->nfsdl_filevp == NULL && + (dp->nfsdl_flags & NFSCLDL_HASCOPY) == 0) || + dp->nfsdl_localsize == size) { + NFSUNLOCKCLSTATE(); + return; + } + + if (size < dp->nfsdl_localsize) + /* Get rid of dirty region(s) that no longer apply. */ + nfscl_truncdirty(dp, size); + /* Update the local size. */ + dp->nfsdl_localsize = size; + dp->nfsdl_flags |= NFSCLDL_LOCALSIZESET; + NFSUNLOCKCLSTATE(); + vnode_pager_setsize(vp, size); +} + +/* + * Get rid of any dirty region(s) that no longer apply after truncation. + */ +static void +nfscl_truncdirty(struct nfscldeleg *dp, uint64_t size) +{ + struct nfsldirty *wp, *nwp; + + wp = LIST_FIRST(&dp->nfsdl_ldirty); + while (wp != NULL) { + nwp = LIST_NEXT(wp, nfsw_list); + if (wp->nfsw_first >= size) { + /* Entire region is past end, so free it up. */ + LIST_REMOVE(wp, nfsw_list); + free(wp, M_NFSLDIRTY); + } else if (wp->nfsw_end > size) + /* Region is being truncated. */ + wp->nfsw_end = size; + wp = nwp; + } +} + +/* + * Put the server's ip address in the file name. + */ +static int +nfscl_packrathostaddr(struct nfsmount *nmp, char *fname, int pathlen) +{ + struct sockaddr_in *saddr; + struct sockaddr_in6 *saddr6; + char iptype, *addr; + int len, i; + + if (nmp->nm_sockreq.nr_nam->sa_family == AF_INET) { + iptype = '4'; + saddr = (struct sockaddr_in *)nmp->nm_sockreq.nr_nam; + } else if (nmp->nm_sockreq.nr_nam->sa_family == AF_INET6) { + iptype = '6'; + saddr6 = (struct sockaddr_in6 *)nmp->nm_sockreq.nr_nam; + } else + return (0); + + fname[pathlen++] = iptype; + if (iptype == '4') { + addr = inet_ntoa(saddr->sin_addr); + len = strlen(addr); + bcopy(addr, &fname[pathlen], len); + pathlen += len; + } else { + for (i = 0; i < 16; i++) { + sprintf(&fname[pathlen], "%2x", + saddr6->sin6_addr.s6_addr[i]); + pathlen += 2; + } + } + return (pathlen); +} + diff -u -r -N sys.dec4/fs/nfsclient/nfs_clport.c sys.dec4.packrats/fs/nfsclient/nfs_clport.c --- sys.dec4/fs/nfsclient/nfs_clport.c 2011-08-15 18:10:32.000000000 -0400 +++ sys.dec4.packrats/fs/nfsclient/nfs_clport.c 2011-12-04 20:14:10.000000000 -0500 @@ -1227,6 +1227,7 @@ struct file *fp; struct nfscbd_args nfscbdarg; struct nfsd_nfscbd_args nfscbdarg2; + char pathbuf[MAXPATHLEN - NFSPCKRAT_MAXFILELEN + 1]; int error; if (uap->flag & NFSSVC_CBADDSOCK) { @@ -1260,6 +1261,14 @@ if (error) return (error); error = nfscbd_nfsd(td, &nfscbdarg2); + } else if (uap->flag & NFSSVC_PACKRAT) { + if (uap->argp == NULL) + return (EINVAL); + error = copyinstr(uap->argp, pathbuf, + MAXPATHLEN - NFSPCKRAT_MAXFILELEN, NULL); + if (error == 0) + error = nfscbd_packrat(pathbuf); +printf("pckerr=%d\n",error); } else { error = EINVAL; } diff -u -r -N sys.dec4/fs/nfsclient/nfs_clrpcops.c sys.dec4.packrats/fs/nfsclient/nfs_clrpcops.c --- sys.dec4/fs/nfsclient/nfs_clrpcops.c 2011-11-20 12:46:04.000000000 -0500 +++ sys.dec4.packrats/fs/nfsclient/nfs_clrpcops.c 2011-12-04 20:14:10.000000000 -0500 @@ -373,6 +373,7 @@ NFSZERO_ATTRBIT(&attrbits); NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE); NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY); + NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE); (void) nfsrv_putattrbit(nd, &attrbits); if (syscred) nd->nd_flag |= ND_USEGSSNAME; @@ -402,9 +403,10 @@ (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG); MALLOC(ndp, struct nfscldeleg *, sizeof (struct nfscldeleg) + newfhlen, - M_NFSCLDELEG, M_WAITOK); + M_NFSCLDELEG, M_WAITOK | M_ZERO); LIST_INIT(&ndp->nfsdl_owner); LIST_INIT(&ndp->nfsdl_lock); + LIST_INIT(&ndp->nfsdl_ldirty); ndp->nfsdl_clp = op->nfso_own->nfsow_clp; ndp->nfsdl_fhlen = newfhlen; NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen); @@ -462,6 +464,7 @@ ndp->nfsdl_change = nfsva.na_filerev; ndp->nfsdl_modtime = nfsva.na_mtime; ndp->nfsdl_flags |= NFSCLDL_MODTIMESET; + ndp->nfsdl_opensize = nfsva.na_size; } if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) { do { @@ -1900,9 +1903,10 @@ (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG); MALLOC(dp, struct nfscldeleg *, sizeof (struct nfscldeleg) + NFSX_V4FHMAX, - M_NFSCLDELEG, M_WAITOK); + M_NFSCLDELEG, M_WAITOK | M_ZERO); LIST_INIT(&dp->nfsdl_owner); LIST_INIT(&dp->nfsdl_lock); + LIST_INIT(&dp->nfsdl_ldirty); dp->nfsdl_clp = owp->nfsow_clp; newnfs_copyincred(cred, &dp->nfsdl_cred); nfscl_lockinit(&dp->nfsdl_rwlock); @@ -1955,6 +1959,7 @@ dp->nfsdl_change = nnap->na_filerev; dp->nfsdl_modtime = nnap->na_mtime; dp->nfsdl_flags |= NFSCLDL_MODTIMESET; + dp->nfsdl_opensize = nnap->na_size; } /* * We can now complete the Open state. diff -u -r -N sys.dec4/fs/nfsclient/nfs_clstate.c sys.dec4.packrats/fs/nfsclient/nfs_clstate.c --- sys.dec4/fs/nfsclient/nfs_clstate.c 2011-12-02 22:26:37.000000000 -0500 +++ sys.dec4.packrats/fs/nfsclient/nfs_clstate.c 2011-12-04 20:14:10.000000000 -0500 @@ -80,6 +80,7 @@ #ifndef APPLEKEXT #include +#include /* * Global variables @@ -110,8 +111,6 @@ static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *); static u_int32_t nfscl_nextcbident(void); static mount_t nfscl_getmnt(u_int32_t); -static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *, - int); static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *, u_int8_t *, struct nfscllock **); static void nfscl_freealllocks(struct nfscllockownerhead *, int); @@ -134,11 +133,11 @@ struct ucred *, NFSPROC_T *); static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t, struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *); -static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *); +static void nfscl_freedeleg(struct nfscldeleg *); static int nfscl_errmap(struct nfsrv_descript *); static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *); static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *, - struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int); + struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int, int); static void nfscl_freeopenowner(struct nfsclowner *, int); static void nfscl_cleandeleg(struct nfscldeleg *); static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *, @@ -383,6 +382,7 @@ int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp) { struct nfscldeleg *dp = *dpp, *tdp; + struct nfsmount *nmp = VFSTONFS(mp); /* * First, if we have received a Read delegation for a file on a @@ -391,7 +391,7 @@ */ if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) && (dp->nfsdl_flags & NFSCLDL_READ)) { - (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p); + (void) nfscl_trydelegreturn(dp, cred, nmp, p); FREE((caddr_t)dp, M_NFSCLDELEG); *dpp = NULL; return (0); @@ -406,12 +406,20 @@ return (NFSERR_BADSTATEID); } *dpp = NULL; + dp->nfsdl_flags |= NFSCLDL_COPYINPROG; TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp, nfsdl_hash); dp->nfsdl_timestamp = NFSD_MONOSEC + 120; newnfsstats.cldelegates++; nfscl_delegcnt++; + NFSUNLOCKCLSTATE(); + + /* + * Call nfscl_packratsetup() to create the local copy + * and start the copying via a kernel thread. + */ + nfscl_packratsetup(dp, nmp, cred, p); } else { /* * Delegation already exists, what do we do if a new one?? @@ -423,15 +431,15 @@ } else { *dpp = tdp; } + NFSUNLOCKCLSTATE(); } - NFSUNLOCKCLSTATE(); return (0); } /* * Find a delegation for this file handle. Return NULL upon failure. */ -static struct nfscldeleg * +APPLESTATIC struct nfscldeleg * nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen) { struct nfscldeleg *dp; @@ -1514,11 +1522,9 @@ * Free a delegation. */ static void -nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp) +nfscl_freedeleg(struct nfscldeleg *dp) { - TAILQ_REMOVE(hdp, dp, nfsdl_list); - LIST_REMOVE(dp, nfsdl_hash); FREE((caddr_t)dp, M_NFSCLDELEG); newnfsstats.cldelegates--; nfscl_delegcnt--; @@ -1609,7 +1615,10 @@ printf("nfsv4 expired locks lost\n"); } nfscl_cleandeleg(dp); - nfscl_freedeleg(&clp->nfsc_deleg, dp); + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + (void) nfscl_deleglocalflush(dp, nmp, p, 0, 0); + nfscl_freedeleg(dp); dp = ndp; } if (!TAILQ_EMPTY(&clp->nfsc_deleg)) @@ -2097,7 +2106,10 @@ * away. Ouch!! */ nfscl_cleandeleg(dp); - nfscl_freedeleg(&clp->nfsc_deleg, dp); + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + (void) nfscl_deleglocalflush(dp, nmp, p, 0, 0); + nfscl_freedeleg(dp); } else { LIST_INSERT_HEAD(&extra_open, nop, nfso_list); } @@ -2482,7 +2494,7 @@ NFSUNLOCKCLSTATE(); newnfs_copycred(&dp->nfsdl_cred, cred); ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp, - NULL, cred, p, 1); + NULL, cred, p, 1, 0); if (!ret) { nfscl_cleandeleg(dp); TAILQ_REMOVE(&clp->nfsc_deleg, dp, @@ -2509,7 +2521,8 @@ dp->nfsdl_rwlock.nfslock_lock == 0 && dp->nfsdl_timestamp < NFSD_MONOSEC && (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED | - NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) { + NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0 && + LIST_EMPTY(&dp->nfsdl_ldirty)) { clearok = 1; LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) { op = LIST_FIRST(&owp->nfsow_open); @@ -2545,6 +2558,8 @@ */ TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) { newnfs_copycred(&dp->nfsdl_cred, cred); + (void) nfscl_deleglocalflush(dp, clp->nfsc_nmp, p, 1, + 0); (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); TAILQ_REMOVE(&dh, dp, nfsdl_list); FREE((caddr_t)dp, M_NFSCLDELEG); @@ -2963,8 +2978,11 @@ cred = newnfs_getcred(); TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) { nfscl_cleandeleg(dp); + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + (void) nfscl_deleglocalflush(dp, clp->nfsc_nmp, p, 0, 0); (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p); - nfscl_freedeleg(&clp->nfsc_deleg, dp); + nfscl_freedeleg(dp); } NFSFREECRED(cred); } @@ -3341,7 +3359,7 @@ static int nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp, struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p, - int called_from_renewthread) + int called_from_renewthread, int called_from_remove) { struct nfsclowner *owp, *lowp, *nowp; struct nfsclopen *op, *lop; @@ -3379,13 +3397,25 @@ if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) { np->n_flag |= NDELEGRECALL; NFSUNLOCKNODE(np); - ret = ncl_flush(vp, MNT_WAIT, cred, p, 1, - called_from_renewthread); + if (called_from_remove == 0) + ret = ncl_flush(vp, MNT_WAIT, cred, p, 1, + called_from_renewthread); + else + ret = ncl_vinvalbuf(vp, 0, p, 0); NFSLOCKNODE(np); np->n_flag &= ~NDELEGRECALL; } NFSINVALATTRCACHE(np); NFSUNLOCKNODE(np); + + /* + * Now, flush any changes done to a locally cached copy of the file + * back to the server. + */ + if (ret == 0) + ret = nfscl_deleglocalflush(dp, nmp, p, called_from_renewthread, + called_from_remove); + if (ret == EIO && called_from_renewthread != 0) { /* * If the flush failed with EIO for the renew thread, @@ -3900,7 +3930,8 @@ NFSUNLOCKCLSTATE(); cred = newnfs_getcred(); newnfs_copycred(&dp->nfsdl_cred, cred); - (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0); + (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0, + 1); NFSFREECRED(cred); triedrecall = 1; NFSLOCKCLSTATE(); @@ -3911,11 +3942,18 @@ *stp = dp->nfsdl_stateid; retcnt = 1; nfscl_cleandeleg(dp); - nfscl_freedeleg(&clp->nfsc_deleg, dp); + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + if (igotlock) + nfsv4_unlock(&clp->nfsc_lock, 0); + NFSUNLOCKCLSTATE(); + (void) nfscl_deleglocalflush(dp, nmp, p, 0, 0); + nfscl_freedeleg(dp); + } else { + if (igotlock) + nfsv4_unlock(&clp->nfsc_lock, 0); + NFSUNLOCKCLSTATE(); } - if (igotlock) - nfsv4_unlock(&clp->nfsc_lock, 0); - NFSUNLOCKCLSTATE(); return (retcnt); } } @@ -3999,7 +4037,8 @@ NFSUNLOCKCLSTATE(); cred = newnfs_getcred(); newnfs_copycred(&dp->nfsdl_cred, cred); - (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0); + (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0, + 0); NFSFREECRED(cred); triedrecall = 1; NFSLOCKCLSTATE(); @@ -4011,9 +4050,18 @@ retcnt++; *gotfdp = 1; nfscl_cleandeleg(dp); - nfscl_freedeleg(&clp->nfsc_deleg, dp); - } - if (igotlock) { + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + if (igotlock) { + nfsv4_unlock(&clp->nfsc_lock, 0); + igotlock = 0; + } + NFSUNLOCKCLSTATE(); + (void) nfscl_deleglocalflush(dp, nmp, p, 0, 0); + nfscl_freedeleg(dp); + NFSLOCKCLSTATE(); + continue; + } else if (igotlock) { nfsv4_unlock(&clp->nfsc_lock, 0); igotlock = 0; } @@ -4047,10 +4095,17 @@ retcnt++; *gottdp = 1; nfscl_cleandeleg(dp); - nfscl_freedeleg(&clp->nfsc_deleg, dp); + TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list); + LIST_REMOVE(dp, nfsdl_hash); + NFSUNLOCKCLSTATE(); + (void) nfscl_deleglocalflush(dp, nmp, p, 0, 0); + nfscl_freedeleg(dp); + } else { + NFSUNLOCKCLSTATE(); } + } else { + NFSUNLOCKCLSTATE(); } - NFSUNLOCKCLSTATE(); return (retcnt); } } @@ -4181,7 +4236,7 @@ * put that modtime in mtime. */ APPLESTATIC void -nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) +nfscl_deleggetmod(vnode_t vp, struct timespec *mtime, uint64_t *siz) { struct nfsclclient *clp; struct nfscldeleg *dp; @@ -4195,13 +4250,19 @@ clp = nfscl_findcl(nmp); if (clp == NULL) { NFSUNLOCKCLSTATE(); + NFSLOCKNODE(np); + np->n_flag &= ~NLOCALCACHE; + NFSUNLOCKNODE(np); return; } dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); if (dp != NULL && - (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) == - (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) - *mtime = dp->nfsdl_modtime; + (dp->nfsdl_flags & NFSCLDL_WRITE) != 0) { + if ((dp->nfsdl_flags & NFSCLDL_MODTIMESET) != 0) + *mtime = dp->nfsdl_modtime; + if ((dp->nfsdl_flags & NFSCLDL_LOCALSIZESET) != 0) + *siz = dp->nfsdl_localsize; + } NFSUNLOCKCLSTATE(); } diff -u -r -N sys.dec4/fs/nfsclient/nfs_clvnops.c sys.dec4.packrats/fs/nfsclient/nfs_clvnops.c --- sys.dec4/fs/nfsclient/nfs_clvnops.c 2011-11-15 19:34:57.000000000 -0500 +++ sys.dec4.packrats/fs/nfsclient/nfs_clvnops.c 2011-12-04 20:14:10.000000000 -0500 @@ -518,6 +518,9 @@ * so that we conform to RFC3530 Sec. 9.3.1. */ if (NFS_ISV4(vp)) { + /* Open any associated locally cached copy. */ + nfscl_packratopen(vp, ap->a_td); + error = nfsrpc_open(vp, fmode, ap->a_cred, ap->a_td); if (error) { error = nfscl_maperr(ap->a_td, error, (uid_t)0, @@ -752,6 +755,9 @@ } } + /* Close any associated locally cached copy. */ + nfscl_packratclose(vp, ap->a_td); + /* * and do the close. */ @@ -823,10 +829,10 @@ vap->va_bytes = vattr.va_bytes; vap->va_filerev = vattr.va_filerev; /* - * Get the local modify time for the case of a write + * Get the local modify time and size for the case of a write * delegation. */ - nfscl_deleggetmodtime(vp, &vap->va_mtime); + nfscl_deleggetmod(vp, &vap->va_mtime, &vap->va_size); return (0); } @@ -835,7 +841,8 @@ NFSINCRGLOBAL(newnfsstats.accesscache_misses); nfs34_access_otw(vp, NFSACCESS_ALL, td, ap->a_cred, NULL); if (ncl_getattrcache(vp, ap->a_vap) == 0) { - nfscl_deleggetmodtime(vp, &ap->a_vap->va_mtime); + nfscl_deleggetmod(vp, &ap->a_vap->va_mtime, + &vap->va_size); return (0); } } @@ -844,10 +851,10 @@ error = nfscl_loadattrcache(&vp, &nfsva, vap, NULL, 0, 0); if (!error) { /* - * Get the local modify time for the case of a write + * Get the local modify time and size for the case of a write * delegation. */ - nfscl_deleggetmodtime(vp, &vap->va_mtime); + nfscl_deleggetmod(vp, &vap->va_mtime, &vap->va_size); } else if (NFS_ISV4(vp)) { error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); } @@ -966,6 +973,9 @@ vnode_pager_setsize(vp, tsize); mtx_unlock(&np->n_mtx); } + if (NFS_ISV4(vp) && error == 0 && vap->va_size != VNOVAL && + vp->v_type == VREG) + nfscl_packratsetsize(vp, vap->va_size); return (error); } @@ -1289,15 +1299,21 @@ /* * nfs read call. - * Just call ncl_bioread() to do the work. + * Just call ncl_bioread() to do the work unless a packrat has made a + * local copy. */ static int nfs_read(struct vop_read_args *ap) { struct vnode *vp = ap->a_vp; + int didread, error; switch (vp->v_type) { case VREG: + error = nfscl_packratread(vp, ap->a_uio, ap->a_ioflag, + ap->a_cred, &didread); + if (didread != 0) + return (error); return (ncl_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); case VDIR: return (EISDIR); @@ -2915,7 +2931,8 @@ np->n_flag &= ~NWRITEERR; } if (commit && bo->bo_dirty.bv_cnt == 0 && - bo->bo_numoutput == 0 && np->n_directio_asyncwr == 0) + bo->bo_numoutput == 0 && np->n_directio_asyncwr == 0 && + (np->n_flag & NLOCALCACHE) == 0) np->n_flag &= ~NMODIFIED; mtx_unlock(&np->n_mtx); done: diff -u -r -N sys.dec4/fs/nfsclient/nfsnode.h sys.dec4.packrats/fs/nfsclient/nfsnode.h --- sys.dec4/fs/nfsclient/nfsnode.h 2011-08-04 17:12:46.000000000 -0400 +++ sys.dec4.packrats/fs/nfsclient/nfsnode.h 2011-12-04 21:06:34.000000000 -0500 @@ -157,6 +157,7 @@ #define NREMOVEWANT 0x00004000 /* Want notification that remove is done */ #define NLOCK 0x00008000 /* Sleep lock the node */ #define NLOCKWANT 0x00010000 /* Want the sleep lock */ +#define NLOCALCACHE 0x00020000 /* Copy cached locally */ /* * Convert between nfsnode pointers and vnode pointers diff -u -r -N sys.dec4/modules/nfscl/Makefile sys.dec4.packrats/modules/nfscl/Makefile --- sys.dec4/modules/nfscl/Makefile 2011-06-28 17:30:19.000000000 -0400 +++ sys.dec4.packrats/modules/nfscl/Makefile 2011-12-04 19:59:46.000000000 -0500 @@ -7,6 +7,7 @@ nfs_clkrpc.c \ nfs_clstate.c \ nfs_clcomsubs.c \ + nfs_clpackrat.c \ nfs_clport.c \ nfs_clbio.c \ nfs_clnfsiod.c \ diff -u -r -N sys.dec4/nfs/nfs_nfssvc.c sys.dec4.packrats/nfs/nfs_nfssvc.c --- sys.dec4/nfs/nfs_nfssvc.c 2011-10-06 20:45:18.000000000 -0400 +++ sys.dec4.packrats/nfs/nfs_nfssvc.c 2011-12-04 19:59:46.000000000 -0500 @@ -91,7 +91,8 @@ if ((uap->flag & (NFSSVC_ADDSOCK | NFSSVC_OLDNFSD | NFSSVC_NFSD)) && nfsd_call_nfsserver != NULL) error = (*nfsd_call_nfsserver)(td, uap); - else if ((uap->flag & (NFSSVC_CBADDSOCK | NFSSVC_NFSCBD)) && + else if ((uap->flag & + (NFSSVC_CBADDSOCK | NFSSVC_NFSCBD | NFSSVC_PACKRAT)) != 0 && nfsd_call_nfscl != NULL) error = (*nfsd_call_nfscl)(td, uap); else if ((uap->flag & (NFSSVC_IDNAME | NFSSVC_GETSTATS | diff -u -r -N sys.dec4/nfs/nfssvc.h sys.dec4.packrats/nfs/nfssvc.h --- sys.dec4/nfs/nfssvc.h 2011-06-11 18:58:25.000000000 -0400 +++ sys.dec4.packrats/nfs/nfssvc.h 2011-12-04 19:59:46.000000000 -0500 @@ -66,5 +66,6 @@ #define NFSSVC_BACKUPSTABLE 0x00800000 #define NFSSVC_ZEROCLTSTATS 0x01000000 /* modifier for GETSTATS */ #define NFSSVC_ZEROSRVSTATS 0x02000000 /* modifier for GETSTATS */ +#define NFSSVC_PACKRAT 0x04000000 #endif /* _NFS_NFSSVC_H */