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>
This commit is contained in:
Родитель
16b1c1cd71
Коммит
0ef97dcfce
56
fs/nfs/dir.c
56
fs/nfs/dir.c
|
@ -1354,10 +1354,10 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_NFS_V4
|
#ifdef CONFIG_NFS_V4
|
||||||
static int nfs_open_revalidate(struct dentry *, struct nameidata *);
|
static int nfs4_lookup_revalidate(struct dentry *, struct nameidata *);
|
||||||
|
|
||||||
const struct dentry_operations nfs4_dentry_operations = {
|
const struct dentry_operations nfs4_dentry_operations = {
|
||||||
.d_revalidate = nfs_open_revalidate,
|
.d_revalidate = nfs4_lookup_revalidate,
|
||||||
.d_delete = nfs_dentry_delete,
|
.d_delete = nfs_dentry_delete,
|
||||||
.d_iput = nfs_dentry_iput,
|
.d_iput = nfs_dentry_iput,
|
||||||
.d_automount = nfs_d_automount,
|
.d_automount = nfs_d_automount,
|
||||||
|
@ -1519,13 +1519,11 @@ no_open:
|
||||||
return nfs_lookup(dir, dentry, nd);
|
return nfs_lookup(dir, dentry, nd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
|
static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct dentry *parent = NULL;
|
struct dentry *parent = NULL;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct inode *dir;
|
struct inode *dir;
|
||||||
struct nfs_open_context *ctx;
|
|
||||||
struct iattr attr;
|
|
||||||
int openflags, ret = 0;
|
int openflags, ret = 0;
|
||||||
|
|
||||||
if (nd->flags & LOOKUP_RCU)
|
if (nd->flags & LOOKUP_RCU)
|
||||||
|
@ -1554,57 +1552,13 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||||
/* We cannot do exclusive creation on a positive dentry */
|
/* We cannot do exclusive creation on a positive dentry */
|
||||||
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
|
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
|
||||||
goto no_open_dput;
|
goto no_open_dput;
|
||||||
/* We can't create new files here */
|
|
||||||
openflags &= ~(O_CREAT|O_EXCL);
|
|
||||||
|
|
||||||
ctx = create_nfs_open_context(dentry, openflags);
|
/* Let f_op->open() actually open (and revalidate) the file */
|
||||||
ret = PTR_ERR(ctx);
|
|
||||||
if (IS_ERR(ctx))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
attr.ia_valid = ATTR_OPEN;
|
|
||||||
if (openflags & O_TRUNC) {
|
|
||||||
attr.ia_valid |= ATTR_SIZE;
|
|
||||||
attr.ia_size = 0;
|
|
||||||
nfs_wb_all(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: we're not holding inode->i_mutex and so may be racing with
|
|
||||||
* operations that change the directory. We therefore save the
|
|
||||||
* change attribute *before* we do the RPC call.
|
|
||||||
*/
|
|
||||||
inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
ret = PTR_ERR(inode);
|
|
||||||
switch (ret) {
|
|
||||||
case -EPERM:
|
|
||||||
case -EACCES:
|
|
||||||
case -EDQUOT:
|
|
||||||
case -ENOSPC:
|
|
||||||
case -EROFS:
|
|
||||||
goto out_put_ctx;
|
|
||||||
default:
|
|
||||||
goto out_drop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iput(inode);
|
|
||||||
if (inode != dentry->d_inode)
|
|
||||||
goto out_drop;
|
|
||||||
|
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
|
||||||
ret = nfs_intent_set_file(nd, ctx);
|
|
||||||
if (ret >= 0)
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
dput(parent);
|
dput(parent);
|
||||||
return ret;
|
return ret;
|
||||||
out_drop:
|
|
||||||
d_drop(dentry);
|
|
||||||
ret = 0;
|
|
||||||
out_put_ctx:
|
|
||||||
put_nfs_open_context(ctx);
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
no_open_dput:
|
no_open_dput:
|
||||||
dput(parent);
|
dput(parent);
|
||||||
|
|
|
@ -879,12 +879,81 @@ const struct file_operations nfs_file_operations = {
|
||||||
static int
|
static int
|
||||||
nfs4_file_open(struct inode *inode, struct file *filp)
|
nfs4_file_open(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
|
struct nfs_open_context *ctx;
|
||||||
|
struct dentry *dentry = filp->f_path.dentry;
|
||||||
|
struct dentry *parent = NULL;
|
||||||
|
struct inode *dir;
|
||||||
|
unsigned openflags = filp->f_flags;
|
||||||
|
struct iattr attr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BUG_ON(inode != dentry->d_inode);
|
||||||
/*
|
/*
|
||||||
* NFSv4 opens are handled in d_lookup and d_revalidate. If we get to
|
* If no cached dentry exists or if it's negative, NFSv4 handled the
|
||||||
* this point, then something is very wrong
|
* opens in ->lookup() or ->create().
|
||||||
|
*
|
||||||
|
* We only get this far for a cached positive dentry. We skipped
|
||||||
|
* revalidation, so handle it here by dropping the dentry and returning
|
||||||
|
* -EOPENSTALE. The VFS will retry the lookup/create/open.
|
||||||
*/
|
*/
|
||||||
dprintk("NFS: %s called! inode=%p filp=%p\n", __func__, inode, filp);
|
|
||||||
return -ENOTDIR;
|
dprintk("NFS: open file(%s/%s)\n",
|
||||||
|
dentry->d_parent->d_name.name,
|
||||||
|
dentry->d_name.name);
|
||||||
|
|
||||||
|
if ((openflags & O_ACCMODE) == 3)
|
||||||
|
openflags--;
|
||||||
|
|
||||||
|
/* We can't create new files here */
|
||||||
|
openflags &= ~(O_CREAT|O_EXCL);
|
||||||
|
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
dir = parent->d_inode;
|
||||||
|
|
||||||
|
ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
|
||||||
|
err = PTR_ERR(ctx);
|
||||||
|
if (IS_ERR(ctx))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
attr.ia_valid = ATTR_OPEN;
|
||||||
|
if (openflags & O_TRUNC) {
|
||||||
|
attr.ia_valid |= ATTR_SIZE;
|
||||||
|
attr.ia_size = 0;
|
||||||
|
nfs_wb_all(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
|
||||||
|
if (IS_ERR(inode)) {
|
||||||
|
err = PTR_ERR(inode);
|
||||||
|
switch (err) {
|
||||||
|
case -EPERM:
|
||||||
|
case -EACCES:
|
||||||
|
case -EDQUOT:
|
||||||
|
case -ENOSPC:
|
||||||
|
case -EROFS:
|
||||||
|
goto out_put_ctx;
|
||||||
|
default:
|
||||||
|
goto out_drop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iput(inode);
|
||||||
|
if (inode != dentry->d_inode)
|
||||||
|
goto out_drop;
|
||||||
|
|
||||||
|
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||||
|
nfs_file_set_open_context(filp, ctx);
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
out_put_ctx:
|
||||||
|
put_nfs_open_context(ctx);
|
||||||
|
out:
|
||||||
|
dput(parent);
|
||||||
|
return err;
|
||||||
|
|
||||||
|
out_drop:
|
||||||
|
d_drop(dentry);
|
||||||
|
err = -EOPENSTALE;
|
||||||
|
goto out_put_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct file_operations nfs4_file_operations = {
|
const struct file_operations nfs4_file_operations = {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче