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/kern/kern_fileassoc.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 /* $NetBSD: kern_fileassoc.c,v 1.31 2008/05/05 17:11:17 ad Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
    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
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. The name of the author may not be used to endorse or promote products
   16  *    derived from this software without specific prior written permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __KERNEL_RCSID(0, "$NetBSD: kern_fileassoc.c,v 1.31 2008/05/05 17:11:17 ad Exp $");
   32 
   33 #include "opt_fileassoc.h"
   34 
   35 #include <sys/param.h>
   36 #include <sys/mount.h>
   37 #include <sys/queue.h>
   38 #include <sys/vnode.h>
   39 #include <sys/namei.h>
   40 #include <sys/exec.h>
   41 #include <sys/proc.h>
   42 #include <sys/inttypes.h>
   43 #include <sys/errno.h>
   44 #include <sys/fileassoc.h>
   45 #include <sys/specificdata.h>
   46 #include <sys/hash.h>
   47 #include <sys/fstypes.h>
   48 #include <sys/kmem.h>
   49 #include <sys/once.h>
   50 
   51 #define FILEASSOC_INITIAL_TABLESIZE     128
   52 
   53 static struct fileassoc_hash_entry *
   54 fileassoc_file_lookup(struct vnode *, fhandle_t *);
   55 static struct fileassoc_hash_entry *
   56 fileassoc_file_add(struct vnode *, fhandle_t *);
   57 static struct fileassoc_table *fileassoc_table_resize(struct fileassoc_table *);
   58 
   59 static specificdata_domain_t fileassoc_domain;
   60 static specificdata_key_t fileassoc_mountspecific_key;
   61 static ONCE_DECL(control);
   62 
   63 /*
   64  * Hook entry.
   65  * Includes the hook name for identification and private hook clear callback.
   66  */
   67 struct fileassoc {
   68         LIST_ENTRY(fileassoc) list;
   69         const char *name;                       /* name. */
   70         fileassoc_cleanup_cb_t cleanup_cb;      /* clear callback. */
   71         specificdata_key_t key;
   72 };
   73 
   74 static LIST_HEAD(, fileassoc) fileassoc_list;
   75 
   76 /* An entry in the per-mount hash table. */
   77 struct fileassoc_hash_entry {
   78         fhandle_t *handle;                              /* File handle */
   79         specificdata_reference data;                    /* Hooks. */
   80         u_int nassocs;                                  /* # of hooks. */
   81         LIST_ENTRY(fileassoc_hash_entry) entries;       /* List pointer. */
   82 };
   83 
   84 LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry);
   85 
   86 struct fileassoc_table {
   87         struct fileassoc_hashhead *hash_tbl;
   88         size_t hash_size;                               /* Number of slots. */
   89         u_long hash_mask;
   90         size_t hash_used;                               /* # of used slots. */
   91         specificdata_reference data;
   92 };
   93 
   94 /*
   95  * Hashing function: Takes a number modulus the mask to give back an
   96  * index into the hash table.
   97  */
   98 #define FILEASSOC_HASH(tbl, handle)     \
   99         (hash32_buf((handle), FHANDLE_SIZE(handle), HASH32_BUF_INIT) \
  100          & ((tbl)->hash_mask))
  101 
  102 static void *
  103 file_getdata(struct fileassoc_hash_entry *e, const struct fileassoc *assoc)
  104 {
  105 
  106         return specificdata_getspecific(fileassoc_domain, &e->data,
  107             assoc->key);
  108 }
  109 
  110 static void
  111 file_setdata(struct fileassoc_hash_entry *e, const struct fileassoc *assoc,
  112     void *data)
  113 {
  114 
  115         specificdata_setspecific(fileassoc_domain, &e->data, assoc->key,
  116             data);
  117 }
  118 
  119 static void
  120 file_cleanup(struct fileassoc_hash_entry *e, const struct fileassoc *assoc)
  121 {
  122         fileassoc_cleanup_cb_t cb;
  123         void *data;
  124 
  125         cb = assoc->cleanup_cb;
  126         if (cb == NULL) {
  127                 return;
  128         }
  129         data = file_getdata(e, assoc);
  130         (*cb)(data);
  131 }
  132 
  133 static void
  134 file_free(struct fileassoc_hash_entry *e)
  135 {
  136         struct fileassoc *assoc;
  137 
  138         LIST_REMOVE(e, entries);
  139 
  140         LIST_FOREACH(assoc, &fileassoc_list, list) {
  141                 file_cleanup(e, assoc);
  142         }
  143         vfs_composefh_free(e->handle);
  144         specificdata_fini(fileassoc_domain, &e->data);
  145         kmem_free(e, sizeof(*e));
  146 }
  147 
  148 static void
  149 table_dtor(void *vp)
  150 {
  151         struct fileassoc_table *tbl = vp;
  152         struct fileassoc_hashhead *hh;
  153         u_long i;
  154 
  155         /* Remove all entries from the table and lists */
  156         hh = tbl->hash_tbl;
  157         for (i = 0; i < tbl->hash_size; i++) {
  158                 struct fileassoc_hash_entry *mhe;
  159 
  160                 while ((mhe = LIST_FIRST(&hh[i])) != NULL) {
  161                         file_free(mhe);
  162                 }
  163         }
  164 
  165         /* Remove hash table and sysctl node */
  166         hashdone(tbl->hash_tbl, HASH_LIST, tbl->hash_mask);
  167         specificdata_fini(fileassoc_domain, &tbl->data);
  168         kmem_free(tbl, sizeof(*tbl));
  169 }
  170 
  171 /*
  172  * Initialize the fileassoc subsystem.
  173  */
  174 static int
  175 fileassoc_init(void)
  176 {
  177         int error;
  178 
  179         error = mount_specific_key_create(&fileassoc_mountspecific_key,
  180             table_dtor);
  181         if (error) {
  182                 return error;
  183         }
  184         fileassoc_domain = specificdata_domain_create();
  185 
  186         return 0;
  187 }
  188 
  189 /*
  190  * Register a new hook.
  191  */
  192 int
  193 fileassoc_register(const char *name, fileassoc_cleanup_cb_t cleanup_cb,
  194     fileassoc_t *result)
  195 {
  196         int error;
  197         specificdata_key_t key;
  198         struct fileassoc *assoc;
  199 
  200         error = RUN_ONCE(&control, fileassoc_init);
  201         if (error) {
  202                 return error;
  203         }
  204         error = specificdata_key_create(fileassoc_domain, &key, NULL);
  205         if (error) {
  206                 return error;
  207         }
  208         assoc = kmem_alloc(sizeof(*assoc), KM_SLEEP);
  209         assoc->name = name;
  210         assoc->cleanup_cb = cleanup_cb;
  211         assoc->key = key;
  212         LIST_INSERT_HEAD(&fileassoc_list, assoc, list);
  213         *result = assoc;
  214 
  215         return 0;
  216 }
  217 
  218 /*
  219  * Deregister a hook.
  220  */
  221 int
  222 fileassoc_deregister(fileassoc_t assoc)
  223 {
  224 
  225         LIST_REMOVE(assoc, list);
  226         specificdata_key_delete(fileassoc_domain, assoc->key);
  227         kmem_free(assoc, sizeof(*assoc));
  228 
  229         return 0;
  230 }
  231 
  232 /*
  233  * Get the hash table for the specified device.
  234  */
  235 static struct fileassoc_table *
  236 fileassoc_table_lookup(struct mount *mp)
  237 {
  238         int error;
  239 
  240         error = RUN_ONCE(&control, fileassoc_init);
  241         if (error) {
  242                 return NULL;
  243         }
  244         return mount_getspecific(mp, fileassoc_mountspecific_key);
  245 }
  246 
  247 /*
  248  * Perform a lookup on a hash table.  If hint is non-zero then use the value
  249  * of the hint as the identifier instead of performing a lookup for the
  250  * fileid.
  251  */
  252 static struct fileassoc_hash_entry *
  253 fileassoc_file_lookup(struct vnode *vp, fhandle_t *hint)
  254 {
  255         struct fileassoc_table *tbl;
  256         struct fileassoc_hashhead *tble;
  257         struct fileassoc_hash_entry *e;
  258         size_t indx;
  259         fhandle_t *th;
  260         int error;
  261 
  262         tbl = fileassoc_table_lookup(vp->v_mount);
  263         if (tbl == NULL) {
  264                 return NULL;
  265         }
  266 
  267         if (hint == NULL) {
  268                 error = vfs_composefh_alloc(vp, &th);
  269                 if (error)
  270                         return (NULL);
  271         } else {
  272                 th = hint;
  273         }
  274 
  275         indx = FILEASSOC_HASH(tbl, th);
  276         tble = &(tbl->hash_tbl[indx]);
  277 
  278         LIST_FOREACH(e, tble, entries) {
  279                 if (((FHANDLE_FILEID(e->handle)->fid_len ==
  280                      FHANDLE_FILEID(th)->fid_len)) &&
  281                     (memcmp(FHANDLE_FILEID(e->handle), FHANDLE_FILEID(th),
  282                            (FHANDLE_FILEID(th))->fid_len) == 0)) {
  283                         break;
  284                 }
  285         }
  286 
  287         if (hint == NULL)
  288                 vfs_composefh_free(th);
  289 
  290         return e;
  291 }
  292 
  293 /*
  294  * Return hook data associated with a vnode.
  295  */
  296 void *
  297 fileassoc_lookup(struct vnode *vp, fileassoc_t assoc)
  298 {
  299         struct fileassoc_hash_entry *mhe;
  300 
  301         mhe = fileassoc_file_lookup(vp, NULL);
  302         if (mhe == NULL)
  303                 return (NULL);
  304 
  305         return file_getdata(mhe, assoc);
  306 }
  307 
  308 static struct fileassoc_table *
  309 fileassoc_table_resize(struct fileassoc_table *tbl)
  310 {
  311         struct fileassoc_table *newtbl;
  312         struct fileassoc_hashhead *hh;
  313         u_long i;
  314 
  315         /*
  316          * Allocate a new table. Like the condition in fileassoc_file_add(),
  317          * this is also temporary -- just double the number of slots.
  318          */
  319         newtbl = kmem_zalloc(sizeof(*newtbl), KM_SLEEP);
  320         newtbl->hash_size = (tbl->hash_size * 2);
  321         if (newtbl->hash_size < tbl->hash_size)
  322                 newtbl->hash_size = tbl->hash_size;
  323         newtbl->hash_tbl = hashinit(newtbl->hash_size, HASH_LIST,
  324             true, &newtbl->hash_mask);
  325         newtbl->hash_used = 0;
  326         specificdata_init(fileassoc_domain, &newtbl->data);
  327 
  328         /* XXX we need to make sure nothing uses fileassoc here! */
  329 
  330         hh = tbl->hash_tbl;
  331         for (i = 0; i < tbl->hash_size; i++) {
  332                 struct fileassoc_hash_entry *mhe;
  333 
  334                 while ((mhe = LIST_FIRST(&hh[i])) != NULL) {
  335                         struct fileassoc_hashhead *vhh;
  336                         size_t indx;
  337 
  338                         LIST_REMOVE(mhe, entries);
  339 
  340                         indx = FILEASSOC_HASH(newtbl, mhe->handle);
  341                         vhh = &(newtbl->hash_tbl[indx]);
  342 
  343                         LIST_INSERT_HEAD(vhh, mhe, entries);
  344 
  345                         newtbl->hash_used++;
  346                 }
  347         }
  348 
  349         if (tbl->hash_used != newtbl->hash_used)
  350                 panic("fileassoc_table_resize: inconsistency detected! "
  351                     "needed %zu entries, got %zu", tbl->hash_used,
  352                     newtbl->hash_used);
  353 
  354         hashdone(tbl->hash_tbl, HASH_LIST, tbl->hash_mask);
  355         specificdata_fini(fileassoc_domain, &tbl->data);
  356         kmem_free(tbl, sizeof(*tbl));
  357 
  358         return (newtbl);
  359 }
  360 
  361 /*
  362  * Create a new fileassoc table.
  363  */
  364 static struct fileassoc_table *
  365 fileassoc_table_add(struct mount *mp)
  366 {
  367         struct fileassoc_table *tbl;
  368 
  369         /* Check for existing table for device. */
  370         tbl = fileassoc_table_lookup(mp);
  371         if (tbl != NULL)
  372                 return (tbl);
  373 
  374         /* Allocate and initialize a table. */
  375         tbl = kmem_zalloc(sizeof(*tbl), KM_SLEEP);
  376         tbl->hash_size = FILEASSOC_INITIAL_TABLESIZE;
  377         tbl->hash_tbl = hashinit(tbl->hash_size, HASH_LIST, true,
  378             &tbl->hash_mask);
  379         tbl->hash_used = 0;
  380         specificdata_init(fileassoc_domain, &tbl->data);
  381 
  382         mount_setspecific(mp, fileassoc_mountspecific_key, tbl);
  383 
  384         return (tbl);
  385 }
  386 
  387 /*
  388  * Delete a table.
  389  */
  390 int
  391 fileassoc_table_delete(struct mount *mp)
  392 {
  393         struct fileassoc_table *tbl;
  394 
  395         tbl = fileassoc_table_lookup(mp);
  396         if (tbl == NULL)
  397                 return (EEXIST);
  398 
  399         mount_setspecific(mp, fileassoc_mountspecific_key, NULL);
  400         table_dtor(tbl);
  401 
  402         return (0);
  403 }
  404 
  405 /*
  406  * Run a callback for each hook entry in a table.
  407  */
  408 int
  409 fileassoc_table_run(struct mount *mp, fileassoc_t assoc, fileassoc_cb_t cb,
  410     void *cookie)
  411 {
  412         struct fileassoc_table *tbl;
  413         struct fileassoc_hashhead *hh;
  414         u_long i;
  415 
  416         tbl = fileassoc_table_lookup(mp);
  417         if (tbl == NULL)
  418                 return (EEXIST);
  419 
  420         hh = tbl->hash_tbl;
  421         for (i = 0; i < tbl->hash_size; i++) {
  422                 struct fileassoc_hash_entry *mhe;
  423 
  424                 LIST_FOREACH(mhe, &hh[i], entries) {
  425                         void *data;
  426 
  427                         data = file_getdata(mhe, assoc);
  428                         if (data != NULL)
  429                                 cb(data, cookie);
  430                 }
  431         }
  432 
  433         return (0);
  434 }
  435 
  436 /*
  437  * Clear a table for a given hook.
  438  */
  439 int
  440 fileassoc_table_clear(struct mount *mp, fileassoc_t assoc)
  441 {
  442         struct fileassoc_table *tbl;
  443         struct fileassoc_hashhead *hh;
  444         u_long i;
  445 
  446         tbl = fileassoc_table_lookup(mp);
  447         if (tbl == NULL)
  448                 return (EEXIST);
  449 
  450         hh = tbl->hash_tbl;
  451         for (i = 0; i < tbl->hash_size; i++) {
  452                 struct fileassoc_hash_entry *mhe;
  453 
  454                 LIST_FOREACH(mhe, &hh[i], entries) {
  455                         file_cleanup(mhe, assoc);
  456                         file_setdata(mhe, assoc, NULL);
  457                 }
  458         }
  459 
  460         return (0);
  461 }
  462 
  463 /*
  464  * Add a file entry to a table.
  465  */
  466 static struct fileassoc_hash_entry *
  467 fileassoc_file_add(struct vnode *vp, fhandle_t *hint)
  468 {
  469         struct fileassoc_table *tbl;
  470         struct fileassoc_hashhead *vhh;
  471         struct fileassoc_hash_entry *e;
  472         size_t indx;
  473         fhandle_t *th;
  474         int error;
  475 
  476         if (hint == NULL) {
  477                 error = vfs_composefh_alloc(vp, &th);
  478                 if (error)
  479                         return (NULL);
  480         } else
  481                 th = hint;
  482 
  483         e = fileassoc_file_lookup(vp, th);
  484         if (e != NULL) {
  485                 if (hint == NULL)
  486                         vfs_composefh_free(th);
  487 
  488                 return (e);
  489         }
  490 
  491         tbl = fileassoc_table_lookup(vp->v_mount);
  492         if (tbl == NULL) {
  493                 tbl = fileassoc_table_add(vp->v_mount);
  494         }
  495 
  496         indx = FILEASSOC_HASH(tbl, th);
  497         vhh = &(tbl->hash_tbl[indx]);
  498 
  499         e = kmem_zalloc(sizeof(*e), KM_SLEEP);
  500         e->handle = th;
  501         specificdata_init(fileassoc_domain, &e->data);
  502         LIST_INSERT_HEAD(vhh, e, entries);
  503 
  504         /*
  505          * This decides when we need to resize the table. For now,
  506          * resize it whenever we "filled" up the number of slots it
  507          * has. That's not really true unless of course we had zero
  508          * collisions. Think positive! :)
  509          */
  510         if (++(tbl->hash_used) == tbl->hash_size) { 
  511                 struct fileassoc_table *newtbl;
  512 
  513                 newtbl = fileassoc_table_resize(tbl);
  514                 mount_setspecific(vp->v_mount, fileassoc_mountspecific_key,
  515                     newtbl);
  516         }
  517 
  518         return (e);
  519 }
  520 
  521 /*
  522  * Delete a file entry from a table.
  523  */
  524 int
  525 fileassoc_file_delete(struct vnode *vp)
  526 {
  527         struct fileassoc_table *tbl;
  528         struct fileassoc_hash_entry *mhe;
  529 
  530         KERNEL_LOCK(1, NULL);
  531 
  532         mhe = fileassoc_file_lookup(vp, NULL);
  533         if (mhe == NULL) {
  534                 KERNEL_UNLOCK_ONE(NULL);
  535                 return (ENOENT);
  536         }
  537 
  538         file_free(mhe);
  539 
  540         tbl = fileassoc_table_lookup(vp->v_mount);
  541         --(tbl->hash_used); /* XXX gc? */
  542 
  543         KERNEL_UNLOCK_ONE(NULL);
  544 
  545         return (0);
  546 }
  547 
  548 /*
  549  * Add a hook to a vnode.
  550  */
  551 int
  552 fileassoc_add(struct vnode *vp, fileassoc_t assoc, void *data)
  553 {
  554         struct fileassoc_hash_entry *e;
  555         void *olddata;
  556 
  557         e = fileassoc_file_lookup(vp, NULL);
  558         if (e == NULL) {
  559                 e = fileassoc_file_add(vp, NULL);
  560                 if (e == NULL)
  561                         return (ENOTDIR);
  562         }
  563 
  564         olddata = file_getdata(e, assoc);
  565         if (olddata != NULL)
  566                 return (EEXIST);
  567 
  568         file_setdata(e, assoc, data);
  569 
  570         e->nassocs++;
  571 
  572         return (0);
  573 }
  574 
  575 /*
  576  * Clear a hook from a vnode.
  577  */
  578 int
  579 fileassoc_clear(struct vnode *vp, fileassoc_t assoc)
  580 {
  581         struct fileassoc_hash_entry *mhe;
  582 
  583         mhe = fileassoc_file_lookup(vp, NULL);
  584         if (mhe == NULL)
  585                 return (ENOENT);
  586 
  587         file_cleanup(mhe, assoc);
  588         file_setdata(mhe, assoc, NULL);
  589 
  590         --(mhe->nassocs); /* XXX gc? */
  591 
  592         return (0);
  593 }

Cache object: a4d2c3b843305a058670a8072b7129d0


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