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