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

Commit 5ef50c3b authored by Sage Weil's avatar Sage Weil
Browse files

ceph: simplify+fix atomic_open



The initial ->atomic_open op was carried over from the old intent code,
which was incomplete and didn't really work.  Replace it with a fresh
method.  In particular:

 * always attempt to do an atomic open+lookup, both for the create case
   and for lookups of existing files.
 * fix symlink handling by returning 1 to the VFS so that we can follow
   the link to its destination. This fixes a longstanding ceph bug (#2392).

Signed-off-by: default avatarSage Weil <sage@inktank.com>
parent 1a9b4993
Loading
Loading
Loading
Loading
+0 −38
Original line number Diff line number Diff line
@@ -633,44 +633,6 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
	return dentry;
}

int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
		     struct file *file, unsigned flags, umode_t mode,
		     int *opened)
{
	int err;
	struct dentry *res = NULL;

	if (!(flags & O_CREAT)) {
		if (dentry->d_name.len > NAME_MAX)
			return -ENAMETOOLONG;

		err = ceph_init_dentry(dentry);
		if (err < 0)
			return err;

		return ceph_lookup_open(dir, dentry, file, flags, mode, opened);
	}

	if (d_unhashed(dentry)) {
		res = ceph_lookup(dir, dentry, 0);
		if (IS_ERR(res))
			return PTR_ERR(res);

		if (res)
			dentry = res;
	}

	/* We don't deal with positive dentries here */
	if (dentry->d_inode)
		return finish_no_open(file, res);

	*opened |= FILE_CREATED;
	err = ceph_lookup_open(dir, dentry, file, flags, mode, opened);
	dput(res);

	return err;
}

/*
 * If we do a create but get no trace back from the MDS, follow up with
 * a lookup (the VFS expects us to link up the provided dentry).
+37 −25
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/writeback.h>

@@ -106,9 +107,6 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
}

/*
 * If the filp already has private_data, that means the file was
 * already opened by intent during lookup, and we do nothing.
 *
 * If we already have the requisite capabilities, we can satisfy
 * the open request locally (no need to request new caps from the
 * MDS).  We do, however, need to inform the MDS (asynchronously)
@@ -207,24 +205,29 @@ int ceph_open(struct inode *inode, struct file *file)


/*
 * Do a lookup + open with a single request.
 *
 * If this succeeds, but some subsequent check in the vfs
 * may_open() fails, the struct *file gets cleaned up (i.e.
 * ceph_release gets called).  So fear not!
 * Do a lookup + open with a single request.  If we get a non-existent
 * file or symlink, return 1 so the VFS can retry.
 */
int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
		     struct file *file, unsigned flags, umode_t mode,
		     int *opened)
{
	struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
	struct ceph_mds_client *mdsc = fsc->mdsc;
	struct ceph_mds_request *req;
	struct dentry *ret;
	struct dentry *dn;
	int err;

	dout("ceph_lookup_open dentry %p '%.*s' flags %d mode 0%o\n",
	     dentry, dentry->d_name.len, dentry->d_name.name, flags, mode);
	dout("atomic_open %p dentry %p '%.*s' %s flags %d mode 0%o\n",
	     dir, dentry, dentry->d_name.len, dentry->d_name.name,
	     d_unhashed(dentry) ? "unhashed" : "hashed", flags, mode);

	if (dentry->d_name.len > NAME_MAX)
		return -ENAMETOOLONG;

	err = ceph_init_dentry(dentry);
	if (err < 0)
		return err;

	/* do the open */
	req = prepare_open_request(dir->i_sb, flags, mode);
@@ -241,22 +244,31 @@ int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
				   (flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
				   req);
	err = ceph_handle_snapdir(req, dentry, err);
	if (err)
		goto out;
	if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
	if (err == 0 && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
		err = ceph_handle_notrace_create(dir, dentry);
	if (err)
		goto out;
	err = finish_open(file, req->r_dentry, ceph_open, opened);
out:
	ret = ceph_finish_lookup(req, dentry, err);
	ceph_mdsc_put_request(req);
	dout("ceph_lookup_open result=%p\n", ret);

	if (IS_ERR(ret))
		return PTR_ERR(ret);
	if (d_unhashed(dentry)) {
		dn = ceph_finish_lookup(req, dentry, err);
		if (IS_ERR(dn))
			err = PTR_ERR(dn);
	} else {
		/* we were given a hashed negative dentry */
		dn = NULL;
	}
	if (err)
		goto out_err;
	if (dn || dentry->d_inode == NULL || S_ISLNK(dentry->d_inode->i_mode)) {
		/* make vfs retry on splice, ENOENT, or symlink */
		dout("atomic_open finish_no_open on dn %p\n", dn);
		err = finish_no_open(file, dn);
	} else {
		dout("atomic_open finish_open on dn %p\n", dn);
		err = finish_open(file, dentry, ceph_open, opened);
	}

	dput(ret);
out_err:
	ceph_mdsc_put_request(req);
	dout("atomic_open result=%d\n", err);
	return err;
}

+3 −3
Original line number Diff line number Diff line
@@ -806,9 +806,9 @@ extern int ceph_copy_from_page_vector(struct page **pages,
				    loff_t off, size_t len);
extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags);
extern int ceph_open(struct inode *inode, struct file *file);
extern int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
			     struct file *od, unsigned flags,
			     umode_t mode, int *opened);
extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
			    struct file *file, unsigned flags, umode_t mode,
			    int *opened);
extern int ceph_release(struct inode *inode, struct file *filp);

/* dir.c */