dcookies.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. #include <linux/syscalls.h>
  15. #include <linux/module.h>
  16. #include <linux/slab.h>
  17. #include <linux/list.h>
  18. #include <linux/mount.h>
  19. #include <linux/capability.h>
  20. #include <linux/dcache.h>
  21. #include <linux/mm.h>
  22. #include <linux/err.h>
  23. #include <linux/errno.h>
  24. #include <linux/dcookies.h>
  25. #include <linux/mutex.h>
  26. #include <asm/uaccess.h>
  27. /* The dcookies are allocated from a kmem_cache and
  28. * hashed onto a small number of lists. None of the
  29. * code here is particularly performance critical
  30. */
  31. struct dcookie_struct {
  32. struct dentry * dentry;
  33. struct vfsmount * vfsmnt;
  34. struct list_head hash_list;
  35. };
  36. static LIST_HEAD(dcookie_users);
  37. static DEFINE_MUTEX(dcookie_mutex);
  38. static struct kmem_cache *dcookie_cache __read_mostly;
  39. static struct list_head *dcookie_hashtable __read_mostly;
  40. static size_t hash_size __read_mostly;
  41. static inline int is_live(void)
  42. {
  43. return !(list_empty(&dcookie_users));
  44. }
  45. /* The dentry is locked, its address will do for the cookie */
  46. static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
  47. {
  48. return (unsigned long)dcs->dentry;
  49. }
  50. static size_t dcookie_hash(unsigned long dcookie)
  51. {
  52. return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
  53. }
  54. static struct dcookie_struct * find_dcookie(unsigned long dcookie)
  55. {
  56. struct dcookie_struct *found = NULL;
  57. struct dcookie_struct * dcs;
  58. struct list_head * pos;
  59. struct list_head * list;
  60. list = dcookie_hashtable + dcookie_hash(dcookie);
  61. list_for_each(pos, list) {
  62. dcs = list_entry(pos, struct dcookie_struct, hash_list);
  63. if (dcookie_value(dcs) == dcookie) {
  64. found = dcs;
  65. break;
  66. }
  67. }
  68. return found;
  69. }
  70. static void hash_dcookie(struct dcookie_struct * dcs)
  71. {
  72. struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
  73. list_add(&dcs->hash_list, list);
  74. }
  75. static struct dcookie_struct * alloc_dcookie(struct dentry * dentry,
  76. struct vfsmount * vfsmnt)
  77. {
  78. struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL);
  79. if (!dcs)
  80. return NULL;
  81. dentry->d_cookie = dcs;
  82. dcs->dentry = dget(dentry);
  83. dcs->vfsmnt = mntget(vfsmnt);
  84. hash_dcookie(dcs);
  85. return dcs;
  86. }
  87. /* This is the main kernel-side routine that retrieves the cookie
  88. * value for a dentry/vfsmnt pair.
  89. */
  90. int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt,
  91. unsigned long * cookie)
  92. {
  93. int err = 0;
  94. struct dcookie_struct * dcs;
  95. mutex_lock(&dcookie_mutex);
  96. if (!is_live()) {
  97. err = -EINVAL;
  98. goto out;
  99. }
  100. dcs = dentry->d_cookie;
  101. if (!dcs)
  102. dcs = alloc_dcookie(dentry, vfsmnt);
  103. if (!dcs) {
  104. err = -ENOMEM;
  105. goto out;
  106. }
  107. *cookie = dcookie_value(dcs);
  108. out:
  109. mutex_unlock(&dcookie_mutex);
  110. return err;
  111. }
  112. /* And here is where the userspace process can look up the cookie value
  113. * to retrieve the path.
  114. */
  115. asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
  116. {
  117. unsigned long cookie = (unsigned long)cookie64;
  118. int err = -EINVAL;
  119. char * kbuf;
  120. char * path;
  121. size_t pathlen;
  122. struct dcookie_struct * dcs;
  123. /* we could leak path information to users
  124. * without dir read permission without this
  125. */
  126. if (!capable(CAP_SYS_ADMIN))
  127. return -EPERM;
  128. mutex_lock(&dcookie_mutex);
  129. if (!is_live()) {
  130. err = -EINVAL;
  131. goto out;
  132. }
  133. if (!(dcs = find_dcookie(cookie)))
  134. goto out;
  135. err = -ENOMEM;
  136. kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  137. if (!kbuf)
  138. goto out;
  139. /* FIXME: (deleted) ? */
  140. path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE);
  141. if (IS_ERR(path)) {
  142. err = PTR_ERR(path);
  143. goto out_free;
  144. }
  145. err = -ERANGE;
  146. pathlen = kbuf + PAGE_SIZE - path;
  147. if (pathlen <= len) {
  148. err = pathlen;
  149. if (copy_to_user(buf, path, pathlen))
  150. err = -EFAULT;
  151. }
  152. out_free:
  153. kfree(kbuf);
  154. out:
  155. mutex_unlock(&dcookie_mutex);
  156. return err;
  157. }
  158. static int dcookie_init(void)
  159. {
  160. struct list_head * d;
  161. unsigned int i, hash_bits;
  162. int err = -ENOMEM;
  163. dcookie_cache = kmem_cache_create("dcookie_cache",
  164. sizeof(struct dcookie_struct),
  165. 0, 0, NULL);
  166. if (!dcookie_cache)
  167. goto out;
  168. dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
  169. if (!dcookie_hashtable)
  170. goto out_kmem;
  171. err = 0;
  172. /*
  173. * Find the power-of-two list-heads that can fit into the allocation..
  174. * We don't guarantee that "sizeof(struct list_head)" is necessarily
  175. * a power-of-two.
  176. */
  177. hash_size = PAGE_SIZE / sizeof(struct list_head);
  178. hash_bits = 0;
  179. do {
  180. hash_bits++;
  181. } while ((hash_size >> hash_bits) != 0);
  182. hash_bits--;
  183. /*
  184. * Re-calculate the actual number of entries and the mask
  185. * from the number of bits we can fit.
  186. */
  187. hash_size = 1UL << hash_bits;
  188. /* And initialize the newly allocated array */
  189. d = dcookie_hashtable;
  190. i = hash_size;
  191. do {
  192. INIT_LIST_HEAD(d);
  193. d++;
  194. i--;
  195. } while (i);
  196. out:
  197. return err;
  198. out_kmem:
  199. kmem_cache_destroy(dcookie_cache);
  200. goto out;
  201. }
  202. static void free_dcookie(struct dcookie_struct * dcs)
  203. {
  204. dcs->dentry->d_cookie = NULL;
  205. dput(dcs->dentry);
  206. mntput(dcs->vfsmnt);
  207. kmem_cache_free(dcookie_cache, dcs);
  208. }
  209. static void dcookie_exit(void)
  210. {
  211. struct list_head * list;
  212. struct list_head * pos;
  213. struct list_head * pos2;
  214. struct dcookie_struct * dcs;
  215. size_t i;
  216. for (i = 0; i < hash_size; ++i) {
  217. list = dcookie_hashtable + i;
  218. list_for_each_safe(pos, pos2, list) {
  219. dcs = list_entry(pos, struct dcookie_struct, hash_list);
  220. list_del(&dcs->hash_list);
  221. free_dcookie(dcs);
  222. }
  223. }
  224. kfree(dcookie_hashtable);
  225. kmem_cache_destroy(dcookie_cache);
  226. }
  227. struct dcookie_user {
  228. struct list_head next;
  229. };
  230. struct dcookie_user * dcookie_register(void)
  231. {
  232. struct dcookie_user * user;
  233. mutex_lock(&dcookie_mutex);
  234. user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
  235. if (!user)
  236. goto out;
  237. if (!is_live() && dcookie_init())
  238. goto out_free;
  239. list_add(&user->next, &dcookie_users);
  240. out:
  241. mutex_unlock(&dcookie_mutex);
  242. return user;
  243. out_free:
  244. kfree(user);
  245. user = NULL;
  246. goto out;
  247. }
  248. void dcookie_unregister(struct dcookie_user * user)
  249. {
  250. mutex_lock(&dcookie_mutex);
  251. list_del(&user->next);
  252. kfree(user);
  253. if (!is_live())
  254. dcookie_exit();
  255. mutex_unlock(&dcookie_mutex);
  256. }
  257. EXPORT_SYMBOL_GPL(dcookie_register);
  258. EXPORT_SYMBOL_GPL(dcookie_unregister);
  259. EXPORT_SYMBOL_GPL(get_dcookie);