Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.20 355 lines 7.2 kB view raw
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);