In preparation for supporting ->follow_link in RCU-walk,
make sure all ->follow_link handers which are not atomic
will fail if LOOKUP_RCU is set.

Later patches will make some of these handle LOOKUP_RCU
more gracefully.

This is current achieved by introducing a new function
"nd_is_rcu" to check if a nameidata has LOOKUP_RCU set.
There must be a better way.

Signed-off-by: NeilBrown <ne...@suse.de>
---
 drivers/staging/lustre/lustre/llite/symlink.c |    3 +++
 fs/9p/vfs_inode.c                             |    6 +++++-
 fs/9p/vfs_inode_dotl.c                        |    5 ++++-
 fs/befs/linuxvfs.c                            |    2 ++
 fs/cifs/link.c                                |    2 ++
 fs/configfs/symlink.c                         |    7 ++++++-
 fs/ecryptfs/inode.c                           |    7 ++++++-
 fs/fuse/dir.c                                 |    2 ++
 fs/gfs2/inode.c                               |    2 ++
 fs/hostfs/hostfs_kern.c                       |    7 ++++++-
 fs/kernfs/symlink.c                           |    7 ++++++-
 fs/namei.c                                    |    8 ++++++++
 fs/nfs/symlink.c                              |    2 ++
 fs/overlayfs/inode.c                          |    3 +++
 fs/proc/base.c                                |    2 ++
 fs/proc/namespaces.c                          |    3 +++
 fs/proc/self.c                                |    7 ++++++-
 fs/proc/thread_self.c                         |   10 ++++++++--
 fs/xfs/xfs_iops.c                             |    2 ++
 include/linux/fs.h                            |    1 +
 mm/shmem.c                                    |    6 +++++-
 21 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/lustre/lustre/llite/symlink.c 
b/drivers/staging/lustre/lustre/llite/symlink.c
index d7a1c6c48846..e8a8d25fcabf 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -125,6 +125,9 @@ static void *ll_follow_link(struct dentry *dentry, struct 
nameidata *nd)
        int rc;
        char *symname = NULL;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
        CDEBUG(D_VFSTRACE, "VFS Op\n");
        ll_inode_size_lock(inode);
        rc = ll_readlink_internal(inode, &request, &symname);
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 3662f1d1d9cf..8aff5d684154 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1281,7 +1281,11 @@ done:
 static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        int len = 0;
-       char *link = __getname();
+       char *link;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+       link = __getname();
 
        p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 6054c16b8fae..51776a3cc842 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -914,9 +914,12 @@ v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct 
nameidata *nd)
 {
        int retval;
        struct p9_fid *fid;
-       char *link = __getname();
+       char *link;
        char *target;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+       link = __getname();
        p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
        if (!link) {
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index e089f1985fca..bbe8f90924b2 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -477,6 +477,8 @@ befs_follow_link(struct dentry *dentry, struct nameidata 
*nd)
        befs_off_t len = data->size;
        char *link;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        if (len == 0) {
                befs_error(sb, "Long symlink with illegal length");
                link = ERR_PTR(-EIO);
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 2ec6037f61c7..0dbe1a326632 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -639,6 +639,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata 
*nd)
        struct cifs_tcon *tcon;
        struct TCP_Server_Info *server;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        xid = get_xid();
 
        tlink = cifs_sb_tlink(cifs_sb);
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index cc9f2546ea4a..1397342aad5b 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -282,7 +282,12 @@ static int configfs_getlink(struct dentry *dentry, char * 
path)
 static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        int error = -ENOMEM;
-       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       unsigned long page;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       page = get_zeroed_page(GFP_KERNEL);
 
        if (page) {
                error = configfs_getlink(dentry, (char *)page);
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b08b5187f662..49d3dd96344c 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -678,7 +678,12 @@ out:
 static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        size_t len;
-       char *buf = ecryptfs_readlink_lower(dentry, &len);
+       char *buf;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       buf = ecryptfs_readlink_lower(dentry, &len);
        if (IS_ERR(buf))
                goto out;
        fsstack_copy_attr_atime(dentry->d_inode,
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 1545b711ddcf..15d326ec5943 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1402,6 +1402,8 @@ static void free_link(char *link)
 
 static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        nd_set_link(nd, read_link(dentry));
        return NULL;
 }
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 73c72253faac..21086c7870f1 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1557,6 +1557,8 @@ static void *gfs2_follow_link(struct dentry *dentry, 
struct nameidata *nd)
        char *buf;
        int error;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
        error = gfs2_glock_nq(&i_gh);
        if (error) {
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index fd62cae0fdcb..374d04909538 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -884,7 +884,12 @@ static const struct inode_operations hostfs_dir_iops = {
 
 static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-       char *link = __getname();
+       char *link;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       link = __getname();
        if (link) {
                char *path = dentry_name(dentry);
                int err = -ENOMEM;
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 8a198898e39a..8e5421f386c0 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -115,7 +115,12 @@ static int kernfs_getlink(struct dentry *dentry, char 
*path)
 static void *kernfs_iop_follow_link(struct dentry *dentry, struct nameidata 
*nd)
 {
        int error = -ENOMEM;
-       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       unsigned long page;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       page = get_zeroed_page(GFP_KERNEL);
        if (page) {
                error = kernfs_getlink(dentry, (char *) page);
                if (error < 0)
diff --git a/fs/namei.c b/fs/namei.c
index 184aaafffaa9..eefa4a00501a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4500,6 +4500,8 @@ EXPORT_SYMBOL(page_readlink);
 void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
 {
        struct page *page = NULL;
+       if (nd->flags & LOOKUP_RCU)
+               return ERR_PTR(-ECHILD);
        nd_set_link(nd, page_getlink(dentry, &page));
        return page;
 }
@@ -4567,3 +4569,9 @@ const struct inode_operations 
page_symlink_inode_operations = {
        .put_link       = page_put_link,
 };
 EXPORT_SYMBOL(page_symlink_inode_operations);
+
+int nd_is_rcu(struct nameidata *nd)
+{
+       return nd->flags & LOOKUP_RCU;
+}
+EXPORT_SYMBOL(nd_is_rcu);
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 05c9e02f4153..c9a2d3cc4619 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -49,6 +49,8 @@ static void *nfs_follow_link(struct dentry *dentry, struct 
nameidata *nd)
        struct page *page;
        void *err;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
        if (err)
                goto read_failed;
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 04f124884687..db370d5d84c4 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -8,6 +8,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
 #include "overlayfs.h"
@@ -146,6 +147,8 @@ static void *ovl_follow_link(struct dentry *dentry, struct 
nameidata *nd)
        struct dentry *realdentry;
        struct inode *realinode;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        realdentry = ovl_dentry_real(dentry);
        realinode = realdentry->d_inode;
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 3f3d7aeb0712..6f5dbfe68516 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1377,6 +1377,8 @@ static void *proc_pid_follow_link(struct dentry *dentry, 
struct nameidata *nd)
        struct path path;
        int error = -EACCES;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        /* Are we allowed to snoop on the tasks file descriptors? */
        if (!proc_fd_access_allowed(inode))
                goto out;
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index c9eac4563fa8..c89a51401bb5 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -38,6 +38,9 @@ static void *proc_ns_follow_link(struct dentry *dentry, 
struct nameidata *nd)
        struct path ns_path;
        void *error = ERR_PTR(-EACCES);
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
        task = get_proc_task(inode);
        if (!task)
                return error;
diff --git a/fs/proc/self.c b/fs/proc/self.c
index 4348bb8907c2..c094ea04e1bb 100644
--- a/fs/proc/self.c
+++ b/fs/proc/self.c
@@ -22,8 +22,13 @@ static int proc_self_readlink(struct dentry *dentry, char 
__user *buffer,
 static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        struct pid_namespace *ns = dentry->d_sb->s_fs_info;
-       pid_t tgid = task_tgid_nr_ns(current, ns);
+       pid_t tgid;
        char *name = ERR_PTR(-ENOENT);
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       tgid = task_tgid_nr_ns(current, ns);
        if (tgid) {
                /* 11 for max length of signed int in decimal + NULL term */
                name = kmalloc(12, GFP_KERNEL);
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c
index 59075b509df3..5d3144d51018 100644
--- a/fs/proc/thread_self.c
+++ b/fs/proc/thread_self.c
@@ -23,9 +23,15 @@ static int proc_thread_self_readlink(struct dentry *dentry, 
char __user *buffer,
 static void *proc_thread_self_follow_link(struct dentry *dentry, struct 
nameidata *nd)
 {
        struct pid_namespace *ns = dentry->d_sb->s_fs_info;
-       pid_t tgid = task_tgid_nr_ns(current, ns);
-       pid_t pid = task_pid_nr_ns(current, ns);
+       pid_t tgid;
+       pid_t pid;
        char *name = ERR_PTR(-ENOENT);
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+
+       tgid = task_tgid_nr_ns(current, ns);
+       pid = task_pid_nr_ns(current, ns);
        if (pid) {
                name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
                if (!name)
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index e53a90331422..23cea798b777 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -417,6 +417,8 @@ xfs_vn_follow_link(
        char                    *link;
        int                     error = -ENOMEM;
 
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
        link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
        if (!link)
                goto out_err;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b821fa32ba3f..eaef987ae3cf 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2167,6 +2167,7 @@ extern struct filename *getname_flags(const char __user 
*, int, int *);
 extern struct filename *getname(const char __user *);
 extern struct filename *getname_kernel(const char *);
 extern void putname(struct filename *name);
+extern int nd_is_rcu(struct nameidata *nd);
 
 enum {
        FILE_CREATED = 1,
diff --git a/mm/shmem.c b/mm/shmem.c
index cf2d0ca010bc..fdf6ba18fce3 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2483,7 +2483,11 @@ static void *shmem_follow_short_symlink(struct dentry 
*dentry, struct nameidata
 static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        struct page *page = NULL;
-       int error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
+       int error;
+
+       if (nd_is_rcu(nd))
+               return ERR_PTR(-ECHILD);
+       error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
        nd_set_link(nd, error ? ERR_PTR(error) : kmap(page));
        if (page)
                unlock_page(page);


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to