nfs_subs.c revision 84002
167754Smsmith/* 267754Smsmith * Copyright (c) 1989, 1993 367754Smsmith * The Regents of the University of California. All rights reserved. 4117521Snjl * 567754Smsmith * This code is derived from software contributed to Berkeley by 667754Smsmith * Rick Macklem at The University of Guelph. 767754Smsmith * 867754Smsmith * Redistribution and use in source and binary forms, with or without 967754Smsmith * modification, are permitted provided that the following conditions 1067754Smsmith * are met: 1167754Smsmith * 1. Redistributions of source code must retain the above copyright 12114237Snjl * notice, this list of conditions and the following disclaimer. 1370243Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1467754Smsmith * notice, this list of conditions and the following disclaimer in the 1567754Smsmith * documentation and/or other materials provided with the distribution. 1667754Smsmith * 3. All advertising materials mentioning features or use of this software 1767754Smsmith * must display the following acknowledgement: 1867754Smsmith * This product includes software developed by the University of 1967754Smsmith * California, Berkeley and its contributors. 2067754Smsmith * 4. Neither the name of the University nor the names of its contributors 2167754Smsmith * may be used to endorse or promote products derived from this software 2267754Smsmith * without specific prior written permission. 2367754Smsmith * 2467754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2567754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2667754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2767754Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2867754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2967754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3067754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3167754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3267754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3367754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3467754Smsmith * SUCH DAMAGE. 3567754Smsmith * 3667754Smsmith * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95 3767754Smsmith */ 3867754Smsmith 3967754Smsmith#include <sys/cdefs.h> 4067754Smsmith__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_subs.c 84002 2001-09-27 02:33:36Z peter $"); 4167754Smsmith 4267754Smsmith/* 4367754Smsmith * These functions support the macros and help fiddle mbuf chains for 4467754Smsmith * the nfs op functions. They do things like create the rpc header and 4567754Smsmith * copy data between mbuf chains and uio lists. 4667754Smsmith */ 4767754Smsmith 4867754Smsmith#include <sys/param.h> 4967754Smsmith#include <sys/systm.h> 5067754Smsmith#include <sys/kernel.h> 5167754Smsmith#include <sys/bio.h> 5267754Smsmith#include <sys/buf.h> 5367754Smsmith#include <sys/proc.h> 5467754Smsmith#include <sys/mount.h> 5567754Smsmith#include <sys/vnode.h> 5667754Smsmith#include <sys/namei.h> 5767754Smsmith#include <sys/mbuf.h> 5867754Smsmith#include <sys/socket.h> 5967754Smsmith#include <sys/stat.h> 6067754Smsmith#include <sys/malloc.h> 6167754Smsmith#include <sys/sysent.h> 6267754Smsmith#include <sys/syscall.h> 6367754Smsmith#include <sys/sysproto.h> 6467754Smsmith 6567754Smsmith#include <vm/vm.h> 6667754Smsmith#include <vm/vm_object.h> 6767754Smsmith#include <vm/vm_extern.h> 6867754Smsmith#include <vm/vm_zone.h> 6967754Smsmith 7067754Smsmith#include <nfs/rpcv2.h> 7167754Smsmith#include <nfs/nfsproto.h> 7267754Smsmith#include <nfsclient/nfs.h> 7367754Smsmith#include <nfsclient/nfsnode.h> 7467754Smsmith#include <nfs/xdr_subs.h> 7567754Smsmith#include <nfsclient/nfsm_subs.h> 7667754Smsmith#include <nfsclient/nfsmount.h> 7767754Smsmith 7867754Smsmith#include <netinet/in.h> 7967754Smsmith 8067754Smsmith/* 8167754Smsmith * Data items converted to xdr at startup, since they are constant 8267754Smsmith * This is kinda hokey, but may save a little time doing byte swaps 8367754Smsmith */ 8467754Smsmithu_int32_t nfs_xdrneg1; 8567754Smsmithu_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr, 8667754Smsmith rpc_mismatch, rpc_auth_unix, rpc_msgaccepted; 8767754Smsmithu_int32_t nfs_true, nfs_false; 8867754Smsmith 8967754Smsmith/* And other global data */ 9067754Smsmithstatic u_int32_t nfs_xid = 0; 9167754Smsmithstatic enum vtype nv2tov_type[8]= { 9267754Smsmith VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON 9367754Smsmith}; 9467754Smsmith 9567754Smsmithint nfs_ticks; 9667754Smsmithint nfs_pbuf_freecnt = -1; /* start out unlimited */ 9767754Smsmith 9867754Smsmithstruct nfs_reqq nfs_reqq; 9967754Smsmithstruct nfs_bufq nfs_bufq; 10067754Smsmith 10167754Smsmithstatic int nfs_prev_nfsclnt_sy_narg; 10267754Smsmithstatic sy_call_t *nfs_prev_nfsclnt_sy_call; 10367754Smsmith 10467754Smsmith/* 10567754Smsmith * and the reverse mapping from generic to Version 2 procedure numbers 10667754Smsmith */ 10767754Smsmithint nfsv2_procid[NFS_NPROCS] = { 10867754Smsmith NFSV2PROC_NULL, 10967754Smsmith NFSV2PROC_GETATTR, 11067754Smsmith NFSV2PROC_SETATTR, 11167754Smsmith NFSV2PROC_LOOKUP, 11267754Smsmith NFSV2PROC_NOOP, 11367754Smsmith NFSV2PROC_READLINK, 11467754Smsmith NFSV2PROC_READ, 11567754Smsmith NFSV2PROC_WRITE, 11667754Smsmith NFSV2PROC_CREATE, 11767754Smsmith NFSV2PROC_MKDIR, 11867754Smsmith NFSV2PROC_SYMLINK, 11967754Smsmith NFSV2PROC_CREATE, 12067754Smsmith NFSV2PROC_REMOVE, 12167754Smsmith NFSV2PROC_RMDIR, 12267754Smsmith NFSV2PROC_RENAME, 12367754Smsmith NFSV2PROC_LINK, 12467754Smsmith NFSV2PROC_READDIR, 12567754Smsmith NFSV2PROC_NOOP, 126102550Siwasaki NFSV2PROC_STATFS, 12767754Smsmith NFSV2PROC_NOOP, 128102550Siwasaki NFSV2PROC_NOOP, 12991116Smsmith NFSV2PROC_NOOP, 13067754Smsmith NFSV2PROC_NOOP, 13167754Smsmith}; 13267754Smsmith 13367754SmsmithLIST_HEAD(nfsnodehashhead, nfsnode); 134114237Snjl 135114237Snjl/* 136114237Snjl * Create the header for an rpc request packet 137114237Snjl * The hsiz is the size of the rest of the nfs request header. 138114237Snjl * (just used to decide if a cluster is a good idea) 139114237Snjl */ 140114237Snjlstruct mbuf * 141114237Snjlnfsm_reqhead(struct vnode *vp, u_long procid, int hsiz) 142114237Snjl{ 143114237Snjl struct mbuf *mb; 144114237Snjl 145114237Snjl MGET(mb, M_TRYWAIT, MT_DATA); 146114237Snjl if (hsiz >= MINCLSIZE) 147114237Snjl MCLGET(mb, M_TRYWAIT); 148114237Snjl mb->m_len = 0; 149114237Snjl return (mb); 150114237Snjl} 151114237Snjl 152114237Snjl/* 153114237Snjl * Build the RPC header and fill in the authorization info. 154114237Snjl * The authorization string argument is only used when the credentials 155114237Snjl * come from outside of the kernel. 156114237Snjl * Returns the head of the mbuf list. 157114237Snjl */ 158114237Snjlstruct mbuf * 159114237Snjlnfsm_rpchead(struct ucred *cr, int nmflag, int procid, int auth_type, 160114237Snjl int auth_len, struct mbuf *mrest, int mrest_len, struct mbuf **mbp, 161114237Snjl u_int32_t *xidp) 162114237Snjl{ 163114237Snjl struct mbuf *mb; 164114237Snjl u_int32_t *tl; 165114237Snjl caddr_t bpos; 166114237Snjl int i; 167114237Snjl struct mbuf *mreq; 168114237Snjl int grpsiz, authsiz; 169114237Snjl 170114237Snjl authsiz = nfsm_rndup(auth_len); 171114237Snjl MGETHDR(mb, M_TRYWAIT, MT_DATA); 172114237Snjl if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) { 173114237Snjl MCLGET(mb, M_TRYWAIT); 17467754Smsmith } else if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) { 17567754Smsmith MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED); 17667754Smsmith } else { 17767754Smsmith MH_ALIGN(mb, 8 * NFSX_UNSIGNED); 17867754Smsmith } 17967754Smsmith mb->m_len = 0; 18067754Smsmith mreq = mb; 18167754Smsmith bpos = mtod(mb, caddr_t); 18267754Smsmith 18367754Smsmith /* 18467754Smsmith * First the RPC header. 18567754Smsmith */ 18667754Smsmith tl = nfsm_build(u_int32_t *, 8 * NFSX_UNSIGNED); 18767754Smsmith 18867754Smsmith /* Get a pretty random xid to start with */ 18967754Smsmith if (!nfs_xid) 19067754Smsmith nfs_xid = random(); 19167754Smsmith /* 19291116Smsmith * Skip zero xid if it should ever happen. 19367754Smsmith */ 19491116Smsmith if (++nfs_xid == 0) 19567754Smsmith nfs_xid++; 19667754Smsmith 19767754Smsmith *tl++ = *xidp = txdr_unsigned(nfs_xid); 19867754Smsmith *tl++ = rpc_call; 19967754Smsmith *tl++ = rpc_vers; 20067754Smsmith *tl++ = txdr_unsigned(NFS_PROG); 20167754Smsmith if (nmflag & NFSMNT_NFSV3) { 20267754Smsmith *tl++ = txdr_unsigned(NFS_VER3); 20367754Smsmith *tl++ = txdr_unsigned(procid); 20467754Smsmith } else { 20567754Smsmith *tl++ = txdr_unsigned(NFS_VER2); 20667754Smsmith *tl++ = txdr_unsigned(nfsv2_procid[procid]); 20767754Smsmith } 20867754Smsmith 20967754Smsmith /* 21067754Smsmith * And then the authorization cred. 21167754Smsmith */ 21267754Smsmith *tl++ = txdr_unsigned(auth_type); 21367754Smsmith *tl = txdr_unsigned(authsiz); 21467754Smsmith switch (auth_type) { 21567754Smsmith case RPCAUTH_UNIX: 21667754Smsmith tl = nfsm_build(u_int32_t *, auth_len); 21767754Smsmith *tl++ = 0; /* stamp ?? */ 21867754Smsmith *tl++ = 0; /* NULL hostname */ 21967754Smsmith *tl++ = txdr_unsigned(cr->cr_uid); 22069746Smsmith *tl++ = txdr_unsigned(cr->cr_groups[0]); 22167754Smsmith grpsiz = (auth_len >> 2) - 5; 22282367Smsmith *tl++ = txdr_unsigned(grpsiz); 223117521Snjl for (i = 1; i <= grpsiz; i++) 224117521Snjl *tl++ = txdr_unsigned(cr->cr_groups[i]); 22567754Smsmith break; 22667754Smsmith } 22767754Smsmith 22867754Smsmith /* 22967754Smsmith * And the verifier... 23067754Smsmith */ 23167754Smsmith tl = nfsm_build(u_int32_t *, 2 * NFSX_UNSIGNED); 23267754Smsmith *tl++ = txdr_unsigned(RPCAUTH_NULL); 23367754Smsmith *tl = 0; 23467754Smsmith mb->m_next = mrest; 23567754Smsmith mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len; 23667754Smsmith mreq->m_pkthdr.rcvif = (struct ifnet *)0; 23767754Smsmith *mbp = mb; 23867754Smsmith return (mreq); 23967754Smsmith} 24067754Smsmith 24167754Smsmith/* 24267754Smsmith * copies a uio scatter/gather list to an mbuf chain. 24367754Smsmith * NOTE: can ony handle iovcnt == 1 24467754Smsmith */ 24567754Smsmithint 24667754Smsmithnfsm_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos) 24767754Smsmith{ 24867754Smsmith char *uiocp; 24967754Smsmith struct mbuf *mp, *mp2; 25067754Smsmith int xfer, left, mlen; 25167754Smsmith int uiosiz, clflg, rem; 25267754Smsmith char *cp; 25367754Smsmith 25467754Smsmith#ifdef DIAGNOSTIC 25567754Smsmith if (uiop->uio_iovcnt != 1) 25667754Smsmith panic("nfsm_uiotombuf: iovcnt != 1"); 25767754Smsmith#endif 25867754Smsmith 25967754Smsmith if (siz > MLEN) /* or should it >= MCLBYTES ?? */ 26067754Smsmith clflg = 1; 26167754Smsmith else 26267754Smsmith clflg = 0; 26367754Smsmith rem = nfsm_rndup(siz)-siz; 26482367Smsmith mp = mp2 = *mq; 26567754Smsmith while (siz > 0) { 26667754Smsmith left = uiop->uio_iov->iov_len; 26767754Smsmith uiocp = uiop->uio_iov->iov_base; 26871867Smsmith if (left > siz) 26982367Smsmith left = siz; 27091116Smsmith uiosiz = left; 27191116Smsmith while (left > 0) { 27291116Smsmith mlen = M_TRAILINGSPACE(mp); 27367754Smsmith if (mlen == 0) { 27467754Smsmith MGET(mp, M_TRYWAIT, MT_DATA); 27567754Smsmith if (clflg) 27667754Smsmith MCLGET(mp, M_TRYWAIT); 27767754Smsmith mp->m_len = 0; 27867754Smsmith mp2->m_next = mp; 27967754Smsmith mp2 = mp; 28067754Smsmith mlen = M_TRAILINGSPACE(mp); 28167754Smsmith } 28267754Smsmith xfer = (left > mlen) ? mlen : left; 28367754Smsmith#ifdef notdef 28467754Smsmith /* Not Yet.. */ 28567754Smsmith if (uiop->uio_iov->iov_op != NULL) 28667754Smsmith (*(uiop->uio_iov->iov_op)) 28767754Smsmith (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); 28867754Smsmith else 28999146Siwasaki#endif 290114237Snjl if (uiop->uio_segflg == UIO_SYSSPACE) 291114237Snjl bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); 292117521Snjl else 293117521Snjl copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer); 294114237Snjl mp->m_len += xfer; 295114237Snjl left -= xfer; 296114237Snjl uiocp += xfer; 297114237Snjl uiop->uio_offset += xfer; 298114237Snjl uiop->uio_resid -= xfer; 29967754Smsmith } 30067754Smsmith uiop->uio_iov->iov_base += uiosiz; 30167754Smsmith uiop->uio_iov->iov_len -= uiosiz; 30267754Smsmith siz -= uiosiz; 30367754Smsmith } 304117521Snjl if (rem > 0) { 305117521Snjl if (rem > M_TRAILINGSPACE(mp)) { 30667754Smsmith MGET(mp, M_TRYWAIT, MT_DATA); 30767754Smsmith mp->m_len = 0; 30867754Smsmith mp2->m_next = mp; 30967754Smsmith } 31067754Smsmith cp = mtod(mp, caddr_t)+mp->m_len; 31167754Smsmith for (left = 0; left < rem; left++) 31267754Smsmith *cp++ = '\0'; 31367754Smsmith mp->m_len += rem; 314107325Siwasaki *bpos = cp; 31582367Smsmith } else 31682367Smsmith *bpos = mtod(mp, caddr_t)+mp->m_len; 31767754Smsmith *mq = mp; 31867754Smsmith return (0); 31982367Smsmith} 32067754Smsmith 32182367Smsmith/* 32267754Smsmith * Copy a string into mbufs for the hard cases... 32367754Smsmith */ 32467754Smsmithint 32582367Smsmithnfsm_strtmbuf(struct mbuf **mb, char **bpos, const char *cp, long siz) 32667754Smsmith{ 32782367Smsmith struct mbuf *m1 = NULL, *m2; 32867754Smsmith long left, xfer, len, tlen; 32967754Smsmith u_int32_t *tl; 33067754Smsmith int putsize; 33182367Smsmith 33267754Smsmith putsize = 1; 33367754Smsmith m2 = *mb; 33469746Smsmith left = M_TRAILINGSPACE(m2); 33567754Smsmith if (left > 0) { 33667754Smsmith tl = ((u_int32_t *)(*bpos)); 33767754Smsmith *tl++ = txdr_unsigned(siz); 33867754Smsmith putsize = 0; 33967754Smsmith left -= NFSX_UNSIGNED; 34067754Smsmith m2->m_len += NFSX_UNSIGNED; 34167754Smsmith if (left > 0) { 34267754Smsmith bcopy(cp, (caddr_t) tl, left); 34367754Smsmith siz -= left; 34467754Smsmith cp += left; 34567754Smsmith m2->m_len += left; 34667754Smsmith left = 0; 34767754Smsmith } 34867754Smsmith } 34967754Smsmith /* Loop around adding mbufs */ 35067754Smsmith while (siz > 0) { 35167754Smsmith MGET(m1, M_TRYWAIT, MT_DATA); 35267754Smsmith if (siz > MLEN) 35367754Smsmith MCLGET(m1, M_TRYWAIT); 354114237Snjl m1->m_len = NFSMSIZ(m1); 35567754Smsmith m2->m_next = m1; 35667754Smsmith m2 = m1; 35767754Smsmith tl = mtod(m1, u_int32_t *); 35867754Smsmith tlen = 0; 35967754Smsmith if (putsize) { 36067754Smsmith *tl++ = txdr_unsigned(siz); 36167754Smsmith m1->m_len -= NFSX_UNSIGNED; 36267754Smsmith tlen = NFSX_UNSIGNED; 36391116Smsmith putsize = 0; 36467754Smsmith } 36567754Smsmith if (siz < m1->m_len) { 36667754Smsmith len = nfsm_rndup(siz); 36767754Smsmith xfer = siz; 36867754Smsmith if (xfer < len) 36967754Smsmith *(tl+(xfer>>2)) = 0; 37067754Smsmith } else { 37167754Smsmith xfer = len = m1->m_len; 37267754Smsmith } 37367754Smsmith bcopy(cp, (caddr_t) tl, xfer); 37467754Smsmith m1->m_len = len+tlen; 37567754Smsmith siz -= xfer; 37667754Smsmith cp += xfer; 37767754Smsmith } 37867754Smsmith *mb = m1; 37967754Smsmith *bpos = mtod(m1, caddr_t)+m1->m_len; 38067754Smsmith return (0); 38167754Smsmith} 38267754Smsmith 38367754Smsmith/* 38467754Smsmith * Called once to initialize data structures... 38567754Smsmith */ 38667754Smsmithint 38767754Smsmithnfs_init(struct vfsconf *vfsp) 38867754Smsmith{ 38967754Smsmith int i; 39067754Smsmith 39167754Smsmith nfsmount_zone = zinit("NFSMOUNT", sizeof(struct nfsmount), 0, 0, 1); 39267754Smsmith rpc_vers = txdr_unsigned(RPC_VER2); 39367754Smsmith rpc_call = txdr_unsigned(RPC_CALL); 39467754Smsmith rpc_reply = txdr_unsigned(RPC_REPLY); 39567754Smsmith rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED); 39667754Smsmith rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED); 39767754Smsmith rpc_mismatch = txdr_unsigned(RPC_MISMATCH); 39867754Smsmith rpc_autherr = txdr_unsigned(RPC_AUTHERR); 39967754Smsmith rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX); 40067754Smsmith nfs_true = txdr_unsigned(TRUE); 40167754Smsmith nfs_false = txdr_unsigned(FALSE); 40267754Smsmith nfs_xdrneg1 = txdr_unsigned(-1); 40367754Smsmith nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000; 40467754Smsmith if (nfs_ticks < 1) 40567754Smsmith nfs_ticks = 1; 40667754Smsmith /* Ensure async daemons disabled */ 40767754Smsmith for (i = 0; i < NFS_MAXASYNCDAEMON; i++) { 40867754Smsmith nfs_iodwant[i] = (struct proc *)0; 40967754Smsmith nfs_iodmount[i] = (struct nfsmount *)0; 41067754Smsmith } 41167754Smsmith nfs_nhinit(); /* Init the nfsnode table */ 41299679Siwasaki 41367754Smsmith /* 41467754Smsmith * Initialize reply list and start timer 41567754Smsmith */ 41667754Smsmith TAILQ_INIT(&nfs_reqq); 41784491Smsmith 41867754Smsmith nfs_timer(0); 41967754Smsmith 42091116Smsmith nfs_prev_nfsclnt_sy_narg = sysent[SYS_nfsclnt].sy_narg; 42184491Smsmith sysent[SYS_nfsclnt].sy_narg = 2; 42284491Smsmith nfs_prev_nfsclnt_sy_call = sysent[SYS_nfsclnt].sy_call; 42367754Smsmith sysent[SYS_nfsclnt].sy_call = (sy_call_t *)nfsclnt; 42467754Smsmith 42567754Smsmith nfs_pbuf_freecnt = nswbuf / 2 + 1; 42667754Smsmith 42799679Siwasaki return (0); 42867754Smsmith} 42999679Siwasaki 43067754Smsmithint 43199679Siwasakinfs_uninit(struct vfsconf *vfsp) 43299679Siwasaki{ 433117521Snjl 43484491Smsmith untimeout(nfs_timer, (void *)NULL, nfs_timer_handle); 43584491Smsmith sysent[SYS_nfsclnt].sy_narg = nfs_prev_nfsclnt_sy_narg; 43684491Smsmith sysent[SYS_nfsclnt].sy_call = nfs_prev_nfsclnt_sy_call; 43784491Smsmith return (0); 43867754Smsmith} 43999679Siwasaki 44084491Smsmith/* 44184491Smsmith * Attribute cache routines. 44299679Siwasaki * nfs_loadattrcache() - loads or updates the cache contents from attributes 44384491Smsmith * that are on the mbuf list 44499679Siwasaki * nfs_getattrcache() - returns valid attributes if found in cache, returns 44584491Smsmith * error otherwise 44684491Smsmith */ 44784491Smsmith 44884491Smsmith/* 44984491Smsmith * Load the attribute cache (that lives in the nfsnode entry) with 45099679Siwasaki * the values on the mbuf list and 45184491Smsmith * Iff vap not NULL 45284491Smsmith * copy the attributes to *vaper 45384491Smsmith */ 45499679Siwasakiint 45599679Siwasakinfs_loadattrcache(struct vnode **vpp, struct mbuf **mdp, caddr_t *dposp, 45667754Smsmith struct vattr *vaper, int dontshrink) 45767754Smsmith{ 45867754Smsmith struct vnode *vp = *vpp; 45967754Smsmith struct vattr *vap; 46099679Siwasaki struct nfs_fattr *fp; 46167754Smsmith struct nfsnode *np; 46267754Smsmith int32_t t1; 46367754Smsmith caddr_t cp2; 46467754Smsmith int error = 0, rdev; 46599679Siwasaki struct mbuf *md; 46667754Smsmith enum vtype vtyp; 46767754Smsmith u_short vmode; 46867754Smsmith struct timespec mtime; 46967754Smsmith int v3 = NFS_ISV3(vp); 47067754Smsmith 47167754Smsmith md = *mdp; 47267754Smsmith t1 = (mtod(md, caddr_t) + md->m_len) - *dposp; 47367754Smsmith if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) != 0) 47467754Smsmith return (error); 47567754Smsmith fp = (struct nfs_fattr *)cp2; 47667754Smsmith if (v3) { 47784491Smsmith vtyp = nfsv3tov_type(fp->fa_type); 47867754Smsmith vmode = fxdr_unsigned(u_short, fp->fa_mode); 47967754Smsmith rdev = makeudev(fxdr_unsigned(int, fp->fa3_rdev.specdata1), 48067754Smsmith fxdr_unsigned(int, fp->fa3_rdev.specdata2)); 48167754Smsmith fxdr_nfsv3time(&fp->fa3_mtime, &mtime); 48267754Smsmith } else { 48367754Smsmith vtyp = nfsv2tov_type(fp->fa_type); 48467754Smsmith vmode = fxdr_unsigned(u_short, fp->fa_mode); 48567754Smsmith /* 48667754Smsmith * XXX 48767754Smsmith * 48867754Smsmith * The duplicate information returned in fa_type and fa_mode 48967754Smsmith * is an ambiguity in the NFS version 2 protocol. 49067754Smsmith * 49167754Smsmith * VREG should be taken literally as a regular file. If a 49267754Smsmith * server intents to return some type information differently 49367754Smsmith * in the upper bits of the mode field (e.g. for sockets, or 49467754Smsmith * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we 49567754Smsmith * leave the examination of the mode bits even in the VREG 49667754Smsmith * case to avoid breakage for bogus servers, but we make sure 49791116Smsmith * that there are actually type bits set in the upper part of 49891116Smsmith * fa_mode (and failing that, trust the va_type field). 49991116Smsmith * 50067754Smsmith * NFSv3 cleared the issue, and requires fa_mode to not 50167754Smsmith * contain any type information (while also introduing sockets 50267754Smsmith * and FIFOs for fa_type). 50367754Smsmith */ 504114237Snjl if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0)) 50567754Smsmith vtyp = IFTOVT(vmode); 506114237Snjl rdev = fxdr_unsigned(int32_t, fp->fa2_rdev); 50767754Smsmith fxdr_nfsv2time(&fp->fa2_mtime, &mtime); 50867754Smsmith 50967754Smsmith /* 51067754Smsmith * Really ugly NFSv2 kludge. 51167754Smsmith */ 51267754Smsmith if (vtyp == VCHR && rdev == 0xffffffff) 51367754Smsmith vtyp = VFIFO; 51467754Smsmith } 51567754Smsmith 51667754Smsmith /* 51767754Smsmith * If v_type == VNON it is a new node, so fill in the v_type, 51867754Smsmith * n_mtime fields. Check to see if it represents a special 51967754Smsmith * device, and if so, check for a possible alias. Once the 52067754Smsmith * correct vnode has been obtained, fill in the rest of the 52167754Smsmith * information. 52291116Smsmith */ 52391116Smsmith np = VTONFS(vp); 52491116Smsmith if (vp->v_type != vtyp) { 52591116Smsmith vp->v_type = vtyp; 52691116Smsmith if (vp->v_type == VFIFO) { 527117521Snjl vp->v_op = fifo_nfsv2nodeop_p; 52867754Smsmith } 52967754Smsmith if (vp->v_type == VCHR || vp->v_type == VBLK) { 530117521Snjl vp->v_op = spec_nfsv2nodeop_p; 531117521Snjl vp = addaliasu(vp, rdev); 53267754Smsmith np->n_vnode = vp; 53367754Smsmith } 53480062Smsmith np->n_mtime = mtime.tv_sec; 53567754Smsmith } 53667754Smsmith vap = &np->n_vattr; 53767754Smsmith vap->va_type = vtyp; 53867754Smsmith vap->va_mode = (vmode & 07777); 539102550Siwasaki vap->va_rdev = rdev; 54067754Smsmith vap->va_mtime = mtime; 54167754Smsmith vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 542 if (v3) { 543 vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink); 544 vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid); 545 vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid); 546 vap->va_size = fxdr_hyper(&fp->fa3_size); 547 vap->va_blocksize = NFS_FABLKSIZE; 548 vap->va_bytes = fxdr_hyper(&fp->fa3_used); 549 vap->va_fileid = fxdr_unsigned(int32_t, 550 fp->fa3_fileid.nfsuquad[1]); 551 fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime); 552 fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime); 553 vap->va_flags = 0; 554 vap->va_filerev = 0; 555 } else { 556 vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink); 557 vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid); 558 vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid); 559 vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size); 560 vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize); 561 vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) 562 * NFS_FABLKSIZE; 563 vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid); 564 fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime); 565 vap->va_flags = 0; 566 vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t, 567 fp->fa2_ctime.nfsv2_sec); 568 vap->va_ctime.tv_nsec = 0; 569 vap->va_gen = fxdr_unsigned(u_int32_t, fp->fa2_ctime.nfsv2_usec); 570 vap->va_filerev = 0; 571 } 572 np->n_attrstamp = time_second; 573 if (vap->va_size != np->n_size) { 574 if (vap->va_type == VREG) { 575 if (dontshrink && vap->va_size < np->n_size) { 576 /* 577 * We've been told not to shrink the file; 578 * zero np->n_attrstamp to indicate that 579 * the attributes are stale. 580 */ 581 vap->va_size = np->n_size; 582 np->n_attrstamp = 0; 583 } else if (np->n_flag & NMODIFIED) { 584 if (vap->va_size < np->n_size) 585 vap->va_size = np->n_size; 586 else 587 np->n_size = vap->va_size; 588 } else { 589 np->n_size = vap->va_size; 590 } 591 vnode_pager_setsize(vp, np->n_size); 592 } else { 593 np->n_size = vap->va_size; 594 } 595 } 596 if (vaper != NULL) { 597 bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap)); 598 if (np->n_flag & NCHG) { 599 if (np->n_flag & NACC) 600 vaper->va_atime = np->n_atim; 601 if (np->n_flag & NUPD) 602 vaper->va_mtime = np->n_mtim; 603 } 604 } 605 return (0); 606} 607 608#ifdef NFS_ACDEBUG 609#include <sys/sysctl.h> 610SYSCTL_DECL(_vfs_nfs); 611static int nfs_acdebug; 612SYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, ""); 613#endif 614 615/* 616 * Check the time stamp 617 * If the cache is valid, copy contents to *vap and return 0 618 * otherwise return an error 619 */ 620int 621nfs_getattrcache(struct vnode *vp, struct vattr *vaper) 622{ 623 struct nfsnode *np; 624 struct vattr *vap; 625 struct nfsmount *nmp; 626 int timeo; 627 628 np = VTONFS(vp); 629 vap = &np->n_vattr; 630 nmp = VFSTONFS(vp->v_mount); 631 /* XXX n_mtime doesn't seem to be updated on a miss-and-reload */ 632 timeo = (time_second - np->n_mtime) / 10; 633 634#ifdef NFS_ACDEBUG 635 if (nfs_acdebug>1) 636 printf("nfs_getattrcache: initial timeo = %d\n", timeo); 637#endif 638 639 if (vap->va_type == VDIR) { 640 if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin) 641 timeo = nmp->nm_acdirmin; 642 else if (timeo > nmp->nm_acdirmax) 643 timeo = nmp->nm_acdirmax; 644 } else { 645 if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin) 646 timeo = nmp->nm_acregmin; 647 else if (timeo > nmp->nm_acregmax) 648 timeo = nmp->nm_acregmax; 649 } 650 651#ifdef NFS_ACDEBUG 652 if (nfs_acdebug > 2) 653 printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n", 654 nmp->nm_acregmin, nmp->nm_acregmax, 655 nmp->nm_acdirmin, nmp->nm_acdirmax); 656 657 if (nfs_acdebug) 658 printf("nfs_getattrcache: age = %d; final timeo = %d\n", 659 (time_second - np->n_attrstamp), timeo); 660#endif 661 662 if ((time_second - np->n_attrstamp) >= timeo) { 663 nfsstats.attrcache_misses++; 664 return (ENOENT); 665 } 666 nfsstats.attrcache_hits++; 667 if (vap->va_size != np->n_size) { 668 if (vap->va_type == VREG) { 669 if (np->n_flag & NMODIFIED) { 670 if (vap->va_size < np->n_size) 671 vap->va_size = np->n_size; 672 else 673 np->n_size = vap->va_size; 674 } else { 675 np->n_size = vap->va_size; 676 } 677 vnode_pager_setsize(vp, np->n_size); 678 } else { 679 np->n_size = vap->va_size; 680 } 681 } 682 bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr)); 683 if (np->n_flag & NCHG) { 684 if (np->n_flag & NACC) 685 vaper->va_atime = np->n_atim; 686 if (np->n_flag & NUPD) 687 vaper->va_mtime = np->n_mtim; 688 } 689 return (0); 690} 691 692static nfsuint64 nfs_nullcookie = { { 0, 0 } }; 693/* 694 * This function finds the directory cookie that corresponds to the 695 * logical byte offset given. 696 */ 697nfsuint64 * 698nfs_getcookie(struct nfsnode *np, off_t off, int add) 699{ 700 struct nfsdmap *dp, *dp2; 701 int pos; 702 703 pos = (uoff_t)off / NFS_DIRBLKSIZ; 704 if (pos == 0 || off < 0) { 705#ifdef DIAGNOSTIC 706 if (add) 707 panic("nfs getcookie add at <= 0"); 708#endif 709 return (&nfs_nullcookie); 710 } 711 pos--; 712 dp = LIST_FIRST(&np->n_cookies); 713 if (!dp) { 714 if (add) { 715 MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap), 716 M_NFSDIROFF, M_WAITOK); 717 dp->ndm_eocookie = 0; 718 LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list); 719 } else 720 return ((nfsuint64 *)0); 721 } 722 while (pos >= NFSNUMCOOKIES) { 723 pos -= NFSNUMCOOKIES; 724 if (LIST_NEXT(dp, ndm_list)) { 725 if (!add && dp->ndm_eocookie < NFSNUMCOOKIES && 726 pos >= dp->ndm_eocookie) 727 return ((nfsuint64 *)0); 728 dp = LIST_NEXT(dp, ndm_list); 729 } else if (add) { 730 MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap), 731 M_NFSDIROFF, M_WAITOK); 732 dp2->ndm_eocookie = 0; 733 LIST_INSERT_AFTER(dp, dp2, ndm_list); 734 dp = dp2; 735 } else 736 return ((nfsuint64 *)0); 737 } 738 if (pos >= dp->ndm_eocookie) { 739 if (add) 740 dp->ndm_eocookie = pos + 1; 741 else 742 return ((nfsuint64 *)0); 743 } 744 return (&dp->ndm_cookies[pos]); 745} 746 747/* 748 * Invalidate cached directory information, except for the actual directory 749 * blocks (which are invalidated separately). 750 * Done mainly to avoid the use of stale offset cookies. 751 */ 752void 753nfs_invaldir(struct vnode *vp) 754{ 755 struct nfsnode *np = VTONFS(vp); 756 757#ifdef DIAGNOSTIC 758 if (vp->v_type != VDIR) 759 panic("nfs: invaldir not dir"); 760#endif 761 np->n_direofoffset = 0; 762 np->n_cookieverf.nfsuquad[0] = 0; 763 np->n_cookieverf.nfsuquad[1] = 0; 764 if (LIST_FIRST(&np->n_cookies)) 765 LIST_FIRST(&np->n_cookies)->ndm_eocookie = 0; 766} 767 768/* 769 * The write verifier has changed (probably due to a server reboot), so all 770 * B_NEEDCOMMIT blocks will have to be written again. Since they are on the 771 * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT 772 * and B_CLUSTEROK flags. Once done the new write verifier can be set for the 773 * mount point. 774 * 775 * B_CLUSTEROK must be cleared along with B_NEEDCOMMIT because stage 1 data 776 * writes are not clusterable. 777 */ 778void 779nfs_clearcommit(struct mount *mp) 780{ 781 struct vnode *vp, *nvp; 782 struct buf *bp, *nbp; 783 int s; 784 785 GIANT_REQUIRED; 786 787 s = splbio(); 788 mtx_lock(&mntvnode_mtx); 789loop: 790 for (vp = LIST_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) { 791 if (vp->v_mount != mp) /* Paranoia */ 792 goto loop; 793 nvp = LIST_NEXT(vp, v_mntvnodes); 794 for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { 795 nbp = TAILQ_NEXT(bp, b_vnbufs); 796 if (BUF_REFCNT(bp) == 0 && 797 (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT)) 798 == (B_DELWRI | B_NEEDCOMMIT)) 799 bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK); 800 } 801 } 802 mtx_unlock(&mntvnode_mtx); 803 splx(s); 804} 805 806/* 807 * Helper functions for former macros. Some of these should be 808 * moved to their callers. 809 */ 810 811int 812nfsm_mtofh_xx(struct vnode *d, struct vnode **v, int v3, int *f, 813 u_int32_t **tl, struct mbuf **md, caddr_t *dpos) 814{ 815 struct nfsnode *ttnp; 816 struct vnode *ttvp; 817 nfsfh_t *ttfhp; 818 int ttfhsize; 819 int t1; 820 821 if (v3) { 822 t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos); 823 if (t1) 824 return (t1); 825 *f = fxdr_unsigned(int, **tl); 826 } else 827 *f = 1; 828 if (*f) { 829 t1 = nfsm_getfh_xx(&ttfhp, &ttfhsize, (v3), tl, md, dpos); 830 if (t1 != 0) 831 return t1; 832 t1 = nfs_nget(d->v_mount, ttfhp, ttfhsize, &ttnp); 833 if (t1 != 0) 834 return t1; 835 *v = NFSTOV(ttnp); 836 } 837 if (v3) { 838 t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos); 839 if (t1) 840 return t1; 841 if (*f) 842 *f = fxdr_unsigned(int, **tl); 843 else if (fxdr_unsigned(int, **tl)) 844 nfsm_adv_xx(NFSX_V3FATTR, tl, md, dpos); 845 } 846 if (*f) { 847 ttvp = *v; 848 t1 = nfs_loadattrcache(&ttvp, md, dpos, (struct vattr *)0, 0); 849 if (t1) 850 return t1; 851 *v = ttvp; 852 } 853 return 0; 854} 855 856int 857nfsm_getfh_xx(nfsfh_t **f, int *s, int v3, 858 u_int32_t **tl, struct mbuf **md, caddr_t *dpos) 859{ 860 int t1; 861 862 if (v3) { 863 t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos); 864 if (t1) 865 return t1; 866 *s = fxdr_unsigned(int, **tl); 867 if (*s <= 0 || *s > NFSX_V3FHMAX) { 868 return EBADRPC; 869 } 870 } else 871 *s = NFSX_V2FH; 872 t1 = nfsm_dissect_xx((void **)f, nfsm_rndup(*s), md, dpos); 873 return t1; 874} 875 876 877int 878nfsm_loadattr_xx(struct vnode **v, struct vattr *va, 879 u_int32_t **tl, struct mbuf **md, caddr_t *dpos) 880{ 881 int t1; 882 883 struct vnode *ttvp = *v; 884 t1 = nfs_loadattrcache(&ttvp, md, dpos, va, 0); 885 if (t1 != 0) 886 return t1; 887 *v = ttvp; 888 return 0; 889} 890 891int 892nfsm_postop_attr_xx(struct vnode **v, int *f, 893 u_int32_t **tl, struct mbuf **md, caddr_t *dpos) 894{ 895 int t1; 896 897 struct vnode *ttvp = *v; 898 t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos); 899 if (t1 != 0) 900 return t1; 901 *f = fxdr_unsigned(int, **tl); 902 if (*f != 0) { 903 t1 = nfs_loadattrcache(&ttvp, md, dpos, (struct vattr *)0, 1); 904 if (t1 != 0) { 905 *f = 0; 906 return t1; 907 } 908 *v = ttvp; 909 } 910 return 0; 911} 912 913int 914nfsm_wcc_data_xx(struct vnode **v, int *f, 915 u_int32_t **tl, struct mbuf **md, caddr_t *dpos) 916{ 917 int ttattrf, ttretf = 0; 918 int t1; 919 920 t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos); 921 if (t1 != 0) 922 return t1; 923 if (**tl == nfs_true) { 924 t1 = nfsm_dissect_xx((void **)tl, 6 * NFSX_UNSIGNED, md, dpos); 925 if (t1 != 0) 926 return t1; 927 if (*f) 928 ttretf = (VTONFS(*v)->n_mtime == 929 fxdr_unsigned(u_int32_t, *((*tl) + 2))); 930 } 931 t1 = nfsm_postop_attr_xx(v, &ttattrf, tl, md, dpos); 932 if (t1) 933 return t1; 934 if (*f) 935 *f = ttretf; 936 else 937 *f = ttattrf; 938 return 0; 939} 940 941int 942nfsm_strtom_xx(const char *a, int s, int m, 943 u_int32_t **tl, struct mbuf **mb, caddr_t *bpos) 944{ 945 int t1; 946 947 if (s > m) 948 return ENAMETOOLONG; 949 t1 = nfsm_rndup(s) + NFSX_UNSIGNED; 950 if (t1 <= M_TRAILINGSPACE(*mb)) { 951 *tl = nfsm_build_xx(t1, mb, bpos); 952 *(*tl)++ = txdr_unsigned(s); 953 *((*tl) + ((t1 >> 2) - 2)) = 0; 954 bcopy(a, *tl, s); 955 } else { 956 t1 = nfsm_strtmbuf(mb, bpos, a, s); 957 if (t1 != 0) 958 return t1; 959 } 960 return 0; 961} 962 963int 964nfsm_fhtom_xx(struct vnode *v, int v3, 965 u_int32_t **tl, struct mbuf **mb, caddr_t *bpos) 966{ 967 int t1; 968 caddr_t cp; 969 970 if (v3) { 971 t1 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED; 972 if (t1 < M_TRAILINGSPACE(*mb)) { 973 *tl = nfsm_build_xx(t1, mb, bpos); 974 *(*tl)++ = txdr_unsigned(VTONFS(v)->n_fhsize); 975 *((*tl) + ((t1 >> 2) - 2)) = 0; 976 bcopy(VTONFS(v)->n_fhp, *tl, VTONFS(v)->n_fhsize); 977 } else { 978 t1 = nfsm_strtmbuf(mb, bpos, 979 (const char *)VTONFS(v)->n_fhp, 980 VTONFS(v)->n_fhsize); 981 if (t1 != 0) 982 return t1; 983 } 984 } else { 985 cp = nfsm_build_xx(NFSX_V2FH, mb, bpos); 986 bcopy(VTONFS(v)->n_fhp, cp, NFSX_V2FH); 987 } 988 return 0; 989} 990 991void 992nfsm_v3attrbuild_xx(struct vattr *va, int full, 993 u_int32_t **tl, struct mbuf **mb, caddr_t *bpos) 994{ 995 996 if (va->va_mode != (mode_t)VNOVAL) { 997 *tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos); 998 *(*tl)++ = nfs_true; 999 **tl = txdr_unsigned(va->va_mode); 1000 } else { 1001 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1002 **tl = nfs_false; 1003 } 1004 if (full && va->va_uid != (uid_t)VNOVAL) { 1005 *tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos); 1006 *(*tl)++ = nfs_true; 1007 **tl = txdr_unsigned(va->va_uid); 1008 } else { 1009 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1010 **tl = nfs_false; 1011 } 1012 if (full && va->va_gid != (gid_t)VNOVAL) { 1013 *tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos); 1014 *(*tl)++ = nfs_true; 1015 **tl = txdr_unsigned(va->va_gid); 1016 } else { 1017 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1018 **tl = nfs_false; 1019 } 1020 if (full && va->va_size != VNOVAL) { 1021 *tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos); 1022 *(*tl)++ = nfs_true; 1023 txdr_hyper(va->va_size, *tl); 1024 } else { 1025 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1026 **tl = nfs_false; 1027 } 1028 if (va->va_atime.tv_sec != VNOVAL) { 1029 if (va->va_atime.tv_sec != time_second) { 1030 *tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos); 1031 *(*tl)++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); 1032 txdr_nfsv3time(&va->va_atime, *tl); 1033 } else { 1034 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1035 **tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); 1036 } 1037 } else { 1038 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1039 **tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); 1040 } 1041 if (va->va_mtime.tv_sec != VNOVAL) { 1042 if (va->va_mtime.tv_sec != time_second) { 1043 *tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos); 1044 *(*tl)++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); 1045 txdr_nfsv3time(&va->va_mtime, *tl); 1046 } else { 1047 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1048 **tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); 1049 } 1050 } else { 1051 *tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos); 1052 **tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); 1053 } 1054} 1055