udf: Refuse RW mount of the filesystem instead of making it RO
[linux-3.10.git] / fs / dcookies.c
index ef758cf..ab5954b 100644 (file)
  * to the pair and can be looked up from userspace.
  */
 
-#include <linux/config.h>
 #include <linux/syscalls.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/mount.h>
 #include <linux/capability.h>
 #include <linux/dcache.h>
 #include <linux/mm.h>
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/dcookies.h>
 #include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/compat.h>
 #include <asm/uaccess.h>
 
 /* The dcookies are allocated from a kmem_cache and
  * code here is particularly performance critical
  */
 struct dcookie_struct {
-       struct dentry * dentry;
-       struct vfsmount * vfsmnt;
+       struct path path;
        struct list_head hash_list;
 };
 
 static LIST_HEAD(dcookie_users);
 static DEFINE_MUTEX(dcookie_mutex);
-static kmem_cache_t * dcookie_cache;
-static struct list_head * dcookie_hashtable;
-static size_t hash_size;
+static struct kmem_cache *dcookie_cache __read_mostly;
+static struct list_head *dcookie_hashtable __read_mostly;
+static size_t hash_size __read_mostly;
 
 static inline int is_live(void)
 {
@@ -51,7 +52,7 @@ static inline int is_live(void)
 /* The dentry is locked, its address will do for the cookie */
 static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
 {
-       return (unsigned long)dcs->dentry;
+       return (unsigned long)dcs->path.dentry;
 }
 
 
@@ -89,19 +90,22 @@ static void hash_dcookie(struct dcookie_struct * dcs)
 }
 
 
-static struct dcookie_struct * alloc_dcookie(struct dentry * dentry,
-       struct vfsmount * vfsmnt)
+static struct dcookie_struct *alloc_dcookie(struct path *path)
 {
-       struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL);
+       struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
+                                                       GFP_KERNEL);
+       struct dentry *d;
        if (!dcs)
                return NULL;
 
-       dentry->d_cookie = dcs;
+       d = path->dentry;
+       spin_lock(&d->d_lock);
+       d->d_flags |= DCACHE_COOKIE;
+       spin_unlock(&d->d_lock);
 
-       dcs->dentry = dget(dentry);
-       dcs->vfsmnt = mntget(vfsmnt);
+       dcs->path = *path;
+       path_get(path);
        hash_dcookie(dcs);
-
        return dcs;
 }
 
@@ -109,8 +113,7 @@ static struct dcookie_struct * alloc_dcookie(struct dentry * dentry,
 /* This is the main kernel-side routine that retrieves the cookie
  * value for a dentry/vfsmnt pair.
  */
-int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt,
-       unsigned long * cookie)
+int get_dcookie(struct path *path, unsigned long *cookie)
 {
        int err = 0;
        struct dcookie_struct * dcs;
@@ -122,14 +125,14 @@ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt,
                goto out;
        }
 
-       dcs = dentry->d_cookie;
-
-       if (!dcs)
-               dcs = alloc_dcookie(dentry, vfsmnt);
-
-       if (!dcs) {
-               err = -ENOMEM;
-               goto out;
+       if (path->dentry->d_flags & DCACHE_COOKIE) {
+               dcs = find_dcookie((unsigned long)path->dentry);
+       } else {
+               dcs = alloc_dcookie(path);
+               if (!dcs) {
+                       err = -ENOMEM;
+                       goto out;
+               }
        }
 
        *cookie = dcookie_value(dcs);
@@ -143,7 +146,7 @@ out:
 /* And here is where the userspace process can look up the cookie value
  * to retrieve the path.
  */
-asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
+SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len)
 {
        unsigned long cookie = (unsigned long)cookie64;
        int err = -EINVAL;
@@ -174,7 +177,9 @@ asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
                goto out;
 
        /* FIXME: (deleted) ? */
-       path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE);
+       path = d_path(&dcs->path, kbuf, PAGE_SIZE);
+
+       mutex_unlock(&dcookie_mutex);
 
        if (IS_ERR(path)) {
                err = PTR_ERR(path);
@@ -192,11 +197,22 @@ asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)
 
 out_free:
        kfree(kbuf);
+       return err;
 out:
        mutex_unlock(&dcookie_mutex);
        return err;
 }
 
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE4(lookup_dcookie, u32, w0, u32, w1, char __user *, buf, size_t, len)
+{
+#ifdef __BIG_ENDIAN
+       return sys_lookup_dcookie(((u64)w0 << 32) | w1, buf, len);
+#else
+       return sys_lookup_dcookie(((u64)w1 << 32) | w0, buf, len);
+#endif
+}
+#endif
 
 static int dcookie_init(void)
 {
@@ -206,7 +222,7 @@ static int dcookie_init(void)
 
        dcookie_cache = kmem_cache_create("dcookie_cache",
                sizeof(struct dcookie_struct),
-               0, 0, NULL, NULL);
+               0, 0, NULL);
 
        if (!dcookie_cache)
                goto out;
@@ -254,9 +270,13 @@ out_kmem:
 
 static void free_dcookie(struct dcookie_struct * dcs)
 {
-       dcs->dentry->d_cookie = NULL;
-       dput(dcs->dentry);
-       mntput(dcs->vfsmnt);
+       struct dentry *d = dcs->path.dentry;
+
+       spin_lock(&d->d_lock);
+       d->d_flags &= ~DCACHE_COOKIE;
+       spin_unlock(&d->d_lock);
+
+       path_put(&dcs->path);
        kmem_cache_free(dcookie_cache, dcs);
 }