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