nfs: implement i_op->atomic_open()

Replace NFS4 specific ->lookup implementation with ->atomic_open impelementation
and use the generic nfs_lookup for other lookups.

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:
Miklos Szeredi 2012-06-05 15:10:18 +02:00 committed by Al Viro
parent d18e9008c3
commit 0dd2b474d0

View File

@ -111,11 +111,15 @@ const struct inode_operations nfs3_dir_inode_operations = {
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); static struct file *nfs_atomic_open(struct inode *, struct dentry *,
static int nfs_open_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct nameidata *nd); struct opendata *, unsigned, umode_t,
bool *);
static int nfs4_create(struct inode *dir, struct dentry *dentry,
umode_t mode, struct nameidata *nd);
const struct inode_operations nfs4_dir_inode_operations = { const struct inode_operations nfs4_dir_inode_operations = {
.create = nfs_open_create, .create = nfs4_create,
.lookup = nfs_atomic_lookup, .lookup = nfs_lookup,
.atomic_open = nfs_atomic_open,
.link = nfs_link, .link = nfs_link,
.unlink = nfs_unlink, .unlink = nfs_unlink,
.symlink = nfs_symlink, .symlink = nfs_symlink,
@ -1403,120 +1407,132 @@ static int do_open(struct inode *inode, struct file *filp)
return 0; return 0;
} }
static int nfs_intent_set_file(struct nameidata *nd, struct nfs_open_context *ctx) static struct file *nfs_finish_open(struct nfs_open_context *ctx,
struct dentry *dentry,
struct opendata *od, unsigned open_flags)
{ {
struct file *filp; struct file *filp;
int ret = 0; int err;
if (ctx->dentry != dentry) {
dput(ctx->dentry);
ctx->dentry = dget(dentry);
}
/* If the open_intent is for execute, we have an extra check to make */ /* If the open_intent is for execute, we have an extra check to make */
if (ctx->mode & FMODE_EXEC) { if (ctx->mode & FMODE_EXEC) {
ret = nfs_may_open(ctx->dentry->d_inode, err = nfs_may_open(dentry->d_inode, ctx->cred, open_flags);
ctx->cred, if (err < 0) {
nd->intent.open.flags); filp = ERR_PTR(err);
if (ret < 0)
goto out; goto out;
}
} }
filp = lookup_instantiate_filp(nd, ctx->dentry, do_open);
if (IS_ERR(filp)) filp = finish_open(od, dentry, do_open);
ret = PTR_ERR(filp); if (!IS_ERR(filp))
else
nfs_file_set_open_context(filp, ctx); nfs_file_set_open_context(filp, ctx);
out: out:
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
return ret; return filp;
} }
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) static struct file *nfs_atomic_open(struct inode *dir, struct dentry *dentry,
struct opendata *od, unsigned open_flags,
umode_t mode, bool *created)
{ {
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
struct iattr attr; struct dentry *res;
struct dentry *res = NULL; struct iattr attr = { .ia_valid = ATTR_OPEN };
struct inode *inode; struct inode *inode;
int open_flags; struct file *filp;
int err; int err;
dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", /* Expect a negative dentry */
BUG_ON(dentry->d_inode);
dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
/* Check that we are indeed trying to open this file */ /* NFS only supports OPEN on regular files */
if (!is_atomic_open(nd)) if ((open_flags & O_DIRECTORY)) {
err = -ENOENT;
if (!d_unhashed(dentry)) {
/*
* Hashed negative dentry with O_DIRECTORY: dentry was
* revalidated and is fine, no need to perform lookup
* again
*/
goto out_err;
}
goto no_open; goto no_open;
if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
res = ERR_PTR(-ENAMETOOLONG);
goto out;
} }
/* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash err = -ENAMETOOLONG;
* the dentry. */ if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
if (nd->flags & LOOKUP_EXCL) { goto out_err;
d_instantiate(dentry, NULL);
goto out;
}
open_flags = nd->intent.open.flags; if (open_flags & O_CREAT) {
attr.ia_valid = ATTR_OPEN;
ctx = create_nfs_open_context(dentry, open_flags);
res = ERR_CAST(ctx);
if (IS_ERR(ctx))
goto out;
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
attr.ia_valid |= ATTR_MODE; attr.ia_valid |= ATTR_MODE;
attr.ia_mode &= ~current_umask(); attr.ia_mode = mode & ~current_umask();
} else }
open_flags &= ~(O_EXCL | O_CREAT);
if (open_flags & O_TRUNC) { if (open_flags & O_TRUNC) {
attr.ia_valid |= ATTR_SIZE; attr.ia_valid |= ATTR_SIZE;
attr.ia_size = 0; attr.ia_size = 0;
} }
/* Open the file on the server */ ctx = create_nfs_open_context(dentry, open_flags);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
goto out_err;
nfs_block_sillyrename(dentry->d_parent); nfs_block_sillyrename(dentry->d_parent);
inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
d_drop(dentry);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
nfs_unblock_sillyrename(dentry->d_parent); nfs_unblock_sillyrename(dentry->d_parent);
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
switch (PTR_ERR(inode)) { err = PTR_ERR(inode);
/* Make a negative dentry */ switch (err) {
case -ENOENT: case -ENOENT:
d_add(dentry, NULL); d_add(dentry, NULL);
res = NULL; break;
goto out; case -EISDIR:
/* This turned out not to be a regular file */ case -ENOTDIR:
case -EISDIR: goto no_open;
case -ENOTDIR: case -ELOOP:
if (!(open_flags & O_NOFOLLOW))
goto no_open; goto no_open;
case -ELOOP: break;
if (!(nd->intent.open.flags & O_NOFOLLOW))
goto no_open;
/* case -EINVAL: */ /* case -EINVAL: */
default: default:
res = ERR_CAST(inode); break;
goto out;
} }
goto out_err;
} }
res = d_add_unique(dentry, inode); res = d_add_unique(dentry, inode);
nfs_unblock_sillyrename(dentry->d_parent); if (res != NULL)
if (res != NULL) {
dput(ctx->dentry);
ctx->dentry = dget(res);
dentry = res; dentry = res;
}
err = nfs_intent_set_file(nd, ctx); nfs_unblock_sillyrename(dentry->d_parent);
if (err < 0) {
if (res != NULL)
dput(res);
return ERR_PTR(err);
}
out:
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
return res;
filp = nfs_finish_open(ctx, dentry, od, open_flags);
dput(res);
return filp;
out_err:
return ERR_PTR(err);
no_open: no_open:
return nfs_lookup(dir, dentry, nd); res = nfs_lookup(dir, dentry, NULL);
err = PTR_ERR(res);
if (IS_ERR(res))
goto out_err;
finish_no_open(od, res);
return NULL;
} }
static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
@ -1566,8 +1582,8 @@ no_open:
return nfs_lookup_revalidate(dentry, nd); return nfs_lookup_revalidate(dentry, nd);
} }
static int nfs_open_create(struct inode *dir, struct dentry *dentry, static int nfs4_create(struct inode *dir, struct dentry *dentry,
umode_t mode, struct nameidata *nd) umode_t mode, struct nameidata *nd)
{ {
struct nfs_open_context *ctx = NULL; struct nfs_open_context *ctx = NULL;
struct iattr attr; struct iattr attr;
@ -1591,19 +1607,14 @@ static int nfs_open_create(struct inode *dir, struct dentry *dentry,
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx);
if (error != 0) if (error != 0)
goto out_put_ctx; goto out_put_ctx;
if (nd) {
error = nfs_intent_set_file(nd, ctx); put_nfs_open_context(ctx);
if (error < 0)
goto out_err;
} else {
put_nfs_open_context(ctx);
}
return 0; return 0;
out_put_ctx: out_put_ctx:
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
out_err_drop: out_err_drop:
d_drop(dentry); d_drop(dentry);
out_err:
return error; return error;
} }