diff options
author | Al Viro <viro@ZenIV.linux.org.uk> | 2011-11-07 16:21:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-07 17:58:06 -0500 |
commit | a3fbbde70a0cec017f2431e8f8de208708c76acc (patch) | |
tree | f584856e638ca916a8aca32eda58a8fe89722c9f | |
parent | 54a0f91301950af3d6ae2ff2bf710c9c68a9bfea (diff) |
VFS: we need to set LOOKUP_JUMPED on mountpoint crossing
Mountpoint crossing is similar to following procfs symlinks - we do
not get ->d_revalidate() called for dentry we have arrived at, with
unpleasant consequences for NFS4.
Simple way to reproduce the problem in mainline:
cat >/tmp/a.c <<'EOF'
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
main()
{
struct flock fl = {.l_type = F_RDLCK, .l_whence = SEEK_SET, .l_len = 1};
if (fcntl(0, F_SETLK, &fl))
perror("setlk");
}
EOF
cc /tmp/a.c -o /tmp/test
then on nfs4:
mount --bind file1 file2
/tmp/test < file1 # ok
/tmp/test < file2 # spews "setlk: No locks available"...
What happens is the missing call of ->d_revalidate() after mountpoint
crossing and that's where NFS4 would issue OPEN request to server.
The fix is simple - treat mountpoint crossing the same way we deal with
following procfs-style symlinks. I.e. set LOOKUP_JUMPED...
Cc: stable@kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/namei.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/fs/namei.c b/fs/namei.c index ac6d214da827..5008f01787f5 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -852,7 +852,7 @@ static int follow_managed(struct path *path, unsigned flags) | |||
852 | mntput(path->mnt); | 852 | mntput(path->mnt); |
853 | if (ret == -EISDIR) | 853 | if (ret == -EISDIR) |
854 | ret = 0; | 854 | ret = 0; |
855 | return ret; | 855 | return ret < 0 ? ret : need_mntput; |
856 | } | 856 | } |
857 | 857 | ||
858 | int follow_down_one(struct path *path) | 858 | int follow_down_one(struct path *path) |
@@ -900,6 +900,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | |||
900 | break; | 900 | break; |
901 | path->mnt = mounted; | 901 | path->mnt = mounted; |
902 | path->dentry = mounted->mnt_root; | 902 | path->dentry = mounted->mnt_root; |
903 | nd->flags |= LOOKUP_JUMPED; | ||
903 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); | 904 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); |
904 | /* | 905 | /* |
905 | * Update the inode too. We don't need to re-check the | 906 | * Update the inode too. We don't need to re-check the |
@@ -1213,6 +1214,8 @@ retry: | |||
1213 | path_put_conditional(path, nd); | 1214 | path_put_conditional(path, nd); |
1214 | return err; | 1215 | return err; |
1215 | } | 1216 | } |
1217 | if (err) | ||
1218 | nd->flags |= LOOKUP_JUMPED; | ||
1216 | *inode = path->dentry->d_inode; | 1219 | *inode = path->dentry->d_inode; |
1217 | return 0; | 1220 | return 0; |
1218 | } | 1221 | } |
@@ -2146,6 +2149,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2146 | } | 2149 | } |
2147 | 2150 | ||
2148 | /* create side of things */ | 2151 | /* create side of things */ |
2152 | /* | ||
2153 | * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED has been | ||
2154 | * cleared when we got to the last component we are about to look up | ||
2155 | */ | ||
2149 | error = complete_walk(nd); | 2156 | error = complete_walk(nd); |
2150 | if (error) | 2157 | if (error) |
2151 | return ERR_PTR(error); | 2158 | return ERR_PTR(error); |
@@ -2214,6 +2221,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2214 | if (error < 0) | 2221 | if (error < 0) |
2215 | goto exit_dput; | 2222 | goto exit_dput; |
2216 | 2223 | ||
2224 | if (error) | ||
2225 | nd->flags |= LOOKUP_JUMPED; | ||
2226 | |||
2217 | error = -ENOENT; | 2227 | error = -ENOENT; |
2218 | if (!path->dentry->d_inode) | 2228 | if (!path->dentry->d_inode) |
2219 | goto exit_dput; | 2229 | goto exit_dput; |
@@ -2223,6 +2233,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2223 | 2233 | ||
2224 | path_to_nameidata(path, nd); | 2234 | path_to_nameidata(path, nd); |
2225 | nd->inode = path->dentry->d_inode; | 2235 | nd->inode = path->dentry->d_inode; |
2236 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ | ||
2237 | error = complete_walk(nd); | ||
2238 | if (error) | ||
2239 | goto exit; | ||
2226 | error = -EISDIR; | 2240 | error = -EISDIR; |
2227 | if (S_ISDIR(nd->inode->i_mode)) | 2241 | if (S_ISDIR(nd->inode->i_mode)) |
2228 | goto exit; | 2242 | goto exit; |