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/dcookies.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  * dcookies.c
    3  *
    4  * Copyright 2002 John Levon <levon@movementarian.org>
    5  *
    6  * Persistent cookie-path mappings. These are used by
    7  * profilers to convert a per-task EIP value into something
    8  * non-transitory that can be processed at a later date.
    9  * This is done by locking the dentry/vfsmnt pair in the
   10  * kernel until released by the tasks needing the persistent
   11  * objects. The tag is simply an unsigned long that refers
   12  * to the pair and can be looked up from userspace.
   13  */
   14 
   15 #include <linux/syscalls.h>
   16 #include <linux/export.h>
   17 #include <linux/slab.h>
   18 #include <linux/list.h>
   19 #include <linux/mount.h>
   20 #include <linux/capability.h>
   21 #include <linux/dcache.h>
   22 #include <linux/mm.h>
   23 #include <linux/err.h>
   24 #include <linux/errno.h>
   25 #include <linux/dcookies.h>
   26 #include <linux/mutex.h>
   27 #include <linux/path.h>
   28 #include <asm/uaccess.h>
   29 
   30 /* The dcookies are allocated from a kmem_cache and
   31  * hashed onto a small number of lists. None of the
   32  * code here is particularly performance critical
   33  */
   34 struct dcookie_struct {
   35         struct path path;
   36         struct list_head hash_list;
   37 };
   38 
   39 static LIST_HEAD(dcookie_users);
   40 static DEFINE_MUTEX(dcookie_mutex);
   41 static struct kmem_cache *dcookie_cache __read_mostly;
   42 static struct list_head *dcookie_hashtable __read_mostly;
   43 static size_t hash_size __read_mostly;
   44 
   45 static inline int is_live(void)
   46 {
   47         return !(list_empty(&dcookie_users));
   48 }
   49 
   50 
   51 /* The dentry is locked, its address will do for the cookie */
   52 static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
   53 {
   54         return (unsigned long)dcs->path.dentry;
   55 }
   56 
   57 
   58 static size_t dcookie_hash(unsigned long dcookie)
   59 {
   60         return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
   61 }
   62 
   63 
   64 static struct dcookie_struct * find_dcookie(unsigned long dcookie)
   65 {
   66         struct dcookie_struct *found = NULL;
   67         struct dcookie_struct * dcs;
   68         struct list_head * pos;
   69         struct list_head * list;
   70 
   71         list = dcookie_hashtable + dcookie_hash(dcookie);
   72 
   73         list_for_each(pos, list) {
   74                 dcs = list_entry(pos, struct dcookie_struct, hash_list);
   75                 if (dcookie_value(dcs) == dcookie) {
   76                         found = dcs;
   77                         break;
   78                 }
   79         }
   80 
   81         return found;
   82 }
   83 
   84 
   85 static void hash_dcookie(struct dcookie_struct * dcs)
   86 {
   87         struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
   88         list_add(&dcs->hash_list, list);
   89 }
   90 
   91 
   92 static struct dcookie_struct *alloc_dcookie(struct path *path)
   93 {
   94         struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
   95                                                         GFP_KERNEL);
   96         struct dentry *d;
   97         if (!dcs)
   98                 return NULL;
   99 
  100         d = path->dentry;
  101         spin_lock(&d->d_lock);
  102         d->d_flags |= DCACHE_COOKIE;
  103         spin_unlock(&d->d_lock);
  104 
  105         dcs->path = *path;
  106         path_get(path);
  107         hash_dcookie(dcs);
  108         return dcs;
  109 }
  110 
  111 
  112 /* This is the main kernel-side routine that retrieves the cookie
  113  * value for a dentry/vfsmnt pair.
  114  */
  115 int get_dcookie(struct path *path, unsigned long *cookie)
  116 {
  117         int err = 0;
  118         struct dcookie_struct * dcs;
  119 
  120         mutex_lock(&dcookie_mutex);
  121 
  122         if (!is_live()) {
  123                 err = -EINVAL;
  124                 goto out;
  125         }
  126 
  127         if (path->dentry->d_flags & DCACHE_COOKIE) {
  128                 dcs = find_dcookie((unsigned long)path->dentry);
  129         } else {
  130                 dcs = alloc_dcookie(path);
  131                 if (!dcs) {
  132                         err = -ENOMEM;
  133                         goto out;
  134                 }
  135         }
  136 
  137         *cookie = dcookie_value(dcs);
  138 
  139 out:
  140         mutex_unlock(&dcookie_mutex);
  141         return err;
  142 }
  143 
  144 
  145 /* And here is where the userspace process can look up the cookie value
  146  * to retrieve the path.
  147  */
  148 SYSCALL_DEFINE(lookup_dcookie)(u64 cookie64, char __user * buf, size_t len)
  149 {
  150         unsigned long cookie = (unsigned long)cookie64;
  151         int err = -EINVAL;
  152         char * kbuf;
  153         char * path;
  154         size_t pathlen;
  155         struct dcookie_struct * dcs;
  156 
  157         /* we could leak path information to users
  158          * without dir read permission without this
  159          */
  160         if (!capable(CAP_SYS_ADMIN))
  161                 return -EPERM;
  162 
  163         mutex_lock(&dcookie_mutex);
  164 
  165         if (!is_live()) {
  166                 err = -EINVAL;
  167                 goto out;
  168         }
  169 
  170         if (!(dcs = find_dcookie(cookie)))
  171                 goto out;
  172 
  173         err = -ENOMEM;
  174         kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  175         if (!kbuf)
  176                 goto out;
  177 
  178         /* FIXME: (deleted) ? */
  179         path = d_path(&dcs->path, kbuf, PAGE_SIZE);
  180 
  181         mutex_unlock(&dcookie_mutex);
  182 
  183         if (IS_ERR(path)) {
  184                 err = PTR_ERR(path);
  185                 goto out_free;
  186         }
  187 
  188         err = -ERANGE;
  189  
  190         pathlen = kbuf + PAGE_SIZE - path;
  191         if (pathlen <= len) {
  192                 err = pathlen;
  193                 if (copy_to_user(buf, path, pathlen))
  194                         err = -EFAULT;
  195         }
  196 
  197 out_free:
  198         kfree(kbuf);
  199         return err;
  200 out:
  201         mutex_unlock(&dcookie_mutex);
  202         return err;
  203 }
  204 #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  205 asmlinkage long SyS_lookup_dcookie(u64 cookie64, long buf, long len)
  206 {
  207         return SYSC_lookup_dcookie(cookie64, (char __user *) buf, (size_t) len);
  208 }
  209 SYSCALL_ALIAS(sys_lookup_dcookie, SyS_lookup_dcookie);
  210 #endif
  211 
  212 static int dcookie_init(void)
  213 {
  214         struct list_head * d;
  215         unsigned int i, hash_bits;
  216         int err = -ENOMEM;
  217 
  218         dcookie_cache = kmem_cache_create("dcookie_cache",
  219                 sizeof(struct dcookie_struct),
  220                 0, 0, NULL);
  221 
  222         if (!dcookie_cache)
  223                 goto out;
  224 
  225         dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
  226         if (!dcookie_hashtable)
  227                 goto out_kmem;
  228 
  229         err = 0;
  230 
  231         /*
  232          * Find the power-of-two list-heads that can fit into the allocation..
  233          * We don't guarantee that "sizeof(struct list_head)" is necessarily
  234          * a power-of-two.
  235          */
  236         hash_size = PAGE_SIZE / sizeof(struct list_head);
  237         hash_bits = 0;
  238         do {
  239                 hash_bits++;
  240         } while ((hash_size >> hash_bits) != 0);
  241         hash_bits--;
  242 
  243         /*
  244          * Re-calculate the actual number of entries and the mask
  245          * from the number of bits we can fit.
  246          */
  247         hash_size = 1UL << hash_bits;
  248 
  249         /* And initialize the newly allocated array */
  250         d = dcookie_hashtable;
  251         i = hash_size;
  252         do {
  253                 INIT_LIST_HEAD(d);
  254                 d++;
  255                 i--;
  256         } while (i);
  257 
  258 out:
  259         return err;
  260 out_kmem:
  261         kmem_cache_destroy(dcookie_cache);
  262         goto out;
  263 }
  264 
  265 
  266 static void free_dcookie(struct dcookie_struct * dcs)
  267 {
  268         struct dentry *d = dcs->path.dentry;
  269 
  270         spin_lock(&d->d_lock);
  271         d->d_flags &= ~DCACHE_COOKIE;
  272         spin_unlock(&d->d_lock);
  273 
  274         path_put(&dcs->path);
  275         kmem_cache_free(dcookie_cache, dcs);
  276 }
  277 
  278 
  279 static void dcookie_exit(void)
  280 {
  281         struct list_head * list;
  282         struct list_head * pos;
  283         struct list_head * pos2;
  284         struct dcookie_struct * dcs;
  285         size_t i;
  286 
  287         for (i = 0; i < hash_size; ++i) {
  288                 list = dcookie_hashtable + i;
  289                 list_for_each_safe(pos, pos2, list) {
  290                         dcs = list_entry(pos, struct dcookie_struct, hash_list);
  291                         list_del(&dcs->hash_list);
  292                         free_dcookie(dcs);
  293                 }
  294         }
  295 
  296         kfree(dcookie_hashtable);
  297         kmem_cache_destroy(dcookie_cache);
  298 }
  299 
  300 
  301 struct dcookie_user {
  302         struct list_head next;
  303 };
  304  
  305 struct dcookie_user * dcookie_register(void)
  306 {
  307         struct dcookie_user * user;
  308 
  309         mutex_lock(&dcookie_mutex);
  310 
  311         user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
  312         if (!user)
  313                 goto out;
  314 
  315         if (!is_live() && dcookie_init())
  316                 goto out_free;
  317 
  318         list_add(&user->next, &dcookie_users);
  319 
  320 out:
  321         mutex_unlock(&dcookie_mutex);
  322         return user;
  323 out_free:
  324         kfree(user);
  325         user = NULL;
  326         goto out;
  327 }
  328 
  329 
  330 void dcookie_unregister(struct dcookie_user * user)
  331 {
  332         mutex_lock(&dcookie_mutex);
  333 
  334         list_del(&user->next);
  335         kfree(user);
  336 
  337         if (!is_live())
  338                 dcookie_exit();
  339 
  340         mutex_unlock(&dcookie_mutex);
  341 }
  342 
  343 EXPORT_SYMBOL_GPL(dcookie_register);
  344 EXPORT_SYMBOL_GPL(dcookie_unregister);
  345 EXPORT_SYMBOL_GPL(get_dcookie);

Cache object: 814af084680568ecc23a49adeb329257


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