Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / fs / namei.c
index f3069a3..8fc6f57 100644 (file)
@@ -554,24 +554,22 @@ static int complete_walk(struct nameidata *nd)
 
 static __always_inline void set_root(struct nameidata *nd)
 {
 
 static __always_inline void set_root(struct nameidata *nd)
 {
-       if (!nd->root.mnt)
-               get_fs_root(current->fs, &nd->root);
+       get_fs_root(current->fs, &nd->root);
 }
 
 static int link_path_walk(const char *, struct nameidata *);
 
 }
 
 static int link_path_walk(const char *, struct nameidata *);
 
-static __always_inline void set_root_rcu(struct nameidata *nd)
+static __always_inline unsigned set_root_rcu(struct nameidata *nd)
 {
 {
-       if (!nd->root.mnt) {
-               struct fs_struct *fs = current->fs;
-               unsigned seq;
+       struct fs_struct *fs = current->fs;
+       unsigned seq, res;
 
 
-               do {
-                       seq = read_seqcount_begin(&fs->seq);
-                       nd->root = fs->root;
-                       nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
-               } while (read_seqcount_retry(&fs->seq, seq));
-       }
+       do {
+               seq = read_seqcount_begin(&fs->seq);
+               nd->root = fs->root;
+               res = __read_seqcount_begin(&nd->root.dentry->d_seq);
+       } while (read_seqcount_retry(&fs->seq, seq));
+       return res;
 }
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 }
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
@@ -582,7 +580,8 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                goto fail;
 
        if (*link == '/') {
                goto fail;
 
        if (*link == '/') {
-               set_root(nd);
+               if (!nd->root.mnt)
+                       set_root(nd);
                path_put(&nd->path);
                nd->path = nd->root;
                path_get(&nd->root);
                path_put(&nd->path);
                nd->path = nd->root;
                path_get(&nd->root);
@@ -912,22 +911,11 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
        return true;
 }
 
        return true;
 }
 
-static void follow_mount_rcu(struct nameidata *nd)
-{
-       while (d_mountpoint(nd->path.dentry)) {
-               struct vfsmount *mounted;
-               mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1);
-               if (!mounted)
-                       break;
-               nd->path.mnt = mounted;
-               nd->path.dentry = mounted->mnt_root;
-               nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
-       }
-}
-
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
-       set_root_rcu(nd);
+       struct inode *inode = nd->inode;
+       if (!nd->root.mnt)
+               set_root_rcu(nd);
 
        while (1) {
                if (nd->path.dentry == nd->root.dentry &&
 
        while (1) {
                if (nd->path.dentry == nd->root.dentry &&
@@ -939,6 +927,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                        struct dentry *parent = old->d_parent;
                        unsigned seq;
 
                        struct dentry *parent = old->d_parent;
                        unsigned seq;
 
+                       inode = parent->d_inode;
                        seq = read_seqcount_begin(&parent->d_seq);
                        if (read_seqcount_retry(&old->d_seq, nd->seq))
                                goto failed;
                        seq = read_seqcount_begin(&parent->d_seq);
                        if (read_seqcount_retry(&old->d_seq, nd->seq))
                                goto failed;
@@ -948,10 +937,20 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                }
                if (!follow_up_rcu(&nd->path))
                        break;
                }
                if (!follow_up_rcu(&nd->path))
                        break;
+               inode = nd->path.dentry->d_inode;
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
        }
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
        }
-       follow_mount_rcu(nd);
-       nd->inode = nd->path.dentry->d_inode;
+       while (d_mountpoint(nd->path.dentry)) {
+               struct vfsmount *mounted;
+               mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1);
+               if (!mounted)
+                       break;
+               nd->path.mnt = mounted;
+               nd->path.dentry = mounted->mnt_root;
+               inode = nd->path.dentry->d_inode;
+               nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
+       }
+       nd->inode = inode;
        return 0;
 
 failed:
        return 0;
 
 failed:
@@ -1030,7 +1029,8 @@ static void follow_mount(struct path *path)
 
 static void follow_dotdot(struct nameidata *nd)
 {
 
 static void follow_dotdot(struct nameidata *nd)
 {
-       set_root(nd);
+       if (!nd->root.mnt)
+               set_root(nd);
 
        while(1) {
                struct dentry *old = nd->path.dentry;
 
        while(1) {
                struct dentry *old = nd->path.dentry;
@@ -1504,7 +1504,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                if (flags & LOOKUP_RCU) {
                        br_read_lock(vfsmount_lock);
                        rcu_read_lock();
                if (flags & LOOKUP_RCU) {
                        br_read_lock(vfsmount_lock);
                        rcu_read_lock();
-                       set_root_rcu(nd);
+                       nd->seq = set_root_rcu(nd);
                } else {
                        set_root(nd);
                        path_get(&nd->root);
                } else {
                        set_root(nd);
                        path_get(&nd->root);
@@ -1560,7 +1560,14 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        }
 
        nd->inode = nd->path.dentry->d_inode;
        }
 
        nd->inode = nd->path.dentry->d_inode;
-       return 0;
+       if (!(flags & LOOKUP_RCU))
+               return 0;
+       if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
+               return 0;
+       if (!(nd->flags & LOOKUP_ROOT))
+               nd->root.mnt = NULL;
+       rcu_read_unlock();
+       return -ECHILD;
 
 fput_fail:
        fput_light(file, fput_needed);
 
 fput_fail:
        fput_light(file, fput_needed);