Back to home page

LXR

 
 

    


0001 /*
0002  * dcookies.c
0003  *
0004  * Copyright 2002 John Levon <levon@movementarian.org>
0005  *
0006  * Persistent cookie-path mappings. These are used by
0007  * profilers to convert a per-task EIP value into something
0008  * non-transitory that can be processed at a later date.
0009  * This is done by locking the dentry/vfsmnt pair in the
0010  * kernel until released by the tasks needing the persistent
0011  * objects. The tag is simply an unsigned long that refers
0012  * to the pair and can be looked up from userspace.
0013  */
0014 
0015 #include <linux/syscalls.h>
0016 #include <linux/export.h>
0017 #include <linux/slab.h>
0018 #include <linux/list.h>
0019 #include <linux/mount.h>
0020 #include <linux/capability.h>
0021 #include <linux/dcache.h>
0022 #include <linux/mm.h>
0023 #include <linux/err.h>
0024 #include <linux/errno.h>
0025 #include <linux/dcookies.h>
0026 #include <linux/mutex.h>
0027 #include <linux/path.h>
0028 #include <linux/compat.h>
0029 #include <linux/uaccess.h>
0030 
0031 /* The dcookies are allocated from a kmem_cache and
0032  * hashed onto a small number of lists. None of the
0033  * code here is particularly performance critical
0034  */
0035 struct dcookie_struct {
0036     struct path path;
0037     struct list_head hash_list;
0038 };
0039 
0040 static LIST_HEAD(dcookie_users);
0041 static DEFINE_MUTEX(dcookie_mutex);
0042 static struct kmem_cache *dcookie_cache __read_mostly;
0043 static struct list_head *dcookie_hashtable __read_mostly;
0044 static size_t hash_size __read_mostly;
0045 
0046 static inline int is_live(void)
0047 {
0048     return !(list_empty(&dcookie_users));
0049 }
0050 
0051 
0052 /* The dentry is locked, its address will do for the cookie */
0053 static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
0054 {
0055     return (unsigned long)dcs->path.dentry;
0056 }
0057 
0058 
0059 static size_t dcookie_hash(unsigned long dcookie)
0060 {
0061     return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
0062 }
0063 
0064 
0065 static struct dcookie_struct * find_dcookie(unsigned long dcookie)
0066 {
0067     struct dcookie_struct *found = NULL;
0068     struct dcookie_struct * dcs;
0069     struct list_head * pos;
0070     struct list_head * list;
0071 
0072     list = dcookie_hashtable + dcookie_hash(dcookie);
0073 
0074     list_for_each(pos, list) {
0075         dcs = list_entry(pos, struct dcookie_struct, hash_list);
0076         if (dcookie_value(dcs) == dcookie) {
0077             found = dcs;
0078             break;
0079         }
0080     }
0081 
0082     return found;
0083 }
0084 
0085 
0086 static void hash_dcookie(struct dcookie_struct * dcs)
0087 {
0088     struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
0089     list_add(&dcs->hash_list, list);
0090 }
0091 
0092 
0093 static struct dcookie_struct *alloc_dcookie(const struct path *path)
0094 {
0095     struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
0096                             GFP_KERNEL);
0097     struct dentry *d;
0098     if (!dcs)
0099         return NULL;
0100 
0101     d = path->dentry;
0102     spin_lock(&d->d_lock);
0103     d->d_flags |= DCACHE_COOKIE;
0104     spin_unlock(&d->d_lock);
0105 
0106     dcs->path = *path;
0107     path_get(path);
0108     hash_dcookie(dcs);
0109     return dcs;
0110 }
0111 
0112 
0113 /* This is the main kernel-side routine that retrieves the cookie
0114  * value for a dentry/vfsmnt pair.
0115  */
0116 int get_dcookie(const struct path *path, unsigned long *cookie)
0117 {
0118     int err = 0;
0119     struct dcookie_struct * dcs;
0120 
0121     mutex_lock(&dcookie_mutex);
0122 
0123     if (!is_live()) {
0124         err = -EINVAL;
0125         goto out;
0126     }
0127 
0128     if (path->dentry->d_flags & DCACHE_COOKIE) {
0129         dcs = find_dcookie((unsigned long)path->dentry);
0130     } else {
0131         dcs = alloc_dcookie(path);
0132         if (!dcs) {
0133             err = -ENOMEM;
0134             goto out;
0135         }
0136     }
0137 
0138     *cookie = dcookie_value(dcs);
0139 
0140 out:
0141     mutex_unlock(&dcookie_mutex);
0142     return err;
0143 }
0144 
0145 
0146 /* And here is where the userspace process can look up the cookie value
0147  * to retrieve the path.
0148  */
0149 SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len)
0150 {
0151     unsigned long cookie = (unsigned long)cookie64;
0152     int err = -EINVAL;
0153     char * kbuf;
0154     char * path;
0155     size_t pathlen;
0156     struct dcookie_struct * dcs;
0157 
0158     /* we could leak path information to users
0159      * without dir read permission without this
0160      */
0161     if (!capable(CAP_SYS_ADMIN))
0162         return -EPERM;
0163 
0164     mutex_lock(&dcookie_mutex);
0165 
0166     if (!is_live()) {
0167         err = -EINVAL;
0168         goto out;
0169     }
0170 
0171     if (!(dcs = find_dcookie(cookie)))
0172         goto out;
0173 
0174     err = -ENOMEM;
0175     kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
0176     if (!kbuf)
0177         goto out;
0178 
0179     /* FIXME: (deleted) ? */
0180     path = d_path(&dcs->path, kbuf, PAGE_SIZE);
0181 
0182     mutex_unlock(&dcookie_mutex);
0183 
0184     if (IS_ERR(path)) {
0185         err = PTR_ERR(path);
0186         goto out_free;
0187     }
0188 
0189     err = -ERANGE;
0190  
0191     pathlen = kbuf + PAGE_SIZE - path;
0192     if (pathlen <= len) {
0193         err = pathlen;
0194         if (copy_to_user(buf, path, pathlen))
0195             err = -EFAULT;
0196     }
0197 
0198 out_free:
0199     kfree(kbuf);
0200     return err;
0201 out:
0202     mutex_unlock(&dcookie_mutex);
0203     return err;
0204 }
0205 
0206 #ifdef CONFIG_COMPAT
0207 COMPAT_SYSCALL_DEFINE4(lookup_dcookie, u32, w0, u32, w1, char __user *, buf, compat_size_t, len)
0208 {
0209 #ifdef __BIG_ENDIAN
0210     return sys_lookup_dcookie(((u64)w0 << 32) | w1, buf, len);
0211 #else
0212     return sys_lookup_dcookie(((u64)w1 << 32) | w0, buf, len);
0213 #endif
0214 }
0215 #endif
0216 
0217 static int dcookie_init(void)
0218 {
0219     struct list_head * d;
0220     unsigned int i, hash_bits;
0221     int err = -ENOMEM;
0222 
0223     dcookie_cache = kmem_cache_create("dcookie_cache",
0224         sizeof(struct dcookie_struct),
0225         0, 0, NULL);
0226 
0227     if (!dcookie_cache)
0228         goto out;
0229 
0230     dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
0231     if (!dcookie_hashtable)
0232         goto out_kmem;
0233 
0234     err = 0;
0235 
0236     /*
0237      * Find the power-of-two list-heads that can fit into the allocation..
0238      * We don't guarantee that "sizeof(struct list_head)" is necessarily
0239      * a power-of-two.
0240      */
0241     hash_size = PAGE_SIZE / sizeof(struct list_head);
0242     hash_bits = 0;
0243     do {
0244         hash_bits++;
0245     } while ((hash_size >> hash_bits) != 0);
0246     hash_bits--;
0247 
0248     /*
0249      * Re-calculate the actual number of entries and the mask
0250      * from the number of bits we can fit.
0251      */
0252     hash_size = 1UL << hash_bits;
0253 
0254     /* And initialize the newly allocated array */
0255     d = dcookie_hashtable;
0256     i = hash_size;
0257     do {
0258         INIT_LIST_HEAD(d);
0259         d++;
0260         i--;
0261     } while (i);
0262 
0263 out:
0264     return err;
0265 out_kmem:
0266     kmem_cache_destroy(dcookie_cache);
0267     goto out;
0268 }
0269 
0270 
0271 static void free_dcookie(struct dcookie_struct * dcs)
0272 {
0273     struct dentry *d = dcs->path.dentry;
0274 
0275     spin_lock(&d->d_lock);
0276     d->d_flags &= ~DCACHE_COOKIE;
0277     spin_unlock(&d->d_lock);
0278 
0279     path_put(&dcs->path);
0280     kmem_cache_free(dcookie_cache, dcs);
0281 }
0282 
0283 
0284 static void dcookie_exit(void)
0285 {
0286     struct list_head * list;
0287     struct list_head * pos;
0288     struct list_head * pos2;
0289     struct dcookie_struct * dcs;
0290     size_t i;
0291 
0292     for (i = 0; i < hash_size; ++i) {
0293         list = dcookie_hashtable + i;
0294         list_for_each_safe(pos, pos2, list) {
0295             dcs = list_entry(pos, struct dcookie_struct, hash_list);
0296             list_del(&dcs->hash_list);
0297             free_dcookie(dcs);
0298         }
0299     }
0300 
0301     kfree(dcookie_hashtable);
0302     kmem_cache_destroy(dcookie_cache);
0303 }
0304 
0305 
0306 struct dcookie_user {
0307     struct list_head next;
0308 };
0309  
0310 struct dcookie_user * dcookie_register(void)
0311 {
0312     struct dcookie_user * user;
0313 
0314     mutex_lock(&dcookie_mutex);
0315 
0316     user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
0317     if (!user)
0318         goto out;
0319 
0320     if (!is_live() && dcookie_init())
0321         goto out_free;
0322 
0323     list_add(&user->next, &dcookie_users);
0324 
0325 out:
0326     mutex_unlock(&dcookie_mutex);
0327     return user;
0328 out_free:
0329     kfree(user);
0330     user = NULL;
0331     goto out;
0332 }
0333 
0334 
0335 void dcookie_unregister(struct dcookie_user * user)
0336 {
0337     mutex_lock(&dcookie_mutex);
0338 
0339     list_del(&user->next);
0340     kfree(user);
0341 
0342     if (!is_live())
0343         dcookie_exit();
0344 
0345     mutex_unlock(&dcookie_mutex);
0346 }
0347 
0348 EXPORT_SYMBOL_GPL(dcookie_register);
0349 EXPORT_SYMBOL_GPL(dcookie_unregister);
0350 EXPORT_SYMBOL_GPL(get_dcookie);