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 <asm/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(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(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 */ 149SYSCALL_DEFINE3(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 206#ifdef CONFIG_COMPAT 207COMPAT_SYSCALL_DEFINE4(lookup_dcookie, u32, w0, u32, w1, char __user *, buf, compat_size_t, len) 208{ 209#ifdef __BIG_ENDIAN 210 return sys_lookup_dcookie(((u64)w0 << 32) | w1, buf, len); 211#else 212 return sys_lookup_dcookie(((u64)w1 << 32) | w0, buf, len); 213#endif 214} 215#endif 216 217static int dcookie_init(void) 218{ 219 struct list_head * d; 220 unsigned int i, hash_bits; 221 int err = -ENOMEM; 222 223 dcookie_cache = kmem_cache_create("dcookie_cache", 224 sizeof(struct dcookie_struct), 225 0, 0, NULL); 226 227 if (!dcookie_cache) 228 goto out; 229 230 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 231 if (!dcookie_hashtable) 232 goto out_kmem; 233 234 err = 0; 235 236 /* 237 * Find the power-of-two list-heads that can fit into the allocation.. 238 * We don't guarantee that "sizeof(struct list_head)" is necessarily 239 * a power-of-two. 240 */ 241 hash_size = PAGE_SIZE / sizeof(struct list_head); 242 hash_bits = 0; 243 do { 244 hash_bits++; 245 } while ((hash_size >> hash_bits) != 0); 246 hash_bits--; 247 248 /* 249 * Re-calculate the actual number of entries and the mask 250 * from the number of bits we can fit. 251 */ 252 hash_size = 1UL << hash_bits; 253 254 /* And initialize the newly allocated array */ 255 d = dcookie_hashtable; 256 i = hash_size; 257 do { 258 INIT_LIST_HEAD(d); 259 d++; 260 i--; 261 } while (i); 262 263out: 264 return err; 265out_kmem: 266 kmem_cache_destroy(dcookie_cache); 267 goto out; 268} 269 270 271static void free_dcookie(struct dcookie_struct * dcs) 272{ 273 struct dentry *d = dcs->path.dentry; 274 275 spin_lock(&d->d_lock); 276 d->d_flags &= ~DCACHE_COOKIE; 277 spin_unlock(&d->d_lock); 278 279 path_put(&dcs->path); 280 kmem_cache_free(dcookie_cache, dcs); 281} 282 283 284static void dcookie_exit(void) 285{ 286 struct list_head * list; 287 struct list_head * pos; 288 struct list_head * pos2; 289 struct dcookie_struct * dcs; 290 size_t i; 291 292 for (i = 0; i < hash_size; ++i) { 293 list = dcookie_hashtable + i; 294 list_for_each_safe(pos, pos2, list) { 295 dcs = list_entry(pos, struct dcookie_struct, hash_list); 296 list_del(&dcs->hash_list); 297 free_dcookie(dcs); 298 } 299 } 300 301 kfree(dcookie_hashtable); 302 kmem_cache_destroy(dcookie_cache); 303} 304 305 306struct dcookie_user { 307 struct list_head next; 308}; 309 310struct dcookie_user * dcookie_register(void) 311{ 312 struct dcookie_user * user; 313 314 mutex_lock(&dcookie_mutex); 315 316 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 317 if (!user) 318 goto out; 319 320 if (!is_live() && dcookie_init()) 321 goto out_free; 322 323 list_add(&user->next, &dcookie_users); 324 325out: 326 mutex_unlock(&dcookie_mutex); 327 return user; 328out_free: 329 kfree(user); 330 user = NULL; 331 goto out; 332} 333 334 335void dcookie_unregister(struct dcookie_user * user) 336{ 337 mutex_lock(&dcookie_mutex); 338 339 list_del(&user->next); 340 kfree(user); 341 342 if (!is_live()) 343 dcookie_exit(); 344 345 mutex_unlock(&dcookie_mutex); 346} 347 348EXPORT_SYMBOL_GPL(dcookie_register); 349EXPORT_SYMBOL_GPL(dcookie_unregister); 350EXPORT_SYMBOL_GPL(get_dcookie); 351