The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/nfsclient/nfs_subs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1989, 1993
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * This code is derived from software contributed to Berkeley by
    6  * Rick Macklem at The University of Guelph.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 4. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
   33  */
   34 
   35 #include <sys/cdefs.h>
   36 __FBSDID("$FreeBSD$");
   37 
   38 /*
   39  * These functions support the macros and help fiddle mbuf chains for
   40  * the nfs op functions. They do things like create the rpc header and
   41  * copy data between mbuf chains and uio lists.
   42  */
   43 
   44 #include <sys/param.h>
   45 #include <sys/systm.h>
   46 #include <sys/kernel.h>
   47 #include <sys/bio.h>
   48 #include <sys/buf.h>
   49 #include <sys/proc.h>
   50 #include <sys/mount.h>
   51 #include <sys/vnode.h>
   52 #include <sys/namei.h>
   53 #include <sys/mbuf.h>
   54 #include <sys/socket.h>
   55 #include <sys/stat.h>
   56 #include <sys/malloc.h>
   57 #include <sys/sysent.h>
   58 #include <sys/syscall.h>
   59 #include <sys/sysproto.h>
   60 
   61 #include <vm/vm.h>
   62 #include <vm/vm_object.h>
   63 #include <vm/vm_extern.h>
   64 #include <vm/uma.h>
   65 
   66 #include <rpc/rpcclnt.h>
   67 
   68 #include <nfs/rpcv2.h>
   69 #include <nfs/nfsproto.h>
   70 #include <nfsclient/nfs.h>
   71 #include <nfsclient/nfsnode.h>
   72 #include <nfs/xdr_subs.h>
   73 #include <nfsclient/nfsm_subs.h>
   74 #include <nfsclient/nfsmount.h>
   75 
   76 #include <netinet/in.h>
   77 
   78 /*
   79  * Data items converted to xdr at startup, since they are constant
   80  * This is kinda hokey, but may save a little time doing byte swaps
   81  */
   82 u_int32_t       nfs_xdrneg1;
   83 u_int32_t       rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
   84                     rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
   85 u_int32_t       nfs_true, nfs_false;
   86 
   87 /* And other global data */
   88 static u_int32_t nfs_xid = 0;
   89 static enum vtype nv2tov_type[8]= {
   90         VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON,  VNON
   91 };
   92 
   93 int             nfs_ticks;
   94 int             nfs_pbuf_freecnt = -1;  /* start out unlimited */
   95 
   96 struct nfs_reqq nfs_reqq;
   97 struct mtx nfs_reqq_mtx;
   98 struct mtx nfs_reply_mtx;
   99 struct nfs_bufq nfs_bufq;
  100 
  101 /*
  102  * and the reverse mapping from generic to Version 2 procedure numbers
  103  */
  104 int nfsv2_procid[NFS_NPROCS] = {
  105         NFSV2PROC_NULL,
  106         NFSV2PROC_GETATTR,
  107         NFSV2PROC_SETATTR,
  108         NFSV2PROC_LOOKUP,
  109         NFSV2PROC_NOOP,
  110         NFSV2PROC_READLINK,
  111         NFSV2PROC_READ,
  112         NFSV2PROC_WRITE,
  113         NFSV2PROC_CREATE,
  114         NFSV2PROC_MKDIR,
  115         NFSV2PROC_SYMLINK,
  116         NFSV2PROC_CREATE,
  117         NFSV2PROC_REMOVE,
  118         NFSV2PROC_RMDIR,
  119         NFSV2PROC_RENAME,
  120         NFSV2PROC_LINK,
  121         NFSV2PROC_READDIR,
  122         NFSV2PROC_NOOP,
  123         NFSV2PROC_STATFS,
  124         NFSV2PROC_NOOP,
  125         NFSV2PROC_NOOP,
  126         NFSV2PROC_NOOP,
  127         NFSV2PROC_NOOP,
  128 };
  129 
  130 LIST_HEAD(nfsnodehashhead, nfsnode);
  131 
  132 u_int32_t
  133 nfs_xid_gen(void)
  134 {
  135 
  136         /* Get a pretty random xid to start with */
  137         if (!nfs_xid)
  138                 nfs_xid = random();
  139         /*
  140          * Skip zero xid if it should ever happen.
  141          */
  142         if (++nfs_xid == 0)
  143                 nfs_xid++;
  144         return (nfs_xid);
  145 }
  146 
  147 /*
  148  * Create the header for an rpc request packet
  149  * The hsiz is the size of the rest of the nfs request header.
  150  * (just used to decide if a cluster is a good idea)
  151  */
  152 struct mbuf *
  153 nfsm_reqhead(struct vnode *vp, u_long procid, int hsiz)
  154 {
  155         struct mbuf *mb;
  156 
  157         MGET(mb, M_TRYWAIT, MT_DATA);
  158         if (hsiz >= MINCLSIZE)
  159                 MCLGET(mb, M_TRYWAIT);
  160         mb->m_len = 0;
  161         return (mb);
  162 }
  163 
  164 /*
  165  * Build the RPC header and fill in the authorization info.
  166  * The authorization string argument is only used when the credentials
  167  * come from outside of the kernel.
  168  * Returns the head of the mbuf list.
  169  */
  170 struct mbuf *
  171 nfsm_rpchead(struct ucred *cr, int nmflag, int procid, int auth_type,
  172     int auth_len, struct mbuf *mrest, int mrest_len, struct mbuf **mbp,
  173     u_int32_t **xidpp)
  174 {
  175         struct mbuf *mb;
  176         u_int32_t *tl;
  177         caddr_t bpos;
  178         int i;
  179         struct mbuf *mreq;
  180         int grpsiz, authsiz;
  181 
  182         authsiz = nfsm_rndup(auth_len);
  183         MGETHDR(mb, M_TRYWAIT, MT_DATA);
  184         if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) {
  185                 MCLGET(mb, M_TRYWAIT);
  186         } else if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) {
  187                 MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED);
  188         } else {
  189                 MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
  190         }
  191         mb->m_len = 0;
  192         mreq = mb;
  193         bpos = mtod(mb, caddr_t);
  194 
  195         /*
  196          * First the RPC header.
  197          */
  198         tl = nfsm_build(u_int32_t *, 8 * NFSX_UNSIGNED);
  199 
  200         *xidpp = tl;
  201         *tl++ = txdr_unsigned(nfs_xid_gen());
  202         *tl++ = rpc_call;
  203         *tl++ = rpc_vers;
  204         *tl++ = txdr_unsigned(NFS_PROG);
  205         if (nmflag & NFSMNT_NFSV3) {
  206                 *tl++ = txdr_unsigned(NFS_VER3);
  207                 *tl++ = txdr_unsigned(procid);
  208         } else {
  209                 *tl++ = txdr_unsigned(NFS_VER2);
  210                 *tl++ = txdr_unsigned(nfsv2_procid[procid]);
  211         }
  212 
  213         /*
  214          * And then the authorization cred.
  215          */
  216         *tl++ = txdr_unsigned(auth_type);
  217         *tl = txdr_unsigned(authsiz);
  218         switch (auth_type) {
  219         case RPCAUTH_UNIX:
  220                 tl = nfsm_build(u_int32_t *, auth_len);
  221                 *tl++ = 0;              /* stamp ?? */
  222                 *tl++ = 0;              /* NULL hostname */
  223                 *tl++ = txdr_unsigned(cr->cr_uid);
  224                 *tl++ = txdr_unsigned(cr->cr_groups[0]);
  225                 grpsiz = (auth_len >> 2) - 5;
  226                 *tl++ = txdr_unsigned(grpsiz);
  227                 for (i = 1; i <= grpsiz; i++)
  228                         *tl++ = txdr_unsigned(cr->cr_groups[i]);
  229                 break;
  230         }
  231 
  232         /*
  233          * And the verifier...
  234          */
  235         tl = nfsm_build(u_int32_t *, 2 * NFSX_UNSIGNED);
  236         *tl++ = txdr_unsigned(RPCAUTH_NULL);
  237         *tl = 0;
  238         mb->m_next = mrest;
  239         mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
  240         mreq->m_pkthdr.rcvif = NULL;
  241         *mbp = mb;
  242         return (mreq);
  243 }
  244 
  245 /*
  246  * copies a uio scatter/gather list to an mbuf chain.
  247  * NOTE: can ony handle iovcnt == 1
  248  */
  249 int
  250 nfsm_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos)
  251 {
  252         char *uiocp;
  253         struct mbuf *mp, *mp2;
  254         int xfer, left, mlen;
  255         int uiosiz, clflg, rem;
  256         char *cp;
  257 
  258 #ifdef DIAGNOSTIC
  259         if (uiop->uio_iovcnt != 1)
  260                 panic("nfsm_uiotombuf: iovcnt != 1");
  261 #endif
  262 
  263         if (siz > MLEN)         /* or should it >= MCLBYTES ?? */
  264                 clflg = 1;
  265         else
  266                 clflg = 0;
  267         rem = nfsm_rndup(siz)-siz;
  268         mp = mp2 = *mq;
  269         while (siz > 0) {
  270                 left = uiop->uio_iov->iov_len;
  271                 uiocp = uiop->uio_iov->iov_base;
  272                 if (left > siz)
  273                         left = siz;
  274                 uiosiz = left;
  275                 while (left > 0) {
  276                         mlen = M_TRAILINGSPACE(mp);
  277                         if (mlen == 0) {
  278                                 MGET(mp, M_TRYWAIT, MT_DATA);
  279                                 if (clflg)
  280                                         MCLGET(mp, M_TRYWAIT);
  281                                 mp->m_len = 0;
  282                                 mp2->m_next = mp;
  283                                 mp2 = mp;
  284                                 mlen = M_TRAILINGSPACE(mp);
  285                         }
  286                         xfer = (left > mlen) ? mlen : left;
  287 #ifdef notdef
  288                         /* Not Yet.. */
  289                         if (uiop->uio_iov->iov_op != NULL)
  290                                 (*(uiop->uio_iov->iov_op))
  291                                 (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
  292                         else
  293 #endif
  294                         if (uiop->uio_segflg == UIO_SYSSPACE)
  295                                 bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
  296                         else
  297                                 copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
  298                         mp->m_len += xfer;
  299                         left -= xfer;
  300                         uiocp += xfer;
  301                         uiop->uio_offset += xfer;
  302                         uiop->uio_resid -= xfer;
  303                 }
  304                 uiop->uio_iov->iov_base =
  305                     (char *)uiop->uio_iov->iov_base + uiosiz;
  306                 uiop->uio_iov->iov_len -= uiosiz;
  307                 siz -= uiosiz;
  308         }
  309         if (rem > 0) {
  310                 if (rem > M_TRAILINGSPACE(mp)) {
  311                         MGET(mp, M_TRYWAIT, MT_DATA);
  312                         mp->m_len = 0;
  313                         mp2->m_next = mp;
  314                 }
  315                 cp = mtod(mp, caddr_t)+mp->m_len;
  316                 for (left = 0; left < rem; left++)
  317                         *cp++ = '\0';
  318                 mp->m_len += rem;
  319                 *bpos = cp;
  320         } else
  321                 *bpos = mtod(mp, caddr_t)+mp->m_len;
  322         *mq = mp;
  323         return (0);
  324 }
  325 
  326 /*
  327  * Copy a string into mbufs for the hard cases...
  328  */
  329 int
  330 nfsm_strtmbuf(struct mbuf **mb, char **bpos, const char *cp, long siz)
  331 {
  332         struct mbuf *m1 = NULL, *m2;
  333         long left, xfer, len, tlen;
  334         u_int32_t *tl;
  335         int putsize;
  336 
  337         putsize = 1;
  338         m2 = *mb;
  339         left = M_TRAILINGSPACE(m2);
  340         if (left > 0) {
  341                 tl = ((u_int32_t *)(*bpos));
  342                 *tl++ = txdr_unsigned(siz);
  343                 putsize = 0;
  344                 left -= NFSX_UNSIGNED;
  345                 m2->m_len += NFSX_UNSIGNED;
  346                 if (left > 0) {
  347                         bcopy(cp, (caddr_t) tl, left);
  348                         siz -= left;
  349                         cp += left;
  350                         m2->m_len += left;
  351                         left = 0;
  352                 }
  353         }
  354         /* Loop around adding mbufs */
  355         while (siz > 0) {
  356                 MGET(m1, M_TRYWAIT, MT_DATA);
  357                 if (siz > MLEN)
  358                         MCLGET(m1, M_TRYWAIT);
  359                 m1->m_len = NFSMSIZ(m1);
  360                 m2->m_next = m1;
  361                 m2 = m1;
  362                 tl = mtod(m1, u_int32_t *);
  363                 tlen = 0;
  364                 if (putsize) {
  365                         *tl++ = txdr_unsigned(siz);
  366                         m1->m_len -= NFSX_UNSIGNED;
  367                         tlen = NFSX_UNSIGNED;
  368                         putsize = 0;
  369                 }
  370                 if (siz < m1->m_len) {
  371                         len = nfsm_rndup(siz);
  372                         xfer = siz;
  373                         if (xfer < len)
  374                                 *(tl+(xfer>>2)) = 0;
  375                 } else {
  376                         xfer = len = m1->m_len;
  377                 }
  378                 bcopy(cp, (caddr_t) tl, xfer);
  379                 m1->m_len = len+tlen;
  380                 siz -= xfer;
  381                 cp += xfer;
  382         }
  383         *mb = m1;
  384         *bpos = mtod(m1, caddr_t)+m1->m_len;
  385         return (0);
  386 }
  387 
  388 /*
  389  * Called once to initialize data structures...
  390  */
  391 int
  392 nfs_init(struct vfsconf *vfsp)
  393 {
  394         int i;
  395 
  396         nfsmount_zone = uma_zcreate("NFSMOUNT", sizeof(struct nfsmount),
  397             NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
  398         rpc_vers = txdr_unsigned(RPC_VER2);
  399         rpc_call = txdr_unsigned(RPC_CALL);
  400         rpc_reply = txdr_unsigned(RPC_REPLY);
  401         rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
  402         rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
  403         rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
  404         rpc_autherr = txdr_unsigned(RPC_AUTHERR);
  405         rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
  406         nfs_true = txdr_unsigned(TRUE);
  407         nfs_false = txdr_unsigned(FALSE);
  408         nfs_xdrneg1 = txdr_unsigned(-1);
  409         nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
  410         if (nfs_ticks < 1)
  411                 nfs_ticks = 1;
  412         /* Ensure async daemons disabled */
  413         for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
  414                 nfs_iodwant[i] = NULL;
  415                 nfs_iodmount[i] = NULL;
  416         }
  417         nfs_nhinit();                   /* Init the nfsnode table */
  418 
  419         /*
  420          * Initialize reply list and start timer
  421          */
  422         TAILQ_INIT(&nfs_reqq);
  423         callout_init(&nfs_callout, 0);
  424         mtx_init(&nfs_reqq_mtx, "NFS reqq lock", NULL, MTX_DEF);
  425         mtx_init(&nfs_reply_mtx, "Synch NFS reply posting", NULL, MTX_DEF);
  426 
  427         nfs_pbuf_freecnt = nswbuf / 2 + 1;
  428 
  429         return (0);
  430 }
  431 
  432 int
  433 nfs_uninit(struct vfsconf *vfsp)
  434 {
  435         int i;
  436 
  437         callout_stop(&nfs_callout);
  438 
  439         KASSERT(TAILQ_EMPTY(&nfs_reqq),
  440             ("nfs_uninit: request queue not empty"));
  441 
  442         /*
  443          * Tell all nfsiod processes to exit. Clear nfs_iodmax, and wakeup
  444          * any sleeping nfsiods so they check nfs_iodmax and exit.
  445          */
  446         nfs_iodmax = 0;
  447         for (i = 0; i < nfs_numasync; i++)
  448                 if (nfs_iodwant[i])
  449                         wakeup(&nfs_iodwant[i]);
  450         /* The last nfsiod to exit will wake us up when nfs_numasync hits 0 */
  451         while (nfs_numasync)
  452                 tsleep(&nfs_numasync, PWAIT, "ioddie", 0);
  453 
  454         nfs_nhuninit();
  455         uma_zdestroy(nfsmount_zone);
  456         return (0);
  457 }
  458 
  459 /*
  460  * Attribute cache routines.
  461  * nfs_loadattrcache() - loads or updates the cache contents from attributes
  462  *      that are on the mbuf list
  463  * nfs_getattrcache() - returns valid attributes if found in cache, returns
  464  *      error otherwise
  465  */
  466 
  467 /*
  468  * Load the attribute cache (that lives in the nfsnode entry) with
  469  * the values on the mbuf list and
  470  * Iff vap not NULL
  471  *    copy the attributes to *vaper
  472  */
  473 int
  474 nfs_loadattrcache(struct vnode **vpp, struct mbuf **mdp, caddr_t *dposp,
  475     struct vattr *vaper, int dontshrink)
  476 {
  477         struct vnode *vp = *vpp;
  478         struct vattr *vap;
  479         struct nfs_fattr *fp;
  480         struct nfsnode *np;
  481         int32_t t1;
  482         caddr_t cp2;
  483         int rdev;
  484         struct mbuf *md;
  485         enum vtype vtyp;
  486         u_short vmode;
  487         struct timespec mtime, mtime_save;
  488         int v3 = NFS_ISV3(vp);
  489 
  490         md = *mdp;
  491         t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
  492         cp2 = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, M_TRYWAIT);
  493         if (cp2 == NULL)
  494                 return EBADRPC;
  495         fp = (struct nfs_fattr *)cp2;
  496         if (v3) {
  497                 vtyp = nfsv3tov_type(fp->fa_type);
  498                 vmode = fxdr_unsigned(u_short, fp->fa_mode);
  499                 rdev = makedev(fxdr_unsigned(int, fp->fa3_rdev.specdata1),
  500                         fxdr_unsigned(int, fp->fa3_rdev.specdata2));
  501                 fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
  502         } else {
  503                 vtyp = nfsv2tov_type(fp->fa_type);
  504                 vmode = fxdr_unsigned(u_short, fp->fa_mode);
  505                 /*
  506                  * XXX
  507                  *
  508                  * The duplicate information returned in fa_type and fa_mode
  509                  * is an ambiguity in the NFS version 2 protocol.
  510                  *
  511                  * VREG should be taken literally as a regular file.  If a
  512                  * server intents to return some type information differently
  513                  * in the upper bits of the mode field (e.g. for sockets, or
  514                  * FIFOs), NFSv2 mandates fa_type to be VNON.  Anyway, we
  515                  * leave the examination of the mode bits even in the VREG
  516                  * case to avoid breakage for bogus servers, but we make sure
  517                  * that there are actually type bits set in the upper part of
  518                  * fa_mode (and failing that, trust the va_type field).
  519                  *
  520                  * NFSv3 cleared the issue, and requires fa_mode to not
  521                  * contain any type information (while also introduing sockets
  522                  * and FIFOs for fa_type).
  523                  */
  524                 if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0))
  525                         vtyp = IFTOVT(vmode);
  526                 rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
  527                 fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
  528 
  529                 /*
  530                  * Really ugly NFSv2 kludge.
  531                  */
  532                 if (vtyp == VCHR && rdev == 0xffffffff)
  533                         vtyp = VFIFO;
  534         }
  535 
  536         /*
  537          * If v_type == VNON it is a new node, so fill in the v_type,
  538          * n_mtime fields. Check to see if it represents a special
  539          * device, and if so, check for a possible alias. Once the
  540          * correct vnode has been obtained, fill in the rest of the
  541          * information.
  542          */
  543         np = VTONFS(vp);
  544         if (vp->v_type != vtyp) {
  545                 vp->v_type = vtyp;
  546                 if (vp->v_type == VFIFO)
  547                         vp->v_op = &nfs_fifoops;
  548                 np->n_mtime = mtime;
  549         }
  550         vap = &np->n_vattr;
  551         vap->va_type = vtyp;
  552         vap->va_mode = (vmode & 07777);
  553         vap->va_rdev = rdev;
  554         mtime_save = vap->va_mtime;
  555         vap->va_mtime = mtime;
  556         vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
  557         if (v3) {
  558                 vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
  559                 vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
  560                 vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
  561                 vap->va_size = fxdr_hyper(&fp->fa3_size);
  562                 vap->va_blocksize = NFS_FABLKSIZE;
  563                 vap->va_bytes = fxdr_hyper(&fp->fa3_used);
  564                 vap->va_fileid = fxdr_unsigned(int32_t,
  565                     fp->fa3_fileid.nfsuquad[1]);
  566                 fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
  567                 fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
  568                 vap->va_flags = 0;
  569                 vap->va_filerev = 0;
  570         } else {
  571                 vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
  572                 vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
  573                 vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
  574                 vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
  575                 vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
  576                 vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks)
  577                     * NFS_FABLKSIZE;
  578                 vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
  579                 fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
  580                 vap->va_flags = 0;
  581                 vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
  582                     fp->fa2_ctime.nfsv2_sec);
  583                 vap->va_ctime.tv_nsec = 0;
  584                 vap->va_gen = fxdr_unsigned(u_int32_t, fp->fa2_ctime.nfsv2_usec);
  585                 vap->va_filerev = 0;
  586         }
  587         np->n_attrstamp = time_second;
  588         if (vap->va_size != np->n_size) {
  589                 if (vap->va_type == VREG) {
  590                         if (dontshrink && vap->va_size < np->n_size) {
  591                                 /*
  592                                  * We've been told not to shrink the file;
  593                                  * zero np->n_attrstamp to indicate that
  594                                  * the attributes are stale.
  595                                  */
  596                                 vap->va_size = np->n_size;
  597                                 np->n_attrstamp = 0;
  598                         } else if (np->n_flag & NMODIFIED) {
  599                                 /*
  600                                  * We've modified the file: Use the larger
  601                                  * of our size, and the server's size.
  602                                  */
  603                                 if (vap->va_size < np->n_size) {
  604                                         vap->va_size = np->n_size;
  605                                 } else {
  606                                         np->n_size = vap->va_size;
  607                                         np->n_flag |= NSIZECHANGED;
  608                                 }
  609                         } else {
  610                                 np->n_size = vap->va_size;
  611                                 np->n_flag |= NSIZECHANGED;
  612                         }
  613                         vnode_pager_setsize(vp, np->n_size);
  614                 } else {
  615                         np->n_size = vap->va_size;
  616                 }
  617         }
  618         /*
  619          * The following checks are added to prevent a race between (say)
  620          * a READDIR+ and a WRITE. 
  621          * READDIR+, WRITE requests sent out.
  622          * READDIR+ resp, WRITE resp received on client.
  623          * However, the WRITE resp was handled before the READDIR+ resp
  624          * causing the post op attrs from the write to be loaded first
  625          * and the attrs from the READDIR+ to be loaded later. If this 
  626          * happens, we have stale attrs loaded into the attrcache.
  627          * We detect this by for the mtime moving back. We invalidate the 
  628          * attrcache when this happens.
  629          */
  630         if (timespeccmp(&mtime_save, &vap->va_mtime, >))
  631                 /* Size changed or mtime went backwards */
  632                 np->n_attrstamp = 0;
  633         if (vaper != NULL) {
  634                 bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
  635                 if (np->n_flag & NCHG) {
  636                         if (np->n_flag & NACC)
  637                                 vaper->va_atime = np->n_atim;
  638                         if (np->n_flag & NUPD)
  639                                 vaper->va_mtime = np->n_mtim;
  640                 }
  641         }
  642         return (0);
  643 }
  644 
  645 #ifdef NFS_ACDEBUG
  646 #include <sys/sysctl.h>
  647 SYSCTL_DECL(_vfs_nfs);
  648 static int nfs_acdebug;
  649 SYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
  650 #endif
  651 
  652 /*
  653  * Check the time stamp
  654  * If the cache is valid, copy contents to *vap and return 0
  655  * otherwise return an error
  656  */
  657 int
  658 nfs_getattrcache(struct vnode *vp, struct vattr *vaper)
  659 {
  660         struct nfsnode *np;
  661         struct vattr *vap;
  662         struct nfsmount *nmp;
  663         int timeo;
  664 
  665         np = VTONFS(vp);
  666         vap = &np->n_vattr;
  667         nmp = VFSTONFS(vp->v_mount);
  668         /* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
  669         timeo = (time_second - np->n_mtime.tv_sec) / 10;
  670 
  671 #ifdef NFS_ACDEBUG
  672         if (nfs_acdebug>1)
  673                 printf("nfs_getattrcache: initial timeo = %d\n", timeo);
  674 #endif
  675 
  676         if (vap->va_type == VDIR) {
  677                 if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
  678                         timeo = nmp->nm_acdirmin;
  679                 else if (timeo > nmp->nm_acdirmax)
  680                         timeo = nmp->nm_acdirmax;
  681         } else {
  682                 if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
  683                         timeo = nmp->nm_acregmin;
  684                 else if (timeo > nmp->nm_acregmax)
  685                         timeo = nmp->nm_acregmax;
  686         }
  687 
  688 #ifdef NFS_ACDEBUG
  689         if (nfs_acdebug > 2)
  690                 printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
  691                         nmp->nm_acregmin, nmp->nm_acregmax,
  692                         nmp->nm_acdirmin, nmp->nm_acdirmax);
  693 
  694         if (nfs_acdebug)
  695                 printf("nfs_getattrcache: age = %d; final timeo = %d\n",
  696                         (time_second - np->n_attrstamp), timeo);
  697 #endif
  698 
  699         if ((time_second - np->n_attrstamp) >= timeo) {
  700                 nfsstats.attrcache_misses++;
  701                 return (ENOENT);
  702         }
  703         nfsstats.attrcache_hits++;
  704         if (vap->va_size != np->n_size) {
  705                 if (vap->va_type == VREG) {
  706                         if (np->n_flag & NMODIFIED) {
  707                                 if (vap->va_size < np->n_size)
  708                                         vap->va_size = np->n_size;
  709                                 else
  710                                         np->n_size = vap->va_size;
  711                         } else {
  712                                 np->n_size = vap->va_size;
  713                         }
  714                         vnode_pager_setsize(vp, np->n_size);
  715                 } else {
  716                         np->n_size = vap->va_size;
  717                 }
  718         }
  719         bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
  720         if (np->n_flag & NCHG) {
  721                 if (np->n_flag & NACC)
  722                         vaper->va_atime = np->n_atim;
  723                 if (np->n_flag & NUPD)
  724                         vaper->va_mtime = np->n_mtim;
  725         }
  726         return (0);
  727 }
  728 
  729 static nfsuint64 nfs_nullcookie = { { 0, 0 } };
  730 /*
  731  * This function finds the directory cookie that corresponds to the
  732  * logical byte offset given.
  733  */
  734 nfsuint64 *
  735 nfs_getcookie(struct nfsnode *np, off_t off, int add)
  736 {
  737         struct nfsdmap *dp, *dp2;
  738         int pos;
  739 
  740         pos = (uoff_t)off / NFS_DIRBLKSIZ;
  741         if (pos == 0 || off < 0) {
  742 #ifdef DIAGNOSTIC
  743                 if (add)
  744                         panic("nfs getcookie add at <= 0");
  745 #endif
  746                 return (&nfs_nullcookie);
  747         }
  748         pos--;
  749         dp = LIST_FIRST(&np->n_cookies);
  750         if (!dp) {
  751                 if (add) {
  752                         MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
  753                                 M_NFSDIROFF, M_WAITOK);
  754                         dp->ndm_eocookie = 0;
  755                         LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
  756                 } else
  757                         return (NULL);
  758         }
  759         while (pos >= NFSNUMCOOKIES) {
  760                 pos -= NFSNUMCOOKIES;
  761                 if (LIST_NEXT(dp, ndm_list)) {
  762                         if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
  763                                 pos >= dp->ndm_eocookie)
  764                                 return (NULL);
  765                         dp = LIST_NEXT(dp, ndm_list);
  766                 } else if (add) {
  767                         MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
  768                                 M_NFSDIROFF, M_WAITOK);
  769                         dp2->ndm_eocookie = 0;
  770                         LIST_INSERT_AFTER(dp, dp2, ndm_list);
  771                         dp = dp2;
  772                 } else
  773                         return (NULL);
  774         }
  775         if (pos >= dp->ndm_eocookie) {
  776                 if (add)
  777                         dp->ndm_eocookie = pos + 1;
  778                 else
  779                         return (NULL);
  780         }
  781         return (&dp->ndm_cookies[pos]);
  782 }
  783 
  784 /*
  785  * Invalidate cached directory information, except for the actual directory
  786  * blocks (which are invalidated separately).
  787  * Done mainly to avoid the use of stale offset cookies.
  788  */
  789 void
  790 nfs_invaldir(struct vnode *vp)
  791 {
  792         struct nfsnode *np = VTONFS(vp);
  793 
  794 #ifdef DIAGNOSTIC
  795         if (vp->v_type != VDIR)
  796                 panic("nfs: invaldir not dir");
  797 #endif
  798         np->n_direofoffset = 0;
  799         np->n_cookieverf.nfsuquad[0] = 0;
  800         np->n_cookieverf.nfsuquad[1] = 0;
  801         if (LIST_FIRST(&np->n_cookies))
  802                 LIST_FIRST(&np->n_cookies)->ndm_eocookie = 0;
  803 }
  804 
  805 /*
  806  * The write verifier has changed (probably due to a server reboot), so all
  807  * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
  808  * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
  809  * and B_CLUSTEROK flags.  Once done the new write verifier can be set for the
  810  * mount point.
  811  *
  812  * B_CLUSTEROK must be cleared along with B_NEEDCOMMIT because stage 1 data
  813  * writes are not clusterable.
  814  */
  815 void
  816 nfs_clearcommit(struct mount *mp)
  817 {
  818         struct vnode *vp, *nvp;
  819         struct buf *bp, *nbp;
  820         int s;
  821 
  822         GIANT_REQUIRED;
  823 
  824         s = splbio();
  825         MNT_ILOCK(mp);
  826         MNT_VNODE_FOREACH(vp, mp, nvp) {
  827                 VI_LOCK(vp);
  828                 if (vp->v_iflag & VI_DOOMED) {
  829                         VI_UNLOCK(vp);
  830                         continue;
  831                 }
  832                 MNT_IUNLOCK(mp);
  833                 TAILQ_FOREACH_SAFE(bp, &vp->v_bufobj.bo_dirty.bv_hd, b_bobufs, nbp) {
  834                         if (BUF_REFCNT(bp) == 0 &&
  835                             (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
  836                                 == (B_DELWRI | B_NEEDCOMMIT))
  837                                 bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
  838                 }
  839                 VI_UNLOCK(vp);
  840                 MNT_ILOCK(mp);
  841         }
  842         MNT_IUNLOCK(mp);
  843         splx(s);
  844 }
  845 
  846 /*
  847  * Helper functions for former macros.  Some of these should be
  848  * moved to their callers.
  849  */
  850 
  851 int
  852 nfsm_mtofh_xx(struct vnode *d, struct vnode **v, int v3, int *f,
  853     struct mbuf **md, caddr_t *dpos)
  854 {
  855         struct nfsnode *ttnp;
  856         struct vnode *ttvp;
  857         nfsfh_t *ttfhp;
  858         u_int32_t *tl;
  859         int ttfhsize;
  860         int t1;
  861 
  862         if (v3) {
  863                 tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
  864                 if (tl == NULL)
  865                         return EBADRPC;
  866                 *f = fxdr_unsigned(int, *tl);
  867         } else
  868                 *f = 1;
  869         if (*f) {
  870                 t1 = nfsm_getfh_xx(&ttfhp, &ttfhsize, (v3), md, dpos);
  871                 if (t1 != 0)
  872                         return t1;
  873                 t1 = nfs_nget(d->v_mount, ttfhp, ttfhsize, &ttnp, LK_EXCLUSIVE);
  874                 if (t1 != 0)
  875                         return t1;
  876                 *v = NFSTOV(ttnp);
  877         }
  878         if (v3) {
  879                 tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
  880                 if (tl == NULL)
  881                         return EBADRPC;
  882                 if (*f)
  883                         *f = fxdr_unsigned(int, *tl);
  884                 else if (fxdr_unsigned(int, *tl))
  885                         nfsm_adv_xx(NFSX_V3FATTR, md, dpos);
  886         }
  887         if (*f) {
  888                 ttvp = *v;
  889                 t1 = nfs_loadattrcache(&ttvp, md, dpos, NULL, 0);
  890                 if (t1)
  891                         return t1;
  892                 *v = ttvp;
  893         }
  894         return 0;
  895 }
  896 
  897 int
  898 nfsm_getfh_xx(nfsfh_t **f, int *s, int v3, struct mbuf **md, caddr_t *dpos)
  899 {
  900         u_int32_t *tl;
  901 
  902         if (v3) {
  903                 tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
  904                 if (tl == NULL)
  905                         return EBADRPC;
  906                 *s = fxdr_unsigned(int, *tl);
  907                 if (*s <= 0 || *s > NFSX_V3FHMAX)
  908                         return EBADRPC;
  909         } else
  910                 *s = NFSX_V2FH;
  911         *f = nfsm_dissect_xx(nfsm_rndup(*s), md, dpos);
  912         if (*f == NULL)
  913                 return EBADRPC;
  914         else
  915                 return 0;
  916 }
  917 
  918 
  919 int
  920 nfsm_loadattr_xx(struct vnode **v, struct vattr *va, struct mbuf **md,
  921     caddr_t *dpos)
  922 {
  923         int t1;
  924 
  925         struct vnode *ttvp = *v;
  926         t1 = nfs_loadattrcache(&ttvp, md, dpos, va, 0);
  927         if (t1 != 0)
  928                 return t1;
  929         *v = ttvp;
  930         return 0;
  931 }
  932 
  933 int
  934 nfsm_postop_attr_xx(struct vnode **v, int *f, struct mbuf **md,
  935     caddr_t *dpos)
  936 {
  937         u_int32_t *tl;
  938         int t1;
  939 
  940         struct vnode *ttvp = *v;
  941         tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
  942         if (tl == NULL)
  943                 return EBADRPC;
  944         *f = fxdr_unsigned(int, *tl);
  945         if (*f != 0) {
  946                 t1 = nfs_loadattrcache(&ttvp, md, dpos, NULL, 1);
  947                 if (t1 != 0) {
  948                         *f = 0;
  949                         return t1;
  950                 }
  951                 *v = ttvp;
  952         }
  953         return 0;
  954 }
  955 
  956 int
  957 nfsm_wcc_data_xx(struct vnode **v, int *f, struct mbuf **md, caddr_t *dpos)
  958 {
  959         u_int32_t *tl;
  960         int ttattrf, ttretf = 0;
  961         int t1;
  962 
  963         tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
  964         if (tl == NULL)
  965                 return EBADRPC;
  966         if (*tl == nfs_true) {
  967                 tl = nfsm_dissect_xx(6 * NFSX_UNSIGNED, md, dpos);
  968                 if (tl == NULL)
  969                         return EBADRPC;
  970                 if (*f)
  971                         ttretf = (VTONFS(*v)->n_mtime.tv_sec == fxdr_unsigned(u_int32_t, *(tl + 2)) && 
  972                                   VTONFS(*v)->n_mtime.tv_nsec == fxdr_unsigned(u_int32_t, *(tl + 3))); 
  973         }
  974         t1 = nfsm_postop_attr_xx(v, &ttattrf, md, dpos);
  975         if (t1)
  976                 return t1;
  977         if (*f)
  978                 *f = ttretf;
  979         else
  980                 *f = ttattrf;
  981         return 0;
  982 }
  983 
  984 int
  985 nfsm_strtom_xx(const char *a, int s, int m, struct mbuf **mb, caddr_t *bpos)
  986 {
  987         u_int32_t *tl;
  988         int t1;
  989 
  990         if (s > m)
  991                 return ENAMETOOLONG;
  992         t1 = nfsm_rndup(s) + NFSX_UNSIGNED;
  993         if (t1 <= M_TRAILINGSPACE(*mb)) {
  994                 tl = nfsm_build_xx(t1, mb, bpos);
  995                 *tl++ = txdr_unsigned(s);
  996                 *(tl + ((t1 >> 2) - 2)) = 0;
  997                 bcopy(a, tl, s);
  998         } else {
  999                 t1 = nfsm_strtmbuf(mb, bpos, a, s);
 1000                 if (t1 != 0)
 1001                         return t1;
 1002         }
 1003         return 0;
 1004 }
 1005 
 1006 int
 1007 nfsm_fhtom_xx(struct vnode *v, int v3, struct mbuf **mb, caddr_t *bpos)
 1008 {
 1009         u_int32_t *tl;
 1010         int t1;
 1011         caddr_t cp;
 1012 
 1013         if (v3) {
 1014                 t1 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED;
 1015                 if (t1 < M_TRAILINGSPACE(*mb)) {
 1016                         tl = nfsm_build_xx(t1, mb, bpos);
 1017                         *tl++ = txdr_unsigned(VTONFS(v)->n_fhsize);
 1018                         *(tl + ((t1 >> 2) - 2)) = 0;
 1019                         bcopy(VTONFS(v)->n_fhp, tl, VTONFS(v)->n_fhsize);
 1020                 } else {
 1021                         t1 = nfsm_strtmbuf(mb, bpos,
 1022                             (const char *)VTONFS(v)->n_fhp,
 1023                             VTONFS(v)->n_fhsize);
 1024                         if (t1 != 0)
 1025                                 return t1;
 1026                 }
 1027         } else {
 1028                 cp = nfsm_build_xx(NFSX_V2FH, mb, bpos);
 1029                 bcopy(VTONFS(v)->n_fhp, cp, NFSX_V2FH);
 1030         }
 1031         return 0;
 1032 }
 1033 
 1034 void
 1035 nfsm_v3attrbuild_xx(struct vattr *va, int full, struct mbuf **mb,
 1036     caddr_t *bpos)
 1037 {
 1038         u_int32_t *tl;
 1039 
 1040         if (va->va_mode != (mode_t)VNOVAL) {
 1041                 tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
 1042                 *tl++ = nfs_true;
 1043                 *tl = txdr_unsigned(va->va_mode);
 1044         } else {
 1045                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1046                 *tl = nfs_false;
 1047         }
 1048         if (full && va->va_uid != (uid_t)VNOVAL) {
 1049                 tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
 1050                 *tl++ = nfs_true;
 1051                 *tl = txdr_unsigned(va->va_uid);
 1052         } else {
 1053                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1054                 *tl = nfs_false;
 1055         }
 1056         if (full && va->va_gid != (gid_t)VNOVAL) {
 1057                 tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
 1058                 *tl++ = nfs_true;
 1059                 *tl = txdr_unsigned(va->va_gid);
 1060         } else {
 1061                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1062                 *tl = nfs_false;
 1063         }
 1064         if (full && va->va_size != VNOVAL) {
 1065                 tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
 1066                 *tl++ = nfs_true;
 1067                 txdr_hyper(va->va_size, tl);
 1068         } else {
 1069                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1070                 *tl = nfs_false;
 1071         }
 1072         if (va->va_atime.tv_sec != VNOVAL) {
 1073                 if (va->va_atime.tv_sec != time_second) {
 1074                         tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
 1075                         *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
 1076                         txdr_nfsv3time(&va->va_atime, tl);
 1077                 } else {
 1078                         tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1079                         *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
 1080                 }
 1081         } else {
 1082                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1083                 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
 1084         }
 1085         if (va->va_mtime.tv_sec != VNOVAL) {
 1086                 if (va->va_mtime.tv_sec != time_second) {
 1087                         tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
 1088                         *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
 1089                         txdr_nfsv3time(&va->va_mtime, tl);
 1090                 } else {
 1091                         tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1092                         *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
 1093                 }
 1094         } else {
 1095                 tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
 1096                 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
 1097         }
 1098 }

Cache object: 38725c4f48d3ab2fbebdbbf7f1b00c3f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.