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.36 2014/07/10 15:00:28 christos 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.36 2014/07/10 15:00:28 christos 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/errno.h>
   40 #include <sys/fileassoc.h>
   41 #include <sys/specificdata.h>
   42 #include <sys/hash.h>
   43 #include <sys/kmem.h>
   44 #include <sys/once.h>
   45 
   46 #define FILEASSOC_INITIAL_TABLESIZE     128
   47 
   48 static specificdata_domain_t fileassoc_domain = NULL;
   49 static specificdata_key_t fileassoc_mountspecific_key;
   50 static ONCE_DECL(control);
   51 
   52 /*
   53  * Assoc entry.
   54  * Includes the assoc name for identification and private clear callback.
   55  */
   56 struct fileassoc {
   57         LIST_ENTRY(fileassoc) assoc_list;
   58         const char *assoc_name;                         /* Name. */
   59         fileassoc_cleanup_cb_t assoc_cleanup_cb;        /* Clear callback. */
   60         specificdata_key_t assoc_key;
   61 };
   62 
   63 static LIST_HEAD(, fileassoc) fileassoc_list;
   64 
   65 /* An entry in the per-mount hash table. */
   66 struct fileassoc_file {
   67         fhandle_t *faf_handle;                          /* File handle */
   68         specificdata_reference faf_data;                /* Assoc data. */
   69         u_int faf_nassocs;                              /* # of assocs. */
   70         LIST_ENTRY(fileassoc_file) faf_list;            /* List pointer. */
   71 };
   72 
   73 LIST_HEAD(fileassoc_hash_entry, fileassoc_file);
   74 
   75 struct fileassoc_table {
   76         struct fileassoc_hash_entry *tbl_hash;
   77         u_long tbl_mask;                                /* Hash table mask. */
   78         size_t tbl_nslots;                              /* Number of slots. */
   79         size_t tbl_nused;                               /* # of used slots. */
   80         specificdata_reference tbl_data;
   81 };
   82 
   83 /*
   84  * Hashing function: Takes a number modulus the mask to give back an
   85  * index into the hash table.
   86  */
   87 #define FILEASSOC_HASH(tbl, handle)     \
   88         (hash32_buf((handle), FHANDLE_SIZE(handle), HASH32_BUF_INIT) \
   89          & ((tbl)->tbl_mask))
   90 
   91 static void *
   92 file_getdata(struct fileassoc_file *faf, const struct fileassoc *assoc)
   93 {
   94 
   95         return specificdata_getspecific(fileassoc_domain, &faf->faf_data,
   96             assoc->assoc_key);
   97 }
   98 
   99 static void
  100 file_setdata(struct fileassoc_file *faf, const struct fileassoc *assoc,
  101     void *data)
  102 {
  103 
  104         specificdata_setspecific(fileassoc_domain, &faf->faf_data,
  105             assoc->assoc_key, data);
  106 }
  107 
  108 static void
  109 file_cleanup(struct fileassoc_file *faf, const struct fileassoc *assoc)
  110 {
  111         fileassoc_cleanup_cb_t cb;
  112         void *data;
  113 
  114         cb = assoc->assoc_cleanup_cb;
  115         if (cb == NULL) {
  116                 return;
  117         }
  118         data = file_getdata(faf, assoc);
  119         (*cb)(data);
  120 }
  121 
  122 static void
  123 file_free(struct fileassoc_file *faf)
  124 {
  125         struct fileassoc *assoc;
  126 
  127         LIST_REMOVE(faf, faf_list);
  128 
  129         LIST_FOREACH(assoc, &fileassoc_list, assoc_list) {
  130                 file_cleanup(faf, assoc);
  131         }
  132         vfs_composefh_free(faf->faf_handle);
  133         specificdata_fini(fileassoc_domain, &faf->faf_data);
  134         kmem_free(faf, sizeof(*faf));
  135 }
  136 
  137 static void
  138 table_dtor(void *v)
  139 {
  140         struct fileassoc_table *tbl = v;
  141         u_long i;
  142 
  143         /* Remove all entries from the table and lists */
  144         for (i = 0; i < tbl->tbl_nslots; i++) {
  145                 struct fileassoc_file *faf;
  146 
  147                 while ((faf = LIST_FIRST(&tbl->tbl_hash[i])) != NULL) {
  148                         file_free(faf);
  149                 }
  150         }
  151 
  152         /* Remove hash table and sysctl node */
  153         hashdone(tbl->tbl_hash, HASH_LIST, tbl->tbl_mask);
  154         specificdata_fini(fileassoc_domain, &tbl->tbl_data);
  155         kmem_free(tbl, sizeof(*tbl));
  156 }
  157 
  158 /*
  159  * Initialize the fileassoc subsystem.
  160  */
  161 static int
  162 fileassoc_init(void)
  163 {
  164         int error;
  165 
  166         error = mount_specific_key_create(&fileassoc_mountspecific_key,
  167             table_dtor);
  168         if (error) {
  169                 return error;
  170         }
  171         fileassoc_domain = specificdata_domain_create();
  172 
  173         return 0;
  174 }
  175 
  176 /*
  177  * Register a new assoc.
  178  */
  179 int
  180 fileassoc_register(const char *name, fileassoc_cleanup_cb_t cleanup_cb,
  181     fileassoc_t *result)
  182 {
  183         int error;
  184         specificdata_key_t key;
  185         struct fileassoc *assoc;
  186 
  187         error = RUN_ONCE(&control, fileassoc_init);
  188         if (error) {
  189                 return error;
  190         }
  191         error = specificdata_key_create(fileassoc_domain, &key, NULL);
  192         if (error) {
  193                 return error;
  194         }
  195         assoc = kmem_alloc(sizeof(*assoc), KM_SLEEP);
  196         assoc->assoc_name = name;
  197         assoc->assoc_cleanup_cb = cleanup_cb;
  198         assoc->assoc_key = key;
  199 
  200         LIST_INSERT_HEAD(&fileassoc_list, assoc, assoc_list);
  201 
  202         *result = assoc;
  203 
  204         return 0;
  205 }
  206 
  207 /*
  208  * Deregister an assoc.
  209  */
  210 int
  211 fileassoc_deregister(fileassoc_t assoc)
  212 {
  213 
  214         LIST_REMOVE(assoc, assoc_list);
  215         specificdata_key_delete(fileassoc_domain, assoc->assoc_key);
  216         kmem_free(assoc, sizeof(*assoc));
  217 
  218         return 0;
  219 }
  220 
  221 /*
  222  * Get the hash table for the specified device.
  223  */
  224 static struct fileassoc_table *
  225 fileassoc_table_lookup(struct mount *mp)
  226 {
  227         int error;
  228 
  229         error = RUN_ONCE(&control, fileassoc_init);
  230         if (error) {
  231                 return NULL;
  232         }
  233         return mount_getspecific(mp, fileassoc_mountspecific_key);
  234 }
  235 
  236 /*
  237  * Perform a lookup on a hash table.  If hint is non-zero then use the value
  238  * of the hint as the identifier instead of performing a lookup for the
  239  * fileid.
  240  */
  241 static struct fileassoc_file *
  242 fileassoc_file_lookup(struct vnode *vp, fhandle_t *hint)
  243 {
  244         struct fileassoc_table *tbl;
  245         struct fileassoc_hash_entry *hash_entry;
  246         struct fileassoc_file *faf;
  247         size_t indx;
  248         fhandle_t *th;
  249         int error;
  250 
  251         tbl = fileassoc_table_lookup(vp->v_mount);
  252         if (tbl == NULL) {
  253                 return NULL;
  254         }
  255 
  256         if (hint == NULL) {
  257                 error = vfs_composefh_alloc(vp, &th);
  258                 if (error)
  259                         return (NULL);
  260         } else {
  261                 th = hint;
  262         }
  263 
  264         indx = FILEASSOC_HASH(tbl, th);
  265         hash_entry = &(tbl->tbl_hash[indx]);
  266 
  267         LIST_FOREACH(faf, hash_entry, faf_list) {
  268                 if (((FHANDLE_FILEID(faf->faf_handle)->fid_len ==
  269                      FHANDLE_FILEID(th)->fid_len)) &&
  270                     (memcmp(FHANDLE_FILEID(faf->faf_handle), FHANDLE_FILEID(th),
  271                            (FHANDLE_FILEID(th))->fid_len) == 0)) {
  272                         break;
  273                 }
  274         }
  275 
  276         if (hint == NULL)
  277                 vfs_composefh_free(th);
  278 
  279         return faf;
  280 }
  281 
  282 /*
  283  * Return assoc data associated with a vnode.
  284  */
  285 void *
  286 fileassoc_lookup(struct vnode *vp, fileassoc_t assoc)
  287 {
  288         struct fileassoc_file *faf;
  289 
  290         faf = fileassoc_file_lookup(vp, NULL);
  291         if (faf == NULL)
  292                 return (NULL);
  293 
  294         return file_getdata(faf, assoc);
  295 }
  296 
  297 static struct fileassoc_table *
  298 fileassoc_table_resize(struct fileassoc_table *tbl)
  299 {
  300         struct fileassoc_table *newtbl;
  301         u_long i;
  302 
  303         /*
  304          * Allocate a new table. Like the condition in fileassoc_file_add(),
  305          * this is also temporary -- just double the number of slots.
  306          */
  307         newtbl = kmem_zalloc(sizeof(*newtbl), KM_SLEEP);
  308         newtbl->tbl_nslots = (tbl->tbl_nslots * 2);
  309         if (newtbl->tbl_nslots < tbl->tbl_nslots)
  310                 newtbl->tbl_nslots = tbl->tbl_nslots;
  311         newtbl->tbl_hash = hashinit(newtbl->tbl_nslots, HASH_LIST,
  312             true, &newtbl->tbl_mask);
  313         newtbl->tbl_nused = 0;
  314         specificdata_init(fileassoc_domain, &newtbl->tbl_data);
  315 
  316         /* XXX we need to make sure nothing uses fileassoc here! */
  317 
  318         for (i = 0; i < tbl->tbl_nslots; i++) {
  319                 struct fileassoc_file *faf;
  320 
  321                 while ((faf = LIST_FIRST(&tbl->tbl_hash[i])) != NULL) {
  322                         struct fileassoc_hash_entry *hash_entry;
  323                         size_t indx;
  324 
  325                         LIST_REMOVE(faf, faf_list);
  326 
  327                         indx = FILEASSOC_HASH(newtbl, faf->faf_handle);
  328                         hash_entry = &(newtbl->tbl_hash[indx]);
  329 
  330                         LIST_INSERT_HEAD(hash_entry, faf, faf_list);
  331 
  332                         newtbl->tbl_nused++;
  333                 }
  334         }
  335 
  336         if (tbl->tbl_nused != newtbl->tbl_nused)
  337                 panic("fileassoc_table_resize: inconsistency detected! "
  338                     "needed %zu entries, got %zu", tbl->tbl_nused,
  339                     newtbl->tbl_nused);
  340 
  341         hashdone(tbl->tbl_hash, HASH_LIST, tbl->tbl_mask);
  342         specificdata_fini(fileassoc_domain, &tbl->tbl_data);
  343         kmem_free(tbl, sizeof(*tbl));
  344 
  345         return (newtbl);
  346 }
  347 
  348 /*
  349  * Create a new fileassoc table.
  350  */
  351 static struct fileassoc_table *
  352 fileassoc_table_add(struct mount *mp)
  353 {
  354         struct fileassoc_table *tbl;
  355 
  356         /* Check for existing table for device. */
  357         tbl = fileassoc_table_lookup(mp);
  358         if (tbl != NULL)
  359                 return (tbl);
  360 
  361         /* Allocate and initialize a table. */
  362         tbl = kmem_zalloc(sizeof(*tbl), KM_SLEEP);
  363         tbl->tbl_nslots = FILEASSOC_INITIAL_TABLESIZE;
  364         tbl->tbl_hash = hashinit(tbl->tbl_nslots, HASH_LIST, true,
  365             &tbl->tbl_mask);
  366         tbl->tbl_nused = 0;
  367         specificdata_init(fileassoc_domain, &tbl->tbl_data);
  368 
  369         mount_setspecific(mp, fileassoc_mountspecific_key, tbl);
  370 
  371         return (tbl);
  372 }
  373 
  374 /*
  375  * Delete a table.
  376  */
  377 int
  378 fileassoc_table_delete(struct mount *mp)
  379 {
  380         struct fileassoc_table *tbl;
  381 
  382         tbl = fileassoc_table_lookup(mp);
  383         if (tbl == NULL)
  384                 return (EEXIST);
  385 
  386         mount_setspecific(mp, fileassoc_mountspecific_key, NULL);
  387         table_dtor(tbl);
  388 
  389         return (0);
  390 }
  391 
  392 /*
  393  * Run a callback for each assoc in a table.
  394  */
  395 int
  396 fileassoc_table_run(struct mount *mp, fileassoc_t assoc, fileassoc_cb_t cb,
  397     void *cookie)
  398 {
  399         struct fileassoc_table *tbl;
  400         u_long i;
  401 
  402         tbl = fileassoc_table_lookup(mp);
  403         if (tbl == NULL)
  404                 return (EEXIST);
  405 
  406         for (i = 0; i < tbl->tbl_nslots; i++) {
  407                 struct fileassoc_file *faf;
  408 
  409                 LIST_FOREACH(faf, &tbl->tbl_hash[i], faf_list) {
  410                         void *data;
  411 
  412                         data = file_getdata(faf, assoc);
  413                         if (data != NULL)
  414                                 cb(data, cookie);
  415                 }
  416         }
  417 
  418         return (0);
  419 }
  420 
  421 /*
  422  * Clear a table for a given assoc.
  423  */
  424 int
  425 fileassoc_table_clear(struct mount *mp, fileassoc_t assoc)
  426 {
  427         struct fileassoc_table *tbl;
  428         u_long i;
  429 
  430         tbl = fileassoc_table_lookup(mp);
  431         if (tbl == NULL)
  432                 return (EEXIST);
  433 
  434         for (i = 0; i < tbl->tbl_nslots; i++) {
  435                 struct fileassoc_file *faf;
  436 
  437                 LIST_FOREACH(faf, &tbl->tbl_hash[i], faf_list) {
  438                         file_cleanup(faf, assoc);
  439                         file_setdata(faf, assoc, NULL);
  440                 }
  441         }
  442 
  443         return (0);
  444 }
  445 
  446 /*
  447  * Add a file entry to a table.
  448  */
  449 static struct fileassoc_file *
  450 fileassoc_file_add(struct vnode *vp, fhandle_t *hint)
  451 {
  452         struct fileassoc_table *tbl;
  453         struct fileassoc_hash_entry *hash_entry;
  454         struct fileassoc_file *faf;
  455         size_t indx;
  456         fhandle_t *th;
  457         int error;
  458 
  459         if (hint == NULL) {
  460                 error = vfs_composefh_alloc(vp, &th);
  461                 if (error)
  462                         return (NULL);
  463         } else
  464                 th = hint;
  465 
  466         faf = fileassoc_file_lookup(vp, th);
  467         if (faf != NULL) {
  468                 if (hint == NULL)
  469                         vfs_composefh_free(th);
  470 
  471                 return (faf);
  472         }
  473 
  474         tbl = fileassoc_table_lookup(vp->v_mount);
  475         if (tbl == NULL) {
  476                 tbl = fileassoc_table_add(vp->v_mount);
  477         }
  478 
  479         indx = FILEASSOC_HASH(tbl, th);
  480         hash_entry = &(tbl->tbl_hash[indx]);
  481 
  482         faf = kmem_zalloc(sizeof(*faf), KM_SLEEP);
  483         faf->faf_handle = th;
  484         specificdata_init(fileassoc_domain, &faf->faf_data);
  485         LIST_INSERT_HEAD(hash_entry, faf, faf_list);
  486 
  487         /*
  488          * This decides when we need to resize the table. For now,
  489          * resize it whenever we "filled" up the number of slots it
  490          * has. That's not really true unless of course we had zero
  491          * collisions. Think positive! :)
  492          */
  493         if (++(tbl->tbl_nused) == tbl->tbl_nslots) { 
  494                 struct fileassoc_table *newtbl;
  495 
  496                 newtbl = fileassoc_table_resize(tbl);
  497                 mount_setspecific(vp->v_mount, fileassoc_mountspecific_key,
  498                     newtbl);
  499         }
  500 
  501         return (faf);
  502 }
  503 
  504 /*
  505  * Delete a file entry from a table.
  506  */
  507 int
  508 fileassoc_file_delete(struct vnode *vp)
  509 {
  510         struct fileassoc_table *tbl;
  511         struct fileassoc_file *faf;
  512 
  513         /* Pre-check if fileassoc is used. XXX */
  514         if (!fileassoc_domain) {
  515                 return ENOENT;
  516         }
  517         KERNEL_LOCK(1, NULL);
  518 
  519         faf = fileassoc_file_lookup(vp, NULL);
  520         if (faf == NULL) {
  521                 KERNEL_UNLOCK_ONE(NULL);
  522                 return (ENOENT);
  523         }
  524 
  525         file_free(faf);
  526 
  527         tbl = fileassoc_table_lookup(vp->v_mount);
  528         KASSERT(tbl != NULL);
  529         --(tbl->tbl_nused); /* XXX gc? */
  530 
  531         KERNEL_UNLOCK_ONE(NULL);
  532 
  533         return (0);
  534 }
  535 
  536 /*
  537  * Add an assoc to a vnode.
  538  */
  539 int
  540 fileassoc_add(struct vnode *vp, fileassoc_t assoc, void *data)
  541 {
  542         struct fileassoc_file *faf;
  543         void *olddata;
  544 
  545         faf = fileassoc_file_lookup(vp, NULL);
  546         if (faf == NULL) {
  547                 faf = fileassoc_file_add(vp, NULL);
  548                 if (faf == NULL)
  549                         return (ENOTDIR);
  550         }
  551 
  552         olddata = file_getdata(faf, assoc);
  553         if (olddata != NULL)
  554                 return (EEXIST);
  555 
  556         file_setdata(faf, assoc, data);
  557 
  558         faf->faf_nassocs++;
  559 
  560         return (0);
  561 }
  562 
  563 /*
  564  * Clear an assoc from a vnode.
  565  */
  566 int
  567 fileassoc_clear(struct vnode *vp, fileassoc_t assoc)
  568 {
  569         struct fileassoc_file *faf;
  570 
  571         faf = fileassoc_file_lookup(vp, NULL);
  572         if (faf == NULL)
  573                 return (ENOENT);
  574 
  575         file_cleanup(faf, assoc);
  576         file_setdata(faf, assoc, NULL);
  577 
  578         --(faf->faf_nassocs); /* XXX gc? */
  579 
  580         return (0);
  581 }

Cache object: 427854cf5161de33b772a40030143ead


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