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/module.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 */ 34struct dcookie_struct { 35 struct path path; 36 struct list_head hash_list; 37}; 38 39static LIST_HEAD(dcookie_users); 40static DEFINE_MUTEX(dcookie_mutex); 41static struct kmem_cache *dcookie_cache __read_mostly; 42static struct list_head *dcookie_hashtable __read_mostly; 43static size_t hash_size __read_mostly; 44 45static 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 */ 52static inline unsigned long dcookie_value(struct dcookie_struct * dcs) 53{ 54 return (unsigned long)dcs->path.dentry; 55} 56 57 58static size_t dcookie_hash(unsigned long dcookie) 59{ 60 return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1); 61} 62 63 64static 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 85static 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 92static 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 */ 115int 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 139out: 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 */ 148SYSCALL_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 path = d_path(&dcs->path, kbuf, PAGE_SIZE); 179 180 if (IS_ERR(path)) { 181 err = PTR_ERR(path); 182 goto out_free; 183 } 184 185 err = -ERANGE; 186 187 pathlen = kbuf + PAGE_SIZE - path; 188 if (pathlen <= len) { 189 err = pathlen; 190 if (copy_to_user(buf, path, pathlen)) 191 err = -EFAULT; 192 } 193 194out_free: 195 kfree(kbuf); 196out: 197 mutex_unlock(&dcookie_mutex); 198 return err; 199} 200#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS 201asmlinkage long SyS_lookup_dcookie(u64 cookie64, long buf, long len) 202{ 203 return SYSC_lookup_dcookie(cookie64, (char __user *) buf, (size_t) len); 204} 205SYSCALL_ALIAS(sys_lookup_dcookie, SyS_lookup_dcookie); 206#endif 207 208static int dcookie_init(void) 209{ 210 struct list_head * d; 211 unsigned int i, hash_bits; 212 int err = -ENOMEM; 213 214 dcookie_cache = kmem_cache_create("dcookie_cache", 215 sizeof(struct dcookie_struct), 216 0, 0, NULL); 217 218 if (!dcookie_cache) 219 goto out; 220 221 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 222 if (!dcookie_hashtable) 223 goto out_kmem; 224 225 err = 0; 226 227 /* 228 * Find the power-of-two list-heads that can fit into the allocation.. 229 * We don't guarantee that "sizeof(struct list_head)" is necessarily 230 * a power-of-two. 231 */ 232 hash_size = PAGE_SIZE / sizeof(struct list_head); 233 hash_bits = 0; 234 do { 235 hash_bits++; 236 } while ((hash_size >> hash_bits) != 0); 237 hash_bits--; 238 239 /* 240 * Re-calculate the actual number of entries and the mask 241 * from the number of bits we can fit. 242 */ 243 hash_size = 1UL << hash_bits; 244 245 /* And initialize the newly allocated array */ 246 d = dcookie_hashtable; 247 i = hash_size; 248 do { 249 INIT_LIST_HEAD(d); 250 d++; 251 i--; 252 } while (i); 253 254out: 255 return err; 256out_kmem: 257 kmem_cache_destroy(dcookie_cache); 258 goto out; 259} 260 261 262static void free_dcookie(struct dcookie_struct * dcs) 263{ 264 struct dentry *d = dcs->path.dentry; 265 266 spin_lock(&d->d_lock); 267 d->d_flags &= ~DCACHE_COOKIE; 268 spin_unlock(&d->d_lock); 269 270 path_put(&dcs->path); 271 kmem_cache_free(dcookie_cache, dcs); 272} 273 274 275static void dcookie_exit(void) 276{ 277 struct list_head * list; 278 struct list_head * pos; 279 struct list_head * pos2; 280 struct dcookie_struct * dcs; 281 size_t i; 282 283 for (i = 0; i < hash_size; ++i) { 284 list = dcookie_hashtable + i; 285 list_for_each_safe(pos, pos2, list) { 286 dcs = list_entry(pos, struct dcookie_struct, hash_list); 287 list_del(&dcs->hash_list); 288 free_dcookie(dcs); 289 } 290 } 291 292 kfree(dcookie_hashtable); 293 kmem_cache_destroy(dcookie_cache); 294} 295 296 297struct dcookie_user { 298 struct list_head next; 299}; 300 301struct dcookie_user * dcookie_register(void) 302{ 303 struct dcookie_user * user; 304 305 mutex_lock(&dcookie_mutex); 306 307 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 308 if (!user) 309 goto out; 310 311 if (!is_live() && dcookie_init()) 312 goto out_free; 313 314 list_add(&user->next, &dcookie_users); 315 316out: 317 mutex_unlock(&dcookie_mutex); 318 return user; 319out_free: 320 kfree(user); 321 user = NULL; 322 goto out; 323} 324 325 326void dcookie_unregister(struct dcookie_user * user) 327{ 328 mutex_lock(&dcookie_mutex); 329 330 list_del(&user->next); 331 kfree(user); 332 333 if (!is_live()) 334 dcookie_exit(); 335 336 mutex_unlock(&dcookie_mutex); 337} 338 339EXPORT_SYMBOL_GPL(dcookie_register); 340EXPORT_SYMBOL_GPL(dcookie_unregister); 341EXPORT_SYMBOL_GPL(get_dcookie); 342