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/fs/fuse/fuse_node.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  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (c) 2007-2009 Google Inc. and Amit Singh
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions are
    9  * met:
   10  *
   11  * * Redistributions of source code must retain the above copyright
   12  *   notice, this list of conditions and the following disclaimer.
   13  * * Redistributions in binary form must reproduce the above
   14  *   copyright notice, this list of conditions and the following disclaimer
   15  *   in the documentation and/or other materials provided with the
   16  *   distribution.
   17  * * Neither the name of Google Inc. nor the names of its
   18  *   contributors may be used to endorse or promote products derived from
   19  *   this software without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   32  *
   33  * Copyright (C) 2005 Csaba Henk.
   34  * All rights reserved.
   35  *
   36  * Copyright (c) 2019 The FreeBSD Foundation
   37  *
   38  * Portions of this software were developed by BFF Storage Systems, LLC under
   39  * sponsorship from the FreeBSD Foundation.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  *
   50  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   53  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   60  * SUCH DAMAGE.
   61  */
   62 
   63 #include <sys/cdefs.h>
   64 __FBSDID("$FreeBSD$");
   65 
   66 #include <sys/types.h>
   67 #include <sys/systm.h>
   68 #include <sys/counter.h>
   69 #include <sys/module.h>
   70 #include <sys/errno.h>
   71 #include <sys/param.h>
   72 #include <sys/kernel.h>
   73 #include <sys/conf.h>
   74 #include <sys/uio.h>
   75 #include <sys/malloc.h>
   76 #include <sys/queue.h>
   77 #include <sys/lock.h>
   78 #include <sys/sx.h>
   79 #include <sys/mutex.h>
   80 #include <sys/proc.h>
   81 #include <sys/vnode.h>
   82 #include <sys/namei.h>
   83 #include <sys/mount.h>
   84 #include <sys/sysctl.h>
   85 #include <sys/fcntl.h>
   86 #include <sys/priv.h>
   87 #include <sys/buf.h>
   88 #include <security/mac/mac_framework.h>
   89 #include <vm/vm.h>
   90 #include <vm/vm_extern.h>
   91 
   92 #include "fuse.h"
   93 #include "fuse_node.h"
   94 #include "fuse_internal.h"
   95 #include "fuse_io.h"
   96 #include "fuse_ipc.h"
   97 
   98 SDT_PROVIDER_DECLARE(fusefs);
   99 /* 
  100  * Fuse trace probe:
  101  * arg0: verbosity.  Higher numbers give more verbose messages
  102  * arg1: Textual message
  103  */
  104 SDT_PROBE_DEFINE2(fusefs, , node, trace, "int", "char*");
  105 
  106 MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data");
  107 
  108 static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS);
  109 
  110 static counter_u64_t fuse_node_count;
  111 
  112 SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, node_count, CTLFLAG_RD,
  113     &fuse_node_count, "Count of FUSE vnodes");
  114 
  115 int     fuse_data_cache_mode = FUSE_CACHE_WT;
  116 
  117 /*
  118  * DEPRECATED
  119  * This sysctl is no longer needed as of fuse protocol 7.23.  Individual
  120  * servers can select the cache behavior they need for each mountpoint:
  121  * - writethrough: the default
  122  * - writeback: set FUSE_WRITEBACK_CACHE in fuse_init_out.flags
  123  * - uncached: set FOPEN_DIRECT_IO for every file
  124  * The sysctl is retained primarily for use by jails supporting older FUSE
  125  * protocols.  It may be removed entirely once FreeBSD 11.3 and 12.0 are EOL.
  126  */
  127 SYSCTL_PROC(_vfs_fusefs, OID_AUTO, data_cache_mode,
  128     CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW,
  129     &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I",
  130     "Zero: disable caching of FUSE file data; One: write-through caching "
  131     "(default); Two: write-back caching (generally unsafe)");
  132 
  133 static int
  134 sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS)
  135 {
  136         int val, error;
  137 
  138         val = *(int *)arg1;
  139         error = sysctl_handle_int(oidp, &val, 0, req);
  140         if (error || !req->newptr)
  141                 return (error);
  142 
  143         switch (val) {
  144         case FUSE_CACHE_UC:
  145         case FUSE_CACHE_WT:
  146         case FUSE_CACHE_WB:
  147                 *(int *)arg1 = val;
  148                 break;
  149         default:
  150                 return (EDOM);
  151         }
  152         return (0);
  153 }
  154 
  155 static void
  156 fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
  157     uint64_t nodeid, enum vtype vtyp)
  158 {
  159         fvdat->nid = nodeid;
  160         LIST_INIT(&fvdat->handles);
  161         vattr_null(&fvdat->cached_attrs);
  162         if (nodeid == FUSE_ROOT_ID) {
  163                 vp->v_vflag |= VV_ROOT;
  164         }
  165         vp->v_type = vtyp;
  166         vp->v_data = fvdat;
  167         cluster_init_vn(&fvdat->clusterw);
  168         timespecclear(&fvdat->last_local_modify);
  169 
  170         counter_u64_add(fuse_node_count, 1);
  171 }
  172 
  173 void
  174 fuse_vnode_destroy(struct vnode *vp)
  175 {
  176         struct fuse_vnode_data *fvdat = vp->v_data;
  177 
  178         vp->v_data = NULL;
  179         KASSERT(LIST_EMPTY(&fvdat->handles),
  180                 ("Destroying fuse vnode with open files!"));
  181         free(fvdat, M_FUSEVN);
  182 
  183         counter_u64_add(fuse_node_count, -1);
  184 }
  185 
  186 int
  187 fuse_vnode_cmp(struct vnode *vp, void *nidp)
  188 {
  189         return (VTOI(vp) != *((uint64_t *)nidp));
  190 }
  191 
  192 SDT_PROBE_DEFINE3(fusefs, , node, stale_vnode, "struct vnode*", "enum vtype",
  193                 "uint64_t");
  194 static int
  195 fuse_vnode_alloc(struct mount *mp,
  196     struct thread *td,
  197     uint64_t nodeid,
  198     enum vtype vtyp,
  199     struct vnode **vpp)
  200 {
  201         struct fuse_data *data;
  202         struct fuse_vnode_data *fvdat;
  203         struct vnode *vp2;
  204         int err = 0;
  205 
  206         data = fuse_get_mpdata(mp);
  207         if (vtyp == VNON) {
  208                 return EINVAL;
  209         }
  210         *vpp = NULL;
  211         err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,
  212             fuse_vnode_cmp, &nodeid);
  213         if (err)
  214                 return (err);
  215 
  216         if (*vpp) {
  217                 if ((*vpp)->v_type == vtyp) {
  218                         /* Reuse a vnode that hasn't yet been reclaimed */
  219                         MPASS((*vpp)->v_data != NULL);
  220                         MPASS(VTOFUD(*vpp)->nid == nodeid);
  221                         SDT_PROBE2(fusefs, , node, trace, 1,
  222                                 "vnode taken from hash");
  223                         return (0);
  224                 } else {
  225                         /*
  226                          * The inode changed types!  If we get here, we can't
  227                          * tell whether the inode's entry cache had expired
  228                          * yet.  So this could be the result of a buggy server,
  229                          * but more likely the server just reused an inode
  230                          * number following an entry cache expiration.
  231                          */
  232                         SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp,
  233                                 nodeid);
  234                         fuse_internal_vnode_disappear(*vpp);
  235                         vgone(*vpp);
  236                         lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  237                 }
  238         }
  239         fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
  240         switch (vtyp) {
  241         case VFIFO:
  242                 err = getnewvnode("fuse", mp, &fuse_fifoops, vpp);
  243                 break;
  244         default:
  245                 err = getnewvnode("fuse", mp, &fuse_vnops, vpp);
  246                 break;
  247         }
  248         if (err) {
  249                 free(fvdat, M_FUSEVN);
  250                 return (err);
  251         }
  252         lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL);
  253         fuse_vnode_init(*vpp, fvdat, nodeid, vtyp);
  254         err = insmntque(*vpp, mp);
  255         ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
  256         if (err) {
  257                 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  258                 free(fvdat, M_FUSEVN);
  259                 *vpp = NULL;
  260                 return (err);
  261         }
  262         /* Disallow async reads for fifos because UFS does.  I don't know why */
  263         if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO)
  264                 VN_LOCK_ASHARE(*vpp);
  265 
  266         vn_set_state(*vpp, VSTATE_CONSTRUCTED);
  267         err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE,
  268             td, &vp2, fuse_vnode_cmp, &nodeid);
  269         if (err) {
  270                 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  271                 free(fvdat, M_FUSEVN);
  272                 *vpp = NULL;
  273                 return (err);
  274         }
  275         if (vp2 != NULL) {
  276                 *vpp = vp2;
  277                 return (0);
  278         }
  279 
  280         ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
  281 
  282         return (0);
  283 }
  284 
  285 int
  286 fuse_vnode_get(struct mount *mp,
  287     struct fuse_entry_out *feo,
  288     uint64_t nodeid,
  289     struct vnode *dvp,
  290     struct vnode **vpp,
  291     struct componentname *cnp,
  292     enum vtype vtyp)
  293 {
  294         struct thread *td = curthread;
  295         /* 
  296          * feo should only be NULL for the root directory, which (when libfuse
  297          * is used) always has generation 0
  298          */
  299         uint64_t generation = feo ? feo->generation : 0;
  300         int err = 0;
  301 
  302         if (dvp != NULL && VTOFUD(dvp)->nid == nodeid) {
  303                 fuse_warn(fuse_get_mpdata(mp), FSESS_WARN_ILLEGAL_INODE,
  304                         "Assigned same inode to both parent and child.");
  305                 return EIO;
  306         }
  307 
  308         err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp);
  309         if (err) {
  310                 return err;
  311         }
  312         if (dvp != NULL) {
  313                 MPASS(cnp && (cnp->cn_flags & ISDOTDOT) == 0);
  314                 MPASS(cnp &&
  315                         !(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
  316                 fuse_vnode_setparent(*vpp, dvp);
  317         }
  318         if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 &&
  319             feo != NULL &&
  320             (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) {
  321                 struct timespec timeout;
  322 
  323                 ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
  324                 ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
  325 
  326                 fuse_validity_2_timespec(feo, &timeout);
  327                 cache_enter_time(dvp, *vpp, cnp, &timeout, NULL);
  328         }
  329 
  330         VTOFUD(*vpp)->generation = generation;
  331         /*
  332          * In userland, libfuse uses cached lookups for dot and dotdot entries,
  333          * thus it does not really bump the nlookup counter for forget.
  334          * Follow the same semantic and avoid the bump in order to keep
  335          * nlookup counters consistent.
  336          */
  337         if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 &&
  338             (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.')))
  339                 VTOFUD(*vpp)->nlookup++;
  340 
  341         return 0;
  342 }
  343 
  344 /*
  345  * Called for every fusefs vnode open to initialize the vnode (not
  346  * fuse_filehandle) for use
  347  */
  348 void
  349 fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
  350 {
  351         if (vnode_vtype(vp) == VREG)
  352                 vnode_create_vobject(vp, 0, td);
  353 }
  354 
  355 int
  356 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
  357 {
  358         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  359         struct thread *td = curthread;
  360         struct fuse_filehandle *fufh = NULL;
  361         struct fuse_dispatcher fdi;
  362         struct fuse_setattr_in *fsai;
  363         int err = 0;
  364 
  365         ASSERT_VOP_ELOCKED(vp, "fuse_io_extend");
  366 
  367         if (fuse_isdeadfs(vp)) {
  368                 return EBADF;
  369         }
  370         if (vnode_vtype(vp) == VDIR) {
  371                 return EISDIR;
  372         }
  373         if (vfs_isrdonly(vnode_mount(vp))) {
  374                 return EROFS;
  375         }
  376         if (cred == NULL) {
  377                 cred = td->td_ucred;
  378         }
  379         fdisp_init(&fdi, sizeof(*fsai));
  380         fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
  381         fsai = fdi.indata;
  382         fsai->valid = 0;
  383 
  384         /* Truncate to a new value. */
  385         MPASS((fvdat->flag & FN_SIZECHANGE) != 0);
  386         fsai->size = fvdat->cached_attrs.va_size;
  387         fsai->valid |= FATTR_SIZE;
  388 
  389         fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
  390         if (fufh) {
  391                 fsai->fh = fufh->fh_id;
  392                 fsai->valid |= FATTR_FH;
  393         }
  394         err = fdisp_wait_answ(&fdi);
  395         fdisp_destroy(&fdi);
  396         if (err == 0) {
  397                 getnanouptime(&fvdat->last_local_modify);
  398                 fvdat->flag &= ~FN_SIZECHANGE;
  399         }
  400 
  401         return err;
  402 }
  403 
  404 /*
  405  * Adjust the vnode's size to a new value.
  406  *
  407  * If the new value came from the server, such as from a FUSE_GETATTR
  408  * operation, set `from_server` true.  But if it came from a local operation,
  409  * such as write(2) or truncate(2), set `from_server` false.
  410  */
  411 int
  412 fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
  413 {
  414         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  415         struct vattr *attrs;
  416         off_t oldsize;
  417         size_t iosize;
  418         struct buf *bp = NULL;
  419         int err = 0;
  420 
  421         ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize");
  422 
  423         iosize = fuse_iosize(vp);
  424         oldsize = fvdat->cached_attrs.va_size;
  425         fvdat->cached_attrs.va_size = newsize;
  426         if ((attrs = VTOVA(vp)) != NULL)
  427                 attrs->va_size = newsize;
  428 
  429         if (newsize < oldsize) {
  430                 daddr_t lbn;
  431 
  432                 err = vtruncbuf(vp, newsize, fuse_iosize(vp));
  433                 if (err)
  434                         goto out;
  435                 if (newsize % iosize == 0)
  436                         goto out;
  437                 /* 
  438                  * Zero the contents of the last partial block.
  439                  * Sure seems like vtruncbuf should do this for us.
  440                  */
  441 
  442                 lbn = newsize / iosize;
  443                 bp = getblk(vp, lbn, iosize, PCATCH, 0, 0);
  444                 if (!bp) {
  445                         err = EINTR;
  446                         goto out;
  447                 }
  448                 if (!(bp->b_flags & B_CACHE))
  449                         goto out;       /* Nothing to do */
  450                 MPASS(bp->b_flags & B_VMIO);
  451                 vfs_bio_clrbuf(bp);
  452                 bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
  453         } else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
  454                 /*
  455                  * The FUSE server changed the file size behind our back.  We
  456                  * should invalidate the entire cache.
  457                  */
  458                 daddr_t end_lbn;
  459 
  460                 end_lbn = howmany(newsize, iosize);
  461                 v_inval_buf_range(vp, 0, end_lbn, iosize);
  462         }
  463 out:
  464         if (bp)
  465                 brelse(bp);
  466         vnode_pager_setsize(vp, newsize);
  467         return err;
  468 }
  469 
  470 /* Get the current, possibly dirty, size of the file */
  471 int
  472 fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
  473         struct thread *td)
  474 {
  475         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  476         int error = 0;
  477 
  478         if (!(fvdat->flag & FN_SIZECHANGE) &&
  479                 (!fuse_vnode_attr_cache_valid(vp) ||
  480                   fvdat->cached_attrs.va_size == VNOVAL)) 
  481                 error = fuse_internal_do_getattr(vp, NULL, cred, td);
  482 
  483         if (!error)
  484                 *filesize = fvdat->cached_attrs.va_size;
  485 
  486         return error;
  487 }
  488 
  489 void
  490 fuse_vnode_undirty_cached_timestamps(struct vnode *vp, bool atime)
  491 {
  492         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  493 
  494         fvdat->flag &= ~(FN_MTIMECHANGE | FN_CTIMECHANGE);
  495         if (atime)
  496                 fvdat->flag &= ~FN_ATIMECHANGE;
  497 }
  498 
  499 /* Update a fuse file's cached timestamps */
  500 void
  501 fuse_vnode_update(struct vnode *vp, int flags)
  502 {
  503         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  504         struct mount *mp = vnode_mount(vp);
  505         struct fuse_data *data = fuse_get_mpdata(mp);
  506         struct timespec ts;
  507 
  508         vfs_timestamp(&ts);
  509 
  510         if (data->time_gran > 1)
  511                 ts.tv_nsec = rounddown(ts.tv_nsec, data->time_gran);
  512 
  513         if (mp->mnt_flag & MNT_NOATIME)
  514                 flags &= ~FN_ATIMECHANGE;
  515 
  516         if (flags & FN_ATIMECHANGE)
  517                 fvdat->cached_attrs.va_atime = ts;
  518         if (flags & FN_MTIMECHANGE)
  519                 fvdat->cached_attrs.va_mtime = ts;
  520         if (flags & FN_CTIMECHANGE)
  521                 fvdat->cached_attrs.va_ctime = ts;
  522 
  523         fvdat->flag |= flags;
  524 }
  525 
  526 void
  527 fuse_node_init(void)
  528 {
  529         fuse_node_count = counter_u64_alloc(M_WAITOK);
  530 }
  531 
  532 void
  533 fuse_node_destroy(void)
  534 {
  535         counter_u64_free(fuse_node_count);
  536 }

Cache object: 73388bb77b882b9a0e9176951b533125


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