|
@@ -644,24 +644,22 @@ static int complete_walk(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 __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 void path_put_conditional(struct path *path, struct nameidata *nd)
|
|
@@ -861,7 +859,8 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
|
|
|
return PTR_ERR(s);
|
|
|
}
|
|
|
if (*s == '/') {
|
|
|
- set_root(nd);
|
|
|
+ if (!nd->root.mnt)
|
|
|
+ set_root(nd);
|
|
|
path_put(&nd->path);
|
|
|
nd->path = nd->root;
|
|
|
path_get(&nd->root);
|
|
@@ -1138,13 +1137,15 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
|
|
*/
|
|
|
*inode = path->dentry->d_inode;
|
|
|
}
|
|
|
- return read_seqretry(&mount_lock, nd->m_seq) &&
|
|
|
+ return !read_seqretry(&mount_lock, nd->m_seq) &&
|
|
|
!(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
|
|
|
}
|
|
|
|
|
|
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 &&
|
|
@@ -1156,6 +1157,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
|
|
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;
|
|
@@ -1165,6 +1167,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
|
|
}
|
|
|
if (!follow_up_rcu(&nd->path))
|
|
|
break;
|
|
|
+ inode = nd->path.dentry->d_inode;
|
|
|
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
|
|
|
}
|
|
|
while (d_mountpoint(nd->path.dentry)) {
|
|
@@ -1174,11 +1177,12 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
|
|
break;
|
|
|
nd->path.mnt = &mounted->mnt;
|
|
|
nd->path.dentry = mounted->mnt.mnt_root;
|
|
|
+ inode = nd->path.dentry->d_inode;
|
|
|
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
|
|
|
- if (!read_seqretry(&mount_lock, nd->m_seq))
|
|
|
+ if (read_seqretry(&mount_lock, nd->m_seq))
|
|
|
goto failed;
|
|
|
}
|
|
|
- nd->inode = nd->path.dentry->d_inode;
|
|
|
+ nd->inode = inode;
|
|
|
return 0;
|
|
|
|
|
|
failed:
|
|
@@ -1257,7 +1261,8 @@ static void follow_mount(struct path *path)
|
|
|
|
|
|
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;
|
|
@@ -1853,7 +1858,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
|
|
|
if (*name=='/') {
|
|
|
if (flags & LOOKUP_RCU) {
|
|
|
rcu_read_lock();
|
|
|
- set_root_rcu(nd);
|
|
|
+ nd->seq = set_root_rcu(nd);
|
|
|
} else {
|
|
|
set_root(nd);
|
|
|
path_get(&nd->root);
|
|
@@ -1904,7 +1909,14 @@ static int path_init(int dfd, const char *name, unsigned int flags,
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
static inline int lookup_last(struct nameidata *nd, struct path *path)
|