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 | |
| 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>
| -rw-r--r-- | fs/nfs/dir.c | 56 | ||||
| -rw-r--r-- | fs/nfs/file.c | 77 |
2 files changed, 78 insertions, 55 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0989a2099688..f430057ff3b3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
| @@ -1354,10 +1354,10 @@ out: | |||
| 1354 | } | 1354 | } |
| 1355 | 1355 | ||
| 1356 | #ifdef CONFIG_NFS_V4 | 1356 | #ifdef CONFIG_NFS_V4 |
| 1357 | static int nfs_open_revalidate(struct dentry *, struct nameidata *); | 1357 | static int nfs4_lookup_revalidate(struct dentry *, struct nameidata *); |
| 1358 | 1358 | ||
| 1359 | const struct dentry_operations nfs4_dentry_operations = { | 1359 | const struct dentry_operations nfs4_dentry_operations = { |
| 1360 | .d_revalidate = nfs_open_revalidate, | 1360 | .d_revalidate = nfs4_lookup_revalidate, |
| 1361 | .d_delete = nfs_dentry_delete, | 1361 | .d_delete = nfs_dentry_delete, |
| 1362 | .d_iput = nfs_dentry_iput, | 1362 | .d_iput = nfs_dentry_iput, |
| 1363 | .d_automount = nfs_d_automount, | 1363 | .d_automount = nfs_d_automount, |
| @@ -1519,13 +1519,11 @@ no_open: | |||
| 1519 | return nfs_lookup(dir, dentry, nd); | 1519 | return nfs_lookup(dir, dentry, nd); |
| 1520 | } | 1520 | } |
| 1521 | 1521 | ||
| 1522 | static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) | 1522 | static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) |
| 1523 | { | 1523 | { |
| 1524 | struct dentry *parent = NULL; | 1524 | struct dentry *parent = NULL; |
| 1525 | struct inode *inode; | 1525 | struct inode *inode; |
| 1526 | struct inode *dir; | 1526 | struct inode *dir; |
| 1527 | struct nfs_open_context *ctx; | ||
| 1528 | struct iattr attr; | ||
| 1529 | int openflags, ret = 0; | 1527 | int openflags, ret = 0; |
| 1530 | 1528 | ||
| 1531 | if (nd->flags & LOOKUP_RCU) | 1529 | if (nd->flags & LOOKUP_RCU) |
| @@ -1554,57 +1552,13 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
| 1554 | /* We cannot do exclusive creation on a positive dentry */ | 1552 | /* We cannot do exclusive creation on a positive dentry */ |
| 1555 | if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) | 1553 | if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) |
| 1556 | goto no_open_dput; | 1554 | goto no_open_dput; |
| 1557 | /* We can't create new files here */ | ||
| 1558 | openflags &= ~(O_CREAT|O_EXCL); | ||
| 1559 | |||
| 1560 | ctx = create_nfs_open_context(dentry, openflags); | ||
| 1561 | ret = PTR_ERR(ctx); | ||
| 1562 | if (IS_ERR(ctx)) | ||
| 1563 | goto out; | ||
| 1564 | 1555 | ||
| 1565 | attr.ia_valid = ATTR_OPEN; | 1556 | /* Let f_op->open() actually open (and revalidate) the file */ |
| 1566 | if (openflags & O_TRUNC) { | 1557 | ret = 1; |
| 1567 | attr.ia_valid |= ATTR_SIZE; | ||
| 1568 | attr.ia_size = 0; | ||
| 1569 | nfs_wb_all(inode); | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | /* | ||
| 1573 | * Note: we're not holding inode->i_mutex and so may be racing with | ||
| 1574 | * operations that change the directory. We therefore save the | ||
| 1575 | * change attribute *before* we do the RPC call. | ||
| 1576 | */ | ||
| 1577 | inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr); | ||
| 1578 | if (IS_ERR(inode)) { | ||
| 1579 | ret = PTR_ERR(inode); | ||
| 1580 | switch (ret) { | ||
| 1581 | case -EPERM: | ||
| 1582 | case -EACCES: | ||
| 1583 | case -EDQUOT: | ||
| 1584 | case -ENOSPC: | ||
| 1585 | case -EROFS: | ||
| 1586 | goto out_put_ctx; | ||
| 1587 | default: | ||
| 1588 | goto out_drop; | ||
| 1589 | } | ||
| 1590 | } | ||
| 1591 | iput(inode); | ||
| 1592 | if (inode != dentry->d_inode) | ||
| 1593 | goto out_drop; | ||
| 1594 | 1558 | ||
| 1595 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
| 1596 | ret = nfs_intent_set_file(nd, ctx); | ||
| 1597 | if (ret >= 0) | ||
| 1598 | ret = 1; | ||
| 1599 | out: | 1559 | out: |
| 1600 | dput(parent); | 1560 | dput(parent); |
| 1601 | return ret; | 1561 | return ret; |
| 1602 | out_drop: | ||
| 1603 | d_drop(dentry); | ||
| 1604 | ret = 0; | ||
| 1605 | out_put_ctx: | ||
| 1606 | put_nfs_open_context(ctx); | ||
| 1607 | goto out; | ||
| 1608 | 1562 | ||
| 1609 | no_open_dput: | 1563 | no_open_dput: |
| 1610 | dput(parent); | 1564 | dput(parent); |
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 = { |
