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

Cache object: 44dc46db94eb0ae23d3e737af1209340


[ 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.