diff options
author | Al Viro <viro@ZenIV.linux.org.uk> | 2011-11-07 16:21:26 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-11 12:37:08 -0500 |
commit | 656460cd7714653cf60a10c79e02cbfe2d9bf732 (patch) | |
tree | a9eb6dd3de35c760ae5d327fd121a7e0ce78c8fb /fs/namei.c | |
parent | 0447f4d5654bb5d95008f16dad839104f8d0d39a (diff) |
VFS: we need to set LOOKUP_JUMPED on mountpoint crossing
commit a3fbbde70a0cec017f2431e8f8de208708c76acc upstream.
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...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/namei.c')
-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 49472a1b79e..f7593c0899d 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -910,7 +910,7 @@ static int follow_managed(struct path *path, unsigned flags) | |||
910 | mntput(path->mnt); | 910 | mntput(path->mnt); |
911 | if (ret == -EISDIR) | 911 | if (ret == -EISDIR) |
912 | ret = 0; | 912 | ret = 0; |
913 | return ret; | 913 | return ret < 0 ? ret : need_mntput; |
914 | } | 914 | } |
915 | 915 | ||
916 | int follow_down_one(struct path *path) | 916 | int follow_down_one(struct path *path) |
@@ -958,6 +958,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | |||
958 | break; | 958 | break; |
959 | path->mnt = mounted; | 959 | path->mnt = mounted; |
960 | path->dentry = mounted->mnt_root; | 960 | path->dentry = mounted->mnt_root; |
961 | nd->flags |= LOOKUP_JUMPED; | ||
961 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); | 962 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); |
962 | /* | 963 | /* |
963 | * Update the inode too. We don't need to re-check the | 964 | * Update the inode too. We don't need to re-check the |
@@ -1232,6 +1233,8 @@ retry: | |||
1232 | path_put_conditional(path, nd); | 1233 | path_put_conditional(path, nd); |
1233 | return err; | 1234 | return err; |
1234 | } | 1235 | } |
1236 | if (err) | ||
1237 | nd->flags |= LOOKUP_JUMPED; | ||
1235 | *inode = path->dentry->d_inode; | 1238 | *inode = path->dentry->d_inode; |
1236 | return 0; | 1239 | return 0; |
1237 | } | 1240 | } |
@@ -2118,6 +2121,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2118 | } | 2121 | } |
2119 | 2122 | ||
2120 | /* create side of things */ | 2123 | /* create side of things */ |
2124 | /* | ||
2125 | * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED has been | ||
2126 | * cleared when we got to the last component we are about to look up | ||
2127 | */ | ||
2121 | error = complete_walk(nd); | 2128 | error = complete_walk(nd); |
2122 | if (error) | 2129 | if (error) |
2123 | return ERR_PTR(error); | 2130 | return ERR_PTR(error); |
@@ -2186,6 +2193,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2186 | if (error < 0) | 2193 | if (error < 0) |
2187 | goto exit_dput; | 2194 | goto exit_dput; |
2188 | 2195 | ||
2196 | if (error) | ||
2197 | nd->flags |= LOOKUP_JUMPED; | ||
2198 | |||
2189 | error = -ENOENT; | 2199 | error = -ENOENT; |
2190 | if (!path->dentry->d_inode) | 2200 | if (!path->dentry->d_inode) |
2191 | goto exit_dput; | 2201 | goto exit_dput; |
@@ -2195,6 +2205,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2195 | 2205 | ||
2196 | path_to_nameidata(path, nd); | 2206 | path_to_nameidata(path, nd); |
2197 | nd->inode = path->dentry->d_inode; | 2207 | nd->inode = path->dentry->d_inode; |
2208 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ | ||
2209 | error = complete_walk(nd); | ||
2210 | if (error) | ||
2211 | goto exit; | ||
2198 | error = -EISDIR; | 2212 | error = -EISDIR; |
2199 | if (S_ISDIR(nd->inode->i_mode)) | 2213 | if (S_ISDIR(nd->inode->i_mode)) |
2200 | goto exit; | 2214 | goto exit; |