Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0dd2b474 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Al Viro
Browse files

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: default avatarMiklos Szeredi <mszeredi@suse.cz>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent d18e9008
Loading
Loading
Loading
Loading
+97 −86
Original line number Diff line number Diff line
@@ -111,11 +111,15 @@ const struct inode_operations nfs3_dir_inode_operations = {

#ifdef CONFIG_NFS_V4

static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);
static int nfs_open_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct nameidata *nd);
static struct file *nfs_atomic_open(struct inode *, struct dentry *,
				    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 = {
	.create		= nfs_open_create,
	.lookup		= nfs_atomic_lookup,
	.create		= nfs4_create,
	.lookup		= nfs_lookup,
	.atomic_open	= nfs_atomic_open,
	.link		= nfs_link,
	.unlink		= nfs_unlink,
	.symlink	= nfs_symlink,
@@ -1403,120 +1407,132 @@ static int do_open(struct inode *inode, struct file *filp)
	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;
	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 (ctx->mode & FMODE_EXEC) {
		ret = nfs_may_open(ctx->dentry->d_inode,
				ctx->cred,
				nd->intent.open.flags);
		if (ret < 0)
		err = nfs_may_open(dentry->d_inode, ctx->cred, open_flags);
		if (err < 0) {
			filp = ERR_PTR(err);
			goto out;
		}
	filp = lookup_instantiate_filp(nd, ctx->dentry, do_open);
	if (IS_ERR(filp))
		ret = PTR_ERR(filp);
	else
	}

	filp = finish_open(od, dentry, do_open);
	if (!IS_ERR(filp))
		nfs_file_set_open_context(filp, ctx);

out:
	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 iattr attr;
	struct dentry *res = NULL;
	struct dentry *res;
	struct iattr attr = { .ia_valid = ATTR_OPEN };
	struct inode *inode;
	int open_flags;
	struct file *filp;
	int err;

	dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n",
			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
	/* Expect a negative dentry */
	BUG_ON(dentry->d_inode);

	/* Check that we are indeed trying to open this file */
	if (!is_atomic_open(nd))
		goto no_open;
	dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n",
			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

	if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
		res = ERR_PTR(-ENAMETOOLONG);
		goto out;
	/* NFS only supports OPEN on regular files */
	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;
		}

	/* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash
	 * the dentry. */
	if (nd->flags & LOOKUP_EXCL) {
		d_instantiate(dentry, NULL);
		goto out;
		goto no_open;
	}

	open_flags = nd->intent.open.flags;
	attr.ia_valid = ATTR_OPEN;

	ctx = create_nfs_open_context(dentry, open_flags);
	res = ERR_CAST(ctx);
	if (IS_ERR(ctx))
		goto out;
	err = -ENAMETOOLONG;
	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
		goto out_err;

	if (nd->flags & LOOKUP_CREATE) {
		attr.ia_mode = nd->intent.open.create_mode;
	if (open_flags & O_CREAT) {
		attr.ia_valid |= ATTR_MODE;
		attr.ia_mode &= ~current_umask();
	} else
		open_flags &= ~(O_EXCL | O_CREAT);

		attr.ia_mode = mode & ~current_umask();
	}
	if (open_flags & O_TRUNC) {
		attr.ia_valid |= ATTR_SIZE;
		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);
	inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
	d_drop(dentry);
	if (IS_ERR(inode)) {
		nfs_unblock_sillyrename(dentry->d_parent);
		put_nfs_open_context(ctx);
		switch (PTR_ERR(inode)) {
			/* Make a negative dentry */
		err = PTR_ERR(inode);
		switch (err) {
		case -ENOENT:
			d_add(dentry, NULL);
				res = NULL;
				goto out;
			/* This turned out not to be a regular file */
			break;
		case -EISDIR:
		case -ENOTDIR:
			goto no_open;
		case -ELOOP:
				if (!(nd->intent.open.flags & O_NOFOLLOW))
			if (!(open_flags & O_NOFOLLOW))
				goto no_open;
			break;
			/* case -EINVAL: */
		default:
				res = ERR_CAST(inode);
				goto out;
			break;
		}
		goto out_err;
	}
	res = d_add_unique(dentry, inode);
	nfs_unblock_sillyrename(dentry->d_parent);
	if (res != NULL) {
		dput(ctx->dentry);
		ctx->dentry = dget(res);
		dentry = res;
	}
	err = nfs_intent_set_file(nd, ctx);
	if (err < 0) {
	if (res != NULL)
		dentry = res;

	nfs_unblock_sillyrename(dentry->d_parent);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));

	filp = nfs_finish_open(ctx, dentry, od, open_flags);

	dput(res);
	return filp;

out_err:
	return ERR_PTR(err);
	}
out:
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	return res;

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)
@@ -1566,7 +1582,7 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *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)
{
	struct nfs_open_context *ctx = NULL;
@@ -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);
	if (error != 0)
		goto out_put_ctx;
	if (nd) {
		error = nfs_intent_set_file(nd, ctx);
		if (error < 0)
			goto out_err;
	} else {

	put_nfs_open_context(ctx);
	}

	return 0;
out_put_ctx:
	put_nfs_open_context(ctx);
out_err_drop:
	d_drop(dentry);
out_err:
	return error;
}