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; |
