Branch data Line data Source code
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 : : */
35 : : struct dcookie_struct {
36 : : struct path path;
37 : : struct list_head hash_list;
38 : : };
39 : :
40 : : static LIST_HEAD(dcookie_users);
41 : : static DEFINE_MUTEX(dcookie_mutex);
42 : : static struct kmem_cache *dcookie_cache __read_mostly;
43 : : static struct list_head *dcookie_hashtable __read_mostly;
44 : : static size_t hash_size __read_mostly;
45 : :
46 : : static 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 */
53 : : static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
54 : : {
55 : 0 : return (unsigned long)dcs->path.dentry;
56 : : }
57 : :
58 : :
59 : : static size_t dcookie_hash(unsigned long dcookie)
60 : : {
61 : 0 : return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
62 : : }
63 : :
64 : :
65 : 0 : static struct dcookie_struct * find_dcookie(unsigned long dcookie)
66 : : {
67 : : struct dcookie_struct *found = NULL;
68 : 0 : struct dcookie_struct * dcs;
69 : : struct list_head * pos;
70 : : struct list_head * list;
71 : :
72 : 0 : list = dcookie_hashtable + dcookie_hash(dcookie);
73 : :
74 [ # # ][ # # ]: 0 : list_for_each(pos, list) {
75 : 0 : dcs = list_entry(pos, struct dcookie_struct, hash_list);
76 [ # # ][ # # ]: 0 : if (dcookie_value(dcs) == dcookie) {
77 : : found = dcs;
78 : : break;
79 : : }
80 : : }
81 : :
82 : 0 : return found;
83 : : }
84 : :
85 : :
86 : 0 : static void hash_dcookie(struct dcookie_struct * dcs)
87 : : {
88 : 0 : struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
89 : 0 : list_add(&dcs->hash_list, list);
90 : : }
91 : :
92 : :
93 : 0 : static struct dcookie_struct *alloc_dcookie(struct path *path)
94 : : {
95 : 0 : struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
96 : : GFP_KERNEL);
97 : : struct dentry *d;
98 [ # # ]: 0 : if (!dcs)
99 : : return NULL;
100 : :
101 : 0 : d = path->dentry;
102 : : spin_lock(&d->d_lock);
103 : 0 : d->d_flags |= DCACHE_COOKIE;
104 : : spin_unlock(&d->d_lock);
105 : :
106 : 0 : dcs->path = *path;
107 : 0 : path_get(path);
108 : : hash_dcookie(dcs);
109 : 0 : 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 : : */
116 : 0 : int get_dcookie(struct path *path, unsigned long *cookie)
117 : : {
118 : : int err = 0;
119 : 0 : struct dcookie_struct * dcs;
120 : :
121 : 0 : mutex_lock(&dcookie_mutex);
122 : :
123 [ # # ]: 0 : if (!is_live()) {
124 : : err = -EINVAL;
125 : : goto out;
126 : : }
127 : :
128 [ # # ]: 0 : if (path->dentry->d_flags & DCACHE_COOKIE) {
129 : 0 : dcs = find_dcookie((unsigned long)path->dentry);
130 : : } else {
131 : 0 : dcs = alloc_dcookie(path);
132 [ # # ]: 0 : if (!dcs) {
133 : : err = -ENOMEM;
134 : : goto out;
135 : : }
136 : : }
137 : :
138 : 0 : *cookie = dcookie_value(dcs);
139 : :
140 : : out:
141 : 0 : mutex_unlock(&dcookie_mutex);
142 : 0 : 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 : : */
149 : 0 : SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len)
150 : : {
151 : 0 : 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 [ # # ]: 0 : if (!capable(CAP_SYS_ADMIN))
162 : : return -EPERM;
163 : :
164 : 0 : mutex_lock(&dcookie_mutex);
165 : :
166 [ # # ]: 0 : if (!is_live()) {
167 : : err = -EINVAL;
168 : : goto out;
169 : : }
170 : :
171 [ # # ]: 0 : if (!(dcs = find_dcookie(cookie)))
172 : : goto out;
173 : :
174 : : err = -ENOMEM;
175 : : kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
176 [ # # ]: 0 : if (!kbuf)
177 : : goto out;
178 : :
179 : : /* FIXME: (deleted) ? */
180 : 0 : path = d_path(&dcs->path, kbuf, PAGE_SIZE);
181 : :
182 : 0 : mutex_unlock(&dcookie_mutex);
183 : :
184 [ # # ]: 0 : if (IS_ERR(path)) {
185 : : err = PTR_ERR(path);
186 : : goto out_free;
187 : : }
188 : :
189 : : err = -ERANGE;
190 : :
191 : 0 : pathlen = kbuf + PAGE_SIZE - path;
192 [ # # ]: 0 : if (pathlen <= len) {
193 : : err = pathlen;
194 [ # # ]: 0 : if (copy_to_user(buf, path, pathlen))
195 : : err = -EFAULT;
196 : : }
197 : :
198 : : out_free:
199 : 0 : kfree(kbuf);
200 : : return err;
201 : : out:
202 : 0 : mutex_unlock(&dcookie_mutex);
203 : : return err;
204 : : }
205 : :
206 : : #ifdef CONFIG_COMPAT
207 : : COMPAT_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 : :
217 : 0 : static int dcookie_init(void)
218 : : {
219 : : struct list_head * d;
220 : : unsigned int i, hash_bits;
221 : : int err = -ENOMEM;
222 : :
223 : 0 : dcookie_cache = kmem_cache_create("dcookie_cache",
224 : : sizeof(struct dcookie_struct),
225 : : 0, 0, NULL);
226 : :
227 [ # # ]: 0 : if (!dcookie_cache)
228 : : goto out;
229 : :
230 : 0 : dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
231 [ # # ]: 0 : 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 : 0 : hash_size = PAGE_SIZE / sizeof(struct list_head);
242 : : hash_bits = 0;
243 : : do {
244 : 0 : hash_bits++;
245 [ # # ]: 0 : } 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 : 0 : 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 : 0 : d++;
260 : 0 : i--;
261 [ # # ]: 0 : } while (i);
262 : :
263 : : out:
264 : 0 : return err;
265 : : out_kmem:
266 : 0 : kmem_cache_destroy(dcookie_cache);
267 : 0 : goto out;
268 : : }
269 : :
270 : :
271 : 0 : static void free_dcookie(struct dcookie_struct * dcs)
272 : : {
273 : 0 : struct dentry *d = dcs->path.dentry;
274 : :
275 : : spin_lock(&d->d_lock);
276 : 0 : d->d_flags &= ~DCACHE_COOKIE;
277 : : spin_unlock(&d->d_lock);
278 : :
279 : 0 : path_put(&dcs->path);
280 : 0 : kmem_cache_free(dcookie_cache, dcs);
281 : 0 : }
282 : :
283 : :
284 : 0 : static 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 [ # # ]: 0 : for (i = 0; i < hash_size; ++i) {
293 : 0 : list = dcookie_hashtable + i;
294 [ # # ]: 0 : list_for_each_safe(pos, pos2, list) {
295 : 0 : dcs = list_entry(pos, struct dcookie_struct, hash_list);
296 : : list_del(&dcs->hash_list);
297 : 0 : free_dcookie(dcs);
298 : : }
299 : : }
300 : :
301 : 0 : kfree(dcookie_hashtable);
302 : 0 : kmem_cache_destroy(dcookie_cache);
303 : 0 : }
304 : :
305 : :
306 : : struct dcookie_user {
307 : : struct list_head next;
308 : : };
309 : :
310 : 0 : struct dcookie_user * dcookie_register(void)
311 : : {
312 : : struct dcookie_user * user;
313 : :
314 : 0 : mutex_lock(&dcookie_mutex);
315 : :
316 : : user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
317 [ # # ]: 0 : if (!user)
318 : : goto out;
319 : :
320 [ # # ][ # # ]: 0 : if (!is_live() && dcookie_init())
321 : : goto out_free;
322 : :
323 : 0 : list_add(&user->next, &dcookie_users);
324 : :
325 : : out:
326 : 0 : mutex_unlock(&dcookie_mutex);
327 : 0 : return user;
328 : : out_free:
329 : 0 : kfree(user);
330 : : user = NULL;
331 : 0 : goto out;
332 : : }
333 : :
334 : :
335 : 0 : void dcookie_unregister(struct dcookie_user * user)
336 : : {
337 : 0 : mutex_lock(&dcookie_mutex);
338 : :
339 : : list_del(&user->next);
340 : 0 : kfree(user);
341 : :
342 [ # # ]: 0 : if (!is_live())
343 : 0 : dcookie_exit();
344 : :
345 : 0 : mutex_unlock(&dcookie_mutex);
346 : 0 : }
347 : :
348 : : EXPORT_SYMBOL_GPL(dcookie_register);
349 : : EXPORT_SYMBOL_GPL(dcookie_unregister);
350 : : EXPORT_SYMBOL_GPL(get_dcookie);
|