aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-05-09 19:02:01 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2015-05-15 01:10:22 -0400
commit8f47a0167c567de4ef552e26101b4f54a9b8ad48 (patch)
tree90fb1b9cc84b9c3e2b51a918cdb140ab1a061218 /fs/namei.c
parent8c1b456689ac0b27e8e16b35190e89a02fd1f121 (diff)
namei: handle absolute symlinks without dropping out of RCU mode
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c31
1 files changed, 20 insertions, 11 deletions
diff --git a/fs/namei.c b/fs/namei.c
index bf46e1010a74..a5ed0d070a20 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -499,7 +499,7 @@ struct nameidata {
499 struct path root; 499 struct path root;
500 struct inode *inode; /* path.dentry.d_inode */ 500 struct inode *inode; /* path.dentry.d_inode */
501 unsigned int flags; 501 unsigned int flags;
502 unsigned seq, m_seq; 502 unsigned seq, m_seq, root_seq;
503 int last_type; 503 int last_type;
504 unsigned depth; 504 unsigned depth;
505 int total_link_count; 505 int total_link_count;
@@ -788,14 +788,14 @@ static __always_inline void set_root(struct nameidata *nd)
788static __always_inline unsigned set_root_rcu(struct nameidata *nd) 788static __always_inline unsigned set_root_rcu(struct nameidata *nd)
789{ 789{
790 struct fs_struct *fs = current->fs; 790 struct fs_struct *fs = current->fs;
791 unsigned seq, res; 791 unsigned seq;
792 792
793 do { 793 do {
794 seq = read_seqcount_begin(&fs->seq); 794 seq = read_seqcount_begin(&fs->seq);
795 nd->root = fs->root; 795 nd->root = fs->root;
796 res = __read_seqcount_begin(&nd->root.dentry->d_seq); 796 nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
797 } while (read_seqcount_retry(&fs->seq, seq)); 797 } while (read_seqcount_retry(&fs->seq, seq));
798 return res; 798 return nd->root_seq;
799} 799}
800 800
801static void path_put_conditional(struct path *path, struct nameidata *nd) 801static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -995,15 +995,23 @@ const char *get_link(struct nameidata *nd)
995 } 995 }
996 if (*res == '/') { 996 if (*res == '/') {
997 if (nd->flags & LOOKUP_RCU) { 997 if (nd->flags & LOOKUP_RCU) {
998 if (unlikely(unlazy_walk(nd, NULL, 0))) 998 struct dentry *d;
999 if (!nd->root.mnt)
1000 set_root_rcu(nd);
1001 nd->path = nd->root;
1002 d = nd->path.dentry;
1003 nd->inode = d->d_inode;
1004 nd->seq = nd->root_seq;
1005 if (unlikely(read_seqcount_retry(&d->d_seq, nd->seq)))
999 return ERR_PTR(-ECHILD); 1006 return ERR_PTR(-ECHILD);
1007 } else {
1008 if (!nd->root.mnt)
1009 set_root(nd);
1010 path_put(&nd->path);
1011 nd->path = nd->root;
1012 path_get(&nd->root);
1013 nd->inode = nd->path.dentry->d_inode;
1000 } 1014 }
1001 if (!nd->root.mnt)
1002 set_root(nd);
1003 path_put(&nd->path);
1004 nd->path = nd->root;
1005 path_get(&nd->root);
1006 nd->inode = nd->path.dentry->d_inode;
1007 nd->flags |= LOOKUP_JUMPED; 1015 nd->flags |= LOOKUP_JUMPED;
1008 while (unlikely(*++res == '/')) 1016 while (unlikely(*++res == '/'))
1009 ; 1017 ;
@@ -1979,6 +1987,7 @@ static const char *path_init(int dfd, const struct filename *name,
1979 if (flags & LOOKUP_RCU) { 1987 if (flags & LOOKUP_RCU) {
1980 rcu_read_lock(); 1988 rcu_read_lock();
1981 nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); 1989 nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
1990 nd->root_seq = nd->seq;
1982 nd->m_seq = read_seqbegin(&mount_lock); 1991 nd->m_seq = read_seqbegin(&mount_lock);
1983 } else { 1992 } else {
1984 path_get(&nd->path); 1993 path_get(&nd->path);