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

Commit 1d9d7cbf authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ceph-for-5.2-rc1' of git://github.com/ceph/ceph-client

Pull ceph updates from Ilya Dryomov:
 "On the filesystem side we have:

   - a fix to enforce quotas set above the mount point (Luis Henriques)

   - support for exporting snapshots through NFS (Zheng Yan)

   - proper statx implementation (Jeff Layton). statx flags are mapped
     to MDS caps, with AT_STATX_{DONT,FORCE}_SYNC taken into account.

   - some follow-up dentry name handling fixes, in particular
     elimination of our hand-rolled helper and the switch to __getname()
     as suggested by Al (Jeff Layton)

   - a set of MDS client cleanups in preparation for async MDS requests
     in the future (Jeff Layton)

   - a fix to sync the filesystem before remounting (Jeff Layton)

  On the rbd side, work is on-going on object-map and fast-diff image
  features"

* tag 'ceph-for-5.2-rc1' of git://github.com/ceph/ceph-client: (29 commits)
  ceph: flush dirty inodes before proceeding with remount
  ceph: fix unaligned access in ceph_send_cap_releases
  libceph: make ceph_pr_addr take an struct ceph_entity_addr pointer
  libceph: fix unaligned accesses in ceph_entity_addr handling
  rbd: don't assert on writes to snapshots
  rbd: client_mutex is never nested
  ceph: print inode number in __caps_issued_mask debugging messages
  ceph: just call get_session in __ceph_lookup_mds_session
  ceph: simplify arguments and return semantics of try_get_cap_refs
  ceph: fix comment over ceph_drop_caps_for_unlink
  ceph: move wait for mds request into helper function
  ceph: have ceph_mdsc_do_request call ceph_mdsc_submit_request
  ceph: after an MDS request, do callback and completions
  ceph: use pathlen values returned by set_request_path_attr
  ceph: use __getname/__putname in ceph_mdsc_build_path
  ceph: use ceph_mdsc_build_path instead of clone_dentry_name
  ceph: fix potential use-after-free in ceph_mdsc_build_path
  ceph: dump granular cap info in "caps" debugfs file
  ceph: make iterate_session_caps a public symbol
  ceph: fix NULL pointer deref when debugging is enabled
  ...
parents 2c45e7fb 00abf69d
Loading
Loading
Loading
Loading
+14 −10
Original line number Diff line number Diff line
@@ -934,7 +934,7 @@ static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
	struct rbd_client *rbdc;
	int ret;

	mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
	mutex_lock(&client_mutex);
	rbdc = rbd_client_find(ceph_opts);
	if (rbdc) {
		ceph_destroy_options(ceph_opts);
@@ -1326,7 +1326,7 @@ static void rbd_obj_zero_range(struct rbd_obj_request *obj_req, u32 off,
		zero_bvecs(&obj_req->bvec_pos, off, bytes);
		break;
	default:
		rbd_assert(0);
		BUG();
	}
}

@@ -1581,7 +1581,7 @@ static void rbd_obj_request_destroy(struct kref *kref)
		kfree(obj_request->bvec_pos.bvecs);
		break;
	default:
		rbd_assert(0);
		BUG();
	}

	kfree(obj_request->img_extents);
@@ -1781,7 +1781,7 @@ static void rbd_osd_req_setup_data(struct rbd_obj_request *obj_req, u32 which)
						    &obj_req->bvec_pos);
		break;
	default:
		rbd_assert(0);
		BUG();
	}
}

@@ -2036,7 +2036,7 @@ static int __rbd_img_fill_request(struct rbd_img_request *img_req)
			ret = rbd_obj_setup_zeroout(obj_req);
			break;
		default:
			rbd_assert(0);
			BUG();
		}
		if (ret < 0)
			return ret;
@@ -2383,7 +2383,7 @@ static int rbd_obj_read_from_parent(struct rbd_obj_request *obj_req)
						      &obj_req->bvec_pos);
			break;
		default:
			rbd_assert(0);
			BUG();
		}
	} else {
		ret = rbd_img_fill_from_bvecs(child_img_req,
@@ -2515,7 +2515,7 @@ static int rbd_obj_issue_copyup_ops(struct rbd_obj_request *obj_req, u32 bytes)
		num_osd_ops += count_zeroout_ops(obj_req);
		break;
	default:
		rbd_assert(0);
		BUG();
	}

	obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
@@ -2542,7 +2542,7 @@ static int rbd_obj_issue_copyup_ops(struct rbd_obj_request *obj_req, u32 bytes)
		__rbd_obj_setup_zeroout(obj_req, which);
		break;
	default:
		rbd_assert(0);
		BUG();
	}

	ret = ceph_osdc_alloc_messages(obj_req->osd_req, GFP_NOIO);
@@ -3842,8 +3842,12 @@ static void rbd_queue_workfn(struct work_struct *work)
		goto err_rq;
	}

	rbd_assert(op_type == OBJ_OP_READ ||
		   rbd_dev->spec->snap_id == CEPH_NOSNAP);
	if (op_type != OBJ_OP_READ && rbd_dev->spec->snap_id != CEPH_NOSNAP) {
		rbd_warn(rbd_dev, "%s on read-only snapshot",
			 obj_op_name(op_type));
		result = -EIO;
		goto err;
	}

	/*
	 * Quit early if the mapped snapshot no longer exists.  It's
+37 −56
Original line number Diff line number Diff line
@@ -892,8 +892,8 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch)
	int have = ci->i_snap_caps;

	if ((have & mask) == mask) {
		dout("__ceph_caps_issued_mask %p snap issued %s"
		     " (mask %s)\n", &ci->vfs_inode,
		dout("__ceph_caps_issued_mask ino 0x%lx snap issued %s"
		     " (mask %s)\n", ci->vfs_inode.i_ino,
		     ceph_cap_string(have),
		     ceph_cap_string(mask));
		return 1;
@@ -904,8 +904,8 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch)
		if (!__cap_is_valid(cap))
			continue;
		if ((cap->issued & mask) == mask) {
			dout("__ceph_caps_issued_mask %p cap %p issued %s"
			     " (mask %s)\n", &ci->vfs_inode, cap,
			dout("__ceph_caps_issued_mask ino 0x%lx cap %p issued %s"
			     " (mask %s)\n", ci->vfs_inode.i_ino, cap,
			     ceph_cap_string(cap->issued),
			     ceph_cap_string(mask));
			if (touch)
@@ -916,8 +916,8 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch)
		/* does a combination of caps satisfy mask? */
		have |= cap->issued;
		if ((have & mask) == mask) {
			dout("__ceph_caps_issued_mask %p combo issued %s"
			     " (mask %s)\n", &ci->vfs_inode,
			dout("__ceph_caps_issued_mask ino 0x%lx combo issued %s"
			     " (mask %s)\n", ci->vfs_inode.i_ino,
			     ceph_cap_string(cap->issued),
			     ceph_cap_string(mask));
			if (touch) {
@@ -2257,8 +2257,6 @@ int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	if (datasync)
		goto out;

	inode_lock(inode);

	dirty = try_flush_caps(inode, &flush_tid);
	dout("fsync dirty caps are %s\n", ceph_cap_string(dirty));

@@ -2273,7 +2271,6 @@ int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync)
		ret = wait_event_interruptible(ci->i_cap_wq,
					caps_are_flushed(inode, flush_tid));
	}
	inode_unlock(inode);
out:
	dout("fsync %p%s result=%d\n", inode, datasync ? " datasync" : "", ret);
	return ret;
@@ -2528,9 +2525,14 @@ static void __take_cap_refs(struct ceph_inode_info *ci, int got,
 * to (when applicable), and check against max_size here as well.
 * Note that caller is responsible for ensuring max_size increases are
 * requested from the MDS.
 *
 * Returns 0 if caps were not able to be acquired (yet), a 1 if they were,
 * or a negative error code.
 *
 * FIXME: how does a 0 return differ from -EAGAIN?
 */
static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
			    loff_t endoff, bool nonblock, int *got, int *err)
			    loff_t endoff, bool nonblock, int *got)
{
	struct inode *inode = &ci->vfs_inode;
	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
@@ -2550,8 +2552,7 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
	if ((file_wanted & need) != need) {
		dout("try_get_cap_refs need %s file_wanted %s, EBADF\n",
		     ceph_cap_string(need), ceph_cap_string(file_wanted));
		*err = -EBADF;
		ret = 1;
		ret = -EBADF;
		goto out_unlock;
	}

@@ -2572,10 +2573,8 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
		if (endoff >= 0 && endoff > (loff_t)ci->i_max_size) {
			dout("get_cap_refs %p endoff %llu > maxsize %llu\n",
			     inode, endoff, ci->i_max_size);
			if (endoff > ci->i_requested_max_size) {
				*err = -EAGAIN;
				ret = 1;
			}
			if (endoff > ci->i_requested_max_size)
				ret = -EAGAIN;
			goto out_unlock;
		}
		/*
@@ -2610,8 +2609,7 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
					 * task isn't in TASK_RUNNING state
					 */
					if (nonblock) {
						*err = -EAGAIN;
						ret = 1;
						ret = -EAGAIN;
						goto out_unlock;
					}

@@ -2640,8 +2638,7 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
		if (session_readonly) {
			dout("get_cap_refs %p needed %s but mds%d readonly\n",
			     inode, ceph_cap_string(need), ci->i_auth_cap->mds);
			*err = -EROFS;
			ret = 1;
			ret = -EROFS;
			goto out_unlock;
		}

@@ -2650,16 +2647,14 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
			if (READ_ONCE(mdsc->fsc->mount_state) ==
			    CEPH_MOUNT_SHUTDOWN) {
				dout("get_cap_refs %p forced umount\n", inode);
				*err = -EIO;
				ret = 1;
				ret = -EIO;
				goto out_unlock;
			}
			mds_wanted = __ceph_caps_mds_wanted(ci, false);
			if (need & ~(mds_wanted & need)) {
				dout("get_cap_refs %p caps were dropped"
				     " (session killed?)\n", inode);
				*err = -ESTALE;
				ret = 1;
				ret = -ESTALE;
				goto out_unlock;
			}
			if (!(file_wanted & ~mds_wanted))
@@ -2710,7 +2705,7 @@ static void check_max_size(struct inode *inode, loff_t endoff)
int ceph_try_get_caps(struct ceph_inode_info *ci, int need, int want,
		      bool nonblock, int *got)
{
	int ret, err = 0;
	int ret;

	BUG_ON(need & ~CEPH_CAP_FILE_RD);
	BUG_ON(want & ~(CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO|CEPH_CAP_FILE_SHARED));
@@ -2718,15 +2713,8 @@ int ceph_try_get_caps(struct ceph_inode_info *ci, int need, int want,
	if (ret < 0)
		return ret;

	ret = try_get_cap_refs(ci, need, want, 0, nonblock, got, &err);
	if (ret) {
		if (err == -EAGAIN) {
			ret = 0;
		} else if (err < 0) {
			ret = err;
		}
	}
	return ret;
	ret = try_get_cap_refs(ci, need, want, 0, nonblock, got);
	return ret == -EAGAIN ? 0 : ret;
}

/*
@@ -2737,7 +2725,7 @@ int ceph_try_get_caps(struct ceph_inode_info *ci, int need, int want,
int ceph_get_caps(struct ceph_inode_info *ci, int need, int want,
		  loff_t endoff, int *got, struct page **pinned_page)
{
	int _got, ret, err = 0;
	int _got, ret;

	ret = ceph_pool_perm_check(ci, need);
	if (ret < 0)
@@ -2747,21 +2735,19 @@ int ceph_get_caps(struct ceph_inode_info *ci, int need, int want,
		if (endoff > 0)
			check_max_size(&ci->vfs_inode, endoff);

		err = 0;
		_got = 0;
		ret = try_get_cap_refs(ci, need, want, endoff,
				       false, &_got, &err);
		if (ret) {
			if (err == -EAGAIN)
				       false, &_got);
		if (ret == -EAGAIN) {
			continue;
			if (err < 0)
				ret = err;
		} else {
		} else if (!ret) {
			int err;

			DEFINE_WAIT_FUNC(wait, woken_wake_function);
			add_wait_queue(&ci->i_cap_wq, &wait);

			while (!try_get_cap_refs(ci, need, want, endoff,
						 true, &_got, &err)) {
			while (!(err = try_get_cap_refs(ci, need, want, endoff,
							true, &_got))) {
				if (signal_pending(current)) {
					ret = -ERESTARTSYS;
					break;
@@ -2770,19 +2756,14 @@ int ceph_get_caps(struct ceph_inode_info *ci, int need, int want,
			}

			remove_wait_queue(&ci->i_cap_wq, &wait);

			if (err == -EAGAIN)
				continue;
			if (err < 0)
				ret = err;
		}
		if (ret < 0) {
			if (err == -ESTALE) {
		if (ret == -ESTALE) {
			/* session was killed, try renew caps */
			ret = ceph_renew_caps(&ci->vfs_inode);
			if (ret == 0)
				continue;
			}
			return ret;
		}

@@ -4099,7 +4080,7 @@ void ceph_put_fmode(struct ceph_inode_info *ci, int fmode)
}

/*
 * For a soon-to-be unlinked file, drop the AUTH_RDCACHE caps. If it
 * For a soon-to-be unlinked file, drop the LINK caps. If it
 * looks like the link count will hit 0, drop any other caps (other
 * than PIN) we don't specifically want (due to the file still being
 * open).
+35 −5
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static int mdsmap_show(struct seq_file *s, void *p)
		struct ceph_entity_addr *addr = &mdsmap->m_info[i].addr;
		int state = mdsmap->m_info[i].state;
		seq_printf(s, "\tmds%d\t%s\t(%s)\n", i,
			       ceph_pr_addr(&addr->in_addr),
			       ceph_pr_addr(addr),
			       ceph_mds_state_name(state));
	}
	return 0;
@@ -88,7 +88,7 @@ static int mdsc_show(struct seq_file *s, void *p)
				   req->r_dentry,
				   path ? path : "");
			spin_unlock(&req->r_dentry->d_lock);
			kfree(path);
			ceph_mdsc_free_path(path, pathlen);
		} else if (req->r_path1) {
			seq_printf(s, " #%llx/%s", req->r_ino1.ino,
				   req->r_path1);
@@ -108,7 +108,7 @@ static int mdsc_show(struct seq_file *s, void *p)
				   req->r_old_dentry,
				   path ? path : "");
			spin_unlock(&req->r_old_dentry->d_lock);
			kfree(path);
			ceph_mdsc_free_path(path, pathlen);
		} else if (req->r_path2 && req->r_op != CEPH_MDS_OP_SYMLINK) {
			if (req->r_ino2.ino)
				seq_printf(s, " #%llx/%s", req->r_ino2.ino,
@@ -124,18 +124,48 @@ static int mdsc_show(struct seq_file *s, void *p)
	return 0;
}

static int caps_show_cb(struct inode *inode, struct ceph_cap *cap, void *p)
{
	struct seq_file *s = p;

	seq_printf(s, "0x%-17lx%-17s%-17s\n", inode->i_ino,
		   ceph_cap_string(cap->issued),
		   ceph_cap_string(cap->implemented));
	return 0;
}

static int caps_show(struct seq_file *s, void *p)
{
	struct ceph_fs_client *fsc = s->private;
	int total, avail, used, reserved, min;
	struct ceph_mds_client *mdsc = fsc->mdsc;
	int total, avail, used, reserved, min, i;

	ceph_reservation_status(fsc, &total, &avail, &used, &reserved, &min);
	seq_printf(s, "total\t\t%d\n"
		   "avail\t\t%d\n"
		   "used\t\t%d\n"
		   "reserved\t%d\n"
		   "min\t%d\n",
		   "min\t\t%d\n\n",
		   total, avail, used, reserved, min);
	seq_printf(s, "ino                issued           implemented\n");
	seq_printf(s, "-----------------------------------------------\n");

	mutex_lock(&mdsc->mutex);
	for (i = 0; i < mdsc->max_sessions; i++) {
		struct ceph_mds_session *session;

		session = __ceph_lookup_mds_session(mdsc, i);
		if (!session)
			continue;
		mutex_unlock(&mdsc->mutex);
		mutex_lock(&session->s_mutex);
		ceph_iterate_session_caps(session, caps_show_cb, s);
		mutex_unlock(&session->s_mutex);
		ceph_put_mds_session(session);
		mutex_lock(&mdsc->mutex);
	}
	mutex_unlock(&mdsc->mutex);

	return 0;
}

+331 −25
Original line number Diff line number Diff line
@@ -22,18 +22,77 @@ struct ceph_nfs_confh {
	u64 ino, parent_ino;
} __attribute__ ((packed));

/*
 * fh for snapped inode
 */
struct ceph_nfs_snapfh {
	u64 ino;
	u64 snapid;
	u64 parent_ino;
	u32 hash;
} __attribute__ ((packed));

static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
			      struct inode *parent_inode)
{
	const static int snap_handle_length =
		sizeof(struct ceph_nfs_snapfh) >> 2;
	struct ceph_nfs_snapfh *sfh = (void *)rawfh;
	u64 snapid = ceph_snap(inode);
	int ret;
	bool no_parent = true;

	if (*max_len < snap_handle_length) {
		*max_len = snap_handle_length;
		ret = FILEID_INVALID;
		goto out;
	}

	ret =  -EINVAL;
	if (snapid != CEPH_SNAPDIR) {
		struct inode *dir;
		struct dentry *dentry = d_find_alias(inode);
		if (!dentry)
			goto out;

		rcu_read_lock();
		dir = d_inode_rcu(dentry->d_parent);
		if (ceph_snap(dir) != CEPH_SNAPDIR) {
			sfh->parent_ino = ceph_ino(dir);
			sfh->hash = ceph_dentry_hash(dir, dentry);
			no_parent = false;
		}
		rcu_read_unlock();
		dput(dentry);
	}

	if (no_parent) {
		if (!S_ISDIR(inode->i_mode))
			goto out;
		sfh->parent_ino = sfh->ino;
		sfh->hash = 0;
	}
	sfh->ino = ceph_ino(inode);
	sfh->snapid = snapid;

	*max_len = snap_handle_length;
	ret = FILEID_BTRFS_WITH_PARENT;
out:
	dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode), ret);
	return ret;
}

static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
			  struct inode *parent_inode)
{
	const static int handle_length =
		sizeof(struct ceph_nfs_fh) >> 2;
	const static int connected_handle_length =
		sizeof(struct ceph_nfs_confh) >> 2;
	int type;
	struct ceph_nfs_fh *fh = (void *)rawfh;
	struct ceph_nfs_confh *cfh = (void *)rawfh;
	int connected_handle_length = sizeof(*cfh)/4;
	int handle_length = sizeof(*fh)/4;

	/* don't re-export snaps */
	if (ceph_snap(inode) != CEPH_NOSNAP)
		return -EINVAL;
		return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);

	if (parent_inode && (*max_len < connected_handle_length)) {
		*max_len = connected_handle_length;
@@ -44,6 +103,7 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
	}

	if (parent_inode) {
		struct ceph_nfs_confh *cfh = (void *)rawfh;
		dout("encode_fh %llx with parent %llx\n",
		     ceph_ino(inode), ceph_ino(parent_inode));
		cfh->ino = ceph_ino(inode);
@@ -51,6 +111,7 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
		*max_len = connected_handle_length;
		type = FILEID_INO32_GEN_PARENT;
	} else {
		struct ceph_nfs_fh *fh = (void *)rawfh;
		dout("encode_fh %llx\n", ceph_ino(inode));
		fh->ino = ceph_ino(inode);
		*max_len = handle_length;
@@ -59,7 +120,7 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
	return type;
}

static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
{
	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
	struct inode *inode;
@@ -81,7 +142,7 @@ static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
		mask = CEPH_STAT_CAP_INODE;
		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
			mask |= CEPH_CAP_XATTR_SHARED;
		req->r_args.getattr.mask = cpu_to_le32(mask);
		req->r_args.lookupino.mask = cpu_to_le32(mask);

		req->r_ino1 = vino;
		req->r_num_caps = 1;
@@ -91,14 +152,112 @@ static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
			ihold(inode);
		ceph_mdsc_put_request(req);
		if (!inode)
			return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
	}
	return inode;
}

struct inode *ceph_lookup_inode(struct super_block *sb, u64 ino)
{
	struct inode *inode = __lookup_inode(sb, ino);
	if (IS_ERR(inode))
		return inode;
	if (inode->i_nlink == 0) {
		iput(inode);
		return ERR_PTR(-ESTALE);
	}
	return inode;
}

static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
{
	struct inode *inode = __lookup_inode(sb, ino);
	if (IS_ERR(inode))
		return ERR_CAST(inode);
	if (inode->i_nlink == 0) {
		iput(inode);
		return ERR_PTR(-ESTALE);
	}
	return d_obtain_alias(inode);
}

static struct dentry *__snapfh_to_dentry(struct super_block *sb,
					  struct ceph_nfs_snapfh *sfh,
					  bool want_parent)
{
	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
	struct ceph_mds_request *req;
	struct inode *inode;
	struct ceph_vino vino;
	int mask;
	int err;
	bool unlinked = false;

	if (want_parent) {
		vino.ino = sfh->parent_ino;
		if (sfh->snapid == CEPH_SNAPDIR)
			vino.snap = CEPH_NOSNAP;
		else if (sfh->ino == sfh->parent_ino)
			vino.snap = CEPH_SNAPDIR;
		else
			vino.snap = sfh->snapid;
	} else {
		vino.ino = sfh->ino;
		vino.snap = sfh->snapid;
	}
	inode = ceph_find_inode(sb, vino);
	if (inode)
		return d_obtain_alias(inode);

	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
				       USE_ANY_MDS);
	if (IS_ERR(req))
		return ERR_CAST(req);

	mask = CEPH_STAT_CAP_INODE;
	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
		mask |= CEPH_CAP_XATTR_SHARED;
	req->r_args.lookupino.mask = cpu_to_le32(mask);
	if (vino.snap < CEPH_NOSNAP) {
		req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
		if (!want_parent && sfh->ino != sfh->parent_ino) {
			req->r_args.lookupino.parent =
					cpu_to_le64(sfh->parent_ino);
			req->r_args.lookupino.hash =
					cpu_to_le32(sfh->hash);
		}
	}

	req->r_ino1 = vino;
	req->r_num_caps = 1;
	err = ceph_mdsc_do_request(mdsc, NULL, req);
	inode = req->r_target_inode;
	if (inode) {
		if (vino.snap == CEPH_SNAPDIR) {
			if (inode->i_nlink == 0)
				unlinked = true;
			inode = ceph_get_snapdir(inode);
		} else if (ceph_snap(inode) == vino.snap) {
			ihold(inode);
		} else {
			/* mds does not support lookup snapped inode */
			err = -EOPNOTSUPP;
			inode = NULL;
		}
	}
	ceph_mdsc_put_request(req);

	if (want_parent) {
		dout("snapfh_to_parent %llx.%llx\n err=%d\n",
		     vino.ino, vino.snap, err);
	} else {
		dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
		      vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
	}
	if (!inode)
		return ERR_PTR(-ESTALE);
	/* see comments in ceph_get_parent() */
	return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
}

/*
@@ -110,6 +269,11 @@ static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
{
	struct ceph_nfs_fh *fh = (void *)fid->raw;

	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
		return __snapfh_to_dentry(sb, sfh, false);
	}

	if (fh_type != FILEID_INO32_GEN  &&
	    fh_type != FILEID_INO32_GEN_PARENT)
		return NULL;
@@ -163,13 +327,49 @@ static struct dentry *__get_parent(struct super_block *sb,

static struct dentry *ceph_get_parent(struct dentry *child)
{
	/* don't re-export snaps */
	if (ceph_snap(d_inode(child)) != CEPH_NOSNAP)
		return ERR_PTR(-EINVAL);

	dout("get_parent %p ino %llx.%llx\n",
	     child, ceph_vinop(d_inode(child)));
	return __get_parent(child->d_sb, child, 0);
	struct inode *inode = d_inode(child);
	struct dentry *dn;

	if (ceph_snap(inode) != CEPH_NOSNAP) {
		struct inode* dir;
		bool unlinked = false;
		/* do not support non-directory */
		if (!d_is_dir(child)) {
			dn = ERR_PTR(-EINVAL);
			goto out;
		}
		dir = __lookup_inode(inode->i_sb, ceph_ino(inode));
		if (IS_ERR(dir)) {
			dn = ERR_CAST(dir);
			goto out;
		}
		/* There can be multiple paths to access snapped inode.
		 * For simplicity, treat snapdir of head inode as parent */
		if (ceph_snap(inode) != CEPH_SNAPDIR) {
			struct inode *snapdir = ceph_get_snapdir(dir);
			if (dir->i_nlink == 0)
				unlinked = true;
			iput(dir);
			if (IS_ERR(snapdir)) {
				dn = ERR_CAST(snapdir);
				goto out;
			}
			dir = snapdir;
		}
		/* If directory has already been deleted, futher get_parent
		 * will fail. Do not mark snapdir dentry as disconnected,
		 * this prevent exportfs from doing futher get_parent. */
		if (unlinked)
			dn = d_obtain_root(dir);
		else
			dn = d_obtain_alias(dir);
	} else {
		dn = __get_parent(child->d_sb, child, 0);
	}
out:
	dout("get_parent %p ino %llx.%llx err=%ld\n",
	     child, ceph_vinop(inode), (IS_ERR(dn) ? PTR_ERR(dn) : 0));
	return dn;
}

/*
@@ -182,6 +382,11 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb,
	struct ceph_nfs_confh *cfh = (void *)fid->raw;
	struct dentry *dentry;

	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
		return __snapfh_to_dentry(sb, sfh, true);
	}

	if (fh_type != FILEID_INO32_GEN_PARENT)
		return NULL;
	if (fh_len < sizeof(*cfh) / 4)
@@ -194,14 +399,115 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb,
	return dentry;
}

static int __get_snap_name(struct dentry *parent, char *name,
			   struct dentry *child)
{
	struct inode *inode = d_inode(child);
	struct inode *dir = d_inode(parent);
	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
	struct ceph_mds_request *req = NULL;
	char *last_name = NULL;
	unsigned next_offset = 2;
	int err = -EINVAL;

	if (ceph_ino(inode) != ceph_ino(dir))
		goto out;
	if (ceph_snap(inode) == CEPH_SNAPDIR) {
		if (ceph_snap(dir) == CEPH_NOSNAP) {
			strcpy(name, fsc->mount_options->snapdir_name);
			err = 0;
		}
		goto out;
	}
	if (ceph_snap(dir) != CEPH_SNAPDIR)
		goto out;

	while (1) {
		struct ceph_mds_reply_info_parsed *rinfo;
		struct ceph_mds_reply_dir_entry *rde;
		int i;

		req = ceph_mdsc_create_request(fsc->mdsc, CEPH_MDS_OP_LSSNAP,
					       USE_AUTH_MDS);
		if (IS_ERR(req)) {
			err = PTR_ERR(req);
			req = NULL;
			goto out;
		}
		err = ceph_alloc_readdir_reply_buffer(req, inode);
		if (err)
			goto out;

		req->r_direct_mode = USE_AUTH_MDS;
		req->r_readdir_offset = next_offset;
		req->r_args.readdir.flags =
				cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS);
		if (last_name) {
			req->r_path2 = last_name;
			last_name = NULL;
		}

		req->r_inode = dir;
		ihold(dir);
		req->r_dentry = dget(parent);

		inode_lock(dir);
		err = ceph_mdsc_do_request(fsc->mdsc, NULL, req);
		inode_unlock(dir);

		if (err < 0)
			goto out;

		 rinfo = &req->r_reply_info;
		 for (i = 0; i < rinfo->dir_nr; i++) {
			 rde = rinfo->dir_entries + i;
			 BUG_ON(!rde->inode.in);
			 if (ceph_snap(inode) ==
			     le64_to_cpu(rde->inode.in->snapid)) {
				 memcpy(name, rde->name, rde->name_len);
				 name[rde->name_len] = '\0';
				 err = 0;
				 goto out;
			 }
		 }

		 if (rinfo->dir_end)
			 break;

		 BUG_ON(rinfo->dir_nr <= 0);
		 rde = rinfo->dir_entries + (rinfo->dir_nr - 1);
		 next_offset += rinfo->dir_nr;
		 last_name = kstrndup(rde->name, rde->name_len, GFP_KERNEL);
		 if (!last_name) {
			 err = -ENOMEM;
			 goto out;
		 }

		 ceph_mdsc_put_request(req);
		 req = NULL;
	}
	err = -ENOENT;
out:
	if (req)
		ceph_mdsc_put_request(req);
	kfree(last_name);
	dout("get_snap_name %p ino %llx.%llx err=%d\n",
	     child, ceph_vinop(inode), err);
	return err;
}

static int ceph_get_name(struct dentry *parent, char *name,
			 struct dentry *child)
{
	struct ceph_mds_client *mdsc;
	struct ceph_mds_request *req;
	struct inode *inode = d_inode(child);
	int err;

	mdsc = ceph_inode_to_client(d_inode(child))->mdsc;
	if (ceph_snap(inode) != CEPH_NOSNAP)
		return __get_snap_name(parent, name, child);

	mdsc = ceph_inode_to_client(inode)->mdsc;
	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
				       USE_ANY_MDS);
	if (IS_ERR(req))
@@ -209,8 +515,8 @@ static int ceph_get_name(struct dentry *parent, char *name,

	inode_lock(d_inode(parent));

	req->r_inode = d_inode(child);
	ihold(d_inode(child));
	req->r_inode = inode;
	ihold(inode);
	req->r_ino2 = ceph_vino(d_inode(parent));
	req->r_parent = d_inode(parent);
	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
@@ -224,10 +530,10 @@ static int ceph_get_name(struct dentry *parent, char *name,
		memcpy(name, rinfo->dname, rinfo->dname_len);
		name[rinfo->dname_len] = 0;
		dout("get_name %p ino %llx.%llx name %s\n",
		     child, ceph_vinop(d_inode(child)), name);
		     child, ceph_vinop(inode), name);
	} else {
		dout("get_name %p ino %llx.%llx err %d\n",
		     child, ceph_vinop(d_inode(child)), err);
		     child, ceph_vinop(inode), err);
	}

	ceph_mdsc_put_request(req);
+1 −1
Original line number Diff line number Diff line
@@ -929,7 +929,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter,

	dout("sync_direct_%s on file %p %lld~%u snapc %p seq %lld\n",
	     (write ? "write" : "read"), file, pos, (unsigned)count,
	     snapc, snapc->seq);
	     snapc, snapc ? snapc->seq : 0);

	ret = filemap_write_and_wait_range(inode->i_mapping,
					   pos, pos + count - 1);
Loading