From patchwork Mon Mar 16 04:43:19 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: NeilBrown X-Patchwork-Id: 6015351 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 5123BBF90F for ; Mon, 16 Mar 2015 04:47:43 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 095D820353 for ; Mon, 16 Mar 2015 04:47:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8748320109 for ; Mon, 16 Mar 2015 04:47:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752205AbbCPEoX (ORCPT ); Mon, 16 Mar 2015 00:44:23 -0400 Received: from cantor2.suse.de ([195.135.220.15]:56158 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750736AbbCPEoU (ORCPT ); Mon, 16 Mar 2015 00:44:20 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 6C2F6ABA2; Mon, 16 Mar 2015 04:44:19 +0000 (UTC) From: NeilBrown To: Al Viro Date: Mon, 16 Mar 2015 15:43:19 +1100 Subject: [PATCH 02/13] VFS: make all ->follow_link handlers aware for LOOKUP_RCU Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Message-ID: <20150316044319.23648.73750.stgit@notabene.brown> In-Reply-To: <20150316043602.23648.52734.stgit@notabene.brown> References: <20150316043602.23648.52734.stgit@notabene.brown> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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(-) -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 +#include #include #include #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);