Version:  2.0.40 2.2.26 2.4.37 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10

Linux/fs/dcookies.c

  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  */
 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         return (unsigned long)dcs->path.dentry;
 56 }
 57 
 58 
 59 static size_t dcookie_hash(unsigned long dcookie)
 60 {
 61         return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
 62 }
 63 
 64 
 65 static 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 
 86 static 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 
 93 static 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  */
116 int 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 
140 out:
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  */
149 SYSCALL_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 
198 out_free:
199         kfree(kbuf);
200         return err;
201 out:
202         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 static 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 
263 out:
264         return err;
265 out_kmem:
266         kmem_cache_destroy(dcookie_cache);
267         goto out;
268 }
269 
270 
271 static 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 
284 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         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 
306 struct dcookie_user {
307         struct list_head next;
308 };
309  
310 struct 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 
325 out:
326         mutex_unlock(&dcookie_mutex);
327         return user;
328 out_free:
329         kfree(user);
330         user = NULL;
331         goto out;
332 }
333 
334 
335 void 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 
348 EXPORT_SYMBOL_GPL(dcookie_register);
349 EXPORT_SYMBOL_GPL(dcookie_unregister);
350 EXPORT_SYMBOL_GPL(get_dcookie);
351 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us