forked from Minki/linux
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:
parent
d18e9008c3
commit
0dd2b474d0
183
fs/nfs/dir.c
183
fs/nfs/dir.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user