diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2012-05-21 11:30:20 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-06-01 12:12:02 -0400 |
commit | 0ef97dcfce4179a2eba046b855ee2f91d6f1b414 (patch) | |
tree | d5a29275a427dafd8fce0131b58f215c6252d3e2 /fs/nfs/file.c | |
parent | 16b1c1cd71176ab0a76b26818fbf12db9183ed57 (diff) |
nfs: don't open in ->d_revalidate
NFSv4 can't do reliable opens in d_revalidate, since it cannot know whether a
mount needs to be followed or not. It does check d_mountpoint() on the dentry,
which can result in a weird error if the VFS found that the mount does not in
fact need to be followed, e.g.:
# mount --bind /mnt/nfs /mnt/nfs-clone
# echo something > /mnt/nfs/tmp/bar
# echo x > /tmp/file
# mount --bind /tmp/file /mnt/nfs-clone/tmp/bar
# cat /mnt/nfs/tmp/bar
cat: /mnt/nfs/tmp/bar: Not a directory
Which should, by any sane filesystem, result in "something" being printed.
So instead do the open in f_op->open() and in the unlikely case that the cached
dentry turned out to be invalid, drop the dentry and return EOPENSTALE to let
the VFS retry.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/nfs/file.c')
-rw-r--r-- | fs/nfs/file.c | 77 |
1 files changed, 73 insertions, 4 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 56311ca5f9f8..a6708e6b438d 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -879,12 +879,81 @@ const struct file_operations nfs_file_operations = { | |||
879 | static int | 879 | static int |
880 | nfs4_file_open(struct inode *inode, struct file *filp) | 880 | nfs4_file_open(struct inode *inode, struct file *filp) |
881 | { | 881 | { |
882 | struct nfs_open_context *ctx; | ||
883 | struct dentry *dentry = filp->f_path.dentry; | ||
884 | struct dentry *parent = NULL; | ||
885 | struct inode *dir; | ||
886 | unsigned openflags = filp->f_flags; | ||
887 | struct iattr attr; | ||
888 | int err; | ||
889 | |||
890 | BUG_ON(inode != dentry->d_inode); | ||
882 | /* | 891 | /* |
883 | * NFSv4 opens are handled in d_lookup and d_revalidate. If we get to | 892 | * If no cached dentry exists or if it's negative, NFSv4 handled the |
884 | * this point, then something is very wrong | 893 | * opens in ->lookup() or ->create(). |
894 | * | ||
895 | * We only get this far for a cached positive dentry. We skipped | ||
896 | * revalidation, so handle it here by dropping the dentry and returning | ||
897 | * -EOPENSTALE. The VFS will retry the lookup/create/open. | ||
885 | */ | 898 | */ |
886 | dprintk("NFS: %s called! inode=%p filp=%p\n", __func__, inode, filp); | 899 | |
887 | return -ENOTDIR; | 900 | dprintk("NFS: open file(%s/%s)\n", |
901 | dentry->d_parent->d_name.name, | ||
902 | dentry->d_name.name); | ||
903 | |||
904 | if ((openflags & O_ACCMODE) == 3) | ||
905 | openflags--; | ||
906 | |||
907 | /* We can't create new files here */ | ||
908 | openflags &= ~(O_CREAT|O_EXCL); | ||
909 | |||
910 | parent = dget_parent(dentry); | ||
911 | dir = parent->d_inode; | ||
912 | |||
913 | ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode); | ||
914 | err = PTR_ERR(ctx); | ||
915 | if (IS_ERR(ctx)) | ||
916 | goto out; | ||
917 | |||
918 | attr.ia_valid = ATTR_OPEN; | ||
919 | if (openflags & O_TRUNC) { | ||
920 | attr.ia_valid |= ATTR_SIZE; | ||
921 | attr.ia_size = 0; | ||
922 | nfs_wb_all(inode); | ||
923 | } | ||
924 | |||
925 | inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr); | ||
926 | if (IS_ERR(inode)) { | ||
927 | err = PTR_ERR(inode); | ||
928 | switch (err) { | ||
929 | case -EPERM: | ||
930 | case -EACCES: | ||
931 | case -EDQUOT: | ||
932 | case -ENOSPC: | ||
933 | case -EROFS: | ||
934 | goto out_put_ctx; | ||
935 | default: | ||
936 | goto out_drop; | ||
937 | } | ||
938 | } | ||
939 | iput(inode); | ||
940 | if (inode != dentry->d_inode) | ||
941 | goto out_drop; | ||
942 | |||
943 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
944 | nfs_file_set_open_context(filp, ctx); | ||
945 | err = 0; | ||
946 | |||
947 | out_put_ctx: | ||
948 | put_nfs_open_context(ctx); | ||
949 | out: | ||
950 | dput(parent); | ||
951 | return err; | ||
952 | |||
953 | out_drop: | ||
954 | d_drop(dentry); | ||
955 | err = -EOPENSTALE; | ||
956 | goto out_put_ctx; | ||
888 | } | 957 | } |
889 | 958 | ||
890 | const struct file_operations nfs4_file_operations = { | 959 | const struct file_operations nfs4_file_operations = { |